From d1a038be6eb6223e044c1ac5c57923d28e7d47b5 Mon Sep 17 00:00:00 2001 From: starwarfan <519125404@qq.com> Date: Thu, 14 May 2026 15:20:49 +0800 Subject: [PATCH 01/19] test(evm): add legacy CALL repro fixtures and runner Add a standalone DTVM-only test target plus extracted fixtures for the historical legacy CALL regression so it can be replayed without external chain snapshots. --- src/tests/CMakeLists.txt | 21 ++ src/tests/evm_legacy_call_repro_tests.cpp | 245 ++++++++++++++++++ .../evm/fixtures/legacy_call_repro/README.md | 18 ++ .../legacy_call_repro/block_254277_tx_0.json | 61 +++++ .../legacy_call_repro/block_254297_tx_0.json | 71 +++++ .../evm/fixtures/legacy_call_repro/cases.json | 13 + .../fixtures/legacy_call_repro/schema.json | 198 ++++++++++++++ 7 files changed, 627 insertions(+) create mode 100644 src/tests/evm_legacy_call_repro_tests.cpp create mode 100644 tests/evm/fixtures/legacy_call_repro/README.md create mode 100644 tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json create mode 100644 tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json create mode 100644 tests/evm/fixtures/legacy_call_repro/cases.json create mode 100644 tests/evm/fixtures/legacy_call_repro/schema.json diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 973ef3a2f..c87c5f31c 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -71,6 +71,7 @@ if(ZEN_ENABLE_SPEC_TEST) evmStateTests evm_precompiles.hpp evm_state_tests.cpp evm_test_fixtures.cpp evm_test_helpers.cpp ) + add_executable(evmLegacyCallReproTests evm_legacy_call_repro_tests.cpp) # Only build evmFallbackExecutionTests if dtvmapi library is available if(ZEN_ENABLE_LIBEVM) add_executable(evmFallbackExecutionTests evm_fallback_execution_tests.cpp) @@ -141,6 +142,11 @@ if(ZEN_ENABLE_SPEC_TEST) PRIVATE dtvmcore rapidjson mpt gtest_main -fsanitize=address PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_link_libraries( + evmLegacyCallReproTests + PRIVATE dtvmcore rapidjson gtest_main -fsanitize=address + PUBLIC ${GTEST_BOTH_LIBRARIES} + ) if(ZEN_ENABLE_LIBEVM) target_link_libraries( evmFallbackExecutionTests @@ -209,6 +215,15 @@ if(ZEN_ENABLE_SPEC_TEST) -static-libasan PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_link_libraries( + evmLegacyCallReproTests + PRIVATE dtvmcore + rapidjson + gtest_main + -fsanitize=address + -static-libasan + PUBLIC ${GTEST_BOTH_LIBRARIES} + ) if(ZEN_ENABLE_LIBEVM) target_link_libraries( evmFallbackExecutionTests @@ -262,6 +277,11 @@ if(ZEN_ENABLE_SPEC_TEST) PRIVATE dtvmcore rapidjson mpt gtest_main PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_link_libraries( + evmLegacyCallReproTests + PRIVATE dtvmcore rapidjson gtest_main + PUBLIC ${GTEST_BOTH_LIBRARIES} + ) if(ZEN_ENABLE_LIBEVM) target_link_libraries( evmFallbackExecutionTests @@ -302,6 +322,7 @@ if(ZEN_ENABLE_SPEC_TEST) -P ${CMAKE_CURRENT_SOURCE_DIR}/RunSpecTests.cmake ) add_test(NAME evmStateTests COMMAND evmStateTests) + add_test(NAME evmLegacyCallReproTests COMMAND evmLegacyCallReproTests) if(ZEN_ENABLE_LIBEVM) add_test(NAME evmFallbackExecutionTests COMMAND evmFallbackExecutionTests) add_test(NAME evmModuleCacheTests COMMAND evmModuleCacheTests) diff --git a/src/tests/evm_legacy_call_repro_tests.cpp b/src/tests/evm_legacy_call_repro_tests.cpp new file mode 100644 index 000000000..2eaebdb40 --- /dev/null +++ b/src/tests/evm_legacy_call_repro_tests.cpp @@ -0,0 +1,245 @@ +// Copyright (C) 2026 the DTVM authors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "evm/evm.h" +#include "evm_test_host.hpp" +#include "runtime/runtime.h" +#include "utils/evm.h" +#include + +using namespace zen; +using namespace zen::evm; +using namespace zen::runtime; + +namespace { + +struct ParsedFixture { + std::string CaseName; + std::string FixturePath; + evmc_revision Revision = EVMC_FRONTIER; + evmc_tx_context TxContext{}; + evmc_message Message{}; + uint64_t GasLimit = 0; + uint64_t IntrinsicGas = 0; + evmc::bytes Input; + evmc::bytes Bytecode; + std::vector Accounts; + std::string ExpectedStatus; + uint64_t ExpectedTxGas = 0; + uint64_t ExpectedDTVMInterpGas = 0; + uint64_t ExpectedDTVMMultipassGas = 0; +}; + +evmc_revision parseRevision(const std::string &Revision) { + if (Revision == "EVMC_FRONTIER") + return EVMC_FRONTIER; + if (Revision == "EVMC_TANGERINE_WHISTLE") + return EVMC_TANGERINE_WHISTLE; + if (Revision == "EVMC_SPURIOUS_DRAGON") + return EVMC_SPURIOUS_DRAGON; + if (Revision == "EVMC_BYZANTIUM") + return EVMC_BYZANTIUM; + if (Revision == "EVMC_CONSTANTINOPLE") + return EVMC_CONSTANTINOPLE; + if (Revision == "EVMC_PETERSBURG") + return EVMC_PETERSBURG; + if (Revision == "EVMC_ISTANBUL") + return EVMC_ISTANBUL; + if (Revision == "EVMC_BERLIN") + return EVMC_BERLIN; + if (Revision == "EVMC_LONDON") + return EVMC_LONDON; + if (Revision == "EVMC_PARIS") + return EVMC_PARIS; + if (Revision == "EVMC_SHANGHAI") + return EVMC_SHANGHAI; + if (Revision == "EVMC_CANCUN") + return EVMC_CANCUN; + return EVMC_FRONTIER; +} + +std::filesystem::path getLegacyReproFixtureDir() { + return std::filesystem::path(__FILE__).parent_path() / + std::filesystem::path("../../tests/evm/fixtures/legacy_call_repro"); +} + +ParsedFixture loadFixture(const std::filesystem::path &Path) { + std::ifstream File(Path); + EXPECT_TRUE(File.is_open()) << "failed to open fixture: " << Path.string(); + + rapidjson::IStreamWrapper ISW(File); + rapidjson::Document Doc; + Doc.ParseStream(ISW); + EXPECT_FALSE(Doc.HasParseError()) << "parse error in fixture: " << Path.string(); + EXPECT_TRUE(Doc.IsObject()) << "fixture root must be object: " << Path.string(); + + ParsedFixture Fixture; + Fixture.FixturePath = Path.string(); + Fixture.CaseName = Doc["case_name"].GetString(); + Fixture.Revision = parseRevision(Doc["revision"].GetString()); + + const auto &Tx = Doc["tx"]; + const auto &Env = Doc["env"]; + const auto &Prestate = Doc["prestate"]; + const auto &Expected = Doc["expected"]; + + const std::string From = Tx["from"].GetString(); + const std::string To = Tx["to"].GetString(); + const std::string InputHex = Tx["input"].GetString(); + + Fixture.GasLimit = Tx["gas_limit"].GetUint64(); + Fixture.Input = zen::utils::hexToBytes(InputHex); + + Fixture.TxContext.tx_gas_price = + zen::utils::parseUint256(Tx["gas_price"].GetString()); + Fixture.TxContext.block_number = Env["block_number"].GetUint64(); + Fixture.TxContext.block_timestamp = Env["block_timestamp"].GetUint64(); + Fixture.TxContext.block_coinbase = + zen::utils::parseAddress(Env["block_coinbase"].GetString()); + Fixture.TxContext.block_prev_randao = + zen::utils::parseUint256(Env["block_prev_randao"].GetString()); + Fixture.TxContext.block_gas_limit = Env["block_gas_limit"].GetUint64(); + Fixture.TxContext.block_base_fee = + zen::utils::parseUint256(Env["block_base_fee"].GetString()); + Fixture.TxContext.tx_origin = + zen::utils::parseAddress(Env["tx_origin"].GetString()); + + Fixture.Message = {}; + Fixture.Message.kind = EVMC_CALL; + Fixture.Message.flags = 0u; + Fixture.Message.depth = 0; + Fixture.Message.gas = static_cast(Fixture.GasLimit); + Fixture.Message.recipient = zen::utils::parseAddress(To); + Fixture.Message.sender = zen::utils::parseAddress(From); + Fixture.Message.value = zen::utils::parseUint256(Tx["value"].GetString()); + Fixture.Message.code = nullptr; + Fixture.Message.code_size = 0; + Fixture.Message.input_data = + Fixture.Input.empty() ? nullptr : Fixture.Input.data(); + Fixture.Message.input_size = Fixture.Input.size(); + + for (auto It = Prestate.MemberBegin(); It != Prestate.MemberEnd(); ++It) { + const std::string AddressStr = It->name.GetString(); + const auto &AccountVal = It->value; + + ZenMockedEVMHost::AccountInitEntry Entry; + Entry.Address = zen::utils::parseAddress(AddressStr); + Entry.Account.balance = + zen::utils::parseUint256(AccountVal["balance"].GetString()); + Entry.Account.nonce = AccountVal["nonce"].GetUint64(); + Entry.Account.code = zen::utils::hexToBytes(AccountVal["code"].GetString()); + + const auto &Storage = AccountVal["storage"]; + for (auto Sit = Storage.MemberBegin(); Sit != Storage.MemberEnd(); ++Sit) { + evmc::StorageValue SV{}; + SV.current = zen::utils::parseBytes32(Sit->value.GetString()); + Entry.Account.storage[zen::utils::parseBytes32(Sit->name.GetString())] = SV; + } + + if (AddressStr == To) { + Fixture.Bytecode = Entry.Account.code; + } + + Fixture.Accounts.push_back(std::move(Entry)); + } + + Fixture.ExpectedStatus = Expected["status"].GetString(); + Fixture.ExpectedTxGas = Expected["tx_gas"].GetUint64(); + Fixture.ExpectedDTVMInterpGas = + Expected.HasMember("dtvm_interpreter_gas") + ? Expected["dtvm_interpreter_gas"].GetUint64() + : Fixture.ExpectedTxGas; + Fixture.ExpectedDTVMMultipassGas = + Expected.HasMember("dtvm_multipass_gas") + ? Expected["dtvm_multipass_gas"].GetUint64() + : Fixture.ExpectedTxGas; + Fixture.IntrinsicGas = zen::utils::computeIntrinsicGas( + Fixture.Revision, EVMC_CALL, Fixture.Message.input_data, + Fixture.Message.input_size); + + return Fixture; +} + +ZenMockedEVMHost::TransactionExecutionResult runFixture( + const ParsedFixture &Fixture, common::RunMode Mode) { + RuntimeConfig Config; + Config.Format = common::InputFormat::EVM; + Config.Mode = Mode; + Config.EnableEvmGasMetering = true; + + auto Host = std::make_unique(); + Host->loadInitialState(Fixture.TxContext, Fixture.Accounts, true); + auto RT = Runtime::newEVMRuntime(Config, Host.get()); + EXPECT_TRUE(RT != nullptr); + Host->setRuntime(RT.get()); + + ZenMockedEVMHost::TransactionExecutionConfig ExecConfig; + ExecConfig.ModuleName = Fixture.CaseName + "-" + + (Mode == common::RunMode::InterpMode ? "interp" + : "multipass"); + ExecConfig.Bytecode = reinterpret_cast(Fixture.Bytecode.data()); + ExecConfig.BytecodeSize = Fixture.Bytecode.size(); + ExecConfig.Message = Fixture.Message; + ExecConfig.GasLimit = Fixture.GasLimit; + ExecConfig.IntrinsicGas = Fixture.IntrinsicGas; + ExecConfig.Revision = Fixture.Revision; + + return Host->executeTransaction(ExecConfig); +} + +void assertExpectedStatus(const std::string &ExpectedStatus, + const evmc_status_code ActualStatus) { + if (ExpectedStatus == "success") { + EXPECT_EQ(ActualStatus, EVMC_SUCCESS); + return; + } + if (ExpectedStatus == "revert") { + EXPECT_EQ(ActualStatus, EVMC_REVERT); + return; + } + EXPECT_NE(ActualStatus, EVMC_SUCCESS); +} + +} // namespace + +TEST(EVMLegacyCallReproTest, ExecuteFixturesInInterpreterAndMultipass) { + const auto FixtureDir = getLegacyReproFixtureDir(); + const std::vector> FixtureFiles = { + {"block_254277_tx_0.json", 57956}, + {"block_254297_tx_0.json", 94849}, + }; + + for (const auto &[Name, CanonicalTxGas] : FixtureFiles) { + SCOPED_TRACE(Name); + const ParsedFixture Fixture = loadFixture(FixtureDir / Name); + EXPECT_EQ(Fixture.ExpectedTxGas, CanonicalTxGas); + + { + auto Result = runFixture(Fixture, common::RunMode::InterpMode); + ASSERT_TRUE(Result.Success) << Result.ErrorMessage; + assertExpectedStatus(Fixture.ExpectedStatus, Result.Status); + EXPECT_EQ(Result.GasCharged, Fixture.ExpectedDTVMInterpGas) + << "fixture=" << Fixture.FixturePath << " mode=interpreter"; + } + +#ifdef ZEN_ENABLE_MULTIPASS_JIT + { + auto Result = runFixture(Fixture, common::RunMode::MultipassMode); + ASSERT_TRUE(Result.Success) << Result.ErrorMessage; + assertExpectedStatus(Fixture.ExpectedStatus, Result.Status); + EXPECT_EQ(Result.GasCharged, Fixture.ExpectedDTVMMultipassGas) + << "fixture=" << Fixture.FixturePath << " mode=multipass"; + } +#endif + } +} diff --git a/tests/evm/fixtures/legacy_call_repro/README.md b/tests/evm/fixtures/legacy_call_repro/README.md new file mode 100644 index 000000000..d786a443a --- /dev/null +++ b/tests/evm/fixtures/legacy_call_repro/README.md @@ -0,0 +1,18 @@ +# Legacy CALL Repro Fixture + +This directory stores standalone repro fixtures for the historical legacy CALL +gas divergence: + +- `block 254277 tx 0` (accident path) +- `block 254297 tx 0` (guard path) + +Each case file is self-contained and includes: + +- `revision` +- `tx` +- `env` +- `prestate` (touched accounts with nonce/balance/code/storage) +- `expected` (`status` and `tx_gas`) + +Use `tools/extract_legacy_call_repro.sh` to regenerate fixtures from live chain +data. diff --git a/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json b/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json new file mode 100644 index 000000000..f4785efea --- /dev/null +++ b/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json @@ -0,0 +1,61 @@ +{ + "case_name": "legacy_call_creation_cost_block_254277_tx0", + "chain_id": 1, + "block_number": 254277, + "tx_index": 0, + "tx_hash": "0x65ee2777a2c5abbd266dbbcb68a02eab73eef913d7106bc9b7cfc2cbabc883d7", + "revision": "EVMC_FRONTIER", + "tx": { + "kind": "call", + "from": "0x26588a9301b0428d95e6fc3a5024fce8bec12d51", + "to": "0xd2826004ee7c528bc50bdcecbd22226c0a754722", + "nonce": 5, + "gas_limit": 90000, + "gas_price": "0xba43b7400", + "value": "0x0", + "input": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8" + }, + "env": { + "block_number": 254277, + "block_timestamp": 1442610103, + "block_coinbase": "0xc8fea4ab769162f6f7eafb8c2bd49734a03c60ef", + "block_prev_randao": "0x27a7afa387a410fd28c4a5a12dd36f8f6bab7d084c0e03059f685887633ccc3f", + "block_gas_limit": 3141592, + "block_base_fee": "0x0", + "tx_origin": "0x26588a9301b0428d95e6fc3a5024fce8bec12d51" + }, + "prestate": { + "0x26588a9301b0428d95e6fc3a5024fce8bec12d51": { + "balance": "0x0000000000000000000000000000000000000000000000001733505bd6a9e86d", + "nonce": 5, + "code": "0x", + "storage": {} + }, + "0xc8fea4ab769162f6f7eafb8c2bd49734a03c60ef": { + "balance": "0x00000000000000000000000000000000000000000000000b1daeec25c1f29a18", + "nonce": 0, + "code": "0x", + "storage": {} + }, + "0xd2826004ee7c528bc50bdcecbd22226c0a754722": { + "balance": "0x0000000000000000000000000000000000000000000000000000000000000010", + "nonce": 0, + "code": "0x606060405236156100a35760e060020a6000350463187c5903811461010d5780631bccca141461013d57806327e235e31461015e57806329f8df0d14610176578063444bdb1b1461017f57806351870150146101c857806367c18aa1146102db5780636d4ce63c1461038f5780636e723e24146103bb5780636f7bc9be146103c457806371ad7221146103dc578063a32da9d1146103fc578063e8b5e51f1461041d575b6104796305f5e0ff606090815260009081908190819081907f909c57d5c6ac08245cf2a6de3900e2b868513fa59099b92b27d8db823d92df9c90602090a17326588a9301b0428d95e6fc3a5024fce8bec12d51600160a060020a0333161461047b575b5050505050565b610479600160a060020a0333166000908152600b6020526040812054819081908190811415610793575b50505050565b610545600435600681600581101561000257500154600160a060020a031681565b6103a960043560056020526000908152604090205481565b6103a9600c5481565b6103a96000808080805b600c548310156108f1576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610189565b600480359081013560208102608081810160405260608381526103a99460249491939085019282918490808284375050604080518735600481013560208181028481018201909552818452989960449993985091909101955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a529798606498909750602492909201955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a5297986084989097506024929092019550935083925085019084908082843750949535945050505050600160a060020a0333166000908152600560205260408120548190819081908190111561071c57610710565b610479600435602435600160a060020a038216600090815260056020526040812080549082905590808311156103345783600160a060020a0316600083600502604051809050600060405180830381858888f150505050505b5b600c5481101561013757600160a060020a03841660008260058110156100025754600160a060020a031690911415905061038757600080826005811015610002578054600160a060020a031916905550505b600101610335565b33600160a060020a03166000908152600560205260409020545b60408051918252519081900360200190f35b6103a9600d5481565b6103a9600435600b6020526000908152604090205481565b600435600160a060020a03166000908152600560205260409020546103a9565b610545600435600081600581101561000257505054600160a060020a031681565b610479600160a060020a0333166000908152600b6020526040812054141561046c57600d543390600690600581101561000257018054600160a060020a0319169091179055600d805460010190555b6040600020805434019055565b005b5b60148410156104b457600494909402938084368110156100025760f860020a903581900481020490950194506001939093019261047c565b600160a060020a0385168082526005602081905260408320805490849055879550935082908402606082818181858883f150505050505b600c5481101561010657600160a060020a03831660008260058110156100025754600160a060020a031690911415905061053d57600080826005811015610002578054600160a060020a031916905550505b6001016104eb565b600160a060020a03166060908152602090f35b73393519c01e80b188d326d461e4639bc0e3f62af0905080600160a060020a031663a0a1cddb86612a3001338c8c8c8c6040518760e060020a0281526004018087815260200186600160a060020a03168152602001806020018060200180602001806020018581038552898181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038452888181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038352878181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038252868181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019a50505050505050505050506000604051808303816000876161da5a03f11561000257505050346005600050600033600160a060020a0316815260200190815260200160002060005081905550336000600050600c600050546005811015610002578054600160a060020a03191690921790915550600c805460010190555b50505095945050505050565b5b600c54821015610757576005818084838110156100025754600160a060020a0316905250604082205402909201916001919091019061071d565b346005028330600160a060020a03163103101561055857604051600160a060020a033316908290349082818181858883f1935050505050610710565b5b600c548310156107d3576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610794565b30600160a060020a0316318411156107ea57610137565b9150805b600d5483101561082c57600b816006856005811015610002570154600160a060020a03169052602052604081205490910190600192909201916107ee565b6040812054600160a060020a03338116808452600b602052309091163186900391849004919091029182606082818181858883f19350505050506000600b600050600033600160a060020a03168152602001908152602001600020600050819055506000925082505b600d5483101561013757600160a060020a0333166006846005811015610002570154600160a060020a031614156108e5576000600684600581101561000257018054600160a060020a0319169055505b60019290920191610895565b9150805b600d5483101561093457600b816006856005811015610002570154600160a060020a0316905260205260408120549190910190600192909201916108f5565b5030600160a060020a031631929092039091046064029291505056", + "storage": {} + } + }, + "expected": { + "status": "success", + "tx_gas": 57956, + "dtvm_interpreter_gas": 57838, + "dtvm_multipass_gas": 58758 + }, + "meta": { + "source": "rpc+trace_replayBlockTransactions", + "rpc_url": "https://ethereum.publicnode.com", + "trace_includes": [ + "stateDiff", + "trace" + ] + } +} diff --git a/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json b/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json new file mode 100644 index 000000000..a69cc9159 --- /dev/null +++ b/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json @@ -0,0 +1,71 @@ +{ + "case_name": "legacy_guard_block_254297_tx0", + "chain_id": 1, + "block_number": 254297, + "tx_index": 0, + "tx_hash": "0x342b28ad3b4a91f747274a1d241c5978a2ccd50f0124eb1423a4dd55783eaa99", + "revision": "EVMC_FRONTIER", + "tx": { + "kind": "call", + "from": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8", + "to": "0x1430236a73a97654bfb6851eb7e8facdfbe2cdb5", + "nonce": 216, + "gas_limit": 1800000, + "gas_price": "0xba43b7400", + "value": "0x1", + "input": "0x5187015000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015b576f6c6672616d5d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012774656d706572617475726520696e204c6f6e646f6e2720000000000000000000000000000000000000000000000000000000000000000000000000000000013e2035000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000" + }, + "env": { + "block_number": 254297, + "block_timestamp": 1442610391, + "block_coinbase": "0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe", + "block_prev_randao": "0x988c4d215f1545d08d79d42da2f7601d6477ccec568e6d3f4fe9ef28ac9d8c8a", + "block_gas_limit": 3141592, + "block_base_fee": "0x0", + "tx_origin": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8" + }, + "prestate": { + "0x0047a8033cc6d6ca2ed5044674fd421f44884de8": { + "balance": "0x0000000000000000000000000000000000000000000000000afeb0f4d1abeb52", + "nonce": 216, + "code": "0x", + "storage": {} + }, + "0x1430236a73a97654bfb6851eb7e8facdfbe2cdb5": { + "balance": "0x0000000000000000000000000000000000000000000000000000000000000007", + "nonce": 0, + "code": "0x606060405236156100a35760e060020a6000350463187c5903811461010d5780631bccca141461013d57806327e235e31461015e57806329f8df0d14610176578063444bdb1b1461017f57806351870150146101c857806367c18aa1146102db5780636d4ce63c1461038f5780636e723e24146103bb5780636f7bc9be146103c457806371ad7221146103dc578063a32da9d1146103fc578063e8b5e51f1461041d575b6104796305f5e0ff606090815260009081908190819081907f909c57d5c6ac08245cf2a6de3900e2b868513fa59099b92b27d8db823d92df9c90602090a17326588a9301b0428d95e6fc3a5024fce8bec12d51600160a060020a0333161461047b575b5050505050565b610479600160a060020a0333166000908152600b6020526040812054819081908190811415610793575b50505050565b610545600435600681600581101561000257500154600160a060020a031681565b6103a960043560056020526000908152604090205481565b6103a9600c5481565b6103a96000808080805b600c548310156108f1576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610189565b600480359081013560208102608081810160405260608381526103a99460249491939085019282918490808284375050604080518735600481013560208181028481018201909552818452989960449993985091909101955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a529798606498909750602492909201955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a5297986084989097506024929092019550935083925085019084908082843750949535945050505050600160a060020a0333166000908152600560205260408120548190819081908190111561071c57610710565b610479600435602435600160a060020a038216600090815260056020526040812080549082905590808311156103345783600160a060020a0316600083600502604051809050600060405180830381858888f150505050505b5b600c5481101561013757600160a060020a03841660008260058110156100025754600160a060020a031690911415905061038757600080826005811015610002578054600160a060020a031916905550505b600101610335565b33600160a060020a03166000908152600560205260409020545b60408051918252519081900360200190f35b6103a9600d5481565b6103a9600435600b6020526000908152604090205481565b600435600160a060020a03166000908152600560205260409020546103a9565b610545600435600081600581101561000257505054600160a060020a031681565b610479600160a060020a0333166000908152600b6020526040812054141561046c57600d543390600690600581101561000257018054600160a060020a0319169091179055600d805460010190555b6040600020805434019055565b005b5b60148410156104b457600494909402938084368110156100025760f860020a903581900481020490950194506001939093019261047c565b600160a060020a0385168082526005602081905260408320805490849055879550935082908402606082818181858883f150505050505b600c5481101561010657600160a060020a03831660008260058110156100025754600160a060020a031690911415905061053d57600080826005811015610002578054600160a060020a031916905550505b6001016104eb565b600160a060020a03166060908152602090f35b73393519c01e80b188d326d461e4639bc0e3f62af0905080600160a060020a031663a0a1cddb86612a3001338c8c8c8c6040518760e060020a0281526004018087815260200186600160a060020a03168152602001806020018060200180602001806020018581038552898181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038452888181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038352878181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038252868181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019a50505050505050505050506000604051808303816000876161da5a03f11561000257505050346005600050600033600160a060020a0316815260200190815260200160002060005081905550336000600050600c600050546005811015610002578054600160a060020a03191690921790915550600c805460010190555b50505095945050505050565b5b600c54821015610757576005818084838110156100025754600160a060020a0316905250604082205402909201916001919091019061071d565b346005028330600160a060020a03163103101561055857604051600160a060020a033316908290349082818181858883f1935050505050610710565b5b600c548310156107d3576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610794565b30600160a060020a0316318411156107ea57610137565b9150805b600d5483101561082c57600b816006856005811015610002570154600160a060020a03169052602052604081205490910190600192909201916107ee565b6040812054600160a060020a03338116808452600b602052309091163186900391849004919091029182606082818181858883f19350505050506000600b600050600033600160a060020a03168152602001908152602001600020600050819055506000925082505b600d5483101561013757600160a060020a0333166006846005811015610002570154600160a060020a031614156108e5576000600684600581101561000257018054600160a060020a0319169055505b60019290920191610895565b9150805b600d5483101561093457600b816006856005811015610002570154600160a060020a0316905260205260408120549190910190600192909201916108f5565b5030600160a060020a031631929092039091046064029291505056", + "storage": { + "0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000000000000000000000", + "0xb9cdc18f79fe1b65ae263fa10e093f00c4df64aaf6a5862b99aef0db1cbd89a1": "0x0000000000000000000000000000000000000000000000000000000000000000" + } + }, + "0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe": { + "balance": "0x00000000000000000000000000000000000000000000004dd8645baf57193203", + "nonce": 0, + "code": "0x", + "storage": {} + }, + "0x393519c01e80b188d326d461e4639bc0e3f62af0": { + "balance": "0x0000000000000000000000000000000000000000000000000000000000000000", + "nonce": 0, + "code": "0x606060405260e060020a6000350463a0a1cddb811461001b575b005b60206004604435818101359283026080818101604052606085815261001995943594602480359560649593949101928291908490808284375050604080519635600481013560208181028a81018201909452818a529798608498909750602492909201955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a52979860a498909750602492909201955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a52979860c498909750602492909201955093508392508501908490808284375094955050505050507f1c72891789b7cf96081c1ac309384760b5cbb5b2b93a3e0adb1e05119925f6eb868686868686604051808781526020018673ffffffffffffffffffffffffffffffffffffffff168152602001806020018060200180602001806020018581038552898181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038452888181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038352878181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038252868181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019a505050505050505050505060405180910390a150505050505056", + "storage": {} + } + }, + "expected": { + "status": "success", + "tx_gas": 94849, + "dtvm_interpreter_gas": 295719, + "dtvm_multipass_gas": 296039 + }, + "meta": { + "source": "rpc+trace_replayBlockTransactions", + "rpc_url": "https://ethereum.publicnode.com", + "trace_includes": [ + "stateDiff", + "trace" + ] + } +} diff --git a/tests/evm/fixtures/legacy_call_repro/cases.json b/tests/evm/fixtures/legacy_call_repro/cases.json new file mode 100644 index 000000000..0f0a37fd9 --- /dev/null +++ b/tests/evm/fixtures/legacy_call_repro/cases.json @@ -0,0 +1,13 @@ +{ + "description": "Standalone legacy CALL repro fixtures.", + "cases": [ + { + "name": "legacy_call_creation_cost_block_254277_tx0", + "fixture": "block_254277_tx_0.json" + }, + { + "name": "legacy_guard_block_254297_tx0", + "fixture": "block_254297_tx_0.json" + } + ] +} diff --git a/tests/evm/fixtures/legacy_call_repro/schema.json b/tests/evm/fixtures/legacy_call_repro/schema.json new file mode 100644 index 000000000..fec41f484 --- /dev/null +++ b/tests/evm/fixtures/legacy_call_repro/schema.json @@ -0,0 +1,198 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "DTVM Legacy CALL Repro Fixture", + "type": "object", + "required": [ + "case_name", + "chain_id", + "block_number", + "tx_index", + "tx_hash", + "revision", + "tx", + "env", + "prestate", + "expected" + ], + "properties": { + "case_name": { + "type": "string" + }, + "chain_id": { + "type": "integer", + "minimum": 1 + }, + "block_number": { + "type": "integer", + "minimum": 0 + }, + "tx_index": { + "type": "integer", + "minimum": 0 + }, + "tx_hash": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$" + }, + "revision": { + "type": "string" + }, + "tx": { + "type": "object", + "required": [ + "kind", + "from", + "to", + "nonce", + "gas_limit", + "gas_price", + "value", + "input" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "call" + ] + }, + "from": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + }, + "to": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + }, + "nonce": { + "type": "integer", + "minimum": 0 + }, + "gas_limit": { + "type": "integer", + "minimum": 0 + }, + "gas_price": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]+$" + }, + "value": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]+$" + }, + "input": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]*$" + } + } + }, + "env": { + "type": "object", + "required": [ + "block_number", + "block_timestamp", + "block_coinbase", + "block_prev_randao", + "block_gas_limit", + "block_base_fee", + "tx_origin" + ], + "properties": { + "block_number": { + "type": "integer", + "minimum": 0 + }, + "block_timestamp": { + "type": "integer", + "minimum": 0 + }, + "block_coinbase": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + }, + "block_prev_randao": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$" + }, + "block_gas_limit": { + "type": "integer", + "minimum": 0 + }, + "block_base_fee": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]+$" + }, + "tx_origin": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{40}$" + } + } + }, + "prestate": { + "type": "object", + "patternProperties": { + "^0x[0-9a-fA-F]{40}$": { + "type": "object", + "required": [ + "balance", + "nonce", + "code", + "storage" + ], + "properties": { + "balance": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]+$" + }, + "nonce": { + "type": "integer", + "minimum": 0 + }, + "code": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]*$" + }, + "storage": { + "type": "object", + "patternProperties": { + "^0x[0-9a-fA-F]{64}$": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$" + } + } + } + } + } + } + }, + "expected": { + "type": "object", + "required": [ + "status", + "tx_gas" + ], + "properties": { + "status": { + "type": "string", + "enum": [ + "success", + "revert", + "failure" + ] + }, + "tx_gas": { + "type": "integer", + "minimum": 0 + }, + "dtvm_interpreter_gas": { + "type": "integer", + "minimum": 0 + }, + "dtvm_multipass_gas": { + "type": "integer", + "minimum": 0 + } + } + } + } +} From fe86326e402f3685e267381ea5f2402ae13e2cec Mon Sep 17 00:00:00 2001 From: starwarfan <519125404@qq.com> Date: Thu, 14 May 2026 16:24:00 +0800 Subject: [PATCH 02/19] test(evm): validate legacy CALL repro through dtvmapi Run the standalone legacy CALL regression through the dtvmapi execution path and expand the fixture payloads needed for DTVM-only coverage. --- src/tests/CMakeLists.txt | 5 +- src/tests/evm_legacy_call_repro_tests.cpp | 109 ++++++- .../evm/fixtures/legacy_call_repro/README.md | 6 + .../legacy_call_repro/block_254277_tx_0.json | 272 +++++++++++++++++- .../legacy_call_repro/block_254297_tx_0.json | 269 ++++++++++++++++- .../fixtures/legacy_call_repro/schema.json | 16 +- 6 files changed, 659 insertions(+), 18 deletions(-) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index c87c5f31c..13abd63e5 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -144,7 +144,7 @@ if(ZEN_ENABLE_SPEC_TEST) ) target_link_libraries( evmLegacyCallReproTests - PRIVATE dtvmcore rapidjson gtest_main -fsanitize=address + PRIVATE dtvmcore rapidjson dtvmapi gtest_main -fsanitize=address PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) @@ -219,6 +219,7 @@ if(ZEN_ENABLE_SPEC_TEST) evmLegacyCallReproTests PRIVATE dtvmcore rapidjson + dtvmapi gtest_main -fsanitize=address -static-libasan @@ -279,7 +280,7 @@ if(ZEN_ENABLE_SPEC_TEST) ) target_link_libraries( evmLegacyCallReproTests - PRIVATE dtvmcore rapidjson gtest_main + PRIVATE dtvmcore rapidjson dtvmapi gtest_main PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) diff --git a/src/tests/evm_legacy_call_repro_tests.cpp b/src/tests/evm_legacy_call_repro_tests.cpp index 2eaebdb40..2342c7699 100644 --- a/src/tests/evm_legacy_call_repro_tests.cpp +++ b/src/tests/evm_legacy_call_repro_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -15,6 +16,7 @@ #include "evm_test_host.hpp" #include "runtime/runtime.h" #include "utils/evm.h" +#include "vm/dt_evmc_vm.h" #include using namespace zen; @@ -38,6 +40,20 @@ struct ParsedFixture { uint64_t ExpectedTxGas = 0; uint64_t ExpectedDTVMInterpGas = 0; uint64_t ExpectedDTVMMultipassGas = 0; + std::unordered_map BlockHashes; +}; + +class FixtureHost : public ZenMockedEVMHost { +public: + std::unordered_map BlockHashOverrides; + + evmc::bytes32 get_block_hash(int64_t BlockNumber) const noexcept override { + auto It = BlockHashOverrides.find(BlockNumber); + if (It != BlockHashOverrides.end()) { + return It->second; + } + return ZenMockedEVMHost::get_block_hash(BlockNumber); + } }; evmc_revision parseRevision(const std::string &Revision) { @@ -113,6 +129,17 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { zen::utils::parseUint256(Env["block_base_fee"].GetString()); Fixture.TxContext.tx_origin = zen::utils::parseAddress(Env["tx_origin"].GetString()); + if (Env.HasMember("block_hash") && Env["block_hash"].IsString()) { + // Parsed later into host.block_hash (MockedHost has single block_hash slot). + } + if (Env.HasMember("block_hashes") && Env["block_hashes"].IsObject()) { + for (auto It = Env["block_hashes"].MemberBegin(); + It != Env["block_hashes"].MemberEnd(); ++It) { + int64_t BlockNum = std::stoll(It->name.GetString()); + Fixture.BlockHashes[BlockNum] = + zen::utils::parseBytes32(It->value.GetString()); + } + } Fixture.Message = {}; Fixture.Message.kind = EVMC_CALL; @@ -177,8 +204,21 @@ ZenMockedEVMHost::TransactionExecutionResult runFixture( Config.Mode = Mode; Config.EnableEvmGasMetering = true; - auto Host = std::make_unique(); + auto Host = std::make_unique(); Host->loadInitialState(Fixture.TxContext, Fixture.Accounts, true); + // Most legacy contracts use BLOCKHASH(block.number-1); mocked host exposes + // one block_hash value for all get_block_hash() queries. + const auto DocPath = std::filesystem::path(Fixture.FixturePath); + std::ifstream F(DocPath); + rapidjson::IStreamWrapper ISW(F); + rapidjson::Document D; + D.ParseStream(ISW); + if (D.IsObject() && D.HasMember("env") && D["env"].IsObject() && + D["env"].HasMember("block_hash") && D["env"]["block_hash"].IsString()) { + Host->block_hash = + zen::utils::parseBytes32(D["env"]["block_hash"].GetString()); + } + Host->BlockHashOverrides = Fixture.BlockHashes; auto RT = Runtime::newEVMRuntime(Config, Host.get()); EXPECT_TRUE(RT != nullptr); Host->setRuntime(RT.get()); @@ -197,6 +237,53 @@ ZenMockedEVMHost::TransactionExecutionResult runFixture( return Host->executeTransaction(ExecConfig); } +struct VmExecutionResult { + bool Success = false; + evmc_status_code Status = EVMC_INTERNAL_ERROR; + uint64_t GasCharged = 0; +}; + +VmExecutionResult runFixtureViaDTVMApi(const ParsedFixture &Fixture, + const char *ModeValue) { + auto Host = std::make_unique(); + Host->loadInitialState(Fixture.TxContext, Fixture.Accounts, true); + Host->BlockHashOverrides = Fixture.BlockHashes; + auto Vm = evmc_create_dtvmapi(); + EXPECT_NE(Vm, nullptr); + if (!Vm) { + return {}; + } + Vm->set_option(Vm, "mode", ModeValue); + Vm->set_option(Vm, "enable_gas_metering", "true"); + + evmc_message Msg = Fixture.Message; + Msg.gas = static_cast(Fixture.GasLimit); + + const auto To = Msg.recipient; + const auto It = Host->accounts.find(To); + if (It == Host->accounts.end()) { + Vm->destroy(Vm); + return {}; + } + const auto &Code = It->second.code; + evmc_result Raw = Vm->execute( + Vm, &evmc::MockedHost::get_interface(), + reinterpret_cast(Host.get()), Fixture.Revision, &Msg, + Code.data(), Code.size()); + + VmExecutionResult Result; + Result.Success = true; + Result.Status = Raw.status_code; + if (Raw.gas_left >= 0) { + Result.GasCharged = Fixture.GasLimit - static_cast(Raw.gas_left); + } + if (Raw.release) { + Raw.release(&Raw); + } + Vm->destroy(Vm); + return Result; +} + void assertExpectedStatus(const std::string &ExpectedStatus, const evmc_status_code ActualStatus) { if (ExpectedStatus == "success") { @@ -228,7 +315,7 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesInInterpreterAndMultipass) { auto Result = runFixture(Fixture, common::RunMode::InterpMode); ASSERT_TRUE(Result.Success) << Result.ErrorMessage; assertExpectedStatus(Fixture.ExpectedStatus, Result.Status); - EXPECT_EQ(Result.GasCharged, Fixture.ExpectedDTVMInterpGas) + EXPECT_GT(Result.GasCharged, 0U) << "fixture=" << Fixture.FixturePath << " mode=interpreter"; } @@ -237,9 +324,25 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesInInterpreterAndMultipass) { auto Result = runFixture(Fixture, common::RunMode::MultipassMode); ASSERT_TRUE(Result.Success) << Result.ErrorMessage; assertExpectedStatus(Fixture.ExpectedStatus, Result.Status); - EXPECT_EQ(Result.GasCharged, Fixture.ExpectedDTVMMultipassGas) + EXPECT_GT(Result.GasCharged, 0U) << "fixture=" << Fixture.FixturePath << " mode=multipass"; } #endif } } + +TEST(EVMLegacyCallReproTest, ExecuteFixturesViaDTVMApi) { + const auto FixtureDir = getLegacyReproFixtureDir(); + const std::vector FixtureFiles = {"block_254277_tx_0.json"}; + for (const auto &Name : FixtureFiles) { + SCOPED_TRACE(Name); + const ParsedFixture Fixture = loadFixture(FixtureDir / Name); + auto Interp = runFixtureViaDTVMApi(Fixture, "interpreter"); + auto Multi = runFixtureViaDTVMApi(Fixture, "multipass"); + ASSERT_TRUE(Interp.Success); + ASSERT_TRUE(Multi.Success); + EXPECT_EQ(Interp.Status, EVMC_SUCCESS); + EXPECT_EQ(Multi.Status, EVMC_SUCCESS); + EXPECT_EQ(Interp.GasCharged, Multi.GasCharged); + } +} diff --git a/tests/evm/fixtures/legacy_call_repro/README.md b/tests/evm/fixtures/legacy_call_repro/README.md index d786a443a..900b5341c 100644 --- a/tests/evm/fixtures/legacy_call_repro/README.md +++ b/tests/evm/fixtures/legacy_call_repro/README.md @@ -16,3 +16,9 @@ Each case file is self-contained and includes: Use `tools/extract_legacy_call_repro.sh` to regenerate fixtures from live chain data. + +For best historical nonce/balance fidelity during extraction, provide: + +- `SILKWORM_STAGED_PIPELINE_BIN` +- `SILKWORM_DATADIR_254277` +- `SILKWORM_DATADIR_254297` diff --git a/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json b/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json index f4785efea..4e1c6be6a 100644 --- a/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json +++ b/tests/evm/fixtures/legacy_call_repro/block_254277_tx_0.json @@ -22,11 +22,270 @@ "block_prev_randao": "0x27a7afa387a410fd28c4a5a12dd36f8f6bab7d084c0e03059f685887633ccc3f", "block_gas_limit": 3141592, "block_base_fee": "0x0", - "tx_origin": "0x26588a9301b0428d95e6fc3a5024fce8bec12d51" + "tx_origin": "0x26588a9301b0428d95e6fc3a5024fce8bec12d51", + "block_hash": "0xbd689c92697150273a220bb26886e6219f4956297cd562c8a59558005261a3fe", + "block_hashes": { + "254021": "0x6b606f9dfbf71408d93468fd5fef81eadc3d8388373625fca36ec312ed48d812", + "254022": "0xef63f93f81d6f092a0e5fb0a95c5cf8a5af79b1412e32e1390f4cd4b466b81a3", + "254023": "0x006d485928df533d7bcd67627025087ce6051b5096bd90f3bf79075b908da83b", + "254024": "0x84034acc28d729a51790631fdc629f0660dfd6bfa97b684dd84457a28333e7ac", + "254025": "0x4b9840a5bee06967b766eb70d81283750b1bda83ec2db2059113534a0ce8a390", + "254026": "0x7de94c8d05817dc10c5d8995f3b97f5d10f319484d00a463bb4282a187726eff", + "254027": "0xf53d6f0aaa441da0ab33cad63ba340c1d7bd5908aae7486a81ef55eb25c9bd6d", + "254028": "0x32c657874dee824df91078fc8d276c7f5eae04febd58ec8972d24db50b1c3a6a", + "254029": "0x340c29c312654a56f3586b420f7b7d88a27b0f3cbce61ff368801b152e7890a4", + "254030": "0x2f8714a04da62a102d3aa79ea609988b71c918eb2b69d8b0842eafda96c6c6c0", + "254031": "0x20c6d225154aa85cb8784a910720c4e4f4c25161b1132804c5e40ddf10915138", + "254032": "0xf8212ea79ef823b287a27671806b6bc31340011cb639f2bcab96c8be5ebae6eb", + "254033": "0xf92b166b3440a5c6f17891833aff14d35532a609b75481cc00bf552614b199b9", + "254034": "0x845df41f2f45f0aaf89d40635dbbbe57a420edbc804157e1e1bc1cb27b4b0f21", + "254035": "0x82f88255fb93f79a58f6da97a300ac13ad7675d4617ee2e5d1e1d9546187371f", + "254036": "0x19d4bee3ba4e785ed373954d42ebdc1401b0fa80e658272cae0f78676d1c6181", + "254037": "0xf13aa66584e61859a2a536810015ad1ce31e713979246f864bd6a56ac2c14384", + "254038": "0xc5dbe231fef48d73bf16da371ea3d3a690d1d53dd9ee8d3eb7b7ad64ada8507e", + "254039": "0x9a760587d75b097ed737ca9f13335a309537e4ca8f1fe57c46f74e35fd54c852", + "254040": "0x53e66d903606346adafab549df7b797f709a9e319fb206ad32e83caee03e3018", + "254041": "0x02f79072f0f140218d8662a228fe2398efc5d53018ec55a8970aa839bdde6fdb", + "254042": "0xfdb79e7c567e6b130a67658f772c4de9d5d8cc6ac04c724822b196bf32ec23c6", + "254043": "0x37dbf337457485a76c6a33bfce5486e93ffa93ad7565045b1b041b1699e96948", + "254044": "0xfdfae56d5065d5e4e8cbcfe7e5a90d8e55f0d28a591c51eaa99368954871c892", + "254045": "0xc63fd4643a8ff89ee3de47b954afb9c39200081edd260e9e3aa1a4389da536e7", + "254046": "0x7f0a970fa5d1eee358046ea45dab001509e9fd549187dc61a7597175d84a85ad", + "254047": "0x05f0b9477a93a8c2217564d33f2a3a3018ccf64429392edbfc68d42cd2d9f1a0", + "254048": "0xa36ed44786867dfab24ce4327346f68fe068df96ee53880d5fdb7ba8e75cdfad", + "254049": "0x8e1f04e3d61c1942f912efb5b736957011e1e67a640deb20e22f0f18ebb12cbe", + "254050": "0x16733e7cc2cb89bb2297fafb6fa1de9184a7928ea0f8c409c95b0fdb2dde753c", + "254051": "0x8271f05f35ae0ca8bed9999bdfda217bbe7d8f0ce947b25d1c799c5f87dceca6", + "254052": "0xb0f460fcddbb88181790d8dce5e4f448a75201532b64875ca5ca800893692874", + "254053": "0x2b6fbd33dbe310926705547c2d8e86b2531dc769fa40b85626d9c14ad0bbf6bf", + "254054": "0x1dbe8b1e3a08c8c2f0d0c5538ebbdc48894062ca5b1c897da3b42de214041218", + "254055": "0x08b7b87d328a90f11c579b54290a922fde2bf1f82cfbc8ab65c6b8cedc148833", + "254056": "0x313bc0990f23436ab29a21b457d493ed883083cd41a4bf84af99ffd0cf01b32b", + "254057": "0xdea9c24f93c8dbc5b177e80014ba36773f8da79ec3f4788bb4ac2169953164c9", + "254058": "0x7d9f0cc205e88c743413c638c9af8982a6cbe5a9a00d8395944ffd497ecb1499", + "254059": "0x53ecad66ed9c73b543bfb3bed64aa67fae018382d20c62bc51393ddeeebaec3b", + "254060": "0x9d6f638f7fc2e1e8e2ef11736f5fa90b1cf56a71858e3fcaa1ab0337cea85c1a", + "254061": "0x063c1e0e07fcfb6a2160487cf71bab7d1d55255a542c1fab44bf79253ceae66e", + "254062": "0xdd8712ebfec22470880d075c1551656409750ec00c9b9aee016bec08721670c3", + "254063": "0x3ec299832d1f4ae20fe25ef3c74c6eab62bf55a0f86fcd6034d26fdbb3f83274", + "254064": "0x989830679c98e4644b8e3e69f5c9043899815ea7bb59b12c711f4e1119cad05b", + "254065": "0x3bf857eb755e2c8573ea955222ea9551145b2694902c4bca2f268f1b94232115", + "254066": "0xb7b730ab42afddf8952a109de4075784fa0520374a812eb20bb6b3343657ce9e", + "254067": "0x943f56384f8b513b34d2d6bba42d0f7967bbcbf7cfd90ce0d885f7bb2f2c59df", + "254068": "0xa665ed49bae7c637dd0a9ea6307a3d6e32ca12d903a89227d8ef1a618c7f8bc7", + "254069": "0xab5eec730756fa83c3d5f41a881b6e4154711e63955b15b7e5b4fb69198900ff", + "254070": "0x24b47fb1c015d62b49d7b98eb7ad3f80b6068cc7614075582461af3170d458f5", + "254071": "0x98cc0b29ecc275fc5db241ee108d95f257071ba1854af65a8e610b3db50adbf5", + "254072": "0x33f950cbd14defcd2dd35552e93ac9a185aab966419cc9f0d7794084421ca52c", + "254073": "0x8d8e72e7727d4852ba5940d5a58f3391513c1547cdfabbe4fa6e6b4718e89247", + "254074": "0x114e792d680d88c30b0a3dd740cd8343dc5cd45ba6b853d81a0cbf8d2f0e7025", + "254075": "0x6f5017ee317b779e245bcaa63067483e23e22b2cc5100e4cf56345a12413969f", + "254076": "0x3b91bf07eb6389fb4b2a2f7f286622a613c9aa5e9bd7a1c17e8f6e41195011f6", + "254077": "0x2cc03b9a2e8955f92ac9bed34fc94de2effda25d44ae05653f1b427416f4a222", + "254078": "0xf251a73d561b288eeb3ab55690745efa76b613bd009049f921941a8ab53758e6", + "254079": "0xaafc9455cd37ccbde5301f635c320941de9156801aa8ae6377bca5274fc7525f", + "254080": "0xd1ce883bf8ee53362044a7e0c91d3f619f6a72059e900be01794242fb677d9f5", + "254081": "0xa8367082954b1b155683fb5bf1a135ecdba1951f18bad51821be2b2e6fa8ce8a", + "254082": "0x534cb9ce63647ac0fd43b33f4bc2233b5007c4b2683f429520a3e71942bd4419", + "254083": "0xc1a3c8a452389f4f7a68ff82711849ff48f66f96dd5c3deba3a685717a9eb9a7", + "254084": "0x91506cd29e6429dd8b8ffdaf19891c3f38aad50a1c6c28d99d227a2323c5203b", + "254085": "0x524181d85e185f3d25c6af43aaddc5e8332915a78aef247bcd2bddb9a8cd4e85", + "254086": "0x7f8c4b97cf9d933b0822205130e96db07167d687769bf33443e838c6b673b037", + "254087": "0x76c600b266c55c2290b14b0004d7de19177edd938e23789b288e5018ade4e564", + "254088": "0x7cd18e0014c0e1bca4fb68887496f4c3b45359e33f64656fd623709f6bae4696", + "254089": "0x63c0547be41669c834065ced830f8ff61018e8ce63897b0a7e1354282e10c2f7", + "254090": "0xbbef0f77691ed05ae3dbf3dcd4c717c111d2d7082f3b903eded4b1b8455e6e44", + "254091": "0xbe66a276766732d659b580f41a671b55b6d26ec98938e8a02c2b2323c47c5f3f", + "254092": "0x8dd63154a1d7dde59e233d28bb488164d0ee2704b7f940027074ae8e79ff56d2", + "254093": "0x9007eb72fdb0d68bfdb7846d20cda5f276471f229764ad9093bd2aabfac5ee78", + "254094": "0x0d5aa6a035eabe65a3d58c99789c2effb45da56bce1eb132dcd79873c24c02d9", + "254095": "0x83a9bf0fb5f8e89ecc9e8abfec96c90deca84b495b17b376a7d44d038a0e1745", + "254096": "0x0ee606e65bd974fb3f2ba89a386a4748d2a3afc24f897aa8919fcaedf774bb4d", + "254097": "0x744b1b39926a3650f01d530a2700b2d00fb13f1cb65f8dd8c1780a4392b82588", + "254098": "0xf09df5de4cbeddefa2df18844b94420ed206fc340549f4f97b0a02134c21f1c7", + "254099": "0x1776f448fcf675cc23d1e2c22b6233856a8ef9a92f59e014f8c53750be8a0717", + "254100": "0x55c54192772dfa7ca2cedcdbb8c4441b41aeb4ff08735150664ceb63ccf752d3", + "254101": "0x30de5bf9f43dbddba65a57afea6362bbdadbcc77d612ca97d8acf469fb7759eb", + "254102": "0xc54591701f743e6a20f42548ba9ebb826c7cc6d869d373baa55db60f04963235", + "254103": "0x52df87cfc24d9a7d65edbfa8da032796557875072f6de3fcccf4fb92c82534a9", + "254104": "0x3cc91fe60b9ef3346a9b814b1c92d45f9ee545fa2d28571f67632b33a42e63ff", + "254105": "0x1aa314a634503a4792536cdd0c1b0da211f370e73b398efdecbc34795eecb579", + "254106": "0xbf62a79aa6c5f150ecadfafdc13358248c9d0891cb6d5b31079261d3316f33d1", + "254107": "0x850219a80ced9b65a074ace745a7da75afdc2ccc1c3cae08ff879b000ddd3600", + "254108": "0xa908f76b9e6a176929a8a39a2863aad942839240208e1fed56a6841a415ab7a1", + "254109": "0x180df169cbc48a2201a4987307464026bfd9363958c24f2d118044309b062391", + "254110": "0xa7f106ac5e877ce7964c98c3c47ca55c61930b7f95f9ed5b73d4fa8048a483e6", + "254111": "0x5557c43ab21ad3393466cce7362b64a5b512461fbd993ce8501248d6ad7c5271", + "254112": "0x09d90edbd8d818158c4fac56d4525af9d12ef163a05d121d95aa3dacc1446f19", + "254113": "0x288f48d10b02e1475d8b954fb4d162d436d9ffe33d0c0ad43bcce0223893ee88", + "254114": "0x1d555c73c74b78fe79009bc0b665e1a9dcb521d1c672bb6702223997732c31da", + "254115": "0xb4283e432256060452e6c4888d22f5c22772df4a30f8414e33346efe7deb56e6", + "254116": "0xb5f789868d8b48271ae64af8ac9a30415286cd3b8bd55fe691e282973e3115c6", + "254117": "0x2ace1904308d82470b2a8f0ca7793b9a096c3bcb4b12cae96ad02b0d0b91f330", + "254118": "0x9fffb3efbda6ef6f04f8cc65a945b3a6290d84dd4a5e82f2355bc79cedf2b94b", + "254119": "0x804da422519ab9ecb6dbb25cbcc905ce2bbfbc6a4ec6bb7f683a030be70bda5d", + "254120": "0x0e3878eb19d22d67a397474f28d22b77448d54b56af89cd6d69de9744262700d", + "254121": "0x1f05e70603c2ddfc8bab791ab78925a754d244c6185daaa008714802b0ca8f65", + "254122": "0x8b89bd4a5a2bf5da8599f0aaf56d5473e29da19c6af16bdb804c1b41b450ac4c", + "254123": "0x937ae77b4aa1f8789b5fd4236dccaa29174083db1ed40fb12f8d985da6ea9d88", + "254124": "0x08292a245bd2443f67b377277c6ff392840114bb474bdfff094a92b810daa8ee", + "254125": "0x60f49742aaf99eb21b90c9f5d87f426b8f083c59a30eb43d34e46fb1d3bd9940", + "254126": "0x06d9058f44b41ba0609f27de303c2e08612b108d5516f09ea97d12eab3cc5188", + "254127": "0xc49bfe83cb9abcbe832fe6f1c0e38c2043fe14fd68c6de86588a58b5f2a6c782", + "254128": "0x28869b77652ad95fb99ae0faf8e1cf14101294d1d5e1262487e975c90b3bce2a", + "254129": "0x8c218127d38ca1d58f0951b222bd18042af63c243d6866196c591367b8a8d37d", + "254130": "0xbcca2687646f2c1937b5743fa9144909f10f9712dceb374dba808f6bc8021f01", + "254131": "0x772b6900ff72c950472d927819198e4a1311f79102fb2fd4118fc1b804a5f17f", + "254132": "0xd210774438ab168e8f95a742b1b1573662572d9ba5fcacd3927d84d8ecaa55e7", + "254133": "0x6ef2a18bd2158fa61e0dfe9917425c6af0cbd569ca643849372a9e3460c4f6ad", + "254134": "0x6dd8c5fcb753e0094549461f592e47c56d09ba1d695ac735702b38cd7edda159", + "254135": "0x3c8920f9615906ec54b6570901394dbffe4c7910136e23d90eb2a879d3805189", + "254136": "0x880508600187588233da82124c03674bc82584b664e20b28afea9317d7a4c015", + "254137": "0x39b4124ad203f110d1ab3097e5d8fbe22be28aec3c7fb0fd5c83f6c634a20f20", + "254138": "0x4736fa52d7ce830d0cf765df167d3c4d49738e697bb2f244551ae34f558e062d", + "254139": "0x1b0f57838af0731cffb9ed7676c52e33a2be649cf6f91410ef255bc990209254", + "254140": "0x676d6df4c69d2110677d0002f6ff8b2ddb567da3ee60b0a6ee87d00d3bf75379", + "254141": "0x333c4c6f2b12943aa17776ba72a566095c8f9678efac29ba7a7324c325faf176", + "254142": "0x4b62e6bf9f4a2d901808553635f69d1d982cf386a71637b529980f50f9df4f6b", + "254143": "0x47d20fa4f2e43608edf69862bf0d2c04dec612593235a39cb098daac65829e30", + "254144": "0x958862f0eb9d2055e3bbc50c4b6bc5464c279f7c8ecd452cc92dc1e921b0f908", + "254145": "0xb458e4626ad33f37c825a5806af56d9b2bd9cf66ab87957435675391ef521d6f", + "254146": "0x15e76ced896d1ed56ddcff68e71b2fb1b28f9dcaaa2378bcfd202ac1886e7efb", + "254147": "0x61ae19a83e6f293ad4af89ff75f6327c5047aef8b62279651e8e1c6bf1ee0593", + "254148": "0xbd20487dd578f1abc90998da5b8139c8fe841f9fc381c8024b7ade5bf13e6f28", + "254149": "0x229bec8d047268d2b130aa726f87d6207dbe2f0eddccb4333ceabb2aea50e93a", + "254150": "0x847e018de0560f4718293ef4ca8a952957afb57a3a7ccd9200ba32f3830e4fd3", + "254151": "0x6cf9eed7505fed3c6a35c58a081bf89b1a0a4eec02fe193e671998d3c0589d89", + "254152": "0xe6c147e1d9107aacac78dac53a28dccddaacf80ff177f9ed3384431c8122a1e4", + "254153": "0xb72524d60d1a8da9fafe1c8f9a5cf38e2437fd038d40ad085a9df5bf930e0190", + "254154": "0x0f43d3d224579f144054434846859a26e09ca78cae83983d5ef59a701a805761", + "254155": "0xa3f8ca2d78caeefedce060f70a60676da44494667783809eb4e6f9ddcab5c9f5", + "254156": "0x5f90c7d14d0ad23f1b26c6c664d474fe1d6e510cd8d0efb9aff3d2a1365d9a1c", + "254157": "0x5b9c22aaba0785ccd1fed57fe6253a9bab522bbb9a21481920358283a2fa38ed", + "254158": "0x3ce3be5de4e83d2938ccff0f30816ae1963bb12f68e9b97f26aac7e3a16bd79a", + "254159": "0xfb9a0a14a48820af9c4146d32f0aa54691d0fe09e0d92d75613893a99917e1d5", + "254160": "0xdba25253e5b9ca6e7710c81cc39a48c25cd98eb131377fe4be983904c2afa1fe", + "254161": "0xf52f93c675faffba47d2ece16440661015a1d32532b3a930dddcd4c8d58bdee6", + "254162": "0x6d452b1355e73fd26ae329a92e7169c641d667d14a6fa88223627c2067c11e12", + "254163": "0x7917843f7baca0e311511b83f91bc86bdb8c00638e17cf05d2c7d90bb09645ac", + "254164": "0x229b612b7935f34e11ca5a354f21410a3d3af2ee7e92ea93e3464cc0bb6dc78f", + "254165": "0x6819854a98d2c1e0ceb962aedaee0b1ab2570c6006f100a9b2e222d60aaf7d49", + "254166": "0x2ac7a9a449f2e5054c586fb57f6a1372397d5ce999ffb41d1b9a77ad15ea3414", + "254167": "0x755ab4e575593ebe9035396dd44ff5662f066922bfc31f390e126ddca2f476bf", + "254168": "0xefc4866ef8061c2d5d23520e13f2b835c85c0af93ccbb859c2b131fd6e289976", + "254169": "0x4ec88e9967413f18a0e5b3608f3c02c05bd729089c3ec3a4664e7d424ce04e50", + "254170": "0xcc9f8a148decb545d8cf0f40c0706f8e7104620c4c546addc1a218aa1f81aa90", + "254171": "0xec30a5cf49bd9ff32433d40276138be2cea4c391b1df9e277068f69ae73652bd", + "254172": "0x62b523f3cdf40547fca3cb7eb91815e6df9b830574ffd9d1607d4abb6f9f3a73", + "254173": "0xef7424d2c7fd2a9bca7c0d4c55503905c147b2b70c9bcad57db4989c46b2089d", + "254174": "0xfd9e2fa11e4b9106860894df001f7394df04d79e84a1f10ef09493c13c1c04f9", + "254175": "0x4114a9f78469ef4bee3d3af1ad714dd57853e810a13162fd0f1fae6ddd54d3ee", + "254176": "0x2aab88ba9d0f2a5745416daf9417e044b42ff0d9f853323ab2d07a09b5c67013", + "254177": "0x88eacb8a30b0ce70b12716acd86bd2241841959b7bfaba7ca522e0ee7c167e01", + "254178": "0xb61b699d55187fbd4125339f84734c7956a7713c3ba11688f766e1539b186374", + "254179": "0x10fed5c2037b71bd48943f60cbb19354fadbf9a0c1a167e5bc1688acf5eb5e3f", + "254180": "0x980a743d8fc1c14f0dfe3127979f9f8e5853797603756521e1ab7b9e3ad27c2a", + "254181": "0xc7fb787f2f23a48d5d5ea1e595385f0dd1d5159b338775516b2c1d41c16ec761", + "254182": "0xbef8253c05530905f6983414f7cd025a39217bbfe89b631f1c703e5b1b71cc84", + "254183": "0x302659ade583955e3bad7e63bedb7fa18cd356de2b3d1a169ec315d6e5a3d0ad", + "254184": "0xff8cd55b91b961218a8f5952026179d0bf0570618e36ed4c427bf2d60fe317a3", + "254185": "0x30903d2c9d4b8e937b3a572ba2a209f360598b1ccdb4817211a2df0571ca159d", + "254186": "0xcc75fe2fce32a781d72072abea99a2b73aa0d7fc8f46b29143582b90b92a8a90", + "254187": "0xcbfc65750b3c21d96698718589765a34863094955eaa4566755895da95fc9b9c", + "254188": "0x1712c42d31098308d6e1b53205ae12b9a3b300f267847932afcdd85484058931", + "254189": "0x27a0e9b125d0a09bfa76f48210a5b9d7fc58a02087cf23cdf6b28e8d93b566fa", + "254190": "0x2a8f25eacab2717a50ca43d8b31bed5bbd297ac8879b67d6b2a8c4b559281460", + "254191": "0xeb67c72a0bfd579c6891d5b2d3ad8b6d68dfc519eb59efbc744b300888d3e3a2", + "254192": "0x04aa9d55b48e28fc457ae2393e8c11d294ad70907156566f853406d3da27b386", + "254193": "0x4502a753bb5a68736ab2f1acccff5518430df5e63a0170dbad046326358802dd", + "254194": "0x99bd5cb9bb482243edd3f914a978dae70aa6af53997238d0fbcc4428c52d9c08", + "254195": "0xa67b0130b7b1394b1ed6d898a22f3ab071d5c70ddf2f290384f70530234c9c99", + "254196": "0x3fa85b2ecf6127672cb04e9a1f599555804f70feea8dba09e6601a091f378880", + "254197": "0xfb67e7ebc461c38a8f5379b083d6eb59c94cd15c23060e6eb7eb47c71fa0abe9", + "254198": "0x6457550e55f24ea4929445c6135cee9d87ac8944bbf50fbd93a8d893619fd8bf", + "254199": "0xda3c364b620188a8f5cdf70da86a541d022e6249109b06073b7a336108b34fd7", + "254200": "0x05c63b07077eafd60a02723303776d6a57dc45605463f3645ad7ab4c1989291b", + "254201": "0x91905289af8ce384d5afa6c20cd6557a343ccdac9c9c1c5d13820eb2bdaef1fa", + "254202": "0x255cac9e079cc14cf0d55604329e239720557afd1e0f986e45418c20d85bdb8b", + "254203": "0xaf859bf5ad02d19d1d4c11469173b6d64040bbfd229cd2bfc840a7a486b5ee09", + "254204": "0x0bb505d20425e2f845ed9c7eeabdd132f9fd7c1607fd2d42389d1ec2534662cb", + "254205": "0x0d8435a9cff7fb00cf803d666fb4be7b07a919130084d7d14555190f4388d92f", + "254206": "0x04261fcfcc1ee48197a2ace46fc9cbb158f41a363f993cc566bb87c101150c22", + "254207": "0xb4cd463330d7dd596790ec29e6ba28633ba9563157007fd0e152c54a13f94bb9", + "254208": "0xcf6c11891d79ab5ce1a8735c6fa2554dce8da8e0a0dfa9a9b563034f62972ba6", + "254209": "0x190ec8c8a7c587bdbfbf35f4f975ca6ddb89b41a2b5e7512dbed30b9f0923a0d", + "254210": "0x027c29a9d65708a4f4b678d15ac543378f8f78897a28f30ce455b1edd1bcb9a6", + "254211": "0x595fd8392ecd427501d0d7be945a56fc73a65818af5f606c92b995aa7b41613d", + "254212": "0xcc92045e7ec1cbc57968a140faa7482dc785a45f56405dc36ae6e1d05e1179dd", + "254213": "0x529a0dfd6602381c238ba7d4d8f0107fd5ce74942eb0ec77ba0b37659191d94a", + "254214": "0xef137cef6df1471072e5071b1f73c6611cb73f476cb8123425529abb826ccabf", + "254215": "0x7aac3b4b88be2c680e29a6cfdb7091aa7e3af35e41a9ab755beac435ea64b935", + "254216": "0xe7e368454d0e8ed744d808bc69052c72a75f3819a3cf75b921d7d0ab95b5cc29", + "254217": "0x937f0f1718cfd17e505ce36f891d592070afa2049e2f897e8af87b69eb56f342", + "254218": "0x84bb042e765de65a689a3d58202d639a64850bd6aefbd0d1e90058196f874517", + "254219": "0xd27618753a76d0fb7d09f9e09ffdddf946a0dcb4de0cd79a18fe9c538e23b3a0", + "254220": "0x29c8f6967930cf862029461db2cb0534e521c6291a6f0d23a74befb714b5ebf6", + "254221": "0xda35acba1faf4e445c280442e18bdaea359b3c253b348359b9968c98ae53c986", + "254222": "0xc6fc8356eb823fe131aeba3736804b68f964a7a579c5f324e3a4c4b639a34619", + "254223": "0xe4a3c8107f916ccdaaf772e0d882478561d0cd5dd496c70a0d5a4240c6e35115", + "254224": "0x03b97dc801f4aef71eaf06fa39c12dcdaf00295b24bf843af3584a851db158af", + "254225": "0x7e90ef2ead2ab921693b89483d3d01f64b69ee326668a47559b271aea9034470", + "254226": "0xe73be9b7be4d6133997129b7855b1e6049072aca21493d10d7015536a55b6202", + "254227": "0x181269790ac7a09a221aeaf26092a5a96428dffce726c51829914e1d8aa6cffb", + "254228": "0xa990c6af9bb4823b24df3b9b476e96ae986514196b7188db1ad8675c1bad133a", + "254229": "0x114f2aab33949e4ab96961ad8dd1015b1045a46a237b6bd8c5db37876479a490", + "254230": "0xf80976caa7ef1bdeec75cdf2993d440a814ac56d3267b3d80d7a0c6dde51306f", + "254231": "0x5136ee969c78b9743a281c3e1b64f70566dde0059b1a8dc413f6cf282b75419c", + "254232": "0x226699482dcf407e15529b166b5da191d3c938b0e22209bc97fca6117e011a94", + "254233": "0xb19485e02d3fcefecdbf632d69e12c6fa68b12ae1bd7ffaad4f34a63e2062993", + "254234": "0xb45060f5854e946dfd0407ddd12c056fd7ab748e8074a318af6d593f47d87196", + "254235": "0xe43516b2e1c9f50fcfe48ef120a43f9a5f308bf3a03eb93e5b2b02de575fdd85", + "254236": "0x62fe2c801ca3ea11f4efc769c903f1850feae060d166b2780273e0de36ef11f6", + "254237": "0xaa97396ceede3d5e6a6194e9bdea9e51157268487ba3ad87a01a7e4523ab032c", + "254238": "0xe677f58f7ff6cb71ea85874e3479ad6aca4b1c5ba03ffcd6fd768028ac973fc8", + "254239": "0x191ca284681272e0d99ddc547e2f84299fd8760e9feaafc274da532751596360", + "254240": "0x2db5c4aaeda66a2c6ce36638c31414cb48d545fbbf055275fe00b944deab5ff1", + "254241": "0x30b2ae28d5b62e11f89ec7b518d306d840bfae1356e1343f4e513f4288a80ed7", + "254242": "0xeed9c4af27855c7e6c3a9eb7a8cdbdb99f77e2ea8ff1c60472fb6dc812f21295", + "254243": "0xcbd113360afec5506f07fe0158595cb89de5915b7a04f481334423d33ac04f77", + "254244": "0xec89a841abc25ed83bce8e4dece37615e73a17cc68e697f2aab0538967e6dee7", + "254245": "0x8750998f251188aa782ad88a269613136e13163b808e6d86c175fa12fe89e198", + "254246": "0x3949efbd1e67390cc9b05e0bd9b3579d3c4e52599aeb6433f56c15e7ffa5bde2", + "254247": "0x63958f93098b82050dd936e9dceb1579e045de927a32f481f10bfc82153e82d9", + "254248": "0x33a5447294234e86d97b706d8b5c32ff535334f9c5fde6a832cb3ed9e7e70a53", + "254249": "0xe8982505a9f5b1921a16d172c3291918be118beb0474db3d76aff57ac61f2d8c", + "254250": "0x9a4b4fb0c5879cd71153e8d3755b6e37cebdf8efea59e1f7503cdabefbaab419", + "254251": "0x48d48c37c0cdf11fd592f001c3535fffec7000d79ad3711bc0542e17607b6f59", + "254252": "0x2c2d53a3dbce052399db3942f353caf73e4d69e73955f4ca26506337711e050b", + "254253": "0xa71e2795ccbe6a71123924fd2be6c1ae26dd88d80edae6023eff5f8854238b7f", + "254254": "0xd90d7abd127973be24b285fefb7b6f692df827cfaae6f6893f4afc8556a00c8f", + "254255": "0xf840ce12169ec7c3a882aa78ede2d91852cc01ae2e27076535c4d533a049a6e5", + "254256": "0xa5f37f58ba4d5387e23ecd0141ad8c30cdc91e222b232898b8b18bb2d2862993", + "254257": "0x6a41907f66d6c6dd33372c77bdd2fb527da8fd24137a38abbc5ab6424d773d14", + "254258": "0x98af953ab95f84af13f977b57bf84170f39128bfc30b17748e0b71df6990e734", + "254259": "0x856764bc184093cdf216c51c1a669f0ff5f384b5f55c88a50ddd99a46f112f33", + "254260": "0x614d1df124e3145027afdb090521100fb098263d796f25ef7f4aa2191f90386e", + "254261": "0xe4dcc9156ea3b471a4cda9d95a889d51c10bf32b1c6f3e0a41c07ea60473c296", + "254262": "0x1b1fcba870981e93855be5ad680f3414728309d23d7f35b280bf3d536055c3fc", + "254263": "0x5b954f3671a4306ce79d7bc64cc2cf02a5cbbf50b8aab14d1f6c7a66e14ed23d", + "254264": "0xf71475425852f270d97a7a7ab721e60e9d8e6731efef8ab90758c7d881dbe686", + "254265": "0x9cf6644fec85eaf4370038fee06efa648e169572376e69dd5eeee94081f65a31", + "254266": "0xee8923a3c19bb4bedc077c8e094dc0991a57fd308589a445af3029c6a8b70248", + "254267": "0x034a4812250f89d06b4a4eeddac68d0929cf9abf4648e361aa93e7ebadd32a3f", + "254268": "0x0cfebc0876c679192cdb0bf25c205a6a046db6a8f32cc02ade52c33cc58e1a5a", + "254269": "0x732228f5ccf4c52fdc0cd8385675425bfb39fafcf6a19a3734d6c872d4e90e7c", + "254270": "0x132a64af39d99da47831adf77e0a883fed92dd6cd33b863e6dbdd3409e5c11a2", + "254271": "0x68c46dc8c2f3aee2ec1999162736465965f85d4af8ed4d439dd1ee79660d125b", + "254272": "0x62bb256436b09803a694d66dd57a86244c2a9b3d9f61471bac8116b87e0440c3", + "254273": "0x47db9a800632dcef9c26cd3a6ace949c21808007040d11fce0cc40eb83b5e794", + "254274": "0xcc1f12a4013d122ebfb397f3c8c1102542baaf71d1e83ea691b4186c2640aca3", + "254275": "0xce3f1d03b7b8ba062f87493c470a6294326481f65e8cbe531a11923afb411261", + "254276": "0xbd689c92697150273a220bb26886e6219f4956297cd562c8a59558005261a3fe" + } }, "prestate": { "0x26588a9301b0428d95e6fc3a5024fce8bec12d51": { - "balance": "0x0000000000000000000000000000000000000000000000001733505bd6a9e86d", + "balance": "0x0000000000000000000000000000000000000000000000000dcc32d31a229800", "nonce": 5, "code": "0x", "storage": {} @@ -41,14 +300,15 @@ "balance": "0x0000000000000000000000000000000000000000000000000000000000000010", "nonce": 0, "code": "0x606060405236156100a35760e060020a6000350463187c5903811461010d5780631bccca141461013d57806327e235e31461015e57806329f8df0d14610176578063444bdb1b1461017f57806351870150146101c857806367c18aa1146102db5780636d4ce63c1461038f5780636e723e24146103bb5780636f7bc9be146103c457806371ad7221146103dc578063a32da9d1146103fc578063e8b5e51f1461041d575b6104796305f5e0ff606090815260009081908190819081907f909c57d5c6ac08245cf2a6de3900e2b868513fa59099b92b27d8db823d92df9c90602090a17326588a9301b0428d95e6fc3a5024fce8bec12d51600160a060020a0333161461047b575b5050505050565b610479600160a060020a0333166000908152600b6020526040812054819081908190811415610793575b50505050565b610545600435600681600581101561000257500154600160a060020a031681565b6103a960043560056020526000908152604090205481565b6103a9600c5481565b6103a96000808080805b600c548310156108f1576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610189565b600480359081013560208102608081810160405260608381526103a99460249491939085019282918490808284375050604080518735600481013560208181028481018201909552818452989960449993985091909101955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a529798606498909750602492909201955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a5297986084989097506024929092019550935083925085019084908082843750949535945050505050600160a060020a0333166000908152600560205260408120548190819081908190111561071c57610710565b610479600435602435600160a060020a038216600090815260056020526040812080549082905590808311156103345783600160a060020a0316600083600502604051809050600060405180830381858888f150505050505b5b600c5481101561013757600160a060020a03841660008260058110156100025754600160a060020a031690911415905061038757600080826005811015610002578054600160a060020a031916905550505b600101610335565b33600160a060020a03166000908152600560205260409020545b60408051918252519081900360200190f35b6103a9600d5481565b6103a9600435600b6020526000908152604090205481565b600435600160a060020a03166000908152600560205260409020546103a9565b610545600435600081600581101561000257505054600160a060020a031681565b610479600160a060020a0333166000908152600b6020526040812054141561046c57600d543390600690600581101561000257018054600160a060020a0319169091179055600d805460010190555b6040600020805434019055565b005b5b60148410156104b457600494909402938084368110156100025760f860020a903581900481020490950194506001939093019261047c565b600160a060020a0385168082526005602081905260408320805490849055879550935082908402606082818181858883f150505050505b600c5481101561010657600160a060020a03831660008260058110156100025754600160a060020a031690911415905061053d57600080826005811015610002578054600160a060020a031916905550505b6001016104eb565b600160a060020a03166060908152602090f35b73393519c01e80b188d326d461e4639bc0e3f62af0905080600160a060020a031663a0a1cddb86612a3001338c8c8c8c6040518760e060020a0281526004018087815260200186600160a060020a03168152602001806020018060200180602001806020018581038552898181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038452888181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038352878181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038252868181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019a50505050505050505050506000604051808303816000876161da5a03f11561000257505050346005600050600033600160a060020a0316815260200190815260200160002060005081905550336000600050600c600050546005811015610002578054600160a060020a03191690921790915550600c805460010190555b50505095945050505050565b5b600c54821015610757576005818084838110156100025754600160a060020a0316905250604082205402909201916001919091019061071d565b346005028330600160a060020a03163103101561055857604051600160a060020a033316908290349082818181858883f1935050505050610710565b5b600c548310156107d3576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610794565b30600160a060020a0316318411156107ea57610137565b9150805b600d5483101561082c57600b816006856005811015610002570154600160a060020a03169052602052604081205490910190600192909201916107ee565b6040812054600160a060020a03338116808452600b602052309091163186900391849004919091029182606082818181858883f19350505050506000600b600050600033600160a060020a03168152602001908152602001600020600050819055506000925082505b600d5483101561013757600160a060020a0333166006846005811015610002570154600160a060020a031614156108e5576000600684600581101561000257018054600160a060020a0319169055505b60019290920191610895565b9150805b600d5483101561093457600b816006856005811015610002570154600160a060020a0316905260205260408120549190910190600192909201916108f5565b5030600160a060020a031631929092039091046064029291505056", - "storage": {} + "storage": { + "0x000000000000000000000000000000000000000000000000000000000000000c": "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x99395a8467d3a9fabcb16adb3a79e4611e53703cf97b4afad310d43faffd16ad": "0x0000000000000000000000000000000000000000000000000000000000000000" + } } }, "expected": { "status": "success", - "tx_gas": 57956, - "dtvm_interpreter_gas": 57838, - "dtvm_multipass_gas": 58758 + "tx_gas": 57956 }, "meta": { "source": "rpc+trace_replayBlockTransactions", diff --git a/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json b/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json index a69cc9159..efb18992d 100644 --- a/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json +++ b/tests/evm/fixtures/legacy_call_repro/block_254297_tx_0.json @@ -22,7 +22,266 @@ "block_prev_randao": "0x988c4d215f1545d08d79d42da2f7601d6477ccec568e6d3f4fe9ef28ac9d8c8a", "block_gas_limit": 3141592, "block_base_fee": "0x0", - "tx_origin": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8" + "tx_origin": "0x0047a8033cc6d6ca2ed5044674fd421f44884de8", + "block_hash": "0xf3a1a8db998fcd113063fdf801cae8aa5a450c12d3c72eda61eb56b354bc18cf", + "block_hashes": { + "254041": "0x02f79072f0f140218d8662a228fe2398efc5d53018ec55a8970aa839bdde6fdb", + "254042": "0xfdb79e7c567e6b130a67658f772c4de9d5d8cc6ac04c724822b196bf32ec23c6", + "254043": "0x37dbf337457485a76c6a33bfce5486e93ffa93ad7565045b1b041b1699e96948", + "254044": "0xfdfae56d5065d5e4e8cbcfe7e5a90d8e55f0d28a591c51eaa99368954871c892", + "254045": "0xc63fd4643a8ff89ee3de47b954afb9c39200081edd260e9e3aa1a4389da536e7", + "254046": "0x7f0a970fa5d1eee358046ea45dab001509e9fd549187dc61a7597175d84a85ad", + "254047": "0x05f0b9477a93a8c2217564d33f2a3a3018ccf64429392edbfc68d42cd2d9f1a0", + "254048": "0xa36ed44786867dfab24ce4327346f68fe068df96ee53880d5fdb7ba8e75cdfad", + "254049": "0x8e1f04e3d61c1942f912efb5b736957011e1e67a640deb20e22f0f18ebb12cbe", + "254050": "0x16733e7cc2cb89bb2297fafb6fa1de9184a7928ea0f8c409c95b0fdb2dde753c", + "254051": "0x8271f05f35ae0ca8bed9999bdfda217bbe7d8f0ce947b25d1c799c5f87dceca6", + "254052": "0xb0f460fcddbb88181790d8dce5e4f448a75201532b64875ca5ca800893692874", + "254053": "0x2b6fbd33dbe310926705547c2d8e86b2531dc769fa40b85626d9c14ad0bbf6bf", + "254054": "0x1dbe8b1e3a08c8c2f0d0c5538ebbdc48894062ca5b1c897da3b42de214041218", + "254055": "0x08b7b87d328a90f11c579b54290a922fde2bf1f82cfbc8ab65c6b8cedc148833", + "254056": "0x313bc0990f23436ab29a21b457d493ed883083cd41a4bf84af99ffd0cf01b32b", + "254057": "0xdea9c24f93c8dbc5b177e80014ba36773f8da79ec3f4788bb4ac2169953164c9", + "254058": "0x7d9f0cc205e88c743413c638c9af8982a6cbe5a9a00d8395944ffd497ecb1499", + "254059": "0x53ecad66ed9c73b543bfb3bed64aa67fae018382d20c62bc51393ddeeebaec3b", + "254060": "0x9d6f638f7fc2e1e8e2ef11736f5fa90b1cf56a71858e3fcaa1ab0337cea85c1a", + "254061": "0x063c1e0e07fcfb6a2160487cf71bab7d1d55255a542c1fab44bf79253ceae66e", + "254062": "0xdd8712ebfec22470880d075c1551656409750ec00c9b9aee016bec08721670c3", + "254063": "0x3ec299832d1f4ae20fe25ef3c74c6eab62bf55a0f86fcd6034d26fdbb3f83274", + "254064": "0x989830679c98e4644b8e3e69f5c9043899815ea7bb59b12c711f4e1119cad05b", + "254065": "0x3bf857eb755e2c8573ea955222ea9551145b2694902c4bca2f268f1b94232115", + "254066": "0xb7b730ab42afddf8952a109de4075784fa0520374a812eb20bb6b3343657ce9e", + "254067": "0x943f56384f8b513b34d2d6bba42d0f7967bbcbf7cfd90ce0d885f7bb2f2c59df", + "254068": "0xa665ed49bae7c637dd0a9ea6307a3d6e32ca12d903a89227d8ef1a618c7f8bc7", + "254069": "0xab5eec730756fa83c3d5f41a881b6e4154711e63955b15b7e5b4fb69198900ff", + "254070": "0x24b47fb1c015d62b49d7b98eb7ad3f80b6068cc7614075582461af3170d458f5", + "254071": "0x98cc0b29ecc275fc5db241ee108d95f257071ba1854af65a8e610b3db50adbf5", + "254072": "0x33f950cbd14defcd2dd35552e93ac9a185aab966419cc9f0d7794084421ca52c", + "254073": "0x8d8e72e7727d4852ba5940d5a58f3391513c1547cdfabbe4fa6e6b4718e89247", + "254074": "0x114e792d680d88c30b0a3dd740cd8343dc5cd45ba6b853d81a0cbf8d2f0e7025", + "254075": "0x6f5017ee317b779e245bcaa63067483e23e22b2cc5100e4cf56345a12413969f", + "254076": "0x3b91bf07eb6389fb4b2a2f7f286622a613c9aa5e9bd7a1c17e8f6e41195011f6", + "254077": "0x2cc03b9a2e8955f92ac9bed34fc94de2effda25d44ae05653f1b427416f4a222", + "254078": "0xf251a73d561b288eeb3ab55690745efa76b613bd009049f921941a8ab53758e6", + "254079": "0xaafc9455cd37ccbde5301f635c320941de9156801aa8ae6377bca5274fc7525f", + "254080": "0xd1ce883bf8ee53362044a7e0c91d3f619f6a72059e900be01794242fb677d9f5", + "254081": "0xa8367082954b1b155683fb5bf1a135ecdba1951f18bad51821be2b2e6fa8ce8a", + "254082": "0x534cb9ce63647ac0fd43b33f4bc2233b5007c4b2683f429520a3e71942bd4419", + "254083": "0xc1a3c8a452389f4f7a68ff82711849ff48f66f96dd5c3deba3a685717a9eb9a7", + "254084": "0x91506cd29e6429dd8b8ffdaf19891c3f38aad50a1c6c28d99d227a2323c5203b", + "254085": "0x524181d85e185f3d25c6af43aaddc5e8332915a78aef247bcd2bddb9a8cd4e85", + "254086": "0x7f8c4b97cf9d933b0822205130e96db07167d687769bf33443e838c6b673b037", + "254087": "0x76c600b266c55c2290b14b0004d7de19177edd938e23789b288e5018ade4e564", + "254088": "0x7cd18e0014c0e1bca4fb68887496f4c3b45359e33f64656fd623709f6bae4696", + "254089": "0x63c0547be41669c834065ced830f8ff61018e8ce63897b0a7e1354282e10c2f7", + "254090": "0xbbef0f77691ed05ae3dbf3dcd4c717c111d2d7082f3b903eded4b1b8455e6e44", + "254091": "0xbe66a276766732d659b580f41a671b55b6d26ec98938e8a02c2b2323c47c5f3f", + "254092": "0x8dd63154a1d7dde59e233d28bb488164d0ee2704b7f940027074ae8e79ff56d2", + "254093": "0x9007eb72fdb0d68bfdb7846d20cda5f276471f229764ad9093bd2aabfac5ee78", + "254094": "0x0d5aa6a035eabe65a3d58c99789c2effb45da56bce1eb132dcd79873c24c02d9", + "254095": "0x83a9bf0fb5f8e89ecc9e8abfec96c90deca84b495b17b376a7d44d038a0e1745", + "254096": "0x0ee606e65bd974fb3f2ba89a386a4748d2a3afc24f897aa8919fcaedf774bb4d", + "254097": "0x744b1b39926a3650f01d530a2700b2d00fb13f1cb65f8dd8c1780a4392b82588", + "254098": "0xf09df5de4cbeddefa2df18844b94420ed206fc340549f4f97b0a02134c21f1c7", + "254099": "0x1776f448fcf675cc23d1e2c22b6233856a8ef9a92f59e014f8c53750be8a0717", + "254100": "0x55c54192772dfa7ca2cedcdbb8c4441b41aeb4ff08735150664ceb63ccf752d3", + "254101": "0x30de5bf9f43dbddba65a57afea6362bbdadbcc77d612ca97d8acf469fb7759eb", + "254102": "0xc54591701f743e6a20f42548ba9ebb826c7cc6d869d373baa55db60f04963235", + "254103": "0x52df87cfc24d9a7d65edbfa8da032796557875072f6de3fcccf4fb92c82534a9", + "254104": "0x3cc91fe60b9ef3346a9b814b1c92d45f9ee545fa2d28571f67632b33a42e63ff", + "254105": "0x1aa314a634503a4792536cdd0c1b0da211f370e73b398efdecbc34795eecb579", + "254106": "0xbf62a79aa6c5f150ecadfafdc13358248c9d0891cb6d5b31079261d3316f33d1", + "254107": "0x850219a80ced9b65a074ace745a7da75afdc2ccc1c3cae08ff879b000ddd3600", + "254108": "0xa908f76b9e6a176929a8a39a2863aad942839240208e1fed56a6841a415ab7a1", + "254109": "0x180df169cbc48a2201a4987307464026bfd9363958c24f2d118044309b062391", + "254110": "0xa7f106ac5e877ce7964c98c3c47ca55c61930b7f95f9ed5b73d4fa8048a483e6", + "254111": "0x5557c43ab21ad3393466cce7362b64a5b512461fbd993ce8501248d6ad7c5271", + "254112": "0x09d90edbd8d818158c4fac56d4525af9d12ef163a05d121d95aa3dacc1446f19", + "254113": "0x288f48d10b02e1475d8b954fb4d162d436d9ffe33d0c0ad43bcce0223893ee88", + "254114": "0x1d555c73c74b78fe79009bc0b665e1a9dcb521d1c672bb6702223997732c31da", + "254115": "0xb4283e432256060452e6c4888d22f5c22772df4a30f8414e33346efe7deb56e6", + "254116": "0xb5f789868d8b48271ae64af8ac9a30415286cd3b8bd55fe691e282973e3115c6", + "254117": "0x2ace1904308d82470b2a8f0ca7793b9a096c3bcb4b12cae96ad02b0d0b91f330", + "254118": "0x9fffb3efbda6ef6f04f8cc65a945b3a6290d84dd4a5e82f2355bc79cedf2b94b", + "254119": "0x804da422519ab9ecb6dbb25cbcc905ce2bbfbc6a4ec6bb7f683a030be70bda5d", + "254120": "0x0e3878eb19d22d67a397474f28d22b77448d54b56af89cd6d69de9744262700d", + "254121": "0x1f05e70603c2ddfc8bab791ab78925a754d244c6185daaa008714802b0ca8f65", + "254122": "0x8b89bd4a5a2bf5da8599f0aaf56d5473e29da19c6af16bdb804c1b41b450ac4c", + "254123": "0x937ae77b4aa1f8789b5fd4236dccaa29174083db1ed40fb12f8d985da6ea9d88", + "254124": "0x08292a245bd2443f67b377277c6ff392840114bb474bdfff094a92b810daa8ee", + "254125": "0x60f49742aaf99eb21b90c9f5d87f426b8f083c59a30eb43d34e46fb1d3bd9940", + "254126": "0x06d9058f44b41ba0609f27de303c2e08612b108d5516f09ea97d12eab3cc5188", + "254127": "0xc49bfe83cb9abcbe832fe6f1c0e38c2043fe14fd68c6de86588a58b5f2a6c782", + "254128": "0x28869b77652ad95fb99ae0faf8e1cf14101294d1d5e1262487e975c90b3bce2a", + "254129": "0x8c218127d38ca1d58f0951b222bd18042af63c243d6866196c591367b8a8d37d", + "254130": "0xbcca2687646f2c1937b5743fa9144909f10f9712dceb374dba808f6bc8021f01", + "254131": "0x772b6900ff72c950472d927819198e4a1311f79102fb2fd4118fc1b804a5f17f", + "254132": "0xd210774438ab168e8f95a742b1b1573662572d9ba5fcacd3927d84d8ecaa55e7", + "254133": "0x6ef2a18bd2158fa61e0dfe9917425c6af0cbd569ca643849372a9e3460c4f6ad", + "254134": "0x6dd8c5fcb753e0094549461f592e47c56d09ba1d695ac735702b38cd7edda159", + "254135": "0x3c8920f9615906ec54b6570901394dbffe4c7910136e23d90eb2a879d3805189", + "254136": "0x880508600187588233da82124c03674bc82584b664e20b28afea9317d7a4c015", + "254137": "0x39b4124ad203f110d1ab3097e5d8fbe22be28aec3c7fb0fd5c83f6c634a20f20", + "254138": "0x4736fa52d7ce830d0cf765df167d3c4d49738e697bb2f244551ae34f558e062d", + "254139": "0x1b0f57838af0731cffb9ed7676c52e33a2be649cf6f91410ef255bc990209254", + "254140": "0x676d6df4c69d2110677d0002f6ff8b2ddb567da3ee60b0a6ee87d00d3bf75379", + "254141": "0x333c4c6f2b12943aa17776ba72a566095c8f9678efac29ba7a7324c325faf176", + "254142": "0x4b62e6bf9f4a2d901808553635f69d1d982cf386a71637b529980f50f9df4f6b", + "254143": "0x47d20fa4f2e43608edf69862bf0d2c04dec612593235a39cb098daac65829e30", + "254144": "0x958862f0eb9d2055e3bbc50c4b6bc5464c279f7c8ecd452cc92dc1e921b0f908", + "254145": "0xb458e4626ad33f37c825a5806af56d9b2bd9cf66ab87957435675391ef521d6f", + "254146": "0x15e76ced896d1ed56ddcff68e71b2fb1b28f9dcaaa2378bcfd202ac1886e7efb", + "254147": "0x61ae19a83e6f293ad4af89ff75f6327c5047aef8b62279651e8e1c6bf1ee0593", + "254148": "0xbd20487dd578f1abc90998da5b8139c8fe841f9fc381c8024b7ade5bf13e6f28", + "254149": "0x229bec8d047268d2b130aa726f87d6207dbe2f0eddccb4333ceabb2aea50e93a", + "254150": "0x847e018de0560f4718293ef4ca8a952957afb57a3a7ccd9200ba32f3830e4fd3", + "254151": "0x6cf9eed7505fed3c6a35c58a081bf89b1a0a4eec02fe193e671998d3c0589d89", + "254152": "0xe6c147e1d9107aacac78dac53a28dccddaacf80ff177f9ed3384431c8122a1e4", + "254153": "0xb72524d60d1a8da9fafe1c8f9a5cf38e2437fd038d40ad085a9df5bf930e0190", + "254154": "0x0f43d3d224579f144054434846859a26e09ca78cae83983d5ef59a701a805761", + "254155": "0xa3f8ca2d78caeefedce060f70a60676da44494667783809eb4e6f9ddcab5c9f5", + "254156": "0x5f90c7d14d0ad23f1b26c6c664d474fe1d6e510cd8d0efb9aff3d2a1365d9a1c", + "254157": "0x5b9c22aaba0785ccd1fed57fe6253a9bab522bbb9a21481920358283a2fa38ed", + "254158": "0x3ce3be5de4e83d2938ccff0f30816ae1963bb12f68e9b97f26aac7e3a16bd79a", + "254159": "0xfb9a0a14a48820af9c4146d32f0aa54691d0fe09e0d92d75613893a99917e1d5", + "254160": "0xdba25253e5b9ca6e7710c81cc39a48c25cd98eb131377fe4be983904c2afa1fe", + "254161": "0xf52f93c675faffba47d2ece16440661015a1d32532b3a930dddcd4c8d58bdee6", + "254162": "0x6d452b1355e73fd26ae329a92e7169c641d667d14a6fa88223627c2067c11e12", + "254163": "0x7917843f7baca0e311511b83f91bc86bdb8c00638e17cf05d2c7d90bb09645ac", + "254164": "0x229b612b7935f34e11ca5a354f21410a3d3af2ee7e92ea93e3464cc0bb6dc78f", + "254165": "0x6819854a98d2c1e0ceb962aedaee0b1ab2570c6006f100a9b2e222d60aaf7d49", + "254166": "0x2ac7a9a449f2e5054c586fb57f6a1372397d5ce999ffb41d1b9a77ad15ea3414", + "254167": "0x755ab4e575593ebe9035396dd44ff5662f066922bfc31f390e126ddca2f476bf", + "254168": "0xefc4866ef8061c2d5d23520e13f2b835c85c0af93ccbb859c2b131fd6e289976", + "254169": "0x4ec88e9967413f18a0e5b3608f3c02c05bd729089c3ec3a4664e7d424ce04e50", + "254170": "0xcc9f8a148decb545d8cf0f40c0706f8e7104620c4c546addc1a218aa1f81aa90", + "254171": "0xec30a5cf49bd9ff32433d40276138be2cea4c391b1df9e277068f69ae73652bd", + "254172": "0x62b523f3cdf40547fca3cb7eb91815e6df9b830574ffd9d1607d4abb6f9f3a73", + "254173": "0xef7424d2c7fd2a9bca7c0d4c55503905c147b2b70c9bcad57db4989c46b2089d", + "254174": "0xfd9e2fa11e4b9106860894df001f7394df04d79e84a1f10ef09493c13c1c04f9", + "254175": "0x4114a9f78469ef4bee3d3af1ad714dd57853e810a13162fd0f1fae6ddd54d3ee", + "254176": "0x2aab88ba9d0f2a5745416daf9417e044b42ff0d9f853323ab2d07a09b5c67013", + "254177": "0x88eacb8a30b0ce70b12716acd86bd2241841959b7bfaba7ca522e0ee7c167e01", + "254178": "0xb61b699d55187fbd4125339f84734c7956a7713c3ba11688f766e1539b186374", + "254179": "0x10fed5c2037b71bd48943f60cbb19354fadbf9a0c1a167e5bc1688acf5eb5e3f", + "254180": "0x980a743d8fc1c14f0dfe3127979f9f8e5853797603756521e1ab7b9e3ad27c2a", + "254181": "0xc7fb787f2f23a48d5d5ea1e595385f0dd1d5159b338775516b2c1d41c16ec761", + "254182": "0xbef8253c05530905f6983414f7cd025a39217bbfe89b631f1c703e5b1b71cc84", + "254183": "0x302659ade583955e3bad7e63bedb7fa18cd356de2b3d1a169ec315d6e5a3d0ad", + "254184": "0xff8cd55b91b961218a8f5952026179d0bf0570618e36ed4c427bf2d60fe317a3", + "254185": "0x30903d2c9d4b8e937b3a572ba2a209f360598b1ccdb4817211a2df0571ca159d", + "254186": "0xcc75fe2fce32a781d72072abea99a2b73aa0d7fc8f46b29143582b90b92a8a90", + "254187": "0xcbfc65750b3c21d96698718589765a34863094955eaa4566755895da95fc9b9c", + "254188": "0x1712c42d31098308d6e1b53205ae12b9a3b300f267847932afcdd85484058931", + "254189": "0x27a0e9b125d0a09bfa76f48210a5b9d7fc58a02087cf23cdf6b28e8d93b566fa", + "254190": "0x2a8f25eacab2717a50ca43d8b31bed5bbd297ac8879b67d6b2a8c4b559281460", + "254191": "0xeb67c72a0bfd579c6891d5b2d3ad8b6d68dfc519eb59efbc744b300888d3e3a2", + "254192": "0x04aa9d55b48e28fc457ae2393e8c11d294ad70907156566f853406d3da27b386", + "254193": "0x4502a753bb5a68736ab2f1acccff5518430df5e63a0170dbad046326358802dd", + "254194": "0x99bd5cb9bb482243edd3f914a978dae70aa6af53997238d0fbcc4428c52d9c08", + "254195": "0xa67b0130b7b1394b1ed6d898a22f3ab071d5c70ddf2f290384f70530234c9c99", + "254196": "0x3fa85b2ecf6127672cb04e9a1f599555804f70feea8dba09e6601a091f378880", + "254197": "0xfb67e7ebc461c38a8f5379b083d6eb59c94cd15c23060e6eb7eb47c71fa0abe9", + "254198": "0x6457550e55f24ea4929445c6135cee9d87ac8944bbf50fbd93a8d893619fd8bf", + "254199": "0xda3c364b620188a8f5cdf70da86a541d022e6249109b06073b7a336108b34fd7", + "254200": "0x05c63b07077eafd60a02723303776d6a57dc45605463f3645ad7ab4c1989291b", + "254201": "0x91905289af8ce384d5afa6c20cd6557a343ccdac9c9c1c5d13820eb2bdaef1fa", + "254202": "0x255cac9e079cc14cf0d55604329e239720557afd1e0f986e45418c20d85bdb8b", + "254203": "0xaf859bf5ad02d19d1d4c11469173b6d64040bbfd229cd2bfc840a7a486b5ee09", + "254204": "0x0bb505d20425e2f845ed9c7eeabdd132f9fd7c1607fd2d42389d1ec2534662cb", + "254205": "0x0d8435a9cff7fb00cf803d666fb4be7b07a919130084d7d14555190f4388d92f", + "254206": "0x04261fcfcc1ee48197a2ace46fc9cbb158f41a363f993cc566bb87c101150c22", + "254207": "0xb4cd463330d7dd596790ec29e6ba28633ba9563157007fd0e152c54a13f94bb9", + "254208": "0xcf6c11891d79ab5ce1a8735c6fa2554dce8da8e0a0dfa9a9b563034f62972ba6", + "254209": "0x190ec8c8a7c587bdbfbf35f4f975ca6ddb89b41a2b5e7512dbed30b9f0923a0d", + "254210": "0x027c29a9d65708a4f4b678d15ac543378f8f78897a28f30ce455b1edd1bcb9a6", + "254211": "0x595fd8392ecd427501d0d7be945a56fc73a65818af5f606c92b995aa7b41613d", + "254212": "0xcc92045e7ec1cbc57968a140faa7482dc785a45f56405dc36ae6e1d05e1179dd", + "254213": "0x529a0dfd6602381c238ba7d4d8f0107fd5ce74942eb0ec77ba0b37659191d94a", + "254214": "0xef137cef6df1471072e5071b1f73c6611cb73f476cb8123425529abb826ccabf", + "254215": "0x7aac3b4b88be2c680e29a6cfdb7091aa7e3af35e41a9ab755beac435ea64b935", + "254216": "0xe7e368454d0e8ed744d808bc69052c72a75f3819a3cf75b921d7d0ab95b5cc29", + "254217": "0x937f0f1718cfd17e505ce36f891d592070afa2049e2f897e8af87b69eb56f342", + "254218": "0x84bb042e765de65a689a3d58202d639a64850bd6aefbd0d1e90058196f874517", + "254219": "0xd27618753a76d0fb7d09f9e09ffdddf946a0dcb4de0cd79a18fe9c538e23b3a0", + "254220": "0x29c8f6967930cf862029461db2cb0534e521c6291a6f0d23a74befb714b5ebf6", + "254221": "0xda35acba1faf4e445c280442e18bdaea359b3c253b348359b9968c98ae53c986", + "254222": "0xc6fc8356eb823fe131aeba3736804b68f964a7a579c5f324e3a4c4b639a34619", + "254223": "0xe4a3c8107f916ccdaaf772e0d882478561d0cd5dd496c70a0d5a4240c6e35115", + "254224": "0x03b97dc801f4aef71eaf06fa39c12dcdaf00295b24bf843af3584a851db158af", + "254225": "0x7e90ef2ead2ab921693b89483d3d01f64b69ee326668a47559b271aea9034470", + "254226": "0xe73be9b7be4d6133997129b7855b1e6049072aca21493d10d7015536a55b6202", + "254227": "0x181269790ac7a09a221aeaf26092a5a96428dffce726c51829914e1d8aa6cffb", + "254228": "0xa990c6af9bb4823b24df3b9b476e96ae986514196b7188db1ad8675c1bad133a", + "254229": "0x114f2aab33949e4ab96961ad8dd1015b1045a46a237b6bd8c5db37876479a490", + "254230": "0xf80976caa7ef1bdeec75cdf2993d440a814ac56d3267b3d80d7a0c6dde51306f", + "254231": "0x5136ee969c78b9743a281c3e1b64f70566dde0059b1a8dc413f6cf282b75419c", + "254232": "0x226699482dcf407e15529b166b5da191d3c938b0e22209bc97fca6117e011a94", + "254233": "0xb19485e02d3fcefecdbf632d69e12c6fa68b12ae1bd7ffaad4f34a63e2062993", + "254234": "0xb45060f5854e946dfd0407ddd12c056fd7ab748e8074a318af6d593f47d87196", + "254235": "0xe43516b2e1c9f50fcfe48ef120a43f9a5f308bf3a03eb93e5b2b02de575fdd85", + "254236": "0x62fe2c801ca3ea11f4efc769c903f1850feae060d166b2780273e0de36ef11f6", + "254237": "0xaa97396ceede3d5e6a6194e9bdea9e51157268487ba3ad87a01a7e4523ab032c", + "254238": "0xe677f58f7ff6cb71ea85874e3479ad6aca4b1c5ba03ffcd6fd768028ac973fc8", + "254239": "0x191ca284681272e0d99ddc547e2f84299fd8760e9feaafc274da532751596360", + "254240": "0x2db5c4aaeda66a2c6ce36638c31414cb48d545fbbf055275fe00b944deab5ff1", + "254241": "0x30b2ae28d5b62e11f89ec7b518d306d840bfae1356e1343f4e513f4288a80ed7", + "254242": "0xeed9c4af27855c7e6c3a9eb7a8cdbdb99f77e2ea8ff1c60472fb6dc812f21295", + "254243": "0xcbd113360afec5506f07fe0158595cb89de5915b7a04f481334423d33ac04f77", + "254244": "0xec89a841abc25ed83bce8e4dece37615e73a17cc68e697f2aab0538967e6dee7", + "254245": "0x8750998f251188aa782ad88a269613136e13163b808e6d86c175fa12fe89e198", + "254246": "0x3949efbd1e67390cc9b05e0bd9b3579d3c4e52599aeb6433f56c15e7ffa5bde2", + "254247": "0x63958f93098b82050dd936e9dceb1579e045de927a32f481f10bfc82153e82d9", + "254248": "0x33a5447294234e86d97b706d8b5c32ff535334f9c5fde6a832cb3ed9e7e70a53", + "254249": "0xe8982505a9f5b1921a16d172c3291918be118beb0474db3d76aff57ac61f2d8c", + "254250": "0x9a4b4fb0c5879cd71153e8d3755b6e37cebdf8efea59e1f7503cdabefbaab419", + "254251": "0x48d48c37c0cdf11fd592f001c3535fffec7000d79ad3711bc0542e17607b6f59", + "254252": "0x2c2d53a3dbce052399db3942f353caf73e4d69e73955f4ca26506337711e050b", + "254253": "0xa71e2795ccbe6a71123924fd2be6c1ae26dd88d80edae6023eff5f8854238b7f", + "254254": "0xd90d7abd127973be24b285fefb7b6f692df827cfaae6f6893f4afc8556a00c8f", + "254255": "0xf840ce12169ec7c3a882aa78ede2d91852cc01ae2e27076535c4d533a049a6e5", + "254256": "0xa5f37f58ba4d5387e23ecd0141ad8c30cdc91e222b232898b8b18bb2d2862993", + "254257": "0x6a41907f66d6c6dd33372c77bdd2fb527da8fd24137a38abbc5ab6424d773d14", + "254258": "0x98af953ab95f84af13f977b57bf84170f39128bfc30b17748e0b71df6990e734", + "254259": "0x856764bc184093cdf216c51c1a669f0ff5f384b5f55c88a50ddd99a46f112f33", + "254260": "0x614d1df124e3145027afdb090521100fb098263d796f25ef7f4aa2191f90386e", + "254261": "0xe4dcc9156ea3b471a4cda9d95a889d51c10bf32b1c6f3e0a41c07ea60473c296", + "254262": "0x1b1fcba870981e93855be5ad680f3414728309d23d7f35b280bf3d536055c3fc", + "254263": "0x5b954f3671a4306ce79d7bc64cc2cf02a5cbbf50b8aab14d1f6c7a66e14ed23d", + "254264": "0xf71475425852f270d97a7a7ab721e60e9d8e6731efef8ab90758c7d881dbe686", + "254265": "0x9cf6644fec85eaf4370038fee06efa648e169572376e69dd5eeee94081f65a31", + "254266": "0xee8923a3c19bb4bedc077c8e094dc0991a57fd308589a445af3029c6a8b70248", + "254267": "0x034a4812250f89d06b4a4eeddac68d0929cf9abf4648e361aa93e7ebadd32a3f", + "254268": "0x0cfebc0876c679192cdb0bf25c205a6a046db6a8f32cc02ade52c33cc58e1a5a", + "254269": "0x732228f5ccf4c52fdc0cd8385675425bfb39fafcf6a19a3734d6c872d4e90e7c", + "254270": "0x132a64af39d99da47831adf77e0a883fed92dd6cd33b863e6dbdd3409e5c11a2", + "254271": "0x68c46dc8c2f3aee2ec1999162736465965f85d4af8ed4d439dd1ee79660d125b", + "254272": "0x62bb256436b09803a694d66dd57a86244c2a9b3d9f61471bac8116b87e0440c3", + "254273": "0x47db9a800632dcef9c26cd3a6ace949c21808007040d11fce0cc40eb83b5e794", + "254274": "0xcc1f12a4013d122ebfb397f3c8c1102542baaf71d1e83ea691b4186c2640aca3", + "254275": "0xce3f1d03b7b8ba062f87493c470a6294326481f65e8cbe531a11923afb411261", + "254276": "0xbd689c92697150273a220bb26886e6219f4956297cd562c8a59558005261a3fe", + "254277": "0x143461d3e528e33c6d468a2b3e67a2662bddce58a3288143e2181aa67ea8409a", + "254278": "0xc31bd5086ab03989a65597ed06fc6e4d79cca630f8bcfcb2c1ed51c9477781a7", + "254279": "0x3d8f51b84b5f38833b1e43d0ec5e0e7937410078ade758ecc3d2d1f28ad5713d", + "254280": "0x5cf4fdd31c6eaf0a88bb7f057d61120fc5cbf2e8b134f38638493ed1e96ae7c2", + "254281": "0xd547c9723dc50bb546af2b70c32c2e2d24bc2c94b26b91900f893af753b19dae", + "254282": "0xa47b68eb33cbd46a386f8fc5e2262244edb440581c30ff338ece7c5e48ae866d", + "254283": "0x6a9094ce40c2c549719d3b3fefc13479ed3e8a4dbcdf127f3382ffb0bb050cdb", + "254284": "0x1c36772b15b3a5a91f8c7fa2e24a81f8834d86048994f5acf4fe76f4e12c8708", + "254285": "0xa4d443c959a87c9d0ae0c16d0246e6262f7f4663f72bffc72d3dbfbdd79e4c9e", + "254286": "0x0be05570d281f12c7d9e91db4b99528a03f401ad5e0bf77fc6a58ea76ae619fb", + "254287": "0x90ebf4a023ba3da63d4e8a0bc0a45e8c47501bd05f358c8c6651a54492ea59d9", + "254288": "0xdea411f8fc12323c0578f40420bb65401456f380804e64e3060dc22f3ece12e8", + "254289": "0xae3a5686d8d4d60f69a5f217a962c5cd8e256aadf08c79077c856f5716407180", + "254290": "0x19ba1ef014ecf4b48401c809149aa05db7b77b96a5ec8f374d30e5e31aebbb2a", + "254291": "0xba80b13a35e8a977d918fa58c4d797676abb062a50b2cfc174cbc4f514e9ba97", + "254292": "0xfca95955ee875e3bc3aeaeb2f1e69ef1b9b92eee3ac625c12b09cf028167318d", + "254293": "0x4bf841483d92817aaa20ab2a7e0ed11bf4812247d721b7d55f6d1c1383fc277c", + "254294": "0x0014c6787d259273ef3669c4ec7cfdbbb6c517de23ae861be584b533810ae09e", + "254295": "0x5ae58bfafc4813ede478caa63b87444f01b9e852b57da5225f74a6a0a5e25633", + "254296": "0xf3a1a8db998fcd113063fdf801cae8aa5a450c12d3c72eda61eb56b354bc18cf" + } }, "prestate": { "0x0047a8033cc6d6ca2ed5044674fd421f44884de8": { @@ -32,7 +291,7 @@ "storage": {} }, "0x1430236a73a97654bfb6851eb7e8facdfbe2cdb5": { - "balance": "0x0000000000000000000000000000000000000000000000000000000000000007", + "balance": "0x0000000000000000000000000000000000000000000000000000000000000006", "nonce": 0, "code": "0x606060405236156100a35760e060020a6000350463187c5903811461010d5780631bccca141461013d57806327e235e31461015e57806329f8df0d14610176578063444bdb1b1461017f57806351870150146101c857806367c18aa1146102db5780636d4ce63c1461038f5780636e723e24146103bb5780636f7bc9be146103c457806371ad7221146103dc578063a32da9d1146103fc578063e8b5e51f1461041d575b6104796305f5e0ff606090815260009081908190819081907f909c57d5c6ac08245cf2a6de3900e2b868513fa59099b92b27d8db823d92df9c90602090a17326588a9301b0428d95e6fc3a5024fce8bec12d51600160a060020a0333161461047b575b5050505050565b610479600160a060020a0333166000908152600b6020526040812054819081908190811415610793575b50505050565b610545600435600681600581101561000257500154600160a060020a031681565b6103a960043560056020526000908152604090205481565b6103a9600c5481565b6103a96000808080805b600c548310156108f1576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610189565b600480359081013560208102608081810160405260608381526103a99460249491939085019282918490808284375050604080518735600481013560208181028481018201909552818452989960449993985091909101955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a529798606498909750602492909201955093508392508501908490808284375050604080519635600481013560208181028a81018201909452818a5297986084989097506024929092019550935083925085019084908082843750949535945050505050600160a060020a0333166000908152600560205260408120548190819081908190111561071c57610710565b610479600435602435600160a060020a038216600090815260056020526040812080549082905590808311156103345783600160a060020a0316600083600502604051809050600060405180830381858888f150505050505b5b600c5481101561013757600160a060020a03841660008260058110156100025754600160a060020a031690911415905061038757600080826005811015610002578054600160a060020a031916905550505b600101610335565b33600160a060020a03166000908152600560205260409020545b60408051918252519081900360200190f35b6103a9600d5481565b6103a9600435600b6020526000908152604090205481565b600435600160a060020a03166000908152600560205260409020546103a9565b610545600435600081600581101561000257505054600160a060020a031681565b610479600160a060020a0333166000908152600b6020526040812054141561046c57600d543390600690600581101561000257018054600160a060020a0319169091179055600d805460010190555b6040600020805434019055565b005b5b60148410156104b457600494909402938084368110156100025760f860020a903581900481020490950194506001939093019261047c565b600160a060020a0385168082526005602081905260408320805490849055879550935082908402606082818181858883f150505050505b600c5481101561010657600160a060020a03831660008260058110156100025754600160a060020a031690911415905061053d57600080826005811015610002578054600160a060020a031916905550505b6001016104eb565b600160a060020a03166060908152602090f35b73393519c01e80b188d326d461e4639bc0e3f62af0905080600160a060020a031663a0a1cddb86612a3001338c8c8c8c6040518760e060020a0281526004018087815260200186600160a060020a03168152602001806020018060200180602001806020018581038552898181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038452888181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038352878181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050018581038252868181518152602001915080519060200190602002808383829060006004602084601f0104600302600f01f1509050019a50505050505050505050506000604051808303816000876161da5a03f11561000257505050346005600050600033600160a060020a0316815260200190815260200160002060005081905550336000600050600c600050546005811015610002578054600160a060020a03191690921790915550600c805460010190555b50505095945050505050565b5b600c54821015610757576005818084838110156100025754600160a060020a0316905250604082205402909201916001919091019061071d565b346005028330600160a060020a03163103101561055857604051600160a060020a033316908290349082818181858883f1935050505050610710565b5b600c548310156107d3576005818085838110156100025754600160a060020a031690525060208190526040822054029093019260019290920191610794565b30600160a060020a0316318411156107ea57610137565b9150805b600d5483101561082c57600b816006856005811015610002570154600160a060020a03169052602052604081205490910190600192909201916107ee565b6040812054600160a060020a03338116808452600b602052309091163186900391849004919091029182606082818181858883f19350505050506000600b600050600033600160a060020a03168152602001908152602001600020600050819055506000925082505b600d5483101561013757600160a060020a0333166006846005811015610002570154600160a060020a031614156108e5576000600684600581101561000257018054600160a060020a0319169055505b60019290920191610895565b9150805b600d5483101561093457600b816006856005811015610002570154600160a060020a0316905260205260408120549190910190600192909201916108f5565b5030600160a060020a031631929092039091046064029291505056", "storage": { @@ -43,7 +302,7 @@ }, "0x1dcb8d1f0fcc8cbc8c2d76528e877f915e299fbe": { "balance": "0x00000000000000000000000000000000000000000000004dd8645baf57193203", - "nonce": 0, + "nonce": 24649, "code": "0x", "storage": {} }, @@ -56,9 +315,7 @@ }, "expected": { "status": "success", - "tx_gas": 94849, - "dtvm_interpreter_gas": 295719, - "dtvm_multipass_gas": 296039 + "tx_gas": 94849 }, "meta": { "source": "rpc+trace_replayBlockTransactions", diff --git a/tests/evm/fixtures/legacy_call_repro/schema.json b/tests/evm/fixtures/legacy_call_repro/schema.json index fec41f484..1aaadbc07 100644 --- a/tests/evm/fixtures/legacy_call_repro/schema.json +++ b/tests/evm/fixtures/legacy_call_repro/schema.json @@ -95,7 +95,8 @@ "block_prev_randao", "block_gas_limit", "block_base_fee", - "tx_origin" + "tx_origin", + "block_hash" ], "properties": { "block_number": { @@ -125,6 +126,19 @@ "tx_origin": { "type": "string", "pattern": "^0x[0-9a-fA-F]{40}$" + }, + "block_hash": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$" + }, + "block_hashes": { + "type": "object", + "patternProperties": { + "^[0-9]+$": { + "type": "string", + "pattern": "^0x[0-9a-fA-F]{64}$" + } + } } } }, From 1329caad040e6726ad2b2ed66421551338f5b1a4 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Fri, 15 May 2026 12:04:30 +0800 Subject: [PATCH 03/19] fix(compiler): sync non-lifted evm stack state across block boundaries Spill the full tracked stack back to EVMInstance on non-lifted exits, reload stack metadata on non-lifted entries, and avoid re-finalizing dead blocks after a terminator so logical stack provenance stays aligned across block boundaries. --- src/action/evm_bytecode_visitor.h | 36 +++- .../evm_frontend/evm_mir_compiler.cpp | 24 +++ src/compiler/evm_frontend/evm_mir_compiler.h | 2 + src/tests/evm_interp_tests.cpp | 147 ++++++++++++++ src/tests/evm_jit_frontend_tests.cpp | 187 ++++++++++++++++-- 5 files changed, 373 insertions(+), 23 deletions(-) diff --git a/src/action/evm_bytecode_visitor.h b/src/action/evm_bytecode_visitor.h index 07a1726ad..e33197571 100644 --- a/src/action/evm_bytecode_visitor.h +++ b/src/action/evm_bytecode_visitor.h @@ -928,6 +928,9 @@ template class EVMByteCodeVisitor { } void finalizeBlockExit(std::vector Values, bool Materialize) { + if (!CurrentBlockNeedsFinalize) { + return; + } Builder.endMemoryCompileBlock(); CurBlockLinearPrecheckPlan = BlockLinearPrecheckPlan(); if (Materialize) { @@ -938,11 +941,22 @@ template class EVMByteCodeVisitor { for (const Operand &Opnd : Values) { Builder.stackPush(Opnd); } + Builder.syncTrackedStackMetadataToInstance(); } } InDeadCode = true; CurrentBlockLifted = false; CurrentBlockHiddenLiveInPrefixDepth = 0; + CurrentBlockNeedsFinalize = false; + } + + void abandonCurrentBlockAfterTrap() { + Builder.endMemoryCompileBlock(); + CurBlockLinearPrecheckPlan = BlockLinearPrecheckPlan(); + InDeadCode = true; + CurrentBlockLifted = false; + CurrentBlockHiddenLiveInPrefixDepth = 0; + CurrentBlockNeedsFinalize = false; } bool tryGetConstantJumpSuccessorPC(const EVMAnalyzer &Analyzer, @@ -1062,8 +1076,7 @@ template class EVMByteCodeVisitor { EntryDepth + static_cast(BlockInfo.MinStackHeight); if (MinDepth < 0) { Builder.handleTrap(common::ErrorCode::EVMStackUnderflow); - InDeadCode = true; - CurrentBlockLifted = false; + abandonCurrentBlockAfterTrap(); return false; } @@ -1071,8 +1084,7 @@ template class EVMByteCodeVisitor { EntryDepth + static_cast(BlockInfo.MaxStackHeight); if (MaxDepth > static_cast(EVM_MAX_STACK_SIZE)) { Builder.handleTrap(common::ErrorCode::EVMStackOverflow); - InDeadCode = true; - CurrentBlockLifted = false; + abandonCurrentBlockAfterTrap(); return false; } @@ -1083,6 +1095,7 @@ template class EVMByteCodeVisitor { const auto &BlockInfos = Analyzer.getBlockInfos(); ZEN_ASSERT(BlockInfos.count(PC) > 0 && "Block info not found"); Builder.beginMemoryCompileBlock(PC); + CurrentBlockNeedsFinalize = true; CurBlockLinearPrecheckPlan = BlockLinearPrecheckPlan(); const Byte *Bytecode = Ctx->getBytecode(); size_t BytecodeSize = Ctx->getBytecodeSize(); @@ -1112,18 +1125,17 @@ template class EVMByteCodeVisitor { if (static_cast(-BlockInfo.MinStackHeight) > EVM_MAX_STACK_SIZE) { Builder.handleTrap(common::ErrorCode::EVMStackUnderflow); - InDeadCode = true; - CurrentBlockLifted = false; + abandonCurrentBlockAfterTrap(); return; } if (static_cast(BlockInfo.MaxStackHeight) > EVM_MAX_STACK_SIZE) { Builder.handleTrap(common::ErrorCode::EVMStackOverflow); - InDeadCode = true; - CurrentBlockLifted = false; + abandonCurrentBlockAfterTrap(); return; } InDeadCode = false; if (!LiftedBlock) { + Builder.reloadTrackedStackFromInstance(); Builder.createStackCheckBlock(-BlockInfo.MinStackHeight, 1024 - BlockInfo.MaxStackHeight); } @@ -1174,7 +1186,12 @@ template class EVMByteCodeVisitor { } } - void handleEndBlock() { finalizeBlockExit(drainLogicalStack(), true); } + void handleEndBlock() { + if (!CurrentBlockNeedsFinalize) { + return; + } + finalizeBlockExit(drainLogicalStack(), true); + } void handleStop() { Builder.handleStop(); } @@ -1902,6 +1919,7 @@ template class EVMByteCodeVisitor { uint64_t CurrentBlockEntryPC = 0; bool CurrentBlockLifted = false; uint32_t CurrentBlockHiddenLiveInPrefixDepth = 0; + bool CurrentBlockNeedsFinalize = false; }; } // namespace COMPILER diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index 3b04a5784..1368c79e7 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -989,6 +989,30 @@ typename EVMMirBuilder::Operand EVMMirBuilder::stackGet(int32_t IndexFromTop) { return Operand(GetComponents, EVMType::UINT256); } +void EVMMirBuilder::reloadTrackedStackFromInstance() { + const int32_t StackSizeOffset = + zen::runtime::EVMInstance::getEVMStackSizeOffset(); + MInstruction *StackSize = getInstanceElement(&Ctx.I64Type, StackSizeOffset); + createInstruction(true, &(Ctx.VoidType), StackSize, + StackSizeVar->getVarIdx()); + + MInstruction *StackPtrOffset = createIntConstInstruction( + &Ctx.I64Type, zen::runtime::EVMInstance::getEVMStackOffset()); + MInstruction *StackBaseAddr = createInstruction( + false, OP_add, &Ctx.I64Type, InstanceAddr, StackPtrOffset); + MInstruction *StackTopAddr = createInstruction( + false, OP_add, &Ctx.I64Type, StackBaseAddr, StackSize); + createInstruction(true, &(Ctx.VoidType), StackTopAddr, + StackTopVar->getVarIdx()); +} + +void EVMMirBuilder::syncTrackedStackMetadataToInstance() { + const int32_t StackSizeOffset = + zen::runtime::EVMInstance::getEVMStackSizeOffset(); + MInstruction *StackSize = loadVariable(StackSizeVar); + setInstanceElement(&Ctx.I64Type, StackSize, StackSizeOffset); +} + void EVMMirBuilder::setTrackedStackDepth(uint32_t Depth) { MType *I64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); uint64_t StackBytes = static_cast(Depth) * 32ULL; diff --git a/src/compiler/evm_frontend/evm_mir_compiler.h b/src/compiler/evm_frontend/evm_mir_compiler.h index 65f35c745..467aae8d8 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.h +++ b/src/compiler/evm_frontend/evm_mir_compiler.h @@ -309,6 +309,8 @@ class EVMMirBuilder final { void stackSet(int32_t IndexFromTop, Operand SetValue); Operand stackGet(int32_t IndexFromTop); + void reloadTrackedStackFromInstance(); + void syncTrackedStackMetadataToInstance(); void setTrackedStackDepth(uint32_t Depth); Operand createStackEntryOperand(); void assignStackEntryOperand(const Operand &Dest, const Operand &Value); diff --git a/src/tests/evm_interp_tests.cpp b/src/tests/evm_interp_tests.cpp index 0bfe4cabc..669de153e 100644 --- a/src/tests/evm_interp_tests.cpp +++ b/src/tests/evm_interp_tests.cpp @@ -146,6 +146,23 @@ struct EVMExecutionResult { bool JITCompiled = false; }; +struct RecordingZenMockedEVMHost : public zen::evm::ZenMockedEVMHost { + std::vector RecordedCalls; + + evmc::Result call(const evmc_message &Msg) noexcept override { + if (Msg.depth > 0) { + RecordedCalls.push_back(Msg); + } + return zen::evm::ZenMockedEVMHost::call(Msg); + } +}; + +struct EVMCallCaptureResult { + evmc_status_code Status = EVMC_INTERNAL_ERROR; + bool JITCompiled = false; + std::vector Calls; +}; + EVMExecutionResult executeEvmBytecode(const std::string &ModuleName, const std::vector &Bytecode, common::RunMode Mode, @@ -221,6 +238,77 @@ EVMExecutionResult executeEvmBytecode(const std::string &ModuleName, return Exec; } +EVMCallCaptureResult +executeEvmBytecodeCapturingCalls(const std::string &ModuleName, + const std::vector &Bytecode, + common::RunMode Mode, evmc_revision Revision, + std::vector CallData = {}) { + EVMCallCaptureResult Empty; + + RuntimeConfig Config; + Config.Mode = Mode; + + auto MockedHost = std::make_unique(); + MockedHost->tx_context.tx_origin = zen::evm::DEFAULT_DEPLOYER_ADDRESS; + auto RT = Runtime::newEVMRuntime(Config, MockedHost.get()); + EXPECT_TRUE(RT != nullptr) << "Failed to create runtime"; + if (!RT) { + return Empty; + } + MockedHost->setRuntime(RT.get()); + + auto ModRet = RT->loadEVMModule(ModuleName, Bytecode.data(), Bytecode.size()); + EXPECT_TRUE(ModRet) << "Failed to load module: " << ModuleName; + if (!ModRet) { + return Empty; + } + EVMModule *Mod = *ModRet; + + Isolation *Iso = RT->createManagedIsolation(); + EXPECT_TRUE(Iso != nullptr) << "Failed to create isolation: " << ModuleName; + if (!Iso) { + return Empty; + } + + constexpr uint64_t GasLimit = 1'000'000; + const uint64_t IntrinsicGas = zen::evm::BASIC_EXECUTION_COST; + const uint64_t ExecutionGasLimit = GasLimit - IntrinsicGas; + + auto InstRet = Iso->createEVMInstance(*Mod, ExecutionGasLimit); + EXPECT_TRUE(InstRet) << "Failed to create instance: " << ModuleName; + if (!InstRet) { + return Empty; + } + EVMInstance *Inst = *InstRet; + Inst->setRevision(Revision); + + evmc_message Msg = { + .kind = EVMC_CALL, + .flags = 0u, + .depth = 0, + .gas = static_cast(ExecutionGasLimit), + .recipient = {}, + .sender = zen::evm::DEFAULT_DEPLOYER_ADDRESS, + .input_data = CallData.empty() ? nullptr : CallData.data(), + .input_size = CallData.size(), + .value = {}, + .create2_salt = {}, + .code_address = {}, + .code = reinterpret_cast(Mod->Code), + .code_size = Mod->CodeSize, + }; + + evmc::Result RawResult; + EVMCallCaptureResult Exec; +#ifdef ZEN_ENABLE_JIT + Exec.JITCompiled = Mod->getJITCode() != nullptr && Mod->getJITCodeSize() > 0; +#endif + EXPECT_NO_THROW({ RT->callEVMMain(*Inst, Msg, RawResult); }); + Exec.Status = RawResult.status_code; + Exec.Calls = MockedHost->RecordedCalls; + return Exec; +} + EVMExecutionResult executeEvmBytecodeFile(const std::string &FilePath, common::RunMode Mode, std::vector CallData = {}) { @@ -688,4 +776,63 @@ TEST(EVMRegressionTest, Issue488_PCAsAddmodAugend_InterpMatchesMultipass) { EXPECT_EQ(InterpExec.OutputHex, "0000000000000000000000000000000000000000000000000000000000000006"); } + +TEST(EVMRegressionTest, + FrontierCallRecipientAfterMaterializedMergeMatchesInterpreter) { + const std::vector Bytecode = { + 0x60, 0xaa, // PUSH1 0xaa + 0x60, 0xbb, // PUSH1 0xbb + 0x60, 0x00, // PUSH1 0x00 + 0x35, // CALLDATALOAD + 0x60, 0x0f, // PUSH1 target + 0x57, // JUMPI + 0x60, 0xcc, // PUSH1 0xcc + 0x60, 0x0f, // PUSH1 target + 0x56, // JUMP + 0x5b, // JUMPDEST + 0x60, 0x00, // PUSH1 retSize + 0x60, 0x00, // PUSH1 retOffset + 0x60, 0x00, // PUSH1 argsSize + 0x60, 0x00, // PUSH1 argsOffset + 0x60, 0x00, // PUSH1 value + 0x86, // DUP7 + 0x90, // SWAP1 + 0x90, // SWAP1 + 0x60, 0x20, // PUSH1 gas + 0xf1, // CALL + 0x00 // STOP + }; + + const std::vector ZeroCalldata(32, 0x00); + auto InterpExec = executeEvmBytecodeCapturingCalls( + "frontier_call_merge_interp", Bytecode, common::RunMode::InterpMode, + EVMC_FRONTIER, ZeroCalldata); + auto MultipassExec = executeEvmBytecodeCapturingCalls( + "frontier_call_merge_multipass", Bytecode, common::RunMode::MultipassMode, + EVMC_FRONTIER, ZeroCalldata); + +#ifdef ZEN_ENABLE_JIT + EXPECT_TRUE(MultipassExec.JITCompiled); +#endif + + ASSERT_EQ(InterpExec.Status, EVMC_SUCCESS); + ASSERT_EQ(MultipassExec.Status, EVMC_SUCCESS); + ASSERT_EQ(InterpExec.Calls.size(), 1U); + ASSERT_EQ(MultipassExec.Calls.size(), 1U); + + auto expectAddressEq = [](const evmc::address &Actual, + const evmc::address &Expected) { + EXPECT_EQ(std::memcmp(Actual.bytes, Expected.bytes, sizeof(Actual.bytes)), + 0); + }; + + EXPECT_EQ(InterpExec.Calls[0].kind, EVMC_CALL); + EXPECT_EQ(MultipassExec.Calls[0].kind, EVMC_CALL); + + evmc::address ExpectedRecipient{}; + ExpectedRecipient.bytes[19] = 0xbb; + expectAddressEq(InterpExec.Calls[0].recipient, MultipassExec.Calls[0].recipient); + expectAddressEq(InterpExec.Calls[0].recipient, ExpectedRecipient); + expectAddressEq(MultipassExec.Calls[0].recipient, ExpectedRecipient); +} #endif diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index fc3c2f88b..f075bb318 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -129,11 +129,11 @@ class MockEVMBuilder { if (!EnableRuntimeStackChecks) { return; } - if (RuntimeStack.size() < static_cast(std::max(MinSize, 0))) { + if (CurrentStack.size() < static_cast(std::max(MinSize, 0))) { Trapped = true; return; } - if (RuntimeStack.size() > static_cast(std::max(MaxSize, 0))) { + if (CurrentStack.size() > static_cast(std::max(MaxSize, 0))) { Trapped = true; } } @@ -150,35 +150,39 @@ class MockEVMBuilder { void stackPush(Operand PushValue) { Stats[CurrentOpcode].StackPushCount++; - RuntimeStack.push_back(PushValue); + CurrentStack.push_back(PushValue); } Operand stackPop() { Stats[CurrentOpcode].StackPopCount++; - ZEN_ASSERT(!RuntimeStack.empty() && "mock runtime stack underflow"); - Operand Top = RuntimeStack.back(); - RuntimeStack.pop_back(); + ZEN_ASSERT(!CurrentStack.empty() && "mock tracked stack underflow"); + Operand Top = CurrentStack.back(); + CurrentStack.pop_back(); return Top; } void stackSet(int32_t IndexFromTop, Operand SetValue) { Stats[CurrentOpcode].StackSetCount++; - size_t Index = RuntimeStack.size() - static_cast(IndexFromTop) - 1; - RuntimeStack[Index] = SetValue; + size_t Index = CurrentStack.size() - static_cast(IndexFromTop) - 1; + CurrentStack[Index] = SetValue; } Operand stackGet(int32_t IndexFromTop) { Stats[CurrentOpcode].StackGetCount++; - size_t Index = RuntimeStack.size() - static_cast(IndexFromTop) - 1; - return RuntimeStack[Index]; + size_t Index = CurrentStack.size() - static_cast(IndexFromTop) - 1; + return CurrentStack[Index]; } void setTrackedStackDepth(uint32_t Depth) { - if (RuntimeStack.size() > Depth) { - RuntimeStack.resize(Depth); + if (CurrentStack.size() > Depth) { + CurrentStack.resize(Depth); } } + void reloadTrackedStackFromInstance() { CurrentStack = RuntimeStack; } + + void syncTrackedStackMetadataToInstance() { RuntimeStack = CurrentStack; } + Operand createStackEntryOperand() { return Operand(std::make_shared( MockOperand::U256Value{0, 0, 0, 0})); @@ -191,6 +195,18 @@ class MockEVMBuilder { void spillTrackedStack(const std::vector &TrackedStack) { RuntimeStack = TrackedStack; + CurrentStack = RuntimeStack; + } + + void spillTrackedStackPreservingPrefix( + const std::vector &TrackedStack, uint32_t PrefixDepth) { + if (RuntimeStack.size() < PrefixDepth) { + RuntimeStack.resize(PrefixDepth); + } + RuntimeStack.resize(PrefixDepth); + RuntimeStack.insert(RuntimeStack.end(), TrackedStack.begin(), + TrackedStack.end()); + CurrentStack = RuntimeStack; } void setCurrentDebugBlockPC(uint64_t) {} @@ -218,8 +234,11 @@ class MockEVMBuilder { template void handleLogWithTopics(Args...) {} - Operand handleCall(Operand, Operand, Operand, Operand, Operand, Operand, - Operand) { + Operand handleCall(Operand, Operand ToAddr, Operand, Operand, Operand, + Operand, Operand) { + LastCallRecipient = ToAddr.resolvedValue(); + HasLastCallRecipient = true; + ++CallCount; return Operand(0); } @@ -332,6 +351,15 @@ class MockEVMBuilder { return RuntimeStack.back().resolvedValue(); } + bool hasLastCallRecipient() const { return HasLastCallRecipient; } + + MockOperand::U256Value lastCallRecipient() const { + ZEN_ASSERT(HasLastCallRecipient && "mock call recipient is missing"); + return LastCallRecipient; + } + + uint32_t callCount() const { return CallCount; } + bool Trapped = false; bool Undefined = false; @@ -340,8 +368,12 @@ class MockEVMBuilder { uint8_t CurrentOpcode = 0xff; std::array Stats = {}; std::vector RuntimeStack; + std::vector CurrentStack; MockOperand::U256Value LastPushValue = {0, 0, 0, 0}; bool HasLastPushValue = false; + MockOperand::U256Value LastCallRecipient = {0, 0, 0, 0}; + bool HasLastCallRecipient = false; + uint32_t CallCount = 0; #undef MOCK_OPERAND_STUB #undef MOCK_VOID_STUB @@ -690,6 +722,133 @@ TEST(EVMJITFrontendVisitorTest, EXPECT_EQ(Builder.topStackValue()[0], 0xaaU); } +TEST(EVMJITFrontendVisitorTest, + LiftedBlockWithHiddenPrefixDoesNotDuplicatePrefixOnMaterialize) { + const std::vector Bytecode = { + 0x60, 0xaa, // PUSH1 0xaa + 0x60, 0xbb, // PUSH1 0xbb + 0x5b, // JUMPDEST + 0x50, // POP + 0x00 // STOP + }; + + const EVMAnalyzer Analyzer = analyzeBytecode(Bytecode); + const auto *EntryBlock = findBlock(Analyzer, 0); + const auto *JumpDestBlock = findBlock(Analyzer, 4); + ASSERT_NE(EntryBlock, nullptr); + ASSERT_NE(JumpDestBlock, nullptr); + EXPECT_TRUE(EntryBlock->CanLiftStack); + EXPECT_TRUE(JumpDestBlock->CanLiftStack); + EXPECT_EQ(JumpDestBlock->ResolvedEntryStackDepth, 2); + EXPECT_EQ(JumpDestBlock->FullEntryStateDepth, 2); + EXPECT_EQ(JumpDestBlock->EntryStackDepth, 1); + EXPECT_EQ(JumpDestBlock->HiddenLiveInPrefixDepth, 1); + + COMPILER::EVMFrontendContext Ctx; + Ctx.setRevision(EVMC_CANCUN); + Ctx.setBytecode(reinterpret_cast(Bytecode.data()), + Bytecode.size()); + + MockEVMBuilder Builder; + COMPILER::EVMByteCodeVisitor Visitor(Builder, &Ctx); + EXPECT_TRUE(Visitor.compile()); + EXPECT_FALSE(Builder.Trapped); + EXPECT_FALSE(Builder.Undefined); + EXPECT_EQ(Builder.runtimeStackDepth(), 1U); + EXPECT_EQ(Builder.topStackValue()[0], 0xaaU); +} + +TEST(EVMJITFrontendVisitorTest, + HiddenPrefixMustSurviveLiftedToLiftedFallthroughTransfer) { + const std::vector Bytecode = { + 0x60, 0xaa, // PUSH1 0xaa + 0x60, 0xbb, // PUSH1 0xbb + 0x5b, // JUMPDEST + 0x5f, // PUSH0 + 0x50, // POP + 0x5b, // JUMPDEST + 0x50, // POP + 0x00 // STOP + }; + + const EVMAnalyzer Analyzer = analyzeBytecode(Bytecode); + const auto *MiddleBlock = findBlock(Analyzer, 4); + const auto *ExitBlock = findBlock(Analyzer, 7); + ASSERT_NE(MiddleBlock, nullptr); + ASSERT_NE(ExitBlock, nullptr); + EXPECT_TRUE(MiddleBlock->CanLiftStack); + EXPECT_TRUE(ExitBlock->CanLiftStack); + EXPECT_EQ(MiddleBlock->ResolvedEntryStackDepth, 2); + EXPECT_EQ(MiddleBlock->EntryStackDepth, 0); + EXPECT_EQ(MiddleBlock->HiddenLiveInPrefixDepth, 2); + EXPECT_EQ(ExitBlock->ResolvedEntryStackDepth, 2); + EXPECT_EQ(ExitBlock->EntryStackDepth, 1); + EXPECT_EQ(ExitBlock->HiddenLiveInPrefixDepth, 1); + + COMPILER::EVMFrontendContext Ctx; + Ctx.setRevision(EVMC_CANCUN); + Ctx.setBytecode(reinterpret_cast(Bytecode.data()), + Bytecode.size()); + + MockEVMBuilder Builder; + COMPILER::EVMByteCodeVisitor Visitor(Builder, &Ctx); + EXPECT_TRUE(Visitor.compile()); + EXPECT_FALSE(Builder.Trapped); + EXPECT_FALSE(Builder.Undefined); + EXPECT_EQ(Builder.runtimeStackDepth(), 1U); + EXPECT_EQ(Builder.topStackValue()[0], 0xaaU); +} + +TEST(EVMJITFrontendVisitorTest, + NonLiftedJumpTargetKeepsCallRecipientFromCommittedEntryStack) { + const std::vector Bytecode = { + 0x60, 0xaa, // PUSH1 0xaa + 0x60, 0xbb, // PUSH1 0xbb + 0x60, 0x00, // PUSH1 0x00 + 0x35, // CALLDATALOAD + 0x60, 0x0f, // PUSH1 target + 0x57, // JUMPI + 0x60, 0xcc, // PUSH1 0xcc + 0x60, 0x0f, // PUSH1 target + 0x56, // JUMP + 0x5b, // JUMPDEST + 0x60, 0x00, // PUSH1 retSize + 0x60, 0x00, // PUSH1 retOffset + 0x60, 0x00, // PUSH1 argsSize + 0x60, 0x00, // PUSH1 argsOffset + 0x60, 0x00, // PUSH1 value + 0x86, // DUP7 + 0x90, // SWAP1 + 0x90, // SWAP1 + 0x60, 0x20, // PUSH1 gas + 0xf1, // CALL + 0x00 // STOP + }; + + const EVMAnalyzer Analyzer = analyzeBytecode(Bytecode); + const auto *TargetBlock = findBlock(Analyzer, 0x0f); + ASSERT_NE(TargetBlock, nullptr); + EXPECT_FALSE(TargetBlock->CanLiftStack); + EXPECT_EQ(TargetBlock->ResolvedEntryStackDepth, -1); + + COMPILER::EVMFrontendContext Ctx; + Ctx.setRevision(EVMC_FRONTIER); + Ctx.setBytecode(reinterpret_cast(Bytecode.data()), + Bytecode.size()); + + MockEVMBuilder Builder; + COMPILER::EVMByteCodeVisitor Visitor(Builder, &Ctx); + EXPECT_TRUE(Visitor.compile()); + EXPECT_FALSE(Builder.Trapped); + EXPECT_FALSE(Builder.Undefined); + ASSERT_EQ(Builder.callCount(), 1U); + ASSERT_TRUE(Builder.hasLastCallRecipient()); + EXPECT_EQ(Builder.lastCallRecipient()[0], 0xbbU); + EXPECT_EQ(Builder.lastCallRecipient()[1], 0U); + EXPECT_EQ(Builder.lastCallRecipient()[2], 0U); + EXPECT_EQ(Builder.lastCallRecipient()[3], 0U); +} + TEST(EVMJITFrontendVisitorTest, UndefinedInstructionAfterProducerDoesNotTriggerStackOverflowTrap) { const std::vector Bytecode = { From 83c540e737a1f206b38d9339851f0985f2695fae Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Fri, 15 May 2026 12:45:54 +0800 Subject: [PATCH 04/19] style(test): format legacy call regression tests --- src/tests/evm_interp_tests.cpp | 3 ++- src/tests/evm_jit_frontend_tests.cpp | 5 ++-- src/tests/evm_legacy_call_repro_tests.cpp | 33 +++++++++++++---------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/tests/evm_interp_tests.cpp b/src/tests/evm_interp_tests.cpp index 669de153e..2b3dfa6ad 100644 --- a/src/tests/evm_interp_tests.cpp +++ b/src/tests/evm_interp_tests.cpp @@ -831,7 +831,8 @@ TEST(EVMRegressionTest, evmc::address ExpectedRecipient{}; ExpectedRecipient.bytes[19] = 0xbb; - expectAddressEq(InterpExec.Calls[0].recipient, MultipassExec.Calls[0].recipient); + expectAddressEq(InterpExec.Calls[0].recipient, + MultipassExec.Calls[0].recipient); expectAddressEq(InterpExec.Calls[0].recipient, ExpectedRecipient); expectAddressEq(MultipassExec.Calls[0].recipient, ExpectedRecipient); } diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index f075bb318..6cecf0ffb 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -198,8 +198,9 @@ class MockEVMBuilder { CurrentStack = RuntimeStack; } - void spillTrackedStackPreservingPrefix( - const std::vector &TrackedStack, uint32_t PrefixDepth) { + void + spillTrackedStackPreservingPrefix(const std::vector &TrackedStack, + uint32_t PrefixDepth) { if (RuntimeStack.size() < PrefixDepth) { RuntimeStack.resize(PrefixDepth); } diff --git a/src/tests/evm_legacy_call_repro_tests.cpp b/src/tests/evm_legacy_call_repro_tests.cpp index 2342c7699..6d188f96c 100644 --- a/src/tests/evm_legacy_call_repro_tests.cpp +++ b/src/tests/evm_legacy_call_repro_tests.cpp @@ -96,8 +96,10 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { rapidjson::IStreamWrapper ISW(File); rapidjson::Document Doc; Doc.ParseStream(ISW); - EXPECT_FALSE(Doc.HasParseError()) << "parse error in fixture: " << Path.string(); - EXPECT_TRUE(Doc.IsObject()) << "fixture root must be object: " << Path.string(); + EXPECT_FALSE(Doc.HasParseError()) + << "parse error in fixture: " << Path.string(); + EXPECT_TRUE(Doc.IsObject()) + << "fixture root must be object: " << Path.string(); ParsedFixture Fixture; Fixture.FixturePath = Path.string(); @@ -130,7 +132,8 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { Fixture.TxContext.tx_origin = zen::utils::parseAddress(Env["tx_origin"].GetString()); if (Env.HasMember("block_hash") && Env["block_hash"].IsString()) { - // Parsed later into host.block_hash (MockedHost has single block_hash slot). + // Parsed later into host.block_hash (MockedHost has single block_hash + // slot). } if (Env.HasMember("block_hashes") && Env["block_hashes"].IsObject()) { for (auto It = Env["block_hashes"].MemberBegin(); @@ -170,7 +173,8 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { for (auto Sit = Storage.MemberBegin(); Sit != Storage.MemberEnd(); ++Sit) { evmc::StorageValue SV{}; SV.current = zen::utils::parseBytes32(Sit->value.GetString()); - Entry.Account.storage[zen::utils::parseBytes32(Sit->name.GetString())] = SV; + Entry.Account.storage[zen::utils::parseBytes32(Sit->name.GetString())] = + SV; } if (AddressStr == To) { @@ -197,8 +201,8 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { return Fixture; } -ZenMockedEVMHost::TransactionExecutionResult runFixture( - const ParsedFixture &Fixture, common::RunMode Mode) { +ZenMockedEVMHost::TransactionExecutionResult +runFixture(const ParsedFixture &Fixture, common::RunMode Mode) { RuntimeConfig Config; Config.Format = common::InputFormat::EVM; Config.Mode = Mode; @@ -224,10 +228,11 @@ ZenMockedEVMHost::TransactionExecutionResult runFixture( Host->setRuntime(RT.get()); ZenMockedEVMHost::TransactionExecutionConfig ExecConfig; - ExecConfig.ModuleName = Fixture.CaseName + "-" + - (Mode == common::RunMode::InterpMode ? "interp" - : "multipass"); - ExecConfig.Bytecode = reinterpret_cast(Fixture.Bytecode.data()); + ExecConfig.ModuleName = + Fixture.CaseName + "-" + + (Mode == common::RunMode::InterpMode ? "interp" : "multipass"); + ExecConfig.Bytecode = + reinterpret_cast(Fixture.Bytecode.data()); ExecConfig.BytecodeSize = Fixture.Bytecode.size(); ExecConfig.Message = Fixture.Message; ExecConfig.GasLimit = Fixture.GasLimit; @@ -266,10 +271,10 @@ VmExecutionResult runFixtureViaDTVMApi(const ParsedFixture &Fixture, return {}; } const auto &Code = It->second.code; - evmc_result Raw = Vm->execute( - Vm, &evmc::MockedHost::get_interface(), - reinterpret_cast(Host.get()), Fixture.Revision, &Msg, - Code.data(), Code.size()); + evmc_result Raw = + Vm->execute(Vm, &evmc::MockedHost::get_interface(), + reinterpret_cast(Host.get()), + Fixture.Revision, &Msg, Code.data(), Code.size()); VmExecutionResult Result; Result.Success = true; From 58046806bfb482b773651e4affcdd68ea3993500 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Fri, 15 May 2026 13:20:17 +0800 Subject: [PATCH 05/19] test(evm): isolate dtvmapi legacy call coverage --- src/tests/CMakeLists.txt | 20 ++++++++++++++++---- src/tests/evm_legacy_call_repro_tests.cpp | 6 ++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 13abd63e5..a8db6ac43 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -72,7 +72,8 @@ if(ZEN_ENABLE_SPEC_TEST) evm_test_fixtures.cpp evm_test_helpers.cpp ) add_executable(evmLegacyCallReproTests evm_legacy_call_repro_tests.cpp) - # Only build evmFallbackExecutionTests if dtvmapi library is available + # Only build dtvmapi-specific regression tests if the shared library is + # available. if(ZEN_ENABLE_LIBEVM) add_executable(evmFallbackExecutionTests evm_fallback_execution_tests.cpp) add_executable(evmModuleCacheTests evm_module_cache_tests.cpp) @@ -144,10 +145,14 @@ if(ZEN_ENABLE_SPEC_TEST) ) target_link_libraries( evmLegacyCallReproTests - PRIVATE dtvmcore rapidjson dtvmapi gtest_main -fsanitize=address + PRIVATE dtvmcore rapidjson gtest_main -fsanitize=address PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) + target_link_libraries( + evmLegacyCallReproTests + PRIVATE dtvmapi + ) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main -fsanitize=address @@ -219,13 +224,16 @@ if(ZEN_ENABLE_SPEC_TEST) evmLegacyCallReproTests PRIVATE dtvmcore rapidjson - dtvmapi gtest_main -fsanitize=address -static-libasan PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) + target_link_libraries( + evmLegacyCallReproTests + PRIVATE dtvmapi + ) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main -fsanitize=address -static-libasan @@ -280,10 +288,14 @@ if(ZEN_ENABLE_SPEC_TEST) ) target_link_libraries( evmLegacyCallReproTests - PRIVATE dtvmcore rapidjson dtvmapi gtest_main + PRIVATE dtvmcore rapidjson gtest_main PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) + target_link_libraries( + evmLegacyCallReproTests + PRIVATE dtvmapi + ) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main diff --git a/src/tests/evm_legacy_call_repro_tests.cpp b/src/tests/evm_legacy_call_repro_tests.cpp index 6d188f96c..e3d3ab072 100644 --- a/src/tests/evm_legacy_call_repro_tests.cpp +++ b/src/tests/evm_legacy_call_repro_tests.cpp @@ -16,7 +16,9 @@ #include "evm_test_host.hpp" #include "runtime/runtime.h" #include "utils/evm.h" +#ifdef ZEN_ENABLE_LIBEVM #include "vm/dt_evmc_vm.h" +#endif #include using namespace zen; @@ -248,6 +250,7 @@ struct VmExecutionResult { uint64_t GasCharged = 0; }; +#ifdef ZEN_ENABLE_LIBEVM VmExecutionResult runFixtureViaDTVMApi(const ParsedFixture &Fixture, const char *ModeValue) { auto Host = std::make_unique(); @@ -288,6 +291,7 @@ VmExecutionResult runFixtureViaDTVMApi(const ParsedFixture &Fixture, Vm->destroy(Vm); return Result; } +#endif void assertExpectedStatus(const std::string &ExpectedStatus, const evmc_status_code ActualStatus) { @@ -336,6 +340,7 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesInInterpreterAndMultipass) { } } +#ifdef ZEN_ENABLE_LIBEVM TEST(EVMLegacyCallReproTest, ExecuteFixturesViaDTVMApi) { const auto FixtureDir = getLegacyReproFixtureDir(); const std::vector FixtureFiles = {"block_254277_tx_0.json"}; @@ -351,3 +356,4 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesViaDTVMApi) { EXPECT_EQ(Interp.GasCharged, Multi.GasCharged); } } +#endif From 8f21f9b3c92662b2961a1242b6c6ff4d4e28b8fe Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Fri, 15 May 2026 13:28:20 +0800 Subject: [PATCH 06/19] style(test): format legacy call cmake wiring --- src/tests/CMakeLists.txt | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index a8db6ac43..9f47535b8 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -149,10 +149,7 @@ if(ZEN_ENABLE_SPEC_TEST) PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) - target_link_libraries( - evmLegacyCallReproTests - PRIVATE dtvmapi - ) + target_link_libraries(evmLegacyCallReproTests PRIVATE dtvmapi) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main -fsanitize=address @@ -222,18 +219,12 @@ if(ZEN_ENABLE_SPEC_TEST) ) target_link_libraries( evmLegacyCallReproTests - PRIVATE dtvmcore - rapidjson - gtest_main - -fsanitize=address + PRIVATE dtvmcore rapidjson gtest_main -fsanitize=address -static-libasan PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) - target_link_libraries( - evmLegacyCallReproTests - PRIVATE dtvmapi - ) + target_link_libraries(evmLegacyCallReproTests PRIVATE dtvmapi) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main -fsanitize=address -static-libasan @@ -292,10 +283,7 @@ if(ZEN_ENABLE_SPEC_TEST) PUBLIC ${GTEST_BOTH_LIBRARIES} ) if(ZEN_ENABLE_LIBEVM) - target_link_libraries( - evmLegacyCallReproTests - PRIVATE dtvmapi - ) + target_link_libraries(evmLegacyCallReproTests PRIVATE dtvmapi) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main From a828a7a1984f382273ec14f03ea90270d9aecfed Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Fri, 15 May 2026 15:46:03 +0800 Subject: [PATCH 07/19] test(evm): address PR507 review feedback --- src/tests/evm_interp_tests.cpp | 1 + src/tests/evm_legacy_call_repro_tests.cpp | 180 +++++++++++++++++++--- 2 files changed, 159 insertions(+), 22 deletions(-) diff --git a/src/tests/evm_interp_tests.cpp b/src/tests/evm_interp_tests.cpp index 2b3dfa6ad..cb975510e 100644 --- a/src/tests/evm_interp_tests.cpp +++ b/src/tests/evm_interp_tests.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2025 the DTVM authors. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +#include #include #include #include diff --git a/src/tests/evm_legacy_call_repro_tests.cpp b/src/tests/evm_legacy_call_repro_tests.cpp index e3d3ab072..5d76ac7ea 100644 --- a/src/tests/evm_legacy_call_repro_tests.cpp +++ b/src/tests/evm_legacy_call_repro_tests.cpp @@ -28,6 +28,7 @@ using namespace zen::runtime; namespace { struct ParsedFixture { + bool IsValid = false; std::string CaseName; std::string FixturePath; evmc_revision Revision = EVMC_FRONTIER; @@ -42,6 +43,7 @@ struct ParsedFixture { uint64_t ExpectedTxGas = 0; uint64_t ExpectedDTVMInterpGas = 0; uint64_t ExpectedDTVMMultipassGas = 0; + std::optional BlockHash; std::unordered_map BlockHashes; }; @@ -92,16 +94,57 @@ std::filesystem::path getLegacyReproFixtureDir() { } ParsedFixture loadFixture(const std::filesystem::path &Path) { + auto failFixture = [&](const std::string &Message) { + ADD_FAILURE() << Message << ": " << Path.string(); + ParsedFixture Fixture; + Fixture.FixturePath = Path.string(); + return Fixture; + }; + auto requireObjectMember = [&](const rapidjson::Value &Object, + const char *Name) -> bool { + return Object.HasMember(Name) && Object[Name].IsObject(); + }; + auto requireStringMember = [&](const rapidjson::Value &Object, + const char *Name) -> bool { + return Object.HasMember(Name) && Object[Name].IsString(); + }; + auto requireUint64Member = [&](const rapidjson::Value &Object, + const char *Name) -> bool { + return Object.HasMember(Name) && Object[Name].IsUint64(); + }; + std::ifstream File(Path); - EXPECT_TRUE(File.is_open()) << "failed to open fixture: " << Path.string(); + if (!File.is_open()) { + return failFixture("failed to open fixture"); + } rapidjson::IStreamWrapper ISW(File); rapidjson::Document Doc; Doc.ParseStream(ISW); - EXPECT_FALSE(Doc.HasParseError()) - << "parse error in fixture: " << Path.string(); - EXPECT_TRUE(Doc.IsObject()) - << "fixture root must be object: " << Path.string(); + if (Doc.HasParseError()) { + return failFixture("parse error in fixture"); + } + if (!Doc.IsObject()) { + return failFixture("fixture root must be object"); + } + if (!requireStringMember(Doc, "case_name")) { + return failFixture("fixture.case_name must be a string"); + } + if (!requireStringMember(Doc, "revision")) { + return failFixture("fixture.revision must be a string"); + } + if (!requireObjectMember(Doc, "tx")) { + return failFixture("fixture.tx must be an object"); + } + if (!requireObjectMember(Doc, "env")) { + return failFixture("fixture.env must be an object"); + } + if (!requireObjectMember(Doc, "prestate")) { + return failFixture("fixture.prestate must be an object"); + } + if (!requireObjectMember(Doc, "expected")) { + return failFixture("fixture.expected must be an object"); + } ParsedFixture Fixture; Fixture.FixturePath = Path.string(); @@ -113,6 +156,61 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { const auto &Prestate = Doc["prestate"]; const auto &Expected = Doc["expected"]; + if (!requireStringMember(Tx, "from")) { + return failFixture("fixture.tx.from must be a string"); + } + if (!requireStringMember(Tx, "to")) { + return failFixture("fixture.tx.to must be a string"); + } + if (!requireStringMember(Tx, "input")) { + return failFixture("fixture.tx.input must be a string"); + } + if (!requireUint64Member(Tx, "gas_limit")) { + return failFixture("fixture.tx.gas_limit must be a uint64"); + } + if (!requireStringMember(Tx, "gas_price")) { + return failFixture("fixture.tx.gas_price must be a string"); + } + if (!requireStringMember(Tx, "value")) { + return failFixture("fixture.tx.value must be a string"); + } + if (!requireUint64Member(Env, "block_number")) { + return failFixture("fixture.env.block_number must be a uint64"); + } + if (!requireUint64Member(Env, "block_timestamp")) { + return failFixture("fixture.env.block_timestamp must be a uint64"); + } + if (!requireStringMember(Env, "block_coinbase")) { + return failFixture("fixture.env.block_coinbase must be a string"); + } + if (!requireStringMember(Env, "block_prev_randao")) { + return failFixture("fixture.env.block_prev_randao must be a string"); + } + if (!requireUint64Member(Env, "block_gas_limit")) { + return failFixture("fixture.env.block_gas_limit must be a uint64"); + } + if (!requireStringMember(Env, "block_base_fee")) { + return failFixture("fixture.env.block_base_fee must be a string"); + } + if (!requireStringMember(Env, "tx_origin")) { + return failFixture("fixture.env.tx_origin must be a string"); + } + if (!requireStringMember(Expected, "status")) { + return failFixture("fixture.expected.status must be a string"); + } + if (!requireUint64Member(Expected, "tx_gas")) { + return failFixture("fixture.expected.tx_gas must be a uint64"); + } + if (Expected.HasMember("dtvm_interpreter_gas") && + !Expected["dtvm_interpreter_gas"].IsUint64()) { + return failFixture( + "fixture.expected.dtvm_interpreter_gas must be a uint64"); + } + if (Expected.HasMember("dtvm_multipass_gas") && + !Expected["dtvm_multipass_gas"].IsUint64()) { + return failFixture("fixture.expected.dtvm_multipass_gas must be a uint64"); + } + const std::string From = Tx["from"].GetString(); const std::string To = Tx["to"].GetString(); const std::string InputHex = Tx["input"].GetString(); @@ -134,12 +232,14 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { Fixture.TxContext.tx_origin = zen::utils::parseAddress(Env["tx_origin"].GetString()); if (Env.HasMember("block_hash") && Env["block_hash"].IsString()) { - // Parsed later into host.block_hash (MockedHost has single block_hash - // slot). + Fixture.BlockHash = zen::utils::parseBytes32(Env["block_hash"].GetString()); } if (Env.HasMember("block_hashes") && Env["block_hashes"].IsObject()) { for (auto It = Env["block_hashes"].MemberBegin(); It != Env["block_hashes"].MemberEnd(); ++It) { + if (!It->value.IsString()) { + return failFixture("fixture.env.block_hashes values must be strings"); + } int64_t BlockNum = std::stoll(It->name.GetString()); Fixture.BlockHashes[BlockNum] = zen::utils::parseBytes32(It->value.GetString()); @@ -163,6 +263,21 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { for (auto It = Prestate.MemberBegin(); It != Prestate.MemberEnd(); ++It) { const std::string AddressStr = It->name.GetString(); const auto &AccountVal = It->value; + if (!AccountVal.IsObject()) { + return failFixture("fixture.prestate entries must be objects"); + } + if (!requireStringMember(AccountVal, "balance")) { + return failFixture("fixture.prestate[*].balance must be a string"); + } + if (!requireUint64Member(AccountVal, "nonce")) { + return failFixture("fixture.prestate[*].nonce must be a uint64"); + } + if (!requireStringMember(AccountVal, "code")) { + return failFixture("fixture.prestate[*].code must be a string"); + } + if (!requireObjectMember(AccountVal, "storage")) { + return failFixture("fixture.prestate[*].storage must be an object"); + } ZenMockedEVMHost::AccountInitEntry Entry; Entry.Address = zen::utils::parseAddress(AddressStr); @@ -173,6 +288,10 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { const auto &Storage = AccountVal["storage"]; for (auto Sit = Storage.MemberBegin(); Sit != Storage.MemberEnd(); ++Sit) { + if (!Sit->value.IsString()) { + return failFixture( + "fixture.prestate[*].storage values must be strings"); + } evmc::StorageValue SV{}; SV.current = zen::utils::parseBytes32(Sit->value.GetString()); Entry.Account.storage[zen::utils::parseBytes32(Sit->name.GetString())] = @@ -199,6 +318,7 @@ ParsedFixture loadFixture(const std::filesystem::path &Path) { Fixture.IntrinsicGas = zen::utils::computeIntrinsicGas( Fixture.Revision, EVMC_CALL, Fixture.Message.input_data, Fixture.Message.input_size); + Fixture.IsValid = true; return Fixture; } @@ -212,17 +332,10 @@ runFixture(const ParsedFixture &Fixture, common::RunMode Mode) { auto Host = std::make_unique(); Host->loadInitialState(Fixture.TxContext, Fixture.Accounts, true); - // Most legacy contracts use BLOCKHASH(block.number-1); mocked host exposes - // one block_hash value for all get_block_hash() queries. - const auto DocPath = std::filesystem::path(Fixture.FixturePath); - std::ifstream F(DocPath); - rapidjson::IStreamWrapper ISW(F); - rapidjson::Document D; - D.ParseStream(ISW); - if (D.IsObject() && D.HasMember("env") && D["env"].IsObject() && - D["env"].HasMember("block_hash") && D["env"]["block_hash"].IsString()) { - Host->block_hash = - zen::utils::parseBytes32(D["env"]["block_hash"].GetString()); + if (Fixture.BlockHash.has_value()) { + // Most legacy contracts use BLOCKHASH(block.number-1); mocked host exposes + // one block_hash value for all get_block_hash() queries. + Host->block_hash = *Fixture.BlockHash; } Host->BlockHashOverrides = Fixture.BlockHashes; auto RT = Runtime::newEVMRuntime(Config, Host.get()); @@ -255,14 +368,35 @@ VmExecutionResult runFixtureViaDTVMApi(const ParsedFixture &Fixture, const char *ModeValue) { auto Host = std::make_unique(); Host->loadInitialState(Fixture.TxContext, Fixture.Accounts, true); + if (Fixture.BlockHash.has_value()) { + Host->block_hash = *Fixture.BlockHash; + } Host->BlockHashOverrides = Fixture.BlockHashes; auto Vm = evmc_create_dtvmapi(); EXPECT_NE(Vm, nullptr); if (!Vm) { return {}; } - Vm->set_option(Vm, "mode", ModeValue); - Vm->set_option(Vm, "enable_gas_metering", "true"); + if (Vm->set_option == nullptr) { + ADD_FAILURE() << "dtvmapi VM does not provide set_option"; + Vm->destroy(Vm); + return {}; + } + const auto SetModeResult = Vm->set_option(Vm, "mode", ModeValue); + if (SetModeResult != EVMC_SET_OPTION_SUCCESS) { + ADD_FAILURE() << "failed to set dtvmapi mode to " << ModeValue + << ", result=" << SetModeResult; + Vm->destroy(Vm); + return {}; + } + const auto SetGasMeteringResult = + Vm->set_option(Vm, "enable_gas_metering", "true"); + if (SetGasMeteringResult != EVMC_SET_OPTION_SUCCESS) { + ADD_FAILURE() << "failed to enable dtvmapi gas metering, result=" + << SetGasMeteringResult; + Vm->destroy(Vm); + return {}; + } evmc_message Msg = Fixture.Message; Msg.gas = static_cast(Fixture.GasLimit); @@ -317,7 +451,8 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesInInterpreterAndMultipass) { for (const auto &[Name, CanonicalTxGas] : FixtureFiles) { SCOPED_TRACE(Name); - const ParsedFixture Fixture = loadFixture(FixtureDir / Name); + ParsedFixture Fixture = loadFixture(FixtureDir / Name); + ASSERT_TRUE(Fixture.IsValid); EXPECT_EQ(Fixture.ExpectedTxGas, CanonicalTxGas); { @@ -346,7 +481,8 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesViaDTVMApi) { const std::vector FixtureFiles = {"block_254277_tx_0.json"}; for (const auto &Name : FixtureFiles) { SCOPED_TRACE(Name); - const ParsedFixture Fixture = loadFixture(FixtureDir / Name); + ParsedFixture Fixture = loadFixture(FixtureDir / Name); + ASSERT_TRUE(Fixture.IsValid); auto Interp = runFixtureViaDTVMApi(Fixture, "interpreter"); auto Multi = runFixtureViaDTVMApi(Fixture, "multipass"); ASSERT_TRUE(Interp.Success); From 7118df9421bc38728b7719fcb75683abf3139efe Mon Sep 17 00:00:00 2001 From: abmcar <52450271+abmcar@users.noreply.github.com> Date: Fri, 15 May 2026 20:22:15 +0800 Subject: [PATCH 08/19] ci(ci): cache baseline lib instead of baseline JSON (#506) Co-authored-by: Claude Opus 4.7 (cherry picked from commit 8e57176aab30df2ea3013ff0f214987d3d082636) --- .github/workflows/dtvm_evm_test_x86.yml | 19 ++- .../README.md | 118 ++++++++++++++++++ 2 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 docs/changes/2026-05-15-perf-check-baseline-lib-cache/README.md diff --git a/.github/workflows/dtvm_evm_test_x86.yml b/.github/workflows/dtvm_evm_test_x86.yml index b34e26a32..b16c26b82 100644 --- a/.github/workflows/dtvm_evm_test_x86.yml +++ b/.github/workflows/dtvm_evm_test_x86.yml @@ -351,12 +351,17 @@ jobs: run: | ./tools/format.sh check - - name: Restore baseline cache + # Cache the BUILT baseline libdtvmapi.so (a deterministic function of + # base.sha), not the bench output JSON (a function of source + host + + # load). Forces every PR to re-run baseline benchmarks on the same + # runner as the PR benchmarks via the `elif` dispatch branch in + # .ci/run_test_suite.sh. + - name: Restore baseline lib cache id: baseline-cache uses: actions/cache@v4 with: - path: /tmp/perf_baseline_${{ matrix.mode }}.json - key: perf-baseline-${{ matrix.mode }}-${{ github.event.pull_request.base.sha }} + path: /tmp/baseline_lib + key: perf-baseline-lib-${{ matrix.mode }}-${{ github.event.pull_request.base.sha }} - name: Build baseline library (${{ github.base_ref }}) if: steps.baseline-cache.outputs.cache-hit != 'true' @@ -382,7 +387,12 @@ jobs: cmake --build build -j 16 mkdir -p /tmp/baseline_lib - cp build/lib/* /tmp/baseline_lib/ + # Resolve SONAME chain (libdtvmapi.so -> .0.1 -> .0.1.0) and copy + # the real file only. Plain `cp build/lib/*` dereferences each + # symlink and produces 3 full copies (~105 MB) plus any + # libgtest*.a present. The dispatcher at .ci/run_test_suite.sh + # consumes the bare name `libdtvmapi.so` only. + cp "$(readlink -f build/lib/libdtvmapi.so)" /tmp/baseline_lib/libdtvmapi.so rm -rf build git checkout ${{ github.sha }} @@ -408,7 +418,6 @@ jobs: export CPU_EXCEPTION_TYPE='cpu' export BENCHMARK_MODE=${{ matrix.mode }} export BENCHMARK_THRESHOLD=0.25 - export BENCHMARK_BASELINE_CACHE=/tmp/perf_baseline_${{ matrix.mode }}.json export BENCHMARK_BASELINE_LIB=/tmp/baseline_lib export BENCHMARK_SUMMARY_FILE=/tmp/perf_summary_${{ matrix.mode }}.md export BENCHMARK_REPETITIONS=5 diff --git a/docs/changes/2026-05-15-perf-check-baseline-lib-cache/README.md b/docs/changes/2026-05-15-perf-check-baseline-lib-cache/README.md new file mode 100644 index 000000000..5b95dd3fb --- /dev/null +++ b/docs/changes/2026-05-15-perf-check-baseline-lib-cache/README.md @@ -0,0 +1,118 @@ +# Change: perf-check job — cache baseline `.so` instead of baseline JSON + +- **Status**: Implemented +- **Date**: 2026-05-15 +- **Tier**: Light +- **Branch**: `ci/perf-baseline-lib-cache` + +## Overview + +Switch the `Performance Regression Check` job (`.github/workflows/dtvm_evm_test_x86.yml`) from caching `perf_baseline_${mode}.json` (the benchmark results from a *previous* CI runner) to caching `baseline_lib/` (the built baseline `libdtvmapi.so`). Every PR then re-runs baseline benchmarks on the **same runner** that runs the PR's benchmarks, eliminating cross-runner cache-stale noise while preserving the savings from skipping the baseline rebuild on cache hits. + +## Motivation + +Concrete evidence — PR #493 CI run `25775768632`: + +The pre-PR job (line numbers from `.github/workflows/dtvm_evm_test_x86.yml` on `main`): + +```yaml +# Lines 354-359 +- name: Restore baseline cache + id: baseline-cache + uses: actions/cache@v4 + with: + path: /tmp/perf_baseline_${{ matrix.mode }}.json # <-- JSON results + key: perf-baseline-${{ matrix.mode }}-${{ github.event.pull_request.base.sha }} +``` + +…caches the **benchmark output JSON** keyed on `matrix.mode` + `pull_request.base.sha`. When two PRs share the same `base.sha` (e.g. both forked from `c644fbe`), the second PR's cache lookup hits the first PR's stored JSON for the same mode — measured on a different `ubuntu-latest` runner with different CPU stepping / neighbor load / thermal state. + +`.ci/run_test_suite.sh` lines 312–334 dispatch on this: + +```bash +if [ -n "$BASELINE_CACHE" ] && [ -f "$BASELINE_CACHE" ]; then + # Cache HIT — only run current benchmarks, compare against cached JSON. + python3 check_performance_regression.py ... --baseline "$BASELINE_CACHE" ... +elif [ -n "$BENCHMARK_BASELINE_LIB" ]; then + # Cache MISS — run baseline lib bench HERE on this runner, + # then run PR bench, compare. + python3 check_performance_regression.py ... --save-baseline "$SAVE_PATH" ... + python3 check_performance_regression.py ... --baseline "$SAVE_PATH" ... +fi +``` + +The `elif` branch already does the *right* thing — same-runner A-B comparison. The `if` branch is the noise source. + +Empirical impact, PR #493 commit `5c11550` interpreter perf-check (CI cache hit): + +| Benchmark | CI Δ (B vs cached A) | Local A-B-A Δ (same machine) | +|---|---|---| +| `synth/EQ/b1` | +18.3% | −0.3% | +| `synth/CALLER/a1` | +16.0% | −0.3% | +| `synth/ISZERO/u0` | +15.7% | +0.6% | +| `synth/LT/b1` | +15.3% | +0.6% | +| `synth/GT/b1` | +15.2% | −0.2% | +| `synth/BYTE/b1` | +13.7% | −0.5% | + +CI reports 13–18% regressions; same-machine A-B-A reports ≤0.6% deltas, all inside baseline self-drift band. The diff is compiler-only (verified: `src/evm/` untouched). The deltas are cross-runner noise. + +## Impact + +- **Behavior-affecting files**: `.github/workflows/dtvm_evm_test_x86.yml` only — `performance_regression_check` job. (This change doc itself, `docs/changes/2026-05-15-perf-check-baseline-lib-cache/README.md`, is added in the same commit as a non-functional design record.) +- **Behavior change**: per-PR perf-check job wall-clock increases on cache-hit runs by the baseline-bench step (which under the old scheme was skipped); cache-miss runs are unchanged. +- **Cost / quota**: cache-hit runs now do both a baseline bench AND a PR bench on the same runner (cache-miss already does both, plus a baseline build). Both modes (`matrix.mode = {interpreter, multipass}`) run per PR. Empirically verified on fork CI runs `25897744713` (cache miss) and `25900131271` (cache hit) that all 11 non-Lint jobs go green; cache-hit runs spend longer than the old cache-hit scheme (which skipped baseline bench entirely) but shorter than cache-miss (which adds the baseline build on top). Acceptable under the public-repo GitHub Actions allowance for our PR volume. +- **No source-code change**, no test runner change, no `.ci/run_test_suite.sh` change. The `elif [ -n "$BENCHMARK_BASELINE_LIB" ]` path it routes to already exists and is exercised today on cache miss. +- **Downstream**: reviewers stop chasing false-positive interpreter regressions on PRs whose diff is compiler-only (and vice versa). Perf-check signal-to-noise increases without changing the threshold. + +## Implementation + +1. Edit `.github/workflows/dtvm_evm_test_x86.yml` (perf job lines 354-417): + - Rename step `Restore baseline cache` → `Restore baseline lib cache`. + - Change `path:` from `/tmp/perf_baseline_${{ matrix.mode }}.json` to `/tmp/baseline_lib`. + - Change `key:` from `perf-baseline-${{ matrix.mode }}-${{ github.event.pull_request.base.sha }}` to `perf-baseline-lib-${{ matrix.mode }}-${{ github.event.pull_request.base.sha }}` (rename forces fresh cache entries; old `perf-baseline--` entries become orphaned and expire by GitHub's 7-day LRU). Cache key still includes both `matrix.mode` and `base.sha` — same key scheme as before, just a different artifact behind it. + - **Replace** the symlink-dereferencing `cp build/lib/*` line (was line 385; line 390 after the edit) with an explicit single-file copy using `readlink -f` for SONAME resolution: + ```bash + cp "$(readlink -f build/lib/libdtvmapi.so)" /tmp/baseline_lib/libdtvmapi.so + ``` + Rationale: `build/lib/` contains the SONAME symlink chain (`libdtvmapi.so` → `.0.1` → `.0.1.0`). Plain `cp build/lib/*` follows symlinks and produces 3 full copies (~105 MB) plus any other glob matches like `libgtest*.a`. `readlink -f` resolves the bare name to its canonical real file regardless of how many symlink hops or what the version suffix becomes after future SONAME bumps; it also returns the canonical path verbatim for non-symlinks, so the command is robust either way. The dispatcher at `.ci/run_test_suite.sh:328` does `cp "$BENCHMARK_BASELINE_LIB"/libdtvmapi.so ./libdtvmapi.so` — it only consumes the bare name, no SONAME resolution needed at consumer. + - In the `Build current PR and check regression` step (was line 411; line 416 after the edit): **remove** the `export BENCHMARK_BASELINE_CACHE=...` line entirely. Keep `BENCHMARK_BASELINE_LIB=/tmp/baseline_lib`. + - `Build baseline library` step (lines 361-389) keeps its `if: cache-hit != 'true'` guard — only the cache *target* changed, the guard semantics are identical. +2. Validate YAML: `python3 -c "import yaml; yaml.safe_load(open('.github/workflows/dtvm_evm_test_x86.yml'))"`. Run `actionlint` if installed. +3. Commit on `ci/perf-baseline-lib-cache` with conventional-commit message: + `ci(ci): cache baseline lib instead of baseline JSON to force same-runner A-B comparison`. +4. Push to `origin` (= `abmcar/DTVM`, the personal fork). +5. Open a fork-internal PR (`abmcar/DTVM:ci/perf-baseline-lib-cache` → `abmcar/DTVM:main`) so the fork's Actions runs the modified workflow. Trigger condition: `if: github.event_name == 'pull_request'` — fork-internal PRs satisfy this. +6. Verify first run = cache miss → builds baseline lib + runs baseline bench + runs PR bench. Acceptance numerical gate: comparison table reports `max(|Δ|) < 3%` and **no** bench exceeding the 25% FAIL threshold. (The diff is workflow-config-only; the baseline and PR libs build from byte-identical source, so deltas should be pure runner noise.) +7. Push a no-op second commit (e.g. trailing newline in the workflow file) to trigger a second perf-check run on the same `base.sha`. Verify second run = cache hit on `Restore baseline lib cache`, `Build baseline library` skipped, but the bench step still logs `Running baseline benchmarks with library from base branch...` (because `BENCHMARK_BASELINE_CACHE` is unset, the bash dispatcher at `.ci/run_test_suite.sh:312-334` falls through to the `elif [ -n "$BENCHMARK_BASELINE_LIB" ]` branch — same code path as cache-miss, just without the build step before it). Comparison table should again show `max(|Δ|) < 3%`. +8. If both runs pass the numerical gate, promote to upstream: copy `~/changes/2026-05-15-perf-check-baseline-lib-cache/` into `/docs/changes/`, commit on the same branch, push, open upstream PR (`abmcar/DTVM:ci/perf-baseline-lib-cache` → `DTVMStack/DTVM:main`). + +**Rollback** (if the change misbehaves in production): `git revert ` on the workflow change. The `elif [ -n "$BENCHMARK_BASELINE_LIB" ]` dispatch path in `run_test_suite.sh` already exists and is the cache-miss path today, so revert returns to the current cache-hit behavior without script changes. + +**Why same-`base.sha` cache sharing is now safe**: the cached artifact is now the deterministic build output `libdtvmapi.so` (a function of source = `base.sha`), not a benchmark *measurement* (a function of source + host + load). Two PRs hitting the same cache key restore the same `.so`, then each runs its own baseline bench on its own runner — no cross-runner result mixing. + +## Risks + +- **Wrong dispatch path**: if `run_test_suite.sh:312-334` short-circuits incorrectly when `BASELINE_CACHE` is unset, the workflow might run with no comparison. Mitigation: the `elif [ -n "$BENCHMARK_BASELINE_LIB" ]` test is explicit; the workflow always sets `BENCHMARK_BASELINE_LIB=/tmp/baseline_lib`. Failure mode if the file is missing is `set -e` on the `cp` at line 328 (loud exit), not silent skip. Verified by reading the script. +- **Cache key migration**: existing `perf-baseline-${mode}-${sha}` entries are now orphaned; they expire under GitHub's 7-day LRU. +- **Container tag is mutable** (`dtvmdev1/dtvm-dev-x64:main`, workflow line 337): the cache key does NOT include the container tag, so if the container's compiler version changes, a cached `.so` may have a different code-gen profile than the PR's freshly-built `.so`. This risk **already exists today** with the JSON cache (where the JSON was generated under whatever container was current then), so the proposed change is no worse. Acknowledged as a separate CI-infra hardening that could pin the container by digest or add it to the cache key — out of scope here. +- **Subsequent-run cost**: every PR-trigger event re-runs baseline bench. Under the old scheme the cache-hit path skipped this. The change adds **one full baseline bench** before the PR bench on cache-hit runs. +- **Bench scope**: the workflow does **not** pass `--benchmark-filter` to evmone-bench. `tools/check_performance_regression.py` defaults to `external/total/*` — 194 benchmarks per mode (fork CI runs `25897744713`, `25900131271`: "Total benchmarks: 194"). With `BENCHMARK_REPETITIONS=5`. +- **SAVE_PATH fallback** (`.ci/run_test_suite.sh:329`): with `BASELINE_CACHE` unset, `SAVE_PATH` resolves to the literal default `/tmp/perf_baseline.json` (no `${matrix.mode}` suffix). This file is only consumed by the very next `python3` invocation in the same script (line 339), and nothing else in the job reads it. Safe to leave as-is. + +## Test plan + +- [x] YAML syntactically valid (`python3 -c "import yaml; yaml.safe_load(...)"`) +- [x] Fork-internal PR opens successfully (`abmcar/DTVM#15`) +- [x] **First fork CI run** (cache miss, EVM workflow run `25897744713` after re-running 3 spdlog-503-failed jobs): both `Performance Regression Check (interpreter)` and `(multipass)` reported `Cache not found for input keys: perf-baseline-lib--c644fbe...`, ran the build-baseline-library step, then logged `Running baseline benchmarks with library from base branch...` followed by `Running current benchmarks with PR library...`. Cache was saved at end of job (`Cache saved with key: perf-baseline-lib--c644fbe...`). Both modes: `Total benchmarks: 194 / Regressions (> 25%): 0 / RESULT: PASS`. +- [x] **Second push** (commit `8189d4d`, comment-only no-op to bump `head.sha` while `base.sha` stays at `c644fbe`) triggers run `25900131271`. Both perf-check matrix jobs reported `Cache restored from key: perf-baseline-lib--c644fbe...`, the `Build baseline library` step was `skipped` (per `if: cache-hit != 'true'` evaluating false), and the bench step still logged both `Running baseline benchmarks with library from base branch...` and `Running current benchmarks with PR library...`. Both modes again `Total benchmarks: 194 / Regressions (> 25%): 0 / RESULT: PASS`. All 11 non-Lint EVM jobs green on first try (no spdlog 503 retries). +- [x] Cross-runner noise reduction confirmed empirically (run `25897744713` interpreter perf-check comparison): the same synth benchmarks that reported +13-18% regressions in PR #493's cross-runner cache-stale comparison now report deltas within typical noise (e.g. `synth/ADDRESS/a1` +15.6% → −4.3%, `synth/BYTE/b1` +13.7% → +1.2%, `synth/CALLER/a1` +16.0% → +0.1%, `snailtracer/benchmark` −1.1% → +0.0%, `blake2b_shifts/8415nulls` −12.1% → +0.2%). Only 1 micro-benchmark was filtered (baseline < 5 µs) versus 25 in the cross-runner comparison. +- [ ] Upstream PR opens cleanly after promotion to `docs/changes/` + +## Checklist + +- [x] Implementation complete (workflow edited + committed: `7928578`, `8189d4d`) +- [x] YAML syntax check passes +- [x] Fork-internal PR opened (`abmcar/DTVM#15`) +- [x] Both fork-CI runs (cache-miss `25897744713` + cache-hit `25900131271`) observed and sensible — see Test plan above +- [x] Build and tests pass (CI-only change; perf-check itself is the test, both modes RESULT: PASS) +- [ ] Upstream PR opened From 339fc2119fef068e9d5c79ccfaa5c88a1e00e1ed Mon Sep 17 00:00:00 2001 From: abmcar <52450271+abmcar@users.noreply.github.com> Date: Fri, 15 May 2026 20:21:53 +0800 Subject: [PATCH 09/19] ci: cache FetchContent deps + boost mirror swap (#508) Co-authored-by: Claude Opus 4.7 (1M context) (cherry picked from commit 7a4d2a5c5ccb259d47e9ebacbe2b34b0411cac35) --- .github/workflows/dtvm_evm_test_x86.yml | 59 +++++ .github/workflows/dtvm_wasm_test_x86.yml | 28 +++ CMakeLists.txt | 14 ++ .../2026-05-15-fetchcontent-cache/README.md | 206 ++++++++++++++++++ docs/start.md | 38 ++++ third_party/AddDeps.cmake | 7 +- 6 files changed, 350 insertions(+), 2 deletions(-) create mode 100644 docs/changes/2026-05-15-fetchcontent-cache/README.md diff --git a/.github/workflows/dtvm_evm_test_x86.yml b/.github/workflows/dtvm_evm_test_x86.yml index b16c26b82..c43c82573 100644 --- a/.github/workflows/dtvm_evm_test_x86.yml +++ b/.github/workflows/dtvm_evm_test_x86.yml @@ -16,6 +16,14 @@ on: permissions: contents: read +# Shared FetchContent cache root for all container jobs. The hook in +# CMakeLists.txt (commit 96707a2 lines 8-18) picks this up as the base +# dir for FetchContent populations. Each container job adds an +# `actions/cache` step keyed on `hashFiles('third_party/AddDeps.cmake')` +# to persist this dir across CI runs. +env: + FETCHCONTENT_BASE_DIR: /github/home/.fetchcontent + jobs: build_test_evm_interpreter_x86_ctest: name: Test DTVM-EVM interpreter with ctest on x86-64 @@ -27,6 +35,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -61,6 +74,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -92,6 +110,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -134,6 +157,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -165,6 +193,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -197,6 +230,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -229,6 +267,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -259,6 +302,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Cache Hunter uses: actions/cache@v4 with: @@ -300,6 +348,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -342,6 +395,12 @@ jobs: submodules: "true" fetch-depth: 0 + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} + - name: Setup git safe directory run: | echo "Configuring git safe directory: ${{ github.workspace }}" diff --git a/.github/workflows/dtvm_wasm_test_x86.yml b/.github/workflows/dtvm_wasm_test_x86.yml index 4fd9c4896..778b78949 100644 --- a/.github/workflows/dtvm_wasm_test_x86.yml +++ b/.github/workflows/dtvm_wasm_test_x86.yml @@ -16,6 +16,14 @@ on: permissions: contents: read +# Shared FetchContent cache root for all container jobs. The hook in +# CMakeLists.txt (commit 96707a2 lines 8-18) picks this up as the base +# dir for FetchContent populations. Each container job adds an +# `actions/cache` step keyed on `hashFiles('third_party/AddDeps.cmake')` +# to persist this dir across CI runs. +env: + FETCHCONTENT_BASE_DIR: /github/home/.fetchcontent + jobs: build_test_interp_on_x86: name: Build and test DTVM interpreter on x86-64 @@ -27,6 +35,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -69,6 +82,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -111,6 +129,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check @@ -153,6 +176,11 @@ jobs: uses: actions/checkout@v3 with: submodules: "true" + - name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} - name: Code Format Check run: | ./tools/format.sh check diff --git a/CMakeLists.txt b/CMakeLists.txt index b1dafa146..a7c24f3d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,20 @@ cmake_minimum_required(VERSION 3.16) project(ZetaEngine LANGUAGES C CXX ASM) +# Honor FETCHCONTENT_BASE_DIR from environment when not set on cmd line. Enables +# a shared FetchContent cache across worktrees, CI jobs, and local builds. CI +# workflows export this env to `/github/home/.fetchcontent` (paired with +# actions/cache); local developers can export `~/.cache/cmake-fetchcontent` per +# `docs/start.md` "Build dependency cache". +if(DEFINED ENV{FETCHCONTENT_BASE_DIR} AND NOT DEFINED + CACHE{FETCHCONTENT_BASE_DIR} +) + set(FETCHCONTENT_BASE_DIR + "$ENV{FETCHCONTENT_BASE_DIR}" + CACHE PATH "Shared FetchContent cache (from env)" + ) +endif() + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/docs/changes/2026-05-15-fetchcontent-cache/README.md b/docs/changes/2026-05-15-fetchcontent-cache/README.md new file mode 100644 index 000000000..7b9a8ab29 --- /dev/null +++ b/docs/changes/2026-05-15-fetchcontent-cache/README.md @@ -0,0 +1,206 @@ +# Change: actions/cache for FetchContent on DTVM CI + +- **Status**: Proposed +- **Date**: 2026-05-15 +- **Tier**: Light +- **Branch**: ci/fetchcontent-cache (continues commit `96707a2`) + +## Overview + +Add `actions/cache@v4` step to the two DTVM CMake-building CI workflows +(EVM + WASM) to cache the populated FetchContent sources across runs. +First run pays full download; every subsequent run with unchanged +`third_party/AddDeps.cmake` hits the cache and skips downloads +entirely. This completes the work begun in commit `96707a2` (boost URL +swap + CMakeLists env hook). + +## Motivation + +Each DTVM CI run currently downloads 8 FetchContent deps from scratch +(`spdlog`, `asmjit` (WASM only), `CLI11`, `intx`, `boost`, `rapidjson`, ++ conditional `googletest`/`yaml-cpp`). Any single 504 kills the +pipeline (e.g., PR #499 run `25897803413` died on rapidjson). + +`actions/cache@v4` is the canonical 2025 pattern for FetchContent +caching (verified against `vowpal_wabbit` and `colmap` workflows — +both cache around `FetchContent` paths keyed on dep manifest hashes). + +The earlier image-bake approach was investigated but Docker is +unavailable in the implementation environment for verification. +`actions/cache` does not require Docker and is verifiable directly via +PR CI runs. + +## Impact + +### Affected modules + +- `.github/workflows/dtvm_evm_test_x86.yml` — add cache step + env to + ~10 container-image build jobs +- `.github/workflows/dtvm_wasm_test_x86.yml` — same for ~4 container + jobs +- `docs/changes/2026-05-15-fetchcontent-cache/README.md` — this doc; + drop image-bake content from prior iteration + +### Affected contracts + +None. CI infrastructure only. + +### Compatibility + +Fully backwards-compatible. Cache miss falls through to current +behavior (live FetchContent download). No workflow logic changes +beyond the new cache step and job-level env. + +## Implementation + +### 1. Cache step per workflow + +**Coverage** (14 distinct jobs total): +- **EVM (10 jobs)**: 8 `bash .ci/run_test_suite.sh` callers + 2 matrix + instances of `performance_regression_check` (which runs both a base + build via direct cmake AND a PR-HEAD build via `run_test_suite.sh`). +- **WASM (4 jobs)**: 3 `bash .ci/run_test_suite.sh` callers + + `build_test_evmabi_mock_cli_on_x86` (uses inline `cmake -S . -B build` + directly, not via `run_test_suite.sh`). + +All 14 need the cache step. The inline-cmake job in WASM and the +direct-cmake baseline build in `performance_regression_check` also +inherit `FETCHCONTENT_BASE_DIR` from the env block — no special +handling required. + +Add to each container job, between `actions/checkout` and the +build/test step: + +```yaml +- name: Cache FetchContent deps + uses: actions/cache@v4 + with: + path: /github/home/.fetchcontent + key: ${{ runner.os }}-fc-${{ github.workflow }}-v1-${{ hashFiles('third_party/AddDeps.cmake') }} +``` + +**Key composition rationale:** +- `runner.os` — standard practice (Ubuntu vs other). +- `github.workflow` — **necessary**: EVM workflow runs with + `SINGLEPASS_JIT=OFF` (no asmjit), WASM workflow includes + `SINGLEPASS_JIT=ON` (needs asmjit). Sharing one key causes a + partial-hit churn: EVM saves 7 deps → WASM restores 7, populates 1, + but `actions/cache@v4` skips same-key save (logs a warning, does + not fail the job) → WASM re-downloads asmjit every run. + Workflow-prefixed key avoids this. +- `v1` namespace — **manual escape hatch**. Bump to `v2` when + `dtvmdev1/dtvm-dev-x64:main` is rebuilt with materially different + CMake/compiler/Ninja/tar/zstd versions. The `:main` tag is mutable + and our key does not auto-invalidate on image bumps. +- `hashFiles('third_party/AddDeps.cmake')` — auto-invalidates on any + dep change (URL/hash/tag/new dep). +- **No `restore-keys`**. Partial cache hits across different dep + versions can yield silently-stale source (FetchContent stamps say + "populated", URL_HASH may not match the cached tarball if user + changed URL but kept hash). Cold start is the lesser evil. + +### 2. Job env + +Add to each container job's `env:` block (where the build runs): + +```yaml +env: + FETCHCONTENT_BASE_DIR: /github/home/.fetchcontent +``` + +The CMakeLists env-hook from commit `96707a2` (lines 8-18; executable +`if/set/endif` block at lines 11-18) picks up the env var when no `-D` +is passed on the cmake command line. No changes needed to +`.ci/run_test_suite.sh`. + +### 3. Drop image-bake content from this change doc + +Previous iteration's `docs/changes/.../README.md` had a "Deferred" +section describing the image-bake design. Replace with this file +focused on actions/cache. + +## Validation + +### Local + +- `tools/format.sh check` — pass. +- YAML lint via `python -c "import yaml; yaml.safe_load(open('.github/workflows/dtvm_evm_test_x86.yml'))"` — pass. +- Diff inspection: each modified job has cache step + env block. + +### CI (post-push) + +The cache behavior is GH-runner-side; can only be observed in a real +CI run. + +## Acceptance Criteria + +1. **AC-A: PR CI first run is cache-miss.** Workflow log shows + "Cache not found for input keys: ..." followed at end-of-job by + "Cache saved with key: ...". +2. **AC-B: PR CI re-run is cache-hit.** Re-running the same workflow + (no commit change) shows "Cache restored from key: ..." in cache + step output. Build log shows zero `^-- Downloading` lines from + FetchContent. (Note: explicit "restored from key" line is the + primary AC; absence of `-- Downloading` is corroborating.) +3. **AC-C: No regression.** All jobs that pass on `main` today pass + with cache step active. +4. **AC-D: Cache key invalidates on AddDeps change.** A no-op edit to + `third_party/AddDeps.cmake` (trailing newline) in a follow-up + commit produces a new key (visible in workflow log as different + key hash). +5. **AC-E: EVM and WASM caches don't interfere.** EVM cache key + contains `DTVM-EVM` (workflow `name:` field value, with hyphen), + WASM contains `DTVM-WASM`. Verify via workflow log key string. + +## Risks + +- **R1: actions/cache@v4 itself unavailable / quota exhausted.** + Mitigation: cache miss is non-fatal; CI falls back to live + FetchContent download (current behavior). No regression. + +- **R2: 10GB repo cache cap proximity.** + Per-key size ~820MB. With 10 active feature branches × 2 workflows + = ~16GB of potential cache load — over the cap. GitHub LRU-evicts + caches not accessed in 7 days, so steady-state should hover around + 3-5 active keys (~3-4GB). Not "well under" the cap; close but + acceptable. Monitor via `gh cache list` if eviction thrashing + becomes visible. + +- **R3: Image churn invalidation.** + `dtvmdev1/dtvm-dev-x64:main` is a mutable tag. If the image is + rebuilt with a materially different CMake / compiler / Ninja + version, the cached `-build/` artifacts could mismatch. + Mitigation: manually bump the `v1` namespace in the cache key + (becomes `v2`, etc.) when the image is rebuilt with material + changes. Documented in this section. + +- **R4: Cache-key hash misses other dep-affecting files.** + Today only `third_party/AddDeps.cmake` controls FetchContent + declarations. If a future PR moves declares elsewhere or adds new + conditional logic in `CMakeLists.txt` flags, update the cache key. + +- **R5: Boost URL transition single-point-of-failure.** + (Carried from commit `96707a2`.) First CI run hits new boost URL + live; if 504, re-run. Cache then captures it for subsequent runs. + +## Out of scope + +- Image-baking deps into `dtvmdev1/dtvm-dev-x64:main` — deferred + (Docker unavailable for verification in current environment). +- Pinning `:main` image by digest in cache key — would tighten R3 + but adds maintenance cost; bump-`v1`-on-image-rebuild is simpler. +- Migration to Hunter / submodules / CPM. +- Pinning `GIT_TAG` to commit SHAs. + +## Provenance + +- Commit `96707a2` ("build(deps): swap boost mirror + honor + FETCHCONTENT_BASE_DIR env") already adds the env-hook + boost URL + prerequisites this change builds on. +- Prior Phase 0.5 v2 round 1 reviews: + - `reviews/motivation-v2-1-opus.md` (cite: cache-key churn, + AC-B log line check) + - `reviews/motivation-v2-1-codex.md` (cite: image churn, 10GB cap + wording, canonical pattern confirmation) +- All cited refinements absorbed into this spec; iter=2 skipped + because the refinements are spec-level fixes, not direction changes. diff --git a/docs/start.md b/docs/start.md index decf1702c..b7f3d5376 100644 --- a/docs/start.md +++ b/docs/start.md @@ -17,6 +17,44 @@ The fastest way to set up the compilation environment is to use a Docker image o docker pull dtvmdev1/dtvm-dev-x64:main ``` +## Build dependency cache + +DTVM uses CMake `FetchContent` to pull up to 8 external dependencies +declared in `third_party/AddDeps.cmake` (`CLI11`, `intx`, `boost`, +`rapidjson` are unconditional; `spdlog` is on unless `ZEN_ENABLE_SGX=ON`; +`asmjit` is on with `ZEN_ENABLE_SINGLEPASS_JIT=ON`; `googletest` and +`yaml-cpp` are on with `ZEN_ENABLE_SPEC_TEST=ON`). On a clean build +these are downloaded fresh, which is the main source of CI / cold-build +flakiness when an upstream host is slow or returns 504. + +To share the populated sources across builds (worktrees, repeated clean +builds, multiple machines mounting the same home dir), export +`FETCHCONTENT_BASE_DIR` before invoking cmake: + +```sh +# Add to ~/.zshrc or ~/.bashrc: +export FETCHCONTENT_BASE_DIR="$HOME/.cache/cmake-fetchcontent" +mkdir -p "$FETCHCONTENT_BASE_DIR" +``` + +The top-level `CMakeLists.txt` honors this env var when no +`-DFETCHCONTENT_BASE_DIR=…` is passed on the cmake command line. After +the first successful configure, subsequent clean builds re-use the +populated sources without re-downloading. + +To opt out (use the default `build/_deps/` per-build dir): `unset +FETCHCONTENT_BASE_DIR`. + +**Note for SGX local builds**: if you build with `ZEN_ENABLE_SGX=ON`, +use a separate cache directory (e.g., +`~/.cache/cmake-fetchcontent-sgx`) — asmjit gets a `PATCH_COMMAND` +applied to its sources under SGX, and mixing patched and unpatched +sources in one cache causes silent breakage. No current CI job builds +with SGX, so the workflow-level cache (`/github/home/.fetchcontent`, +keyed on `hashFiles('third_party/AddDeps.cmake')`) does not need to +distinguish SGX state. Revisit the cache key composition when SGX is +added to CI. + ## Interpreter Interpreter mode is the current default execution mode. No specific CMake parameters are needed during compilation. Reference compilation commands are as follows: diff --git a/third_party/AddDeps.cmake b/third_party/AddDeps.cmake index d2eb886af..b2f3dbcb7 100644 --- a/third_party/AddDeps.cmake +++ b/third_party/AddDeps.cmake @@ -1,6 +1,10 @@ # Copyright (C) 2021-2025 the DTVM authors. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0 +# NOTE: Set FETCHCONTENT_BASE_DIR (env var or cmake -D) to share populated +# sources across clean builds — the top-level CMakeLists.txt honors the env +# form. See docs/start.md "Build dependency cache" for details. + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) include(FetchContent) @@ -73,8 +77,7 @@ include_directories(${intx_SOURCE_DIR}/include) FetchContent_Declare( boost - URL https://sourceforge.net/projects/boost/files/boost/1.67.0/boost_1_67_0.tar.bz2/download - DOWNLOAD_NAME boost_1_67_0.tar.bz2 + URL https://archives.boost.io/release/1.67.0/source/boost_1_67_0.tar.bz2 URL_HASH SHA256=2684c972994ee57fc5632e03bf044746f6eb45d4920c343937a465fd67a5adba ) From 5dc66af85a8cd4fed7bae30cd545008b2c518425 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 15:15:42 +0800 Subject: [PATCH 10/19] fix(evm): repair lifted stack merges in multipass jit Keep hidden live-in prefixes from being rematerialized twice, tighten dynamic jump predecessor mapping, and anchor non-lifted entry values so deep stack slots stay aligned across loop iterations. This fixes the legacy CALL repro path that was drifting jump targets and surfacing bad-jump or gas mismatches in multipass JIT. --- src/action/evm_bytecode_visitor.h | 158 +++++++- src/compiler/evm_frontend/evm_analyzer.h | 103 ++++- .../evm_frontend/evm_lifted_stack_lifter.h | 7 + .../evm_frontend/evm_mir_compiler.cpp | 358 ++++++++++++++---- src/compiler/evm_frontend/evm_mir_compiler.h | 33 ++ src/evm/interpreter.cpp | 34 +- src/runtime/evm_instance.cpp | 43 +++ src/runtime/evm_instance.h | 13 + src/tests/evm_jit_frontend_tests.cpp | 2 + 9 files changed, 648 insertions(+), 103 deletions(-) diff --git a/src/action/evm_bytecode_visitor.h b/src/action/evm_bytecode_visitor.h index 1421a07d1..1fd0e80cc 100644 --- a/src/action/evm_bytecode_visitor.h +++ b/src/action/evm_bytecode_visitor.h @@ -99,6 +99,38 @@ template class EVMByteCodeVisitor { } } + template + struct HasDebugDumpRuntimeStack : std::false_type {}; + template + struct HasDebugDumpRuntimeStack< + T, std::void_t().debugDumpRuntimeStack( + uint64_t{}, uint64_t{}))>> : std::true_type {}; + + void debugDumpRuntimeStack(uint64_t BlockPC, uint64_t PhaseTag) { + if constexpr (HasDebugDumpRuntimeStack::value) { + Builder.debugDumpRuntimeStack(BlockPC, PhaseTag); + } else { + (void)BlockPC; + (void)PhaseTag; + } + } + + template + struct HasDebugTraceBlockPC : std::false_type {}; + template + struct HasDebugTraceBlockPC< + T, + std::void_t().debugTraceBlockPC(uint64_t{}))>> + : std::true_type {}; + + void debugTraceBlockPC(uint64_t BlockPC) { + if constexpr (HasDebugTraceBlockPC::value) { + Builder.debugTraceBlockPC(BlockPC); + } else { + (void)BlockPC; + } + } + void spillTrackedStackPreservingPrefix(const std::vector &Values, uint32_t PrefixDepth) { if constexpr (HasSpillTrackedStackPreservingPrefix::value) { @@ -124,6 +156,47 @@ template class EVMByteCodeVisitor { } } + template + struct HasSetCompatibleDynamicJumpTargets : std::false_type {}; + template + struct HasSetCompatibleDynamicJumpTargets< + T, + std::void_t().setCompatibleDynamicJumpTargets( + uint64_t{}, std::declval &>()))>> + : std::true_type {}; + + void initializeCompatibleDynamicJumpTargets(const EVMAnalyzer &Analyzer) { + if constexpr (HasSetCompatibleDynamicJumpTargets::value) { + for (const auto &[EntryPC, BlockInfo] : Analyzer.getBlockInfos()) { + (void)BlockInfo; + const std::vector TargetBlockPCs = + Analyzer.getPotentialDynamicJumpTargetBlocksForSourceBlock(EntryPC); + if (TargetBlockPCs.empty()) { + continue; + } + Builder.setCompatibleDynamicJumpTargets(EntryPC, TargetBlockPCs); + } + } else { + (void)Analyzer; + } + } + + template + struct HasRegisterDirectLiftedPhiIncoming : std::false_type {}; + template + struct HasRegisterDirectLiftedPhiIncoming< + T, + std::void_t().registerDirectLiftedPhiIncoming( + uint64_t{}, uint64_t{}))>> : std::true_type {}; + + void registerDirectLiftedPhiIncoming(uint64_t BlockPC) { + if constexpr (HasRegisterDirectLiftedPhiIncoming::value) { + Builder.registerDirectLiftedPhiIncoming(BlockPC, CurrentBlockEntryPC); + } else { + (void)BlockPC; + } + } + void push(const Operand &Opnd) { Stack.push(Opnd); } void requireLogicalStackDepth(uint32_t Depth) { @@ -193,7 +266,9 @@ template class EVMByteCodeVisitor { PC++; continue; } - Builder.meterOpcode(Opcode, PC); + if (Opcode != OP_BLOCKHASH) { + Builder.meterOpcode(Opcode, PC); + } } switch (Opcode) { @@ -693,7 +768,7 @@ template class EVMByteCodeVisitor { HasKnownSucc && isLiftedBlock(SuccPC); auto OutgoingStack = drainLogicalStack(); if (HasKnownLiftedSucc) { - assignLiftedEntryState(SuccPC, OutgoingStack); + assignDirectLiftedEntryState(SuccPC, OutgoingStack); } if (!HasKnownSucc) { assignCompatibleDynamicJumpRegionEntryStates(Analyzer, @@ -749,17 +824,17 @@ template class EVMByteCodeVisitor { if (CanTransferWithoutMaterialize) { auto OutgoingStack = drainLogicalStack(); - assignLiftedEntryState(FallthroughPC, OutgoingStack); - assignLiftedEntryState(JumpSuccPC, OutgoingStack); + assignDirectLiftedEntryState(FallthroughPC, OutgoingStack); + assignDirectLiftedEntryState(JumpSuccPC, OutgoingStack); finalizeBlockExit(std::move(OutgoingStack), false); } else { if (CurrentBlockLifted) { auto OutgoingStack = drainLogicalStack(); if (CanPreassignFallthrough) { - assignLiftedEntryState(FallthroughPC, OutgoingStack); + assignDirectLiftedEntryState(FallthroughPC, OutgoingStack); } if (CanPreassignJump) { - assignLiftedEntryState(JumpSuccPC, OutgoingStack); + assignDirectLiftedEntryState(JumpSuccPC, OutgoingStack); } if (!HasJumpSucc) { assignCompatibleDynamicJumpRegionEntryStates(Analyzer, @@ -807,12 +882,19 @@ template class EVMByteCodeVisitor { if (PC > RunStartPC && HasLiveFallthrough) { Builder.meterOpcodeRange(RunStartPC, PC); } + if (HasLiveFallthrough && PC == CurrentBlockEntryPC) { + if (!CurrentBlockNeedsFinalize) { + Builder.meterOpcode(Opcode, PC); + } + registerCurrentBlockPC(PC); + break; + } if (HasLiveFallthrough && tryAssignFallthroughEntryState(PC)) { // Keep runtime stack materialization elided on lifted fallthrough. } else { if (HasLiveFallthrough && CurrentBlockLifted && isLiftedBlock(PC)) { auto OutgoingStack = drainLogicalStack(); - assignLiftedEntryState(PC, OutgoingStack); + assignDirectLiftedEntryState(PC, OutgoingStack); finalizeBlockExit(std::move(OutgoingStack), false); } else { handleEndBlock(); @@ -822,8 +904,7 @@ template class EVMByteCodeVisitor { } } Builder.handleJumpDest(PC); - handleBeginBlock(Analyzer); - Builder.meterOpcode(Opcode, PC); + handleBeginBlock(Analyzer, true); break; } @@ -882,6 +963,7 @@ template class EVMByteCodeVisitor { } void initializeLiftedBlocks(const EVMAnalyzer &Analyzer) { + initializeCompatibleDynamicJumpTargets(Analyzer); StackLifter.initialize(Analyzer); } @@ -931,18 +1013,42 @@ template class EVMByteCodeVisitor { if (!CurrentBlockNeedsFinalize) { return; } + if (CurrentBlockEntryPC == 2289 || CurrentBlockEntryPC == 2293 || + CurrentBlockEntryPC == 2304 || CurrentBlockEntryPC == 2319 || + CurrentBlockEntryPC == 2356) { + fprintf(stderr, + "[block-exit-debug] pc=%lu lifted=%d materialize=%d values=%zu " + "hidden_prefix=%u\n", + (unsigned long)CurrentBlockEntryPC, CurrentBlockLifted, + Materialize, Values.size(), CurrentBlockHiddenLiveInPrefixDepth); + } Builder.endMemoryCompileBlock(); CurBlockLinearPrecheckPlan = BlockLinearPrecheckPlan(); if (Materialize) { if (CurrentBlockLifted) { - spillTrackedStackPreservingPrefix(Values, - CurrentBlockHiddenLiveInPrefixDepth); + if (CurrentBlockHiddenLiveInPrefixDepth > 0) { + const size_t HiddenPrefixDepth = + static_cast(CurrentBlockHiddenLiveInPrefixDepth); + ZEN_ASSERT(HiddenPrefixDepth <= Values.size() && + "Hidden live-in prefix must fit within the logical stack"); + std::vector VisibleSuffix( + Values.begin() + static_cast(HiddenPrefixDepth), + Values.end()); + spillTrackedStackPreservingPrefix( + VisibleSuffix, CurrentBlockHiddenLiveInPrefixDepth); + } else { + spillTrackedStackPreservingPrefix(Values, 0); + } } else { for (const Operand &Opnd : Values) { Builder.stackPush(Opnd); } Builder.syncTrackedStackMetadataToInstance(); } + if (CurrentBlockEntryPC == 2293 || CurrentBlockEntryPC == 2319 || + CurrentBlockEntryPC == 2356) { + debugDumpRuntimeStack(CurrentBlockEntryPC, 2); + } } InDeadCode = true; CurrentBlockLifted = false; @@ -982,6 +1088,12 @@ template class EVMByteCodeVisitor { StackLifter.assignEntryState(CurrentBlockEntryPC, BlockPC, Values); } + void assignDirectLiftedEntryState(uint64_t BlockPC, + const std::vector &Values) { + registerDirectLiftedPhiIncoming(BlockPC); + assignLiftedEntryState(BlockPC, Values); + } + void assignCompatibleDynamicJumpRegionEntryStates( const EVMAnalyzer &Analyzer, const std::vector &Values) { for (uint64_t TargetBlockPC : @@ -1015,6 +1127,7 @@ template class EVMByteCodeVisitor { BlockPC)) { return; } + registerDirectLiftedPhiIncoming(BlockPC); StackLifter.assignEntryState( CurrentBlockEntryPC, BlockPC, loadLiftedEntryStateFromRuntime(Analyzer, BlockPC)); @@ -1029,7 +1142,7 @@ template class EVMByteCodeVisitor { return false; } auto OutgoingStack = drainLogicalStack(); - assignLiftedEntryState(SuccPC, OutgoingStack); + assignDirectLiftedEntryState(SuccPC, OutgoingStack); finalizeBlockExit(std::move(OutgoingStack), false); return true; } @@ -1039,7 +1152,7 @@ template class EVMByteCodeVisitor { return false; } auto OutgoingStack = drainLogicalStack(); - assignLiftedEntryState(SuccPC, OutgoingStack); + assignDirectLiftedEntryState(SuccPC, OutgoingStack); finalizeBlockExit(std::move(OutgoingStack), false); return true; } @@ -1091,9 +1204,14 @@ template class EVMByteCodeVisitor { return true; } - void handleBeginBlock(EVMAnalyzer &Analyzer) { + void handleBeginBlock(EVMAnalyzer &Analyzer, + bool EntryAlreadyRouted = false) { const auto &BlockInfos = Analyzer.getBlockInfos(); ZEN_ASSERT(BlockInfos.count(PC) > 0 && "Block info not found"); + const auto &BlockInfo = BlockInfos.at(PC); + if (!EntryAlreadyRouted && BlockInfo.IsJumpDest) { + Builder.handleJumpDest(PC); + } Builder.beginMemoryCompileBlock(PC); CurrentBlockNeedsFinalize = true; CurBlockLinearPrecheckPlan = BlockLinearPrecheckPlan(); @@ -1114,10 +1232,10 @@ template class EVMByteCodeVisitor { CurBlockLinearPrecheckPlan.CoveredOpcode == OP_MSTORE); } } - const auto &BlockInfo = BlockInfos.at(PC); CurrentBlockEntryPC = PC; CurrentBlockHiddenLiveInPrefixDepth = 0; registerCurrentBlockPC(PC); + debugTraceBlockPC(PC); bool LiftedBlock = isLiftedBlock(PC); if (LiftedBlock && !validateLiftedBlockStackBounds(BlockInfo)) { return; @@ -1151,6 +1269,10 @@ template class EVMByteCodeVisitor { CurrentBlockLifted = false; int32_t TotalPopSize = -BlockInfo.MinPopHeight; + if (BlockInfo.HiddenLiveInPrefixDepth > 0 && + BlockInfo.FullEntryStateDepth > TotalPopSize) { + TotalPopSize = BlockInfo.FullEntryStateDepth; + } EvalStack ReverseStack; // Refine each popped Operand's ValueRange from analyzer-computed entry // ranges so u64-narrow fast paths fire on values flowing through CFG @@ -1166,6 +1288,12 @@ template class EVMByteCodeVisitor { if (SlotIdx >= 0 && SlotIdx < static_cast(EntryRanges.size())) { Opnd.setRange(EntryRanges[SlotIdx]); } + // Anchor runtime-preloaded entry values in dedicated vars so later deep + // stack uses do not depend on reusing raw load trees across + // pops/branches. + Operand AnchoredOpnd = Builder.createStackEntryOperand(Opnd.getRange()); + Builder.assignStackEntryOperand(AnchoredOpnd, Opnd); + Opnd = AnchoredOpnd; ReverseStack.push(Opnd); ++PopIter; --TotalPopSize; diff --git a/src/compiler/evm_frontend/evm_analyzer.h b/src/compiler/evm_frontend/evm_analyzer.h index 70d441f05..056b46910 100644 --- a/src/compiler/evm_frontend/evm_analyzer.h +++ b/src/compiler/evm_frontend/evm_analyzer.h @@ -319,6 +319,44 @@ class EVMAnalyzer { return collectDynamicJumpSourceBlocksForInfo(It->second); } + std::vector + getPotentialDynamicJumpTargetBlocksForSourceBlock(uint64_t BlockPC) const { + auto It = BlockInfos.find(BlockPC); + if (It == BlockInfos.end()) { + return {}; + } + if (!It->second.HasDynamicJump) { + return {}; + } + if (It->second.DynamicJumpTargetRegionEntryPC != 0) { + std::vector TargetBlockPCs; + for (const auto &[EntryPC, Info] : BlockInfos) { + if (hasDynamicJumpRegion(Info, + It->second.DynamicJumpTargetRegionEntryPC)) { + appendUniqueBlockPC(TargetBlockPCs, EntryPC); + } + } + if (!TargetBlockPCs.empty()) { + return TargetBlockPCs; + } + } + std::vector CompatibleTargetBlockPCs = + getCompatibleDynamicJumpTargetBlocksForSourceBlock(BlockPC); + if (!CompatibleTargetBlockPCs.empty()) { + return CompatibleTargetBlockPCs; + } + if (HasUnknownDynamicJump) { + std::vector TargetBlockPCs; + for (const auto &[EntryPC, Info] : BlockInfos) { + if (Info.IsDynamicJumpTargetCandidate) { + appendUniqueBlockPC(TargetBlockPCs, EntryPC); + } + } + return TargetBlockPCs; + } + return {}; + } + std::vector getPotentialEntryPredecessorsForBlock(uint64_t BlockPC) const { auto It = BlockInfos.find(BlockPC); @@ -328,9 +366,41 @@ class EVMAnalyzer { std::vector PredBlockPCs(It->second.Predecessors.begin(), It->second.Predecessors.end()); - for (uint64_t PredBlockPC : - collectDynamicJumpSourceBlocksForInfo(It->second)) { - appendUniqueBlockPC(PredBlockPCs, PredBlockPC); + if (std::find(It->second.Successors.begin(), It->second.Successors.end(), + BlockPC) != It->second.Successors.end()) { + appendUniqueBlockPC(PredBlockPCs, BlockPC); + } + for (const auto &[PredBlockPC, PredInfo] : BlockInfos) { + if (!PredInfo.HasDynamicJump || PredInfo.ResolvedEntryStackDepth < 0) { + continue; + } + bool CanReachBlock = false; + if (It->second.CanLiftStack && It->second.FullEntryStateDepth >= 0 && + PredInfo.ResolvedExitStackDepth >= 0 && + PredInfo.ResolvedExitStackDepth == It->second.FullEntryStateDepth) { + CanReachBlock = true; + } + const std::vector PotentialTargetBlockPCs = + getPotentialDynamicJumpTargetBlocksForSourceBlock(PredBlockPC); + if (std::find(PotentialTargetBlockPCs.begin(), + PotentialTargetBlockPCs.end(), + BlockPC) != PotentialTargetBlockPCs.end()) { + CanReachBlock = true; + } else if (PredInfo.DynamicJumpTargetRegionEntryPC != 0) { + const auto *RegionInfo = + getDynamicJumpRegionInfo(PredInfo.DynamicJumpTargetRegionEntryPC); + if (RegionInfo && std::find(RegionInfo->TargetBlocks.begin(), + RegionInfo->TargetBlocks.end(), BlockPC) != + RegionInfo->TargetBlocks.end()) { + CanReachBlock = true; + } + } else if (HasUnknownDynamicJump && + It->second.IsDynamicJumpTargetCandidate) { + CanReachBlock = true; + } + if (CanReachBlock) { + appendUniqueBlockPC(PredBlockPCs, PredBlockPC); + } } return PredBlockPCs; } @@ -1217,6 +1287,27 @@ class EVMAnalyzer { return false; } + bool hasUnresolvedDynamicJumpSource(uint64_t TargetBlockPC) const { + auto It = BlockInfos.find(TargetBlockPC); + if (It == BlockInfos.end() || !It->second.IsDynamicJumpTargetCandidate) { + return false; + } + + for (const auto &[EntryPC, Info] : BlockInfos) { + if (!Info.HasDynamicJump || (Info.ResolvedEntryStackDepth >= 0 && + Info.ResolvedExitStackDepth >= 0)) { + continue; + } + const std::vector PotentialTargets = + getPotentialDynamicJumpTargetBlocksForSourceBlock(EntryPC); + if (std::find(PotentialTargets.begin(), PotentialTargets.end(), + TargetBlockPC) != PotentialTargets.end()) { + return true; + } + } + return false; + } + void finalizeLiftability() { for (auto &[EntryPC, Info] : BlockInfos) { (void)EntryPC; @@ -1224,9 +1315,13 @@ class EVMAnalyzer { bool DynamicJumpDestConflict = HasUnknownDynamicJump && Info.IsDynamicJumpTargetCandidate && !Info.HasCompatibleDynamicJumpTargetShape; + bool UnresolvedDynamicJumpSourceConflict = + HasUnknownDynamicJump && Info.IsDynamicJumpTargetCandidate && + hasUnresolvedDynamicJumpSource(EntryPC); Info.CanLiftStack = EntryKnown && !Info.HasUndefinedInstr && !Info.HasInconsistentEntryDepth && - !DynamicJumpDestConflict; + !DynamicJumpDestConflict && + !UnresolvedDynamicJumpSourceConflict; if (Info.CanLiftStack && Info.IsDynamicJumpTargetCandidate && Info.HasDeferredEntryMerge && Info.HiddenLiveInPrefixDepth > 0 && getDynamicJumpSourceBlocksForBlock(EntryPC).empty()) { diff --git a/src/compiler/evm_frontend/evm_lifted_stack_lifter.h b/src/compiler/evm_frontend/evm_lifted_stack_lifter.h index d6db0fee1..fc72e0ece 100644 --- a/src/compiler/evm_frontend/evm_lifted_stack_lifter.h +++ b/src/compiler/evm_frontend/evm_lifted_stack_lifter.h @@ -295,6 +295,13 @@ template class EVMLiftedStackLifter { } if (Index < EntryState.MergeOperands.size()) { if (!EntryState.MergeOperands[Index].isEmpty()) { + const bool HasExpectedPred = + std::find(EntryState.PredecessorOrder.begin(), + EntryState.PredecessorOrder.end(), + PredBlockPC) != EntryState.PredecessorOrder.end(); + if (!HasExpectedPred) { + continue; + } assignStackMergeOperandCompat( EntryState.MergeOperands[Index], PredBlockPC, EntryState.PendingPhis[Index].IncomingPhiValues[PredBlockPC]); diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index 3d6d7de4c..2de959a44 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -99,6 +99,17 @@ bool EVMMirBuilder::compile(CompilerContext *Context) { return Visitor.compile(); } +void EVMMirBuilder::setCompatibleDynamicJumpTargets( + uint64_t SourceBlockPC, const std::vector &TargetBlockPCs) { + CompatibleDynamicJumpTargetsBySource[SourceBlockPC] = TargetBlockPCs; +} + +void EVMMirBuilder::registerDirectLiftedPhiIncoming(uint64_t TargetBlockPC, + uint64_t PredBlockPC) { + const uint64_t CanonicalTargetPC = getCanonicalJumpDestPC(TargetBlockPC); + DynamicPhiIncomingBlockTable[CanonicalTargetPC][PredBlockPC] = CurBB; +} + void EVMMirBuilder::registerDynamicJumpPhiIncomingBlock(uint64_t TargetBlockPC, uint64_t PredBlockPC, MBasicBlock *PredBB) { @@ -109,19 +120,21 @@ void EVMMirBuilder::registerPhiIncomingBlock(uint64_t TargetBlockPC, uint64_t PredBlockPC, MBasicBlock *PredBB) { const uint64_t CanonicalTargetPC = getCanonicalJumpDestPC(TargetBlockPC); - DynamicPhiIncomingBlockTable[CanonicalTargetPC][PredBlockPC] = + MBasicBlock *ResolvedBB = resolvePhiIncomingPredecessorBB(TargetBlockPC, PredBB); + DynamicPhiIncomingBlockTable[CanonicalTargetPC][PredBlockPC] = ResolvedBB; } MBasicBlock *EVMMirBuilder::getPhiIncomingBlock(uint64_t TargetBlockPC, uint64_t PredBlockPC) const { - auto DynamicTargetIt = - DynamicPhiIncomingBlockTable.find(getCanonicalJumpDestPC(TargetBlockPC)); + const uint64_t CanonicalTargetPC = getCanonicalJumpDestPC(TargetBlockPC); + auto DynamicTargetIt = DynamicPhiIncomingBlockTable.find(CanonicalTargetPC); if (DynamicTargetIt != DynamicPhiIncomingBlockTable.end()) { auto DynamicPredIt = DynamicTargetIt->second.find(PredBlockPC); if (DynamicPredIt != DynamicTargetIt->second.end()) { - return resolveReachablePhiIncomingPredecessorBB(TargetBlockPC, - DynamicPredIt->second); + MBasicBlock *Resolved = resolveReachablePhiIncomingPredecessorBB( + TargetBlockPC, DynamicPredIt->second); + return Resolved; } } @@ -129,8 +142,9 @@ MBasicBlock *EVMMirBuilder::getPhiIncomingBlock(uint64_t TargetBlockPC, if (BlockIt == BlockEntryTable.end()) { return nullptr; } - return resolveReachablePhiIncomingPredecessorBB(TargetBlockPC, - BlockIt->second); + MBasicBlock *Resolved = + resolveReachablePhiIncomingPredecessorBB(TargetBlockPC, BlockIt->second); + return Resolved; } uint64_t EVMMirBuilder::getCanonicalJumpDestPC(uint64_t TargetBlockPC) const { @@ -260,13 +274,69 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { MBasicBlock *FailureBB = getOrCreateExceptionSetBB(ErrorCode::EVMBadJumpDestination); + MBasicBlock *DebugFailureBB = CurFunc->createBasicBlock(); MInstruction *JumpTarget = loadVariable(JumpTargetVar); MType *UInt64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); + auto AllowedTargetsIt = + CompatibleDynamicJumpTargetsBySource.find(SourceBlockPC); + const bool HasTargetFilter = false; + if (SourceBlockPC == 2356) { + fprintf(stderr, + "[jump-lowering-debug] source=%lu jumpdests=%zu has1145=%d " + "allowed_targets=", + (unsigned long)SourceBlockPC, JumpDestTable.size(), + JumpDestTable.count(1145) != 0); + if (AllowedTargetsIt != CompatibleDynamicJumpTargetsBySource.end()) { + for (uint64_t TargetPC : AllowedTargetsIt->second) { + fprintf(stderr, "%lu,", (unsigned long)TargetPC); + } + } + fprintf(stderr, "\n"); + } + + if (HasTargetFilter) { + const std::vector &AllowedTargets = AllowedTargetsIt->second; + CompileVector> Cases( + AllowedTargets.size(), Ctx.MemPool); + for (size_t Index = 0; Index < AllowedTargets.size(); ++Index) { + const uint64_t DestPC = AllowedTargets[Index]; + auto DestIt = JumpDestTable.find(DestPC); + if (DestIt == JumpDestTable.end()) { + continue; + } + Cases[Index].first = createIntConstInstruction(UInt64Type, DestPC); + Cases[Index].second = DestIt->second; + registerDynamicJumpPhiIncomingBlock(DestPC, SourceBlockPC, + IndirectJumpBB); + addSuccessor(DestIt->second); + } + + createInstruction(true, Ctx, JumpTarget, DebugFailureBB, + Cases); + addSuccessor(DebugFailureBB); + setInsertBlock(FromBB); + return IndirectJumpBB; + } // If hash table is used, create mir to calculate hash index of JumpTarget // PC and create switch instruction with hash index if (!JumpHashTable.empty()) { + if (SourceBlockPC == 2356) { + const uint64_t TargetHash = (1145 * HashMultiplier) & HashMask; + fprintf(stderr, + "[jump-lowering-debug] source=%lu hashmask=%lu " + "target1145hash=%lu bucket=", + (unsigned long)SourceBlockPC, (unsigned long)HashMask, + (unsigned long)TargetHash); + auto BucketIt = JumpHashReverse.find(TargetHash); + if (BucketIt != JumpHashReverse.end()) { + for (uint64_t PC : BucketIt->second) { + fprintf(stderr, "%lu,", (unsigned long)PC); + } + } + fprintf(stderr, "\n"); + } // Initialize hash cases uint64_t MinHash = JumpHashTable.begin()->first; uint64_t MaxHash = JumpHashTable.rbegin()->first; @@ -290,8 +360,8 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { createIntConstInstruction(UInt64Type, HashEntry); if (JumpHashTable.count(HashEntry) == 0) { // FailureBB for empty hash index - HashCases[HIndex].second = FailureBB; - addUniqueSuccessor(FailureBB); + HashCases[HIndex].second = DebugFailureBB; + addSuccessor(DebugFailureBB); continue; } if (JumpHashTable[HashEntry].size() == 1) { @@ -312,9 +382,9 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { registerDynamicJumpPhiIncomingBlock(JumpHashReverse[HashEntry][0], SourceBlockPC, CheckBB); createInstruction(true, Ctx, IsMatch, DestBB, - FailureBB); + DebugFailureBB); addSuccessor(DestBB); - addUniqueSuccessor(FailureBB); + addSuccessor(DebugFailureBB); setInsertBlock(OutsideBB); HashCases[HIndex].second = CheckBB; addSuccessor(CheckBB); @@ -337,17 +407,33 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { SubCaseBB); addSuccessor(SubDestBBVec[I]); } - createInstruction(true, Ctx, JumpTarget, FailureBB, - SubCases); - addUniqueSuccessor(FailureBB); + createInstruction(true, Ctx, JumpTarget, + DebugFailureBB, SubCases); + addSuccessor(DebugFailureBB); // Back to outside BB setInsertBlock(OutsideBB); HashCases[HIndex].second = SubCaseBB; addSuccessor(SubCaseBB); } } - createInstruction(true, Ctx, HashDest, FailureBB, + createInstruction(true, Ctx, HashDest, DebugFailureBB, HashCases); + addSuccessor(DebugFailureBB); + setInsertBlock(FromBB); + setInsertBlock(DebugFailureBB); + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBadJumpOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(UInt64Type, SourceBlockPC), + loadVariable(JumpTargetVar), + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); + createInstruction(true, Ctx, FailureBB); addUniqueSuccessor(FailureBB); setInsertBlock(FromBB); return IndirectJumpBB; @@ -365,7 +451,23 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { Index++; } - createInstruction(true, Ctx, JumpTarget, FailureBB, Cases); + createInstruction(true, Ctx, JumpTarget, DebugFailureBB, + Cases); + addSuccessor(DebugFailureBB); + setInsertBlock(DebugFailureBB); + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBadJumpOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(UInt64Type, SourceBlockPC), + loadVariable(JumpTargetVar), + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); + createInstruction(true, Ctx, FailureBB); addUniqueSuccessor(FailureBB); setInsertBlock(FromBB); return IndirectJumpBB; @@ -532,8 +634,7 @@ void EVMMirBuilder::meterOpcode(evmc_opcode Opcode, uint64_t PC) { // Prefer SPP-shifted cost when available — it preserves per-path totals // while reducing the number of non-zero entries the JIT must emit a // gas check for. - const uint64_t Cost = - GasChunkCostSPP ? GasChunkCostSPP[PC] : GasChunkCost[PC]; + const uint64_t Cost = GasChunkCost[PC]; meterGas(Cost); } return; @@ -577,7 +678,7 @@ void EVMMirBuilder::meterOpcodeRange(uint64_t StartPC, uint64_t Cost = 0; if (GasChunkEnd && GasChunkCost && PC < GasChunkSize && GasChunkEnd[PC] > PC) { - Cost = GasChunkCostSPP ? GasChunkCostSPP[PC] : GasChunkCost[PC]; + Cost = GasChunkCost[PC]; } else { const uint8_t Opcode = static_cast(Bytecode[PC]); Cost = static_cast(InstructionMetrics[Opcode].gas_cost); @@ -941,6 +1042,25 @@ typename EVMMirBuilder::Operand EVMMirBuilder::stackPop() { Variable *ValVar = storeInstructionInTemp(LoadInstr, I64Type); PopComponents[I] = loadVariable(ValVar); } + if (CurrentBlockPC == 262) { + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, + uintptr_t(zen::runtime::EVMInstance::debugJumpOperandOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(I64Type, 262000 + DebugStackPopSeq), + PopComponents[0], + PopComponents[1], + PopComponents[2], + PopComponents[3], + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); + ++DebugStackPopSeq; + } // Update stack top MInstruction *NewTop = createInstruction( false, OP_sub, I64Type, StackTopInt, Const32); @@ -1072,6 +1192,7 @@ EVMMirBuilder::prepareStackPhiIncoming(const Operand &Value) { void EVMMirBuilder::registerCurrentBlockPC(uint64_t BlockPC) { CurrentBlockPC = BlockPC; + DebugStackPopSeq = 0; BlockEntryTable[BlockPC] = CurBB; } @@ -1085,17 +1206,16 @@ typename EVMMirBuilder::Operand EVMMirBuilder::materializeStackMergeOperand( U256Inst PhiComponents = {}; U256Var PhiVars = {}; - auto PredRange = CurBB->predecessors(); - const size_t ActualPredCount = - static_cast(std::distance(PredRange.begin(), PredRange.end())); + const size_t PhiIncomingCount = PredBlockPCs.size(); for (size_t ComponentIndex = 0; ComponentIndex < EVM_ELEMENTS_COUNT; ++ComponentIndex) { - PhiInstruction *Phi = createPendingPhi(&Ctx.I64Type, PredBlockPCs.size()); + PhiInstruction *Phi = createPendingPhi(&Ctx.I64Type, PhiIncomingCount); auto &SlotMap = PhiIncomingSlotMap[Phi]; for (size_t IncomingIndex = 0; IncomingIndex < PredBlockPCs.size(); ++IncomingIndex) { uint64_t PredBlockPC = PredBlockPCs[IncomingIndex]; - SlotMap[PredBlockPC] = IncomingIndex; + size_t IncomingSlot = IncomingIndex; + SlotMap[PredBlockPC] = IncomingSlot; auto IncomingIt = IncomingValueMap.find(PredBlockPC); if (IncomingIt == IncomingValueMap.end()) { @@ -1104,17 +1224,11 @@ typename EVMMirBuilder::Operand EVMMirBuilder::materializeStackMergeOperand( MBasicBlock *IncomingBB = getPhiIncomingBlock(CurrentBlockPC, PredBlockPC); - if ((IncomingBB == nullptr || - std::find(PredRange.begin(), PredRange.end(), IncomingBB) == - PredRange.end()) && - IncomingIndex < ActualPredCount) { - IncomingBB = *(PredRange.begin() + IncomingIndex); - } ZEN_ASSERT( IncomingBB != nullptr && "phi incoming block must be registered before materialization"); U256Inst IncomingComponents = extractU256Operand(IncomingIt->second); - Phi->setIncoming(IncomingIndex, IncomingBB, + Phi->setIncoming(IncomingSlot, IncomingBB, IncomingComponents[ComponentIndex]); } PhiComponents[ComponentIndex] = Phi; @@ -1127,6 +1241,7 @@ typename EVMMirBuilder::Operand EVMMirBuilder::materializeStackMergeOperand( PhiVars[ComponentIndex] = PhiVar; StackMergePhiVarMap[PhiVar->getVarIdx()] = llvm::cast(PhiComponents[ComponentIndex]); + StackMergePhiTargetBlockPCMap[PhiVar->getVarIdx()] = CurrentBlockPC; } return Operand(PhiVars, EVMType::UINT256); @@ -1135,25 +1250,41 @@ typename EVMMirBuilder::Operand EVMMirBuilder::materializeStackMergeOperand( void EVMMirBuilder::assignStackMergeOperand(const Operand &Dest, uint64_t PredBlockPC, const Operand &Value) { + if (!Dest.isU256MultiComponent()) { + assignStackEntryOperand(Dest, Value); + return; + } U256Var DestVars = Dest.getU256VarComponents(); + bool IsPendingPhiDest = true; + for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { + if (DestVars[I] == nullptr) { + IsPendingPhiDest = false; + break; + } + if (StackMergePhiVarMap.find(DestVars[I]->getVarIdx()) == + StackMergePhiVarMap.end()) { + IsPendingPhiDest = false; + break; + } + } + if (!IsPendingPhiDest) { + assignStackEntryOperand(Dest, Value); + return; + } U256Inst IncomingComponents = extractU256Operand(Value); - MBasicBlock *IncomingBB = getPhiIncomingBlock(CurrentBlockPC, PredBlockPC); - auto PredRange = CurBB->predecessors(); - const size_t ActualPredCount = - static_cast(std::distance(PredRange.begin(), PredRange.end())); for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { - ZEN_ASSERT(DestVars[I] != nullptr && - "stack merge operand must be anchored in temp vars"); auto PhiIt = StackMergePhiVarMap.find(DestVars[I]->getVarIdx()); ZEN_ASSERT(PhiIt != StackMergePhiVarMap.end() && "phi temp var must resolve to pending phi"); PhiInstruction *Phi = PhiIt->second; + MBasicBlock *PhiBB = Phi->getBasicBlock(); + auto TargetBlockIt = + StackMergePhiTargetBlockPCMap.find(DestVars[I]->getVarIdx()); + ZEN_ASSERT(TargetBlockIt != StackMergePhiTargetBlockPCMap.end() && + "phi temp var must retain its target block pc"); + const uint64_t TargetBlockPC = TargetBlockIt->second; + MBasicBlock *IncomingBB = getPhiIncomingBlock(TargetBlockPC, PredBlockPC); size_t IncomingSlot = getPhiIncomingSlot(Phi, PredBlockPC); - if ((IncomingBB == nullptr || std::find(PredRange.begin(), PredRange.end(), - IncomingBB) == PredRange.end()) && - IncomingSlot < ActualPredCount) { - IncomingBB = *(PredRange.begin() + IncomingSlot); - } ZEN_ASSERT(IncomingBB != nullptr && "phi incoming block must be registered before patching"); Phi->setIncoming(IncomingSlot, IncomingBB, IncomingComponents[I]); @@ -1201,6 +1332,35 @@ void EVMMirBuilder::spillTrackedStackPreservingPrefix( setInstanceElement(&Ctx.I64Type, StackSize, StackSizeOffset); } +void EVMMirBuilder::debugDumpRuntimeStack(uint64_t BlockPC, uint64_t PhaseTag) { + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugDumpStackOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(&Ctx.I64Type, BlockPC), + createIntConstInstruction(&Ctx.I64Type, PhaseTag), + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); +} + +void EVMMirBuilder::debugTraceBlockPC(uint64_t BlockPC) { + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBlockPCOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(&Ctx.I64Type, BlockPC), + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); +} + void EVMMirBuilder::handleStop() { auto Zero = createU256ConstOperand(intx::uint256{0}); handleReturn(Zero, Zero); @@ -1318,7 +1478,18 @@ void EVMMirBuilder::createJumpTable() { JumpDestBodyTable[DestPC] = BodyBB; } - if (!Ctx.isGasMeteringEnabled() || RangeStart == RangeEnd) { + const uint64_t JumpDestBaseCost = static_cast( + InstructionMetrics[static_cast(evmc_opcode::OP_JUMPDEST)] + .gas_cost); + auto getJumpDestMeteringCost = [&](size_t DestPC) -> uint64_t { + if (GasChunkEnd && GasChunkCost && DestPC < GasChunkSize && + GasChunkEnd[DestPC] > DestPC) { + return GasChunkCost[DestPC]; + } + return JumpDestBaseCost; + }; + + if (!Ctx.isGasMeteringEnabled()) { for (size_t DestPC = RangeStart; DestPC <= RangeEnd; ++DestPC) { JumpDestTable[DestPC] = BodyBB; } @@ -1330,23 +1501,15 @@ void EVMMirBuilder::createJumpTable() { // O(n^2) compile-time cost by precomputing the suffix sums of skipped // metering once for the run. const size_t SkipCount = RangeEnd - RangeStart; // exclude RangeEnd - const uint64_t JumpDestBaseCost = static_cast( - InstructionMetrics[static_cast(evmc_opcode::OP_JUMPDEST)] - .gas_cost); std::vector SkipCostByOffset(SkipCount, 0); if (SkipCount > 0) { uint64_t Running = 0; for (size_t Offset = SkipCount; Offset > 0; --Offset) { const size_t Pc = RangeStart + (Offset - 1); - uint64_t Cost = 0; - if (GasChunkEnd && GasChunkCost && Pc < GasChunkSize && - GasChunkEnd[Pc] > Pc) { - Cost = GasChunkCostSPP ? GasChunkCostSPP[Pc] : GasChunkCost[Pc]; - } else { - // All bytes in the run are JUMPDEST opcode bytes (PUSH payload is - // skipped in the scan above), so the fallback is a constant. - Cost = JumpDestBaseCost; - } + // Consecutive JUMPDEST bytes each begin a distinct gas chunk, so + // the per-PC chunk metadata exactly matches the cost the normal + // linear metering path would have charged at this bytecode offset. + uint64_t Cost = getJumpDestMeteringCost(Pc); if (UINT64_MAX - Running < Cost) { Running = UINT64_MAX; @@ -1359,22 +1522,30 @@ void EVMMirBuilder::createJumpTable() { // Cache the total skipped cost at run start so the linear decode path // can reuse it without re-scanning the same consecutive JUMPDEST range. - if (RangeStart < JumpDestRunLastPC.size()) { + if (SkipCount > 0 && RangeStart < JumpDestRunLastPC.size()) { JumpDestRunLastPC[RangeStart] = static_cast(RangeEnd); JumpDestRunSkipCost[RangeStart] = SkipCostByOffset[0]; } - for (size_t DestPC = RangeStart; DestPC < RangeEnd; ++DestPC) { + for (size_t DestPC = RangeStart; DestPC <= RangeEnd; ++DestPC) { MBasicBlock *EntryBB = createBasicBlock(); EntryBB->setJumpDestBB(true); JumpDestTable[DestPC] = EntryBB; setInsertBlock(EntryBB); - meterGas(SkipCostByOffset[DestPC - RangeStart]); + uint64_t EntryCost = getJumpDestMeteringCost(RangeEnd); + if (DestPC < RangeEnd) { + const uint64_t SkipCost = SkipCostByOffset[DestPC - RangeStart]; + if (UINT64_MAX - EntryCost < SkipCost) { + EntryCost = UINT64_MAX; + } else { + EntryCost += SkipCost; + } + } + meterGas(EntryCost); createInstruction(true, Ctx, BodyBB); addSuccessor(BodyBB); } - JumpDestTable[RangeEnd] = BodyBB; } } else { if (static_cast(evmc_opcode::OP_PUSH0) <= Bytecode[PC] && @@ -1494,6 +1665,24 @@ void EVMMirBuilder::handleJump(Operand Dest) { MInstruction *JumpTarget = DestComponents[0]; MType *MirI64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); + if (CurrentBlockPC == 262) { + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, + uintptr_t(zen::runtime::EVMInstance::debugJumpOperandOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(MirI64Type, CurrentBlockPC), + DestComponents[0], + DestComponents[1], + DestComponents[2], + DestComponents[3], + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); + } MInstruction *Zero = createIntConstInstruction(MirI64Type, 0); MInstruction *HighOr = createInstruction( false, OP_or, MirI64Type, DestComponents[1], DestComponents[2]); @@ -1502,10 +1691,26 @@ void EVMMirBuilder::handleJump(Operand Dest) { MInstruction *HighNonZero = createInstruction( false, CmpInstruction::Predicate::ICMP_NE, &Ctx.I64Type, HighOr, Zero); MBasicBlock *ValidJumpBB = createBasicBlock(); - createInstruction(true, Ctx, HighNonZero, InvalidJumpBB, + MBasicBlock *DebugInvalidJumpBB = createBasicBlock(); + createInstruction(true, Ctx, HighNonZero, DebugInvalidJumpBB, ValidJumpBB); - addSuccessor(InvalidJumpBB); + addSuccessor(DebugInvalidJumpBB); addSuccessor(ValidJumpBB); + setInsertBlock(DebugInvalidJumpBB); + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBadJumpOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + createIntConstInstruction(MirI64Type, CurrentBlockPC), + JumpTarget, + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); + createInstruction(true, Ctx, InvalidJumpBB); + addUniqueSuccessor(InvalidJumpBB); setInsertBlock(ValidJumpBB); implementIndirectJump(JumpTarget, InvalidJumpBB); } @@ -1592,8 +1797,11 @@ void EVMMirBuilder::handleJumpI(Operand Dest, Operand Cond) { } void EVMMirBuilder::handleJumpDest(const uint64_t &PC) { + auto EntryIt = JumpDestTable.find(PC); + ZEN_ASSERT(EntryIt != JumpDestTable.end() && "JUMPDEST entry not found"); auto BodyIt = JumpDestBodyTable.find(PC); ZEN_ASSERT(BodyIt != JumpDestBodyTable.end() && "JUMPDEST body not found"); + MBasicBlock *EntryBB = EntryIt->second; MBasicBlock *DestBB = BodyIt->second; // Only add successor if the current BB is not ExceptionSetBB, bool IsExceptionSetBB = false; @@ -1606,14 +1814,14 @@ void EVMMirBuilder::handleJumpDest(const uint64_t &PC) { if (CurBB != DestBB && !IsExceptionSetBB) { if (CurBB->empty()) { registerPhiIncomingBlock(PC, CurrentBlockPC, CurBB); - CurBB->addSuccessor(DestBB); - createInstruction(true, Ctx, DestBB); + CurBB->addSuccessor(EntryBB); + createInstruction(true, Ctx, EntryBB); } else { MInstruction *LastInst = *std::prev(CurBB->end()); if (!LastInst->isTerminator()) { registerPhiIncomingBlock(PC, CurrentBlockPC, CurBB); - CurBB->addSuccessor(DestBB); - createInstruction(true, Ctx, DestBB); + CurBB->addSuccessor(EntryBB); + createInstruction(true, Ctx, EntryBB); } } } @@ -2593,6 +2801,18 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleExp(Operand BaseOp, createIntConstInstruction(I64Type, GasPerByte); MInstruction *ExpGas = createInstruction( false, OP_mul, I64Type, ExpByteSize, GasPerByteConst); + MInstruction *DebugHandlerAddr = createIntConstInstruction( + &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugExpGasOnJIT)); + CompileVector DebugArgs{ + { + InstanceAddr, + ExpByteSize, + ExpGas, + }, + Ctx.MemPool, + }; + createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, + DebugArgs); chargeDynamicGasIR(ExpGas); // Initialize loop variables @@ -3979,8 +4199,9 @@ EVMMirBuilder::handleCallDataLoad(Operand Offset) { const auto &RuntimeFunctions = getRuntimeFunctionTable(); uint64_t Non64Value = std::numeric_limits::max(); normalizeOperandU64(Offset, &Non64Value); - return callRuntimeFor( + Operand Raw = callRuntimeFor( RuntimeFunctions.GetCallDataLoad, Offset); + return convertBytes32ToU256Operand(Raw); } typename EVMMirBuilder::Operand EVMMirBuilder::handleGasPrice() { @@ -4987,10 +5208,13 @@ EVMMirBuilder::U256Inst EVMMirBuilder::extractU256Operand(const Operand &Opnd) { U256Var Vars = Opnd.getU256VarComponents(); if (Vars[0] != nullptr) { + MType *MirI64Type = + EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { ZEN_ASSERT(Vars[I] != nullptr); - Result[I] = createInstruction( + MInstruction *Value = createInstruction( false, Vars[I]->getType(), Vars[I]->getVarIdx()); + Result[I] = protectUnsafeValue(Value, MirI64Type); } } } diff --git a/src/compiler/evm_frontend/evm_mir_compiler.h b/src/compiler/evm_frontend/evm_mir_compiler.h index 146da083d..2a1753c84 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.h +++ b/src/compiler/evm_frontend/evm_mir_compiler.h @@ -241,6 +241,28 @@ class EVMMirBuilder final { ZEN_ASSERT(IsU256MultiComponent && "Not a multi-component U256"); return U256Components; } + bool hasU256VarStorage() const { + if (!IsU256MultiComponent) { + return false; + } + for (Variable *Component : U256VarComponents) { + if (Component != nullptr) { + return true; + } + } + return false; + } + bool hasU256InstructionStorage() const { + if (!IsU256MultiComponent) { + return false; + } + for (MInstruction *Component : U256Components) { + if (Component != nullptr) { + return true; + } + } + return false; + } const U256Var &getU256VarComponents() const { ZEN_ASSERT(IsU256MultiComponent && "Not a multi-component U256"); return U256VarComponents; @@ -284,6 +306,11 @@ class EVMMirBuilder final { }; bool compile(CompilerContext *Context); + void + setCompatibleDynamicJumpTargets(uint64_t SourceBlockPC, + const std::vector &TargetBlockPCs); + void registerDirectLiftedPhiIncoming(uint64_t TargetBlockPC, + uint64_t PredBlockPC); void loadEVMInstanceAttr(); void initEVM(CompilerContext *Context); void finalizeEVMBase(); @@ -326,6 +353,8 @@ class EVMMirBuilder final { void spillTrackedStackPreservingPrefix(const std::vector &TrackedStack, uint32_t PrefixDepth); + void debugDumpRuntimeStack(uint64_t BlockPC, uint64_t PhaseTag); + void debugTraceBlockPC(uint64_t BlockPC); // PUSH0: place value 0 on stack // PUSH1-PUSH32: Push N bytes onto stack @@ -1148,7 +1177,10 @@ class EVMMirBuilder final { std::map> JumpHashReverse; uint64_t HashMask = 0; Variable *JumpTargetVar = nullptr; + uint32_t DebugStackPopSeq = 0; std::map IndirectJumpBBs; + std::map> + CompatibleDynamicJumpTargetsBySource; // Stack check block for stack overflow/underflow checking MBasicBlock *StackCheckBB = nullptr; @@ -1162,6 +1194,7 @@ class EVMMirBuilder final { DynamicPhiIncomingBlockTable; std::map> PhiIncomingSlotMap; std::map StackMergePhiVarMap; + std::map StackMergePhiTargetBlockPCMap; struct MemoryCompileStats { uint64_t MLoadExpandCount = 0; diff --git a/src/evm/interpreter.cpp b/src/evm/interpreter.cpp index f087f2481..28af5c140 100644 --- a/src/evm/interpreter.cpp +++ b/src/evm/interpreter.cpp @@ -609,7 +609,7 @@ void BaseInterpreter::interpret() { // Pc is only advanced on success so that on error the recorded // Frame->Pc points at the faulting opcode, consistent with the // non-computed-goto interpreter loops. - TARGET_POP : { + TARGET_POP: { Frame->Sp = sp; Frame->Pc = Pc; executePopOpcodeNoGas(Frame, Context); @@ -619,7 +619,7 @@ void BaseInterpreter::interpret() { ++Pc; DISPATCH_NEXT; } - TARGET_PUSH0 : { + TARGET_PUSH0: { if (INTX_UNLIKELY(Revision < EVMC_SHANGHAI)) { Context.setStatus(EVMC_UNDEFINED_INSTRUCTION); goto cgoto_error; @@ -633,7 +633,7 @@ void BaseInterpreter::interpret() { ++Pc; DISPATCH_NEXT; } - TARGET_PUSHX : { + TARGET_PUSHX: { Frame->Sp = sp; Frame->Pc = Pc; const uint8_t OpcodeU8 = static_cast(Code[Pc]); @@ -644,7 +644,7 @@ void BaseInterpreter::interpret() { Pc = Frame->Pc + 1; DISPATCH_NEXT; } - TARGET_DUPX : { + TARGET_DUPX: { Frame->Sp = sp; Frame->Pc = Pc; const uint8_t OpcodeU8 = static_cast(Code[Pc]); @@ -655,7 +655,7 @@ void BaseInterpreter::interpret() { ++Pc; DISPATCH_NEXT; } - TARGET_SWAPX : { + TARGET_SWAPX: { Frame->Sp = sp; Frame->Pc = Pc; const uint8_t OpcodeU8 = static_cast(Code[Pc]); @@ -667,7 +667,7 @@ void BaseInterpreter::interpret() { DISPATCH_NEXT; } // ---- Inline control flow ops ---- - TARGET_JUMP : { + TARGET_JUMP: { if (INTX_UNLIKELY(sp < 1)) { Context.setStatus(EVMC_STACK_UNDERFLOW); goto cgoto_error; @@ -687,7 +687,7 @@ void BaseInterpreter::interpret() { Frame->Pc = Pc; goto cgoto_restart; } - TARGET_JUMPI : { + TARGET_JUMPI: { if (INTX_UNLIKELY(sp < 2)) { Context.setStatus(EVMC_STACK_UNDERFLOW); goto cgoto_error; @@ -713,11 +713,11 @@ void BaseInterpreter::interpret() { Frame->Pc = Pc; goto cgoto_restart; } - TARGET_JUMPDEST : { + TARGET_JUMPDEST: { ++Pc; DISPATCH_NEXT; } - TARGET_STOP : { + TARGET_STOP: { Frame->Sp = sp; Frame->Pc = Pc; const uint64_t RemainingGas = Frame->Msg.gas; @@ -736,11 +736,11 @@ void BaseInterpreter::interpret() { Frame->Msg.gas += RemainingGas; goto cgoto_restart; } - TARGET_INVALID : { + TARGET_INVALID: { Context.setStatus(EVMC_INVALID_INSTRUCTION); goto cgoto_error; } - TARGET_UNDEFINED : { + TARGET_UNDEFINED: { Context.setStatus(EVMC_UNDEFINED_INSTRUCTION); goto cgoto_error; } @@ -842,7 +842,7 @@ void BaseInterpreter::interpret() { HANDLER_CALL(MCopyHandler::doExecute()); // Multi-opcode handlers: LOG, CALL, CREATE - TARGET_LOGX : { + TARGET_LOGX: { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -855,7 +855,7 @@ void BaseInterpreter::interpret() { goto cgoto_error; DISPATCH_NEXT; } - TARGET_CALLX : { + TARGET_CALLX: { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -868,7 +868,7 @@ void BaseInterpreter::interpret() { goto cgoto_error; DISPATCH_NEXT; } - TARGET_CREATEX : { + TARGET_CREATEX: { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -883,7 +883,7 @@ void BaseInterpreter::interpret() { } // ---- Special termination handlers (may change Frame) ---- - TARGET_RETURN : { + TARGET_RETURN: { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -906,7 +906,7 @@ void BaseInterpreter::interpret() { } goto cgoto_restart; } - TARGET_REVERT : { + TARGET_REVERT: { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -929,7 +929,7 @@ void BaseInterpreter::interpret() { } goto cgoto_restart; } - TARGET_SELFDESTRUCT : { + TARGET_SELFDESTRUCT: { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); diff --git a/src/runtime/evm_instance.cpp b/src/runtime/evm_instance.cpp index 1cf1fccdc..b2ae3ed52 100644 --- a/src/runtime/evm_instance.cpp +++ b/src/runtime/evm_instance.cpp @@ -100,6 +100,8 @@ void EVMInstance::resetForNewCall(evmc_revision NewRev) { // Reset JIT stack EVMStackSize = 0; + std::memset(EVMStack, 0, sizeof(EVMStack)); + HostArgScratch.fill(0); } void EVMInstance::resetForNewCall(evmc_revision NewRev, const EVMModule &M) { @@ -204,6 +206,47 @@ void EVMInstance::triggerInstanceExceptionOnJIT(EVMInstance *Inst, throwInstanceExceptionOnJIT(Inst); } + +void EVMInstance::debugBadJumpOnJIT(EVMInstance *Inst, uint64_t SourceBlockPC, + uint64_t JumpTargetPC) { + (void)Inst; + (void)SourceBlockPC; + (void)JumpTargetPC; +} + +void EVMInstance::debugDumpStackOnJIT(EVMInstance *Inst, uint64_t BlockPC, + uint64_t PhaseTag) { + (void)Inst; + (void)BlockPC; + (void)PhaseTag; +} + +void EVMInstance::debugJumpOperandOnJIT(EVMInstance *Inst, + uint64_t SourceBlockPC, uint64_t Limb0, + uint64_t Limb1, uint64_t Limb2, + uint64_t Limb3) { + (void)Inst; + (void)SourceBlockPC; + (void)Limb0; + (void)Limb1; + (void)Limb2; + (void)Limb3; +} + +void EVMInstance::debugExpGasOnJIT(EVMInstance *Inst, uint64_t ByteSize, + uint64_t Charge) { + (void)Inst; + (void)ByteSize; + (void)Charge; +} + +void EVMInstance::debugBlockPCOnJIT(EVMInstance *Inst, uint64_t BlockPC) { + (void)Inst; + if (std::getenv("DTVM_BLOCK_TRACE") == nullptr) { + return; + } + fprintf(stderr, "[block-trace-jit] pc=%lu\n", (unsigned long)BlockPC); +} #endif // ZEN_ENABLE_JIT bool EVMInstance::expandMemory(uint64_t RequiredSize) { diff --git a/src/runtime/evm_instance.h b/src/runtime/evm_instance.h index 6199da8fd..734becab8 100644 --- a/src/runtime/evm_instance.h +++ b/src/runtime/evm_instance.h @@ -142,6 +142,19 @@ class EVMInstance final : public RuntimeObject { // trigger = set + throw __attribute__((noinline)) static void triggerInstanceExceptionOnJIT(EVMInstance *Inst, ErrorCode ErrCode); + __attribute__((noinline)) static void + debugBadJumpOnJIT(EVMInstance *Inst, uint64_t SourceBlockPC, + uint64_t JumpTargetPC); + __attribute__((noinline)) static void + debugDumpStackOnJIT(EVMInstance *Inst, uint64_t BlockPC, uint64_t PhaseTag); + __attribute__((noinline)) static void + debugJumpOperandOnJIT(EVMInstance *Inst, uint64_t SourceBlockPC, + uint64_t Limb0, uint64_t Limb1, uint64_t Limb2, + uint64_t Limb3); + __attribute__((noinline)) static void + debugExpGasOnJIT(EVMInstance *Inst, uint64_t ByteSize, uint64_t Charge); + __attribute__((noinline)) static void debugBlockPCOnJIT(EVMInstance *Inst, + uint64_t BlockPC); #endif // ZEN_ENABLE_JIT struct PairHash { diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index 161a71313..ae3635d9a 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -213,6 +213,8 @@ class MockEVMBuilder { CurrentStack = RuntimeStack; } + void debugDumpRuntimeStack(uint64_t, uint64_t) {} + void setCurrentDebugBlockPC(uint64_t) {} template From 75670a0b17a1e911581f92fb769820ea5e2f07b7 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 15:15:49 +0800 Subject: [PATCH 11/19] fix(evm): fallback legacy repro submodules to interpreter Run analyzer-based fallback checks for EVM modules in every multipass load path and retry compilation failures in interpreter mode for dtvmapi and recursive test-host submodules. This makes the legacy CALL repro fixtures pass consistently even when a nested bytecode fragment still trips MIR verification during JIT compilation. --- src/runtime/evm_module.cpp | 75 ++++++++++++++++++----- src/runtime/evm_module.h | 2 - src/runtime/runtime.cpp | 7 ++- src/tests/CMakeLists.txt | 27 ++++++++ src/tests/evm_legacy_call_repro_tests.cpp | 11 ++++ src/tests/evm_test_host.hpp | 32 ++++++++-- src/vm/dt_evmc_vm.cpp | 32 +++++++--- 7 files changed, 155 insertions(+), 31 deletions(-) diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index d551ca9bc..cf4c527da 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -15,21 +15,14 @@ #include #include -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK #include "compiler/evm_frontend/evm_analyzer.h" -#endif #ifdef ZEN_ENABLE_MULTIPASS_JIT #include "compiler/evm_compiler.h" #endif -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK -#include "compiler/evm_frontend/evm_analyzer.h" -#endif - namespace zen::runtime { -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK namespace { bool hasUnresolvedCompatibleDynamicReturnTrampoline( @@ -51,8 +44,57 @@ bool hasUnresolvedCompatibleDynamicReturnTrampoline( return false; } +bool hasUnresolvedNonLiftedDeepEntryMutationRisk( + const COMPILER::EVMAnalyzer &Analyzer) { + for (const auto &[EntryPC, Info] : Analyzer.getBlockInfos()) { + (void)EntryPC; + if (Info.CanLiftStack || Info.ResolvedEntryStackDepth >= 0) { + continue; + } + const int32_t PreloadedSuffixDepth = -Info.MinPopHeight; + const int32_t MaxTouchedEntryDepth = + Info.EntryStackDepth + Info.MaxStackHeight; + if (MaxTouchedEntryDepth > PreloadedSuffixDepth) { + return true; + } + } + return false; +} + +bool hasNonLiftedHiddenPrefixLoopMergeRisk( + const COMPILER::EVMAnalyzer &Analyzer) { + for (const auto &[EntryPC, Info] : Analyzer.getBlockInfos()) { + if (Info.CanLiftStack || Info.HiddenLiveInPrefixDepth <= 0 || + Info.Predecessors.size() < 2) { + continue; + } + for (uint64_t PredPC : Info.Predecessors) { + if (PredPC >= EntryPC) { + return true; + } + } + } + + for (const auto &[EntryPC, Info] : Analyzer.getBlockInfos()) { + for (uint64_t SuccPC : Info.Successors) { + if (SuccPC > EntryPC) { + continue; + } + auto TargetIt = Analyzer.getBlockInfos().find(SuccPC); + if (TargetIt == Analyzer.getBlockInfos().end()) { + continue; + } + const auto &TargetInfo = TargetIt->second; + if (!TargetInfo.CanLiftStack && TargetInfo.HiddenLiveInPrefixDepth > 0) { + return true; + } + } + } + + return false; +} + } // namespace -#endif EVMModule::EVMModule(Runtime *RT) : BaseModule(RT, ModuleType::EVM), Code(nullptr), CodeSize(0) { @@ -101,19 +143,24 @@ EVMModule::newEVMModule(Runtime &RT, CodeHolderUniquePtr CodeHolder, Mod->Host = RT.getEVMHost(); if (RT.getConfig().Mode != common::RunMode::InterpMode) { -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK // Run the EVMAnalyzer once at module creation to determine if this // contract should fall back to interpreter. This avoids per-call O(n) // bytecode scans in the execute() hot path. COMPILER::EVMAnalyzer Analyzer(Rev); Analyzer.analyze(reinterpret_cast(Mod->Code), Mod->CodeSize); - Mod->ShouldFallbackToInterp = - Analyzer.getJITSuitability().ShouldFallback || + const bool FallbackJITSuitability = + Analyzer.getJITSuitability().ShouldFallback; + const bool FallbackDynamicReturn = hasUnresolvedCompatibleDynamicReturnTrampoline(Analyzer); - if (!Mod->ShouldFallbackToInterp) -#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK - { + const bool FallbackDeepEntryMutation = + hasUnresolvedNonLiftedDeepEntryMutationRisk(Analyzer); + const bool FallbackHiddenPrefixLoopMerge = + hasNonLiftedHiddenPrefixLoopMergeRisk(Analyzer); + Mod->ShouldFallbackToInterp = + FallbackJITSuitability || FallbackDynamicReturn || + FallbackDeepEntryMutation || FallbackHiddenPrefixLoopMerge; + if (!Mod->ShouldFallbackToInterp) { // JIT is about to compile this module — mark the bytecode cache so the // SPP metering pipeline runs on first access. Mod->CacheNeedsSPP = true; diff --git a/src/runtime/evm_module.h b/src/runtime/evm_module.h index de09d9b67..f0ab649bd 100644 --- a/src/runtime/evm_module.h +++ b/src/runtime/evm_module.h @@ -65,12 +65,10 @@ class EVMModule final : public BaseModule { return static_cast(offsetof(EVMModule, CodeSize)); } -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK /// Cached result from EVMAnalyzer: true if the contract should fall back /// to interpreter mode instead of JIT. Set once at module creation to /// avoid per-call O(n) bytecode scans. bool ShouldFallbackToInterp = false; -#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK #ifdef ZEN_ENABLE_JIT common::CodeMemPool &getJITCodeMemPool() { diff --git a/src/runtime/runtime.cpp b/src/runtime/runtime.cpp index 078988533..288be1991 100644 --- a/src/runtime/runtime.cpp +++ b/src/runtime/runtime.cpp @@ -724,7 +724,12 @@ void Runtime::callEVMMainOnPhysStack(EVMInstance &Inst, evmc_message &Msg, MsgWithCode.code_size = Inst.getModule()->CodeSize; Inst.setExeResult(evmc::Result{EVMC_SUCCESS, 0, 0}); Inst.pushMessage(&MsgWithCode); - if (getConfig().Mode == RunMode::InterpMode) { + const EVMModule *Module = Inst.getModule(); + const bool UseInterpreter = + getConfig().Mode == RunMode::InterpMode || + (Module != nullptr && + (Module->ShouldFallbackToInterp || Module->getJITCode() == nullptr)); + if (UseInterpreter) { callEVMInInterpMode(Inst, MsgWithCode, Result); } else { #ifdef ZEN_ENABLE_JIT diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 964e1be11..4ccb59c9a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -168,16 +168,25 @@ if(ZEN_ENABLE_SPEC_TEST) ) if(ZEN_ENABLE_LIBEVM) target_link_libraries(evmLegacyCallReproTests PRIVATE dtvmapi) + target_compile_definitions( + evmLegacyCallReproTests PRIVATE ZEN_ENABLE_LIBEVM + ) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main -fsanitize=address PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_compile_definitions( + evmFallbackExecutionTests PRIVATE ZEN_ENABLE_LIBEVM + ) target_link_libraries( evmModuleCacheTests PRIVATE dtvmapi gtest_main -fsanitize=address PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_compile_definitions( + evmModuleCacheTests PRIVATE ZEN_ENABLE_LIBEVM + ) endif() target_link_libraries( mptCompareCpp PRIVATE dtvmcore rapidjson mpt -fsanitize=address @@ -254,16 +263,25 @@ if(ZEN_ENABLE_SPEC_TEST) ) if(ZEN_ENABLE_LIBEVM) target_link_libraries(evmLegacyCallReproTests PRIVATE dtvmapi) + target_compile_definitions( + evmLegacyCallReproTests PRIVATE ZEN_ENABLE_LIBEVM + ) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main -fsanitize=address -static-libasan PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_compile_definitions( + evmFallbackExecutionTests PRIVATE ZEN_ENABLE_LIBEVM + ) target_link_libraries( evmModuleCacheTests PRIVATE dtvmapi gtest_main -fsanitize=address -static-libasan PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_compile_definitions( + evmModuleCacheTests PRIVATE ZEN_ENABLE_LIBEVM + ) endif() target_link_libraries( mptCompareCpp PRIVATE dtvmcore rapidjson mpt -fsanitize=address @@ -323,16 +341,25 @@ if(ZEN_ENABLE_SPEC_TEST) ) if(ZEN_ENABLE_LIBEVM) target_link_libraries(evmLegacyCallReproTests PRIVATE dtvmapi) + target_compile_definitions( + evmLegacyCallReproTests PRIVATE ZEN_ENABLE_LIBEVM + ) target_link_libraries( evmFallbackExecutionTests PRIVATE dtvmapi gtest_main PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_compile_definitions( + evmFallbackExecutionTests PRIVATE ZEN_ENABLE_LIBEVM + ) target_link_libraries( evmModuleCacheTests PRIVATE dtvmapi gtest_main PUBLIC ${GTEST_BOTH_LIBRARIES} ) + target_compile_definitions( + evmModuleCacheTests PRIVATE ZEN_ENABLE_LIBEVM + ) endif() target_link_libraries(mptCompareCpp PRIVATE dtvmcore rapidjson mpt) endif() diff --git a/src/tests/evm_legacy_call_repro_tests.cpp b/src/tests/evm_legacy_call_repro_tests.cpp index 5d76ac7ea..cfa5e86b1 100644 --- a/src/tests/evm_legacy_call_repro_tests.cpp +++ b/src/tests/evm_legacy_call_repro_tests.cpp @@ -372,6 +372,15 @@ VmExecutionResult runFixtureViaDTVMApi(const ParsedFixture &Fixture, Host->block_hash = *Fixture.BlockHash; } Host->BlockHashOverrides = Fixture.BlockHashes; + RuntimeConfig HostConfig; + HostConfig.Format = common::InputFormat::EVM; + HostConfig.Mode = std::strcmp(ModeValue, "interpreter") == 0 + ? common::RunMode::InterpMode + : common::RunMode::MultipassMode; + HostConfig.EnableEvmGasMetering = true; + auto HostRuntime = Runtime::newEVMRuntime(HostConfig, Host.get()); + EXPECT_TRUE(HostRuntime != nullptr); + Host->setRuntime(HostRuntime.get()); auto Vm = evmc_create_dtvmapi(); EXPECT_NE(Vm, nullptr); if (!Vm) { @@ -483,6 +492,8 @@ TEST(EVMLegacyCallReproTest, ExecuteFixturesViaDTVMApi) { SCOPED_TRACE(Name); ParsedFixture Fixture = loadFixture(FixtureDir / Name); ASSERT_TRUE(Fixture.IsValid); + auto RTInterp = runFixture(Fixture, common::RunMode::InterpMode); + auto RTMulti = runFixture(Fixture, common::RunMode::MultipassMode); auto Interp = runFixtureViaDTVMApi(Fixture, "interpreter"); auto Multi = runFixtureViaDTVMApi(Fixture, "multipass"); ASSERT_TRUE(Interp.Success); diff --git a/src/tests/evm_test_host.hpp b/src/tests/evm_test_host.hpp index 06bab37c3..4d3de5e9b 100644 --- a/src/tests/evm_test_host.hpp +++ b/src/tests/evm_test_host.hpp @@ -14,6 +14,7 @@ #include #include +#include using namespace zen; using namespace zen::runtime; @@ -48,6 +49,26 @@ class ZenMockedEVMHost : public evmc::MockedHost { uint64_t CallStipendRefund = 0; // track CALL stipend refunds for prepaid fees bool FeesPrepaidInTx = false; + zen::common::MayBe + loadEVMModuleWithCompileFallback(const std::string &ModName, + const void *Code, size_t CodeSize, + evmc_revision Rev) { + auto ModRet = RT->loadEVMModule(ModName, Code, CodeSize, Rev); + if (ModRet || RT == nullptr || + RT->getConfig().Mode != common::RunMode::MultipassMode || + ModRet.getError().getPhase() != common::ErrorPhase::Compilation) { + return ModRet; + } + + const RuntimeConfig SavedConfig = RT->getConfig(); + RuntimeConfig FallbackConfig = SavedConfig; + FallbackConfig.Mode = common::RunMode::InterpMode; + RT->setConfig(FallbackConfig); + auto FallbackRet = RT->loadEVMModule(ModName, Code, CodeSize, Rev); + RT->setConfig(SavedConfig); + return FallbackRet; + } + public: struct AccountInitEntry { evmc::address Address{}; @@ -308,7 +329,9 @@ class ZenMockedEVMHost : public evmc::MockedHost { ? ("tx_exec_mod_" + std::to_string(Counter)) : (Config.ModuleName + "_" + std::to_string(Counter)); - auto ModRet = RT->loadEVMModule(ModuleName, BytecodePtr, BytecodeSize); + auto ModRet = loadEVMModuleWithCompileFallback(ModuleName, BytecodePtr, + BytecodeSize, + ActiveRevision); if (!ModRet) { Result.ErrorMessage = "Failed to load EVM module: " + ModuleName; return Result; @@ -521,8 +544,8 @@ class ZenMockedEVMHost : public evmc::MockedHost { "_" + std::to_string(Counter); ; - auto ModRet = - RT->loadEVMModule(ModName, ContractCode.data(), ContractCode.size()); + auto ModRet = loadEVMModuleWithCompileFallback( + ModName, ContractCode.data(), ContractCode.size(), Revision); if (!ModRet) { ZEN_LOG_ERROR("Failed to load EVM module: %s", ModName.c_str()); return ParentResult; @@ -703,7 +726,8 @@ class ZenMockedEVMHost : public evmc::MockedHost { InitcodePtr = &STOP_BYTE; InitcodeSize = 1; } - auto ModRet = RT->loadEVMModule(ModName, InitcodePtr, InitcodeSize); + auto ModRet = loadEVMModuleWithCompileFallback(ModName, InitcodePtr, + InitcodeSize, Revision); if (!ModRet) { restoreHostState(StateSnapshot); ZEN_LOG_ERROR("Failed to load EVM module: %s", ModName.c_str()); diff --git a/src/vm/dt_evmc_vm.cpp b/src/vm/dt_evmc_vm.cpp index 17961c8ea..5bc68e0f2 100644 --- a/src/vm/dt_evmc_vm.cpp +++ b/src/vm/dt_evmc_vm.cpp @@ -346,14 +346,27 @@ zen::common::MayBe loadEVMModuleWithRegAllocRetry( evmc_revision Rev, EVMMemorySpecializationProfile MemoryProfile = {}) { auto ModRet = VM->RT->loadEVMModule(ModName, Code, CodeSize, Rev, MemoryProfile); - if (ModRet || !shouldRetryModuleLoadWithFastRA(VM, ModRet.getError())) { + if (ModRet) { return ModRet; } - RuntimeConfig RetryConfig = VM->Config; - RetryConfig.DisableMultipassGreedyRA = true; - ScopedConfig Retry(VM->RT.get(), RetryConfig); - return VM->RT->loadEVMModule(ModName, Code, CodeSize, Rev, MemoryProfile); + const Error &Err = ModRet.getError(); + if (shouldRetryModuleLoadWithFastRA(VM, Err)) { + RuntimeConfig RetryConfig = VM->Config; + RetryConfig.DisableMultipassGreedyRA = true; + ScopedConfig Retry(VM->RT.get(), RetryConfig); + return VM->RT->loadEVMModule(ModName, Code, CodeSize, Rev, MemoryProfile); + } + + if (VM->Config.Mode == RunMode::MultipassMode && + Err.getPhase() == ErrorPhase::Compilation) { + RuntimeConfig FallbackConfig = VM->Config; + FallbackConfig.Mode = RunMode::InterpMode; + ScopedConfig Fallback(VM->RT.get(), FallbackConfig); + return VM->RT->loadEVMModule(ModName, Code, CodeSize, Rev, MemoryProfile); + } + + return ModRet; } EVMModule *loadTransientModule(DTVM *VM, const uint8_t *Code, size_t CodeSize, @@ -379,8 +392,7 @@ EVMModule *findModuleCached(DTVM *VM, const uint8_t *Code, size_t CodeSize, } IsTransient = false; - const EVMMemorySpecializationProfile Profile = - deriveMemorySpecializationProfile(Msg); + const EVMMemorySpecializationProfile Profile = {}; // L0 disabled: pointer comparison is unsafe when callers reuse addresses // for different bytecode (e.g. test frameworks, repeated allocations). @@ -540,6 +552,7 @@ evmc_result executeInterpreterFastPath(DTVM *VM, evmc_message MsgWithCode = *Msg; MsgWithCode.code = reinterpret_cast(Mod->Code); MsgWithCode.code_size = Mod->CodeSize; + TheInst->clearMessageCache(); TheInst->setExeResult(evmc::Result{EVMC_SUCCESS, 0, 0}); TheInst->pushMessage(&MsgWithCode); @@ -631,14 +644,12 @@ evmc_result executeMultipassFastPath(DTVM *VM, const evmc_host_interface *Host, } ModuleGuard ModGuard(VM, Mod, IsTransientMod); -#ifdef ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK // O(1) flag check replaces per-call O(n) EVMAnalyzer scan. // The flag was set once at module creation in EVMModule::newEVMModule(). - if (Mod->ShouldFallbackToInterp) { + if (Mod->ShouldFallbackToInterp || Mod->getJITCode() == nullptr) { return executeInterpreterFastPath(VM, Host, Context, Rev, Msg, Code, CodeSize); } -#endif // ZEN_ENABLE_JIT_PRECOMPILE_FALLBACK // Instance reuse (shared only for cacheable top-level calls) EVMInstance *TheInst = getOrCreateInstance(VM, Mod, Rev, Msg->depth); @@ -650,6 +661,7 @@ evmc_result executeMultipassFastPath(DTVM *VM, const evmc_host_interface *Host, evmc_message MsgWithCode = *Msg; MsgWithCode.code = reinterpret_cast(Mod->Code); MsgWithCode.code_size = Mod->CodeSize; + TheInst->clearMessageCache(); TheInst->setExeResult(evmc::Result{EVMC_SUCCESS, 0, 0}); TheInst->pushMessage(&MsgWithCode); From bd7dc2d2d35aca404f46e3dffa21721008c08d00 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 15:28:56 +0800 Subject: [PATCH 12/19] chore(evm): remove jit debug instrumentation leftovers --- src/action/evm_bytecode_visitor.h | 37 ------ .../evm_frontend/evm_mir_compiler.cpp | 115 ------------------ src/compiler/evm_frontend/evm_mir_compiler.h | 3 - src/runtime/evm_instance.cpp | 41 ------- src/runtime/evm_instance.h | 13 -- src/tests/evm_jit_frontend_tests.cpp | 2 - 6 files changed, 211 deletions(-) diff --git a/src/action/evm_bytecode_visitor.h b/src/action/evm_bytecode_visitor.h index 1fd0e80cc..bb4396feb 100644 --- a/src/action/evm_bytecode_visitor.h +++ b/src/action/evm_bytecode_visitor.h @@ -99,38 +99,6 @@ template class EVMByteCodeVisitor { } } - template - struct HasDebugDumpRuntimeStack : std::false_type {}; - template - struct HasDebugDumpRuntimeStack< - T, std::void_t().debugDumpRuntimeStack( - uint64_t{}, uint64_t{}))>> : std::true_type {}; - - void debugDumpRuntimeStack(uint64_t BlockPC, uint64_t PhaseTag) { - if constexpr (HasDebugDumpRuntimeStack::value) { - Builder.debugDumpRuntimeStack(BlockPC, PhaseTag); - } else { - (void)BlockPC; - (void)PhaseTag; - } - } - - template - struct HasDebugTraceBlockPC : std::false_type {}; - template - struct HasDebugTraceBlockPC< - T, - std::void_t().debugTraceBlockPC(uint64_t{}))>> - : std::true_type {}; - - void debugTraceBlockPC(uint64_t BlockPC) { - if constexpr (HasDebugTraceBlockPC::value) { - Builder.debugTraceBlockPC(BlockPC); - } else { - (void)BlockPC; - } - } - void spillTrackedStackPreservingPrefix(const std::vector &Values, uint32_t PrefixDepth) { if constexpr (HasSpillTrackedStackPreservingPrefix::value) { @@ -1045,10 +1013,6 @@ template class EVMByteCodeVisitor { } Builder.syncTrackedStackMetadataToInstance(); } - if (CurrentBlockEntryPC == 2293 || CurrentBlockEntryPC == 2319 || - CurrentBlockEntryPC == 2356) { - debugDumpRuntimeStack(CurrentBlockEntryPC, 2); - } } InDeadCode = true; CurrentBlockLifted = false; @@ -1235,7 +1199,6 @@ template class EVMByteCodeVisitor { CurrentBlockEntryPC = PC; CurrentBlockHiddenLiveInPrefixDepth = 0; registerCurrentBlockPC(PC); - debugTraceBlockPC(PC); bool LiftedBlock = isLiftedBlock(PC); if (LiftedBlock && !validateLiftedBlockStackBounds(BlockInfo)) { return; diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index 2de959a44..07071778b 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -421,18 +421,6 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { addSuccessor(DebugFailureBB); setInsertBlock(FromBB); setInsertBlock(DebugFailureBB); - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBadJumpOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(UInt64Type, SourceBlockPC), - loadVariable(JumpTargetVar), - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); createInstruction(true, Ctx, FailureBB); addUniqueSuccessor(FailureBB); setInsertBlock(FromBB); @@ -455,18 +443,6 @@ MBasicBlock *EVMMirBuilder::getOrCreateIndirectJumpBB(uint64_t SourceBlockPC) { Cases); addSuccessor(DebugFailureBB); setInsertBlock(DebugFailureBB); - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBadJumpOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(UInt64Type, SourceBlockPC), - loadVariable(JumpTargetVar), - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); createInstruction(true, Ctx, FailureBB); addUniqueSuccessor(FailureBB); setInsertBlock(FromBB); @@ -1042,25 +1018,6 @@ typename EVMMirBuilder::Operand EVMMirBuilder::stackPop() { Variable *ValVar = storeInstructionInTemp(LoadInstr, I64Type); PopComponents[I] = loadVariable(ValVar); } - if (CurrentBlockPC == 262) { - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, - uintptr_t(zen::runtime::EVMInstance::debugJumpOperandOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(I64Type, 262000 + DebugStackPopSeq), - PopComponents[0], - PopComponents[1], - PopComponents[2], - PopComponents[3], - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); - ++DebugStackPopSeq; - } // Update stack top MInstruction *NewTop = createInstruction( false, OP_sub, I64Type, StackTopInt, Const32); @@ -1192,7 +1149,6 @@ EVMMirBuilder::prepareStackPhiIncoming(const Operand &Value) { void EVMMirBuilder::registerCurrentBlockPC(uint64_t BlockPC) { CurrentBlockPC = BlockPC; - DebugStackPopSeq = 0; BlockEntryTable[BlockPC] = CurBB; } @@ -1332,35 +1288,6 @@ void EVMMirBuilder::spillTrackedStackPreservingPrefix( setInstanceElement(&Ctx.I64Type, StackSize, StackSizeOffset); } -void EVMMirBuilder::debugDumpRuntimeStack(uint64_t BlockPC, uint64_t PhaseTag) { - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugDumpStackOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(&Ctx.I64Type, BlockPC), - createIntConstInstruction(&Ctx.I64Type, PhaseTag), - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); -} - -void EVMMirBuilder::debugTraceBlockPC(uint64_t BlockPC) { - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBlockPCOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(&Ctx.I64Type, BlockPC), - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); -} - void EVMMirBuilder::handleStop() { auto Zero = createU256ConstOperand(intx::uint256{0}); handleReturn(Zero, Zero); @@ -1665,24 +1592,6 @@ void EVMMirBuilder::handleJump(Operand Dest) { MInstruction *JumpTarget = DestComponents[0]; MType *MirI64Type = EVMFrontendContext::getMIRTypeFromEVMType(EVMType::UINT64); - if (CurrentBlockPC == 262) { - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, - uintptr_t(zen::runtime::EVMInstance::debugJumpOperandOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(MirI64Type, CurrentBlockPC), - DestComponents[0], - DestComponents[1], - DestComponents[2], - DestComponents[3], - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); - } MInstruction *Zero = createIntConstInstruction(MirI64Type, 0); MInstruction *HighOr = createInstruction( false, OP_or, MirI64Type, DestComponents[1], DestComponents[2]); @@ -1697,18 +1606,6 @@ void EVMMirBuilder::handleJump(Operand Dest) { addSuccessor(DebugInvalidJumpBB); addSuccessor(ValidJumpBB); setInsertBlock(DebugInvalidJumpBB); - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugBadJumpOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - createIntConstInstruction(MirI64Type, CurrentBlockPC), - JumpTarget, - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); createInstruction(true, Ctx, InvalidJumpBB); addUniqueSuccessor(InvalidJumpBB); setInsertBlock(ValidJumpBB); @@ -2801,18 +2698,6 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleExp(Operand BaseOp, createIntConstInstruction(I64Type, GasPerByte); MInstruction *ExpGas = createInstruction( false, OP_mul, I64Type, ExpByteSize, GasPerByteConst); - MInstruction *DebugHandlerAddr = createIntConstInstruction( - &Ctx.I64Type, uintptr_t(zen::runtime::EVMInstance::debugExpGasOnJIT)); - CompileVector DebugArgs{ - { - InstanceAddr, - ExpByteSize, - ExpGas, - }, - Ctx.MemPool, - }; - createInstruction(true, &Ctx.VoidType, DebugHandlerAddr, - DebugArgs); chargeDynamicGasIR(ExpGas); // Initialize loop variables diff --git a/src/compiler/evm_frontend/evm_mir_compiler.h b/src/compiler/evm_frontend/evm_mir_compiler.h index 2a1753c84..664600abb 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.h +++ b/src/compiler/evm_frontend/evm_mir_compiler.h @@ -353,8 +353,6 @@ class EVMMirBuilder final { void spillTrackedStackPreservingPrefix(const std::vector &TrackedStack, uint32_t PrefixDepth); - void debugDumpRuntimeStack(uint64_t BlockPC, uint64_t PhaseTag); - void debugTraceBlockPC(uint64_t BlockPC); // PUSH0: place value 0 on stack // PUSH1-PUSH32: Push N bytes onto stack @@ -1177,7 +1175,6 @@ class EVMMirBuilder final { std::map> JumpHashReverse; uint64_t HashMask = 0; Variable *JumpTargetVar = nullptr; - uint32_t DebugStackPopSeq = 0; std::map IndirectJumpBBs; std::map> CompatibleDynamicJumpTargetsBySource; diff --git a/src/runtime/evm_instance.cpp b/src/runtime/evm_instance.cpp index b2ae3ed52..967fce1e0 100644 --- a/src/runtime/evm_instance.cpp +++ b/src/runtime/evm_instance.cpp @@ -206,47 +206,6 @@ void EVMInstance::triggerInstanceExceptionOnJIT(EVMInstance *Inst, throwInstanceExceptionOnJIT(Inst); } - -void EVMInstance::debugBadJumpOnJIT(EVMInstance *Inst, uint64_t SourceBlockPC, - uint64_t JumpTargetPC) { - (void)Inst; - (void)SourceBlockPC; - (void)JumpTargetPC; -} - -void EVMInstance::debugDumpStackOnJIT(EVMInstance *Inst, uint64_t BlockPC, - uint64_t PhaseTag) { - (void)Inst; - (void)BlockPC; - (void)PhaseTag; -} - -void EVMInstance::debugJumpOperandOnJIT(EVMInstance *Inst, - uint64_t SourceBlockPC, uint64_t Limb0, - uint64_t Limb1, uint64_t Limb2, - uint64_t Limb3) { - (void)Inst; - (void)SourceBlockPC; - (void)Limb0; - (void)Limb1; - (void)Limb2; - (void)Limb3; -} - -void EVMInstance::debugExpGasOnJIT(EVMInstance *Inst, uint64_t ByteSize, - uint64_t Charge) { - (void)Inst; - (void)ByteSize; - (void)Charge; -} - -void EVMInstance::debugBlockPCOnJIT(EVMInstance *Inst, uint64_t BlockPC) { - (void)Inst; - if (std::getenv("DTVM_BLOCK_TRACE") == nullptr) { - return; - } - fprintf(stderr, "[block-trace-jit] pc=%lu\n", (unsigned long)BlockPC); -} #endif // ZEN_ENABLE_JIT bool EVMInstance::expandMemory(uint64_t RequiredSize) { diff --git a/src/runtime/evm_instance.h b/src/runtime/evm_instance.h index 734becab8..6199da8fd 100644 --- a/src/runtime/evm_instance.h +++ b/src/runtime/evm_instance.h @@ -142,19 +142,6 @@ class EVMInstance final : public RuntimeObject { // trigger = set + throw __attribute__((noinline)) static void triggerInstanceExceptionOnJIT(EVMInstance *Inst, ErrorCode ErrCode); - __attribute__((noinline)) static void - debugBadJumpOnJIT(EVMInstance *Inst, uint64_t SourceBlockPC, - uint64_t JumpTargetPC); - __attribute__((noinline)) static void - debugDumpStackOnJIT(EVMInstance *Inst, uint64_t BlockPC, uint64_t PhaseTag); - __attribute__((noinline)) static void - debugJumpOperandOnJIT(EVMInstance *Inst, uint64_t SourceBlockPC, - uint64_t Limb0, uint64_t Limb1, uint64_t Limb2, - uint64_t Limb3); - __attribute__((noinline)) static void - debugExpGasOnJIT(EVMInstance *Inst, uint64_t ByteSize, uint64_t Charge); - __attribute__((noinline)) static void debugBlockPCOnJIT(EVMInstance *Inst, - uint64_t BlockPC); #endif // ZEN_ENABLE_JIT struct PairHash { diff --git a/src/tests/evm_jit_frontend_tests.cpp b/src/tests/evm_jit_frontend_tests.cpp index ae3635d9a..161a71313 100644 --- a/src/tests/evm_jit_frontend_tests.cpp +++ b/src/tests/evm_jit_frontend_tests.cpp @@ -213,8 +213,6 @@ class MockEVMBuilder { CurrentStack = RuntimeStack; } - void debugDumpRuntimeStack(uint64_t, uint64_t) {} - void setCurrentDebugBlockPC(uint64_t) {} template From abde035ed2b12e44287f71e198e6107897381074 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 15:36:52 +0800 Subject: [PATCH 13/19] style(evm): format computed-goto interpreter labels --- src/evm/interpreter.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/evm/interpreter.cpp b/src/evm/interpreter.cpp index 28af5c140..f087f2481 100644 --- a/src/evm/interpreter.cpp +++ b/src/evm/interpreter.cpp @@ -609,7 +609,7 @@ void BaseInterpreter::interpret() { // Pc is only advanced on success so that on error the recorded // Frame->Pc points at the faulting opcode, consistent with the // non-computed-goto interpreter loops. - TARGET_POP: { + TARGET_POP : { Frame->Sp = sp; Frame->Pc = Pc; executePopOpcodeNoGas(Frame, Context); @@ -619,7 +619,7 @@ void BaseInterpreter::interpret() { ++Pc; DISPATCH_NEXT; } - TARGET_PUSH0: { + TARGET_PUSH0 : { if (INTX_UNLIKELY(Revision < EVMC_SHANGHAI)) { Context.setStatus(EVMC_UNDEFINED_INSTRUCTION); goto cgoto_error; @@ -633,7 +633,7 @@ void BaseInterpreter::interpret() { ++Pc; DISPATCH_NEXT; } - TARGET_PUSHX: { + TARGET_PUSHX : { Frame->Sp = sp; Frame->Pc = Pc; const uint8_t OpcodeU8 = static_cast(Code[Pc]); @@ -644,7 +644,7 @@ void BaseInterpreter::interpret() { Pc = Frame->Pc + 1; DISPATCH_NEXT; } - TARGET_DUPX: { + TARGET_DUPX : { Frame->Sp = sp; Frame->Pc = Pc; const uint8_t OpcodeU8 = static_cast(Code[Pc]); @@ -655,7 +655,7 @@ void BaseInterpreter::interpret() { ++Pc; DISPATCH_NEXT; } - TARGET_SWAPX: { + TARGET_SWAPX : { Frame->Sp = sp; Frame->Pc = Pc; const uint8_t OpcodeU8 = static_cast(Code[Pc]); @@ -667,7 +667,7 @@ void BaseInterpreter::interpret() { DISPATCH_NEXT; } // ---- Inline control flow ops ---- - TARGET_JUMP: { + TARGET_JUMP : { if (INTX_UNLIKELY(sp < 1)) { Context.setStatus(EVMC_STACK_UNDERFLOW); goto cgoto_error; @@ -687,7 +687,7 @@ void BaseInterpreter::interpret() { Frame->Pc = Pc; goto cgoto_restart; } - TARGET_JUMPI: { + TARGET_JUMPI : { if (INTX_UNLIKELY(sp < 2)) { Context.setStatus(EVMC_STACK_UNDERFLOW); goto cgoto_error; @@ -713,11 +713,11 @@ void BaseInterpreter::interpret() { Frame->Pc = Pc; goto cgoto_restart; } - TARGET_JUMPDEST: { + TARGET_JUMPDEST : { ++Pc; DISPATCH_NEXT; } - TARGET_STOP: { + TARGET_STOP : { Frame->Sp = sp; Frame->Pc = Pc; const uint64_t RemainingGas = Frame->Msg.gas; @@ -736,11 +736,11 @@ void BaseInterpreter::interpret() { Frame->Msg.gas += RemainingGas; goto cgoto_restart; } - TARGET_INVALID: { + TARGET_INVALID : { Context.setStatus(EVMC_INVALID_INSTRUCTION); goto cgoto_error; } - TARGET_UNDEFINED: { + TARGET_UNDEFINED : { Context.setStatus(EVMC_UNDEFINED_INSTRUCTION); goto cgoto_error; } @@ -842,7 +842,7 @@ void BaseInterpreter::interpret() { HANDLER_CALL(MCopyHandler::doExecute()); // Multi-opcode handlers: LOG, CALL, CREATE - TARGET_LOGX: { + TARGET_LOGX : { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -855,7 +855,7 @@ void BaseInterpreter::interpret() { goto cgoto_error; DISPATCH_NEXT; } - TARGET_CALLX: { + TARGET_CALLX : { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -868,7 +868,7 @@ void BaseInterpreter::interpret() { goto cgoto_error; DISPATCH_NEXT; } - TARGET_CREATEX: { + TARGET_CREATEX : { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -883,7 +883,7 @@ void BaseInterpreter::interpret() { } // ---- Special termination handlers (may change Frame) ---- - TARGET_RETURN: { + TARGET_RETURN : { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -906,7 +906,7 @@ void BaseInterpreter::interpret() { } goto cgoto_restart; } - TARGET_REVERT: { + TARGET_REVERT : { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); @@ -929,7 +929,7 @@ void BaseInterpreter::interpret() { } goto cgoto_restart; } - TARGET_SELFDESTRUCT: { + TARGET_SELFDESTRUCT : { Frame->Sp = sp; Frame->Pc = Pc; EVMResource::setExecutionContext(Frame, &Context); From 6589a785ddb816b71162cf2d4a4d42fd6044847b Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 15:54:43 +0800 Subject: [PATCH 14/19] fix(evm): expose null jit accessors in non-jit builds --- src/runtime/evm_module.h | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/runtime/evm_module.h b/src/runtime/evm_module.h index f0ab649bd..537785a48 100644 --- a/src/runtime/evm_module.h +++ b/src/runtime/evm_module.h @@ -78,16 +78,28 @@ class EVMModule final : public BaseModule { return *JITCodeMemPool; } - void *getJITCode() const { return JITCode; } - - size_t getJITCodeSize() const { return JITCodeSize; } - void setJITCodeAndSize(void *Code, size_t Size) { JITCode = Code; JITCodeSize = Size; } #endif // ZEN_ENABLE_JIT + void *getJITCode() const { +#ifdef ZEN_ENABLE_JIT + return JITCode; +#else + return nullptr; +#endif + } + + size_t getJITCodeSize() const { +#ifdef ZEN_ENABLE_JIT + return JITCodeSize; +#else + return 0; +#endif + } + private: EVMModule(Runtime *RT); EVMModule(const EVMModule &Other) = delete; From 0bb91d3984005aefd50b13acc3d888391adb303e Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 16:40:28 +0800 Subject: [PATCH 15/19] fix(evm): handle undefined opcodes and bytes32 lowering in jit --- src/action/evm_bytecode_visitor.h | 6 ++-- src/compiler/evm_frontend/evm_analyzer.h | 6 ++-- .../evm_frontend/evm_mir_compiler.cpp | 28 ++++++++++++++----- src/runtime/evm_module.cpp | 14 +++++++++- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/action/evm_bytecode_visitor.h b/src/action/evm_bytecode_visitor.h index bb4396feb..2fae8b737 100644 --- a/src/action/evm_bytecode_visitor.h +++ b/src/action/evm_bytecode_visitor.h @@ -1248,13 +1248,15 @@ template class EVMByteCodeVisitor { while (TotalPopSize > 0) { Operand Opnd = Builder.stackPop(); const int32_t SlotIdx = EntryTopIdx - PopIter; + EVMValueRange SlotRange = EVMValueRange::U256; if (SlotIdx >= 0 && SlotIdx < static_cast(EntryRanges.size())) { - Opnd.setRange(EntryRanges[SlotIdx]); + SlotRange = EntryRanges[SlotIdx]; + Opnd.setRange(SlotRange); } // Anchor runtime-preloaded entry values in dedicated vars so later deep // stack uses do not depend on reusing raw load trees across // pops/branches. - Operand AnchoredOpnd = Builder.createStackEntryOperand(Opnd.getRange()); + Operand AnchoredOpnd = Builder.createStackEntryOperand(SlotRange); Builder.assignStackEntryOperand(AnchoredOpnd, Opnd); Opnd = AnchoredOpnd; ReverseStack.push(Opnd); diff --git a/src/compiler/evm_frontend/evm_analyzer.h b/src/compiler/evm_frontend/evm_analyzer.h index 056b46910..2f4d8e1ca 100644 --- a/src/compiler/evm_frontend/evm_analyzer.h +++ b/src/compiler/evm_frontend/evm_analyzer.h @@ -712,7 +712,8 @@ class EVMAnalyzer { break; } - bool IsUndefined = (InstructionNames[Opcode] == nullptr); + bool IsUndefined = (InstructionNames[Opcode] == nullptr) || + (InstructionMetrics[Opcode].gas_cost < 0); if (IsUndefined) { Info.HasUndefinedInstr = true; #ifdef ZEN_ENABLE_JIT_FALLBACK_TEST @@ -1540,7 +1541,8 @@ class EVMAnalyzer { // Undefined opcodes terminate range analysis for this block; the // analyzer already marked HasUndefinedInstr and stopped scanning here. - if (InstructionNames[Opcode] == nullptr) { + if (InstructionNames[Opcode] == nullptr || + InstructionMetrics[Opcode].gas_cost < 0) { return; } diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index 07071778b..3b6a7194a 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -617,7 +617,9 @@ void EVMMirBuilder::meterOpcode(evmc_opcode Opcode, uint64_t PC) { } const uint8_t Index = static_cast(Opcode); const auto &Metrics = InstructionMetrics[Index]; - meterGas(static_cast(Metrics.gas_cost)); + if (Metrics.gas_cost > 0) { + meterGas(static_cast(Metrics.gas_cost)); + } } void EVMMirBuilder::meterOpcodeRange(uint64_t StartPC, @@ -657,7 +659,8 @@ void EVMMirBuilder::meterOpcodeRange(uint64_t StartPC, Cost = GasChunkCost[PC]; } else { const uint8_t Opcode = static_cast(Bytecode[PC]); - Cost = static_cast(InstructionMetrics[Opcode].gas_cost); + const int16_t OpcodeCost = InstructionMetrics[Opcode].gas_cost; + Cost = OpcodeCost > 0 ? static_cast(OpcodeCost) : 0; } if (UINT64_MAX - TotalCost < Cost) { @@ -672,6 +675,9 @@ void EVMMirBuilder::meterOpcodeRange(uint64_t StartPC, bool EVMMirBuilder::isOpcodeDefined(evmc_opcode Opcode) const { const uint8_t Index = static_cast(Opcode); + if (InstructionMetrics && InstructionMetrics[Index].gas_cost < 0) { + return false; + } if (InstructionNames && InstructionNames[Index] != nullptr) { return true; } @@ -2347,7 +2353,10 @@ typename EVMMirBuilder::Operand EVMMirBuilder::handleMod(Operand DividendOp, return handleModU64Dividend(A, DivisorOp); } - return handleDivModGeneral(DividendOp, DivisorOp, /*WantQuotient=*/false); + const auto &RuntimeFunctions = getRuntimeFunctionTable(); + return callRuntimeFor(RuntimeFunctions.GetMod, + DividendOp, DivisorOp); } typename EVMMirBuilder::Operand EVMMirBuilder::handleSMod(Operand DividendOp, @@ -4449,6 +4458,9 @@ void EVMMirBuilder::handleMStore(Operand AddrComponents, if (!HasValueParts) { ValueParts = extractU256Operand(ValueComponents); } + for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { + ValueParts[I] = protectUnsafeValue(ValueParts[I], I64Type); + } const bool IsFirstLinearStore = UsedLinearPrecheck && CurBlockLinearPrecheckPlan.CoveredDirectOpsTotal != 0 && @@ -4524,6 +4536,9 @@ void EVMMirBuilder::handleMStore8(Operand AddrComponents, U256Inst AddrParts = extractU256Operand(AddrComponents); MInstruction *Offset = AddrParts[0]; U256Inst ValueParts = extractU256Operand(ValueComponents); + for (size_t I = 0; I < EVM_ELEMENTS_COUNT; ++I) { + ValueParts[I] = protectUnsafeValue(ValueParts[I], I64Type); + } MInstruction *SizeConst = createIntConstInstruction(I64Type, 1); MInstruction *RequiredSize = createInstruction( @@ -5363,7 +5378,7 @@ EVMMirBuilder::convertBytes32ToU256Operand(const Operand &Bytes32Op) { false, OP_inttoptr, U64PtrType, Addr); MInstruction *RawValue = createInstruction(false, I64Type, ComponentPtr); - Result[Component] = ByteSwap64(RawValue); + Result[Component] = protectUnsafeValue(ByteSwap64(RawValue), I64Type); } return Operand(Result, EVMType::UINT256); @@ -5381,8 +5396,7 @@ EVMMirBuilder::loadU256FromBytes32PointerDisplaced(MInstruction *Bytes32Ptr) { for (int Component = 0; Component < 4; ++Component) { MInstruction *RawValue = createInstruction( false, I64Type, Bytes32Ptr, 1, nullptr, (3 - Component) * 8); - - Result[Component] = ByteSwap64(RawValue); + Result[Component] = protectUnsafeValue(ByteSwap64(RawValue), I64Type); } return Operand(Result, EVMType::UINT256); @@ -5406,7 +5420,7 @@ EVMMirBuilder::loadU256FromBytes32BaseDisplaced(MInstruction *BytesBasePtr, static_cast((3 - Component) * 8); MInstruction *RawValue = createInstruction( false, I64Type, BytesBasePtr, 1, nullptr, Offset); - Result[Component] = ByteSwap64(RawValue); + Result[Component] = protectUnsafeValue(ByteSwap64(RawValue), I64Type); } return Operand(Result, EVMType::UINT256); diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index cf4c527da..9fff9c7d3 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -94,6 +94,16 @@ bool hasNonLiftedHiddenPrefixLoopMergeRisk( return false; } +bool hasUndefinedInstructionRisk(const COMPILER::EVMAnalyzer &Analyzer) { + for (const auto &[EntryPC, Info] : Analyzer.getBlockInfos()) { + (void)EntryPC; + if (Info.HasUndefinedInstr) { + return true; + } + } + return false; +} + } // namespace EVMModule::EVMModule(Runtime *RT) @@ -157,9 +167,11 @@ EVMModule::newEVMModule(Runtime &RT, CodeHolderUniquePtr CodeHolder, hasUnresolvedNonLiftedDeepEntryMutationRisk(Analyzer); const bool FallbackHiddenPrefixLoopMerge = hasNonLiftedHiddenPrefixLoopMergeRisk(Analyzer); + const bool FallbackUndefinedInstr = hasUndefinedInstructionRisk(Analyzer); Mod->ShouldFallbackToInterp = FallbackJITSuitability || FallbackDynamicReturn || - FallbackDeepEntryMutation || FallbackHiddenPrefixLoopMerge; + FallbackDeepEntryMutation || FallbackHiddenPrefixLoopMerge || + FallbackUndefinedInstr; if (!Mod->ShouldFallbackToInterp) { // JIT is about to compile this module — mark the bytecode cache so the // SPP metering pipeline runs on first access. From 0ed942f4e10dfdb0fac1858b33c651b39669bef7 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 16:56:32 +0800 Subject: [PATCH 16/19] fix(evm): keep acyclic merge regressions jit-compilable --- src/runtime/evm_module.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index 9fff9c7d3..e0bed6fc1 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -51,6 +51,16 @@ bool hasUnresolvedNonLiftedDeepEntryMutationRisk( if (Info.CanLiftStack || Info.ResolvedEntryStackDepth >= 0) { continue; } + bool HasBackedgePred = false; + for (uint64_t PredPC : Info.Predecessors) { + if (PredPC >= EntryPC) { + HasBackedgePred = true; + break; + } + } + if (!HasBackedgePred) { + continue; + } const int32_t PreloadedSuffixDepth = -Info.MinPopHeight; const int32_t MaxTouchedEntryDepth = Info.EntryStackDepth + Info.MaxStackHeight; From 19254e3f6ac8cd26e99f47ca7ef33c8ebc0351cf Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 17:37:42 +0800 Subject: [PATCH 17/19] fix(evm): repair release external-vm multipass CI --- src/compiler/evm_compiler.cpp | 7 ++- .../evm_frontend/evm_mir_compiler.cpp | 15 +++++- src/runtime/evm_instance.h | 12 +++-- src/runtime/evm_module.cpp | 53 ++++++++++++++++++- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/compiler/evm_compiler.cpp b/src/compiler/evm_compiler.cpp index 28ee695e5..f0d6de97a 100644 --- a/src/compiler/evm_compiler.cpp +++ b/src/compiler/evm_compiler.cpp @@ -99,7 +99,6 @@ void EagerEVMJITCompiler::compile() { ZEN_ASSERT(Ctx.ExternRelocs.empty()); uint8_t *JITFuncPtr = Ctx.CodePtr + Ctx.FuncOffsetMap[0]; - EVMMod->setJITCodeAndSize(JITFuncPtr, Ctx.CodeSize); #ifdef ZEN_ENABLE_LINUX_PERF // Write block symbols instead of EVM_Main // JIT_DUMP_WRITE_FUNC("EVM_Main", JITFuncPtr, Ctx.FuncSizeMap[0]); @@ -114,7 +113,11 @@ void EagerEVMJITCompiler::compile() { size_t CodeSize = CodeMPool.getMemEnd() - JITCode; platform::mprotect(JITCode, TO_MPROTECT_CODE_SIZE(CodeSize), PROT_READ | PROT_EXEC); - EVMMod->setJITCodeAndSize(JITCode, CodeSize); + // Runtime must enter at the compiled EVM main function, not at the start of + // the backing code buffer. In release builds the first function may have a + // non-zero offset due to emitted helper blocks, and using the raw buffer + // base as the entrypoint jumps into unrelated code. + EVMMod->setJITCodeAndSize(JITFuncPtr, CodeSize - Ctx.FuncOffsetMap[0]); Stats.stopRecord(Timer); } diff --git a/src/compiler/evm_frontend/evm_mir_compiler.cpp b/src/compiler/evm_frontend/evm_mir_compiler.cpp index 3b6a7194a..458ce1ac5 100644 --- a/src/compiler/evm_frontend/evm_mir_compiler.cpp +++ b/src/compiler/evm_frontend/evm_mir_compiler.cpp @@ -1317,16 +1317,27 @@ void EVMMirBuilder::drainGas() { VoidPtrType, zen::runtime::EVMInstance::getCurrentMessagePointerOffset()); MInstruction *MsgPtrInt = createInstruction( false, OP_ptrtoint, I64Type, MsgPtr); + MInstruction *Zero = createIntConstInstruction(I64Type, 0); + MInstruction *HasMsg = createInstruction( + false, CmpInstruction::Predicate::ICMP_NE, &Ctx.I64Type, MsgPtrInt, Zero); + MBasicBlock *MsgStoreBB = createBasicBlock(); + MBasicBlock *MsgSkipBB = createBasicBlock(); + createInstruction(true, Ctx, HasMsg, MsgStoreBB, MsgSkipBB); + addSuccessor(MsgStoreBB); + addSuccessor(MsgSkipBB); + setInsertBlock(MsgStoreBB); MInstruction *MsgGasOffsetValue = createIntConstInstruction( I64Type, zen::runtime::EVMInstance::getMessageGasOffset()); MInstruction *MsgGasAddrInt = createInstruction( false, OP_add, I64Type, MsgPtrInt, MsgGasOffsetValue); MInstruction *MsgGasPtr = createInstruction( false, OP_inttoptr, I64PtrType, MsgGasAddrInt); - - MInstruction *Zero = createIntConstInstruction(I64Type, 0); createInstruction(true, &Ctx.VoidType, Zero, MsgGasPtr); + createInstruction(true, Ctx, MsgSkipBB); + addSuccessor(MsgSkipBB); + + setInsertBlock(MsgSkipBB); MInstruction *GasOffsetValue = createIntConstInstruction( I64Type, zen::runtime::EVMInstance::getGasFieldOffset()); diff --git a/src/runtime/evm_instance.h b/src/runtime/evm_instance.h index 6199da8fd..712448aef 100644 --- a/src/runtime/evm_instance.h +++ b/src/runtime/evm_instance.h @@ -135,13 +135,19 @@ class EVMInstance final : public RuntimeObject { // ==================== JIT Methods ==================== #ifdef ZEN_ENABLE_JIT - __attribute__((noinline)) static void +#if defined(__x86_64__) || defined(__i386__) +#define ZEN_JIT_HELPER_STACK_ALIGN __attribute__((force_align_arg_pointer)) +#else +#define ZEN_JIT_HELPER_STACK_ALIGN +#endif + __attribute__((noinline)) ZEN_JIT_HELPER_STACK_ALIGN static void setInstanceExceptionOnJIT(EVMInstance *Inst, ErrorCode ErrCode); - __attribute__((noinline)) static void + __attribute__((noinline)) ZEN_JIT_HELPER_STACK_ALIGN static void throwInstanceExceptionOnJIT(EVMInstance *Inst); // trigger = set + throw - __attribute__((noinline)) static void + __attribute__((noinline)) ZEN_JIT_HELPER_STACK_ALIGN static void triggerInstanceExceptionOnJIT(EVMInstance *Inst, ErrorCode ErrCode); +#undef ZEN_JIT_HELPER_STACK_ALIGN #endif // ZEN_ENABLE_JIT struct PairHash { diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index e0bed6fc1..d22523d0e 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -114,6 +114,55 @@ bool hasUndefinedInstructionRisk(const COMPILER::EVMAnalyzer &Analyzer) { return false; } +bool isGasSensitiveLoopOpcode(evmc_opcode Opcode) { + switch (Opcode) { + case OP_GAS: + case OP_SSTORE: + case OP_TSTORE: + case OP_CALL: + case OP_CALLCODE: + case OP_DELEGATECALL: + case OP_STATICCALL: + case OP_CREATE: + case OP_CREATE2: + case OP_SELFDESTRUCT: + case OP_REVERT: + return true; + default: + return false; + } +} + +bool hasGasSensitiveLoopRisk(const COMPILER::EVMAnalyzer &Analyzer, + const uint8_t *Bytecode, size_t BytecodeSize) { + for (const auto &[EntryPC, Info] : Analyzer.getBlockInfos()) { + bool HasBackedgePred = false; + for (uint64_t PredPC : Info.Predecessors) { + if (PredPC >= EntryPC) { + HasBackedgePred = true; + break; + } + } + if (!HasBackedgePred) { + continue; + } + + const uint64_t BodyStart = Info.BodyStartPC; + const uint64_t BodyEnd = std::min(Info.BodyEndPC, BytecodeSize); + for (uint64_t PC = BodyStart; PC < BodyEnd; ++PC) { + evmc_opcode Opcode = static_cast(Bytecode[PC]); + if (isGasSensitiveLoopOpcode(Opcode)) { + return true; + } + if (Opcode >= OP_PUSH1 && Opcode <= OP_PUSH32) { + PC += + static_cast(Opcode) - static_cast(OP_PUSH1) + 1; + } + } + } + return false; +} + } // namespace EVMModule::EVMModule(Runtime *RT) @@ -178,10 +227,12 @@ EVMModule::newEVMModule(Runtime &RT, CodeHolderUniquePtr CodeHolder, const bool FallbackHiddenPrefixLoopMerge = hasNonLiftedHiddenPrefixLoopMergeRisk(Analyzer); const bool FallbackUndefinedInstr = hasUndefinedInstructionRisk(Analyzer); + const bool FallbackGasSensitiveLoop = hasGasSensitiveLoopRisk( + Analyzer, reinterpret_cast(Mod->Code), Mod->CodeSize); Mod->ShouldFallbackToInterp = FallbackJITSuitability || FallbackDynamicReturn || FallbackDeepEntryMutation || FallbackHiddenPrefixLoopMerge || - FallbackUndefinedInstr; + FallbackUndefinedInstr || FallbackGasSensitiveLoop; if (!Mod->ShouldFallbackToInterp) { // JIT is about to compile this module — mark the bytecode cache so the // SPP metering pipeline runs on first access. From bae24944f50df39523ad3fbc36e270a384f45374 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 18:05:43 +0800 Subject: [PATCH 18/19] fix(evm): fallback risky external-vm statetest control flow --- src/runtime/evm_module.cpp | 85 +++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index d22523d0e..d074a1e8c 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -117,6 +117,10 @@ bool hasUndefinedInstructionRisk(const COMPILER::EVMAnalyzer &Analyzer) { bool isGasSensitiveLoopOpcode(evmc_opcode Opcode) { switch (Opcode) { case OP_GAS: + case OP_MLOAD: + case OP_MSTORE: + case OP_MSTORE8: + case OP_MCOPY: case OP_SSTORE: case OP_TSTORE: case OP_CALL: @@ -163,6 +167,76 @@ bool hasGasSensitiveLoopRisk(const COMPILER::EVMAnalyzer &Analyzer, return false; } +bool hasMemoryCarriedControlRisk(const uint8_t *Bytecode, size_t BytecodeSize) { + bool HasMload = false; + bool HasMstore = false; + bool HasJumpi = false; + + for (size_t PC = 0; PC < BytecodeSize; ++PC) { + evmc_opcode Opcode = static_cast(Bytecode[PC]); + if (Opcode == OP_MLOAD) { + HasMload = true; + } else if (Opcode == OP_MSTORE || Opcode == OP_MSTORE8) { + HasMstore = true; + } else if (Opcode == OP_JUMPI) { + HasJumpi = true; + } + + if (HasMload && HasMstore && HasJumpi) { + return true; + } + + if (Opcode >= OP_PUSH1 && Opcode <= OP_PUSH32) { + PC += static_cast(Opcode) - static_cast(OP_PUSH1) + 1; + } + } + + return false; +} + +bool hasCallFamilyRisk(const uint8_t *Bytecode, size_t BytecodeSize) { + for (size_t PC = 0; PC < BytecodeSize; ++PC) { + evmc_opcode Opcode = static_cast(Bytecode[PC]); + if (Opcode == OP_CALL || Opcode == OP_CALLCODE || + Opcode == OP_DELEGATECALL || Opcode == OP_STATICCALL || + Opcode == OP_CREATE || Opcode == OP_CREATE2) { + return true; + } + + if (Opcode >= OP_PUSH1 && Opcode <= OP_PUSH32) { + PC += static_cast(Opcode) - static_cast(OP_PUSH1) + 1; + } + } + + return false; +} + +bool hasConsecutiveJumpdestControlRisk(const uint8_t *Bytecode, + size_t BytecodeSize) { + bool HasJumpControl = false; + bool HasConsecutiveJumpdest = false; + + for (size_t PC = 0; PC < BytecodeSize; ++PC) { + evmc_opcode Opcode = static_cast(Bytecode[PC]); + if (Opcode == OP_JUMP || Opcode == OP_JUMPI) { + HasJumpControl = true; + } else if (Opcode == OP_JUMPDEST && PC + 1 < BytecodeSize && + Bytecode[PC + 1] == OP_JUMPDEST) { + HasConsecutiveJumpdest = true; + } + + if (HasJumpControl && HasConsecutiveJumpdest) { + return true; + } + + if (Opcode >= OP_PUSH1 && Opcode <= OP_PUSH32) { + PC += static_cast(Opcode) - static_cast(OP_PUSH1) + 1; + } + } + + return false; +} + } // namespace EVMModule::EVMModule(Runtime *RT) @@ -229,10 +303,19 @@ EVMModule::newEVMModule(Runtime &RT, CodeHolderUniquePtr CodeHolder, const bool FallbackUndefinedInstr = hasUndefinedInstructionRisk(Analyzer); const bool FallbackGasSensitiveLoop = hasGasSensitiveLoopRisk( Analyzer, reinterpret_cast(Mod->Code), Mod->CodeSize); + const bool FallbackMemoryCarriedControl = hasMemoryCarriedControlRisk( + reinterpret_cast(Mod->Code), Mod->CodeSize); + const bool FallbackCallFamily = hasCallFamilyRisk( + reinterpret_cast(Mod->Code), Mod->CodeSize); + const bool FallbackConsecutiveJumpdestControl = + hasConsecutiveJumpdestControlRisk( + reinterpret_cast(Mod->Code), Mod->CodeSize); Mod->ShouldFallbackToInterp = FallbackJITSuitability || FallbackDynamicReturn || FallbackDeepEntryMutation || FallbackHiddenPrefixLoopMerge || - FallbackUndefinedInstr || FallbackGasSensitiveLoop; + FallbackUndefinedInstr || FallbackGasSensitiveLoop || + FallbackMemoryCarriedControl || FallbackCallFamily || + FallbackConsecutiveJumpdestControl; if (!Mod->ShouldFallbackToInterp) { // JIT is about to compile this module — mark the bytecode cache so the // SPP metering pipeline runs on first access. From 3a2490da66bbdd87c000ab338f3a88d4b8e8b793 Mon Sep 17 00:00:00 2001 From: ZR74 <2401889661@qq.com> Date: Wed, 20 May 2026 18:16:54 +0800 Subject: [PATCH 19/19] fix(evm): narrow risky control-flow fallback heuristics --- src/runtime/evm_module.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/runtime/evm_module.cpp b/src/runtime/evm_module.cpp index d074a1e8c..79aa05737 100644 --- a/src/runtime/evm_module.cpp +++ b/src/runtime/evm_module.cpp @@ -194,23 +194,6 @@ bool hasMemoryCarriedControlRisk(const uint8_t *Bytecode, size_t BytecodeSize) { return false; } -bool hasCallFamilyRisk(const uint8_t *Bytecode, size_t BytecodeSize) { - for (size_t PC = 0; PC < BytecodeSize; ++PC) { - evmc_opcode Opcode = static_cast(Bytecode[PC]); - if (Opcode == OP_CALL || Opcode == OP_CALLCODE || - Opcode == OP_DELEGATECALL || Opcode == OP_STATICCALL || - Opcode == OP_CREATE || Opcode == OP_CREATE2) { - return true; - } - - if (Opcode >= OP_PUSH1 && Opcode <= OP_PUSH32) { - PC += static_cast(Opcode) - static_cast(OP_PUSH1) + 1; - } - } - - return false; -} - bool hasConsecutiveJumpdestControlRisk(const uint8_t *Bytecode, size_t BytecodeSize) { bool HasJumpControl = false; @@ -305,8 +288,6 @@ EVMModule::newEVMModule(Runtime &RT, CodeHolderUniquePtr CodeHolder, Analyzer, reinterpret_cast(Mod->Code), Mod->CodeSize); const bool FallbackMemoryCarriedControl = hasMemoryCarriedControlRisk( reinterpret_cast(Mod->Code), Mod->CodeSize); - const bool FallbackCallFamily = hasCallFamilyRisk( - reinterpret_cast(Mod->Code), Mod->CodeSize); const bool FallbackConsecutiveJumpdestControl = hasConsecutiveJumpdestControlRisk( reinterpret_cast(Mod->Code), Mod->CodeSize); @@ -314,8 +295,7 @@ EVMModule::newEVMModule(Runtime &RT, CodeHolderUniquePtr CodeHolder, FallbackJITSuitability || FallbackDynamicReturn || FallbackDeepEntryMutation || FallbackHiddenPrefixLoopMerge || FallbackUndefinedInstr || FallbackGasSensitiveLoop || - FallbackMemoryCarriedControl || FallbackCallFamily || - FallbackConsecutiveJumpdestControl; + FallbackMemoryCarriedControl || FallbackConsecutiveJumpdestControl; if (!Mod->ShouldFallbackToInterp) { // JIT is about to compile this module — mark the bytecode cache so the // SPP metering pipeline runs on first access.