diff --git a/silkworm/silkrpc/commands/eth_api.cpp b/silkworm/silkrpc/commands/eth_api.cpp index 616e059343..2c013b441a 100644 --- a/silkworm/silkrpc/commands/eth_api.cpp +++ b/silkworm/silkrpc/commands/eth_api.cpp @@ -1118,12 +1118,10 @@ awaitable EthereumRpcApi::handle_eth_call(const nlohmann::json& request, s return tx->create_state(io_executor, db_reader, block_num); }); - if (execution_result.pre_check_error) { - make_glaze_json_error(reply, request["id"], -32000, execution_result.pre_check_error.value()); - } else if (execution_result.error_code == evmc_status_code::EVMC_SUCCESS) { + if (execution_result.success()) { make_glaze_json_content(reply, request["id"], execution_result.data); } else { - const auto error_message = EVMExecutor::get_error_message(execution_result.error_code, execution_result.data); + const auto error_message = execution_result.error_message(); if (execution_result.data.empty()) { make_glaze_json_error(reply, request["id"], -32000, error_message); } else { @@ -1313,9 +1311,8 @@ awaitable EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js AccessListResult access_list_result; access_list_result.access_list = current_access_list; access_list_result.gas_used = txn.gas_limit - execution_result.gas_left; - if (execution_result.error_code != evmc_status_code::EVMC_SUCCESS) { - const auto error_message = EVMExecutor::get_error_message(execution_result.error_code, execution_result.data, false /* full_error */); - access_list_result.error = error_message; + if (execution_result.success() == false) { + access_list_result.error = execution_result.error_message(false /* full_error */); } reply = make_json_content(request["id"], access_list_result); break; @@ -1408,9 +1405,8 @@ awaitable EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req tx_info.gas_used = tx_with_block->transaction.gas_limit - execution_result.gas_left; tx_info.hash = hash_of_transaction(tx_with_block->transaction); - if (execution_result.error_code != evmc_status_code::EVMC_SUCCESS) { - const auto error_message = EVMExecutor::get_error_message(execution_result.error_code, execution_result.data, false /* full_error */); - tx_info.error_message = error_message; + if (execution_result.success() == false) { + tx_info.error_message = execution_result.error_message(false /* full_error */); } else { tx_info.value = silkworm::to_bytes32(execution_result.data); } diff --git a/silkworm/silkrpc/core/call_many.cpp b/silkworm/silkrpc/core/call_many.cpp index d9bb0e8627..8ff6978758 100644 --- a/silkworm/silkrpc/core/call_many.cpp +++ b/silkworm/silkrpc/core/call_many.cpp @@ -128,7 +128,7 @@ CallManyResult CallExecutor::executes_all_bundles(const silkworm::ChainConfig* c if (call_execution_result.error_code == evmc_status_code::EVMC_SUCCESS) { reply["value"] = "0x" + silkworm::to_hex(call_execution_result.data); } else { - const auto error_message = EVMExecutor::get_error_message(call_execution_result.error_code, call_execution_result.data); + const auto error_message = call_execution_result.error_message(); if (call_execution_result.data.empty()) { reply["error"] = error_message; } else { diff --git a/silkworm/silkrpc/core/estimate_gas_oracle.cpp b/silkworm/silkrpc/core/estimate_gas_oracle.cpp index f15c10f578..b6319b2a1a 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle.cpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle.cpp @@ -91,7 +91,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call transaction.gas_limit = mid; result = try_execution(executor, block, transaction); - if (result.error_code == evmc_status_code::EVMC_SUCCESS) { + if (result.success()) { hi = mid; } else { lo = mid; @@ -105,7 +105,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call transaction.gas_limit = hi; result = try_execution(executor, block, transaction); SILK_DEBUG << "HI == cap tested again with " << (result.error_code == evmc_status_code::EVMC_SUCCESS ? "succeed" : "failed"); - } else if (result.error_code == pre_check_error) { + } else if (result.error_code == std::nullopt) { result.pre_check_error = std::nullopt; result.error_code = evmc_status_code::EVMC_SUCCESS; } @@ -119,7 +119,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call }, boost::asio::use_awaitable); - if (exec_result.error_code != evmc_status_code::EVMC_SUCCESS || exec_result.pre_check_error) { + if (exec_result.success() == false) { throw_exception(exec_result, cap); } co_return hi; @@ -134,8 +134,8 @@ void EstimateGasOracle::throw_exception(ExecutionResult& result, uint64_t cap) { SILK_DEBUG << "result error " << result.pre_check_error.value(); throw EstimateGasException{-1, "gas required exceeds allowance (" + std::to_string(cap) + ")"}; } else { - const auto error_message = EVMExecutor::get_error_message(result.error_code, result.data); - SILK_DEBUG << "result message " << error_message << ", code " << result.error_code; + auto error_message = result.error_message(); + SILK_DEBUG << "result message: " << error_message << ", code " << *result.error_code; if (result.data.empty()) { throw EstimateGasException{-32000, error_message}; } else { diff --git a/silkworm/silkrpc/core/estimate_gas_oracle.hpp b/silkworm/silkrpc/core/estimate_gas_oracle.hpp index 5e9214ad26..a61860c54e 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle.hpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle.hpp @@ -40,7 +40,6 @@ namespace silkworm::rpc { const std::uint64_t kTxGas = 21'000; const std::uint64_t kGasCap = 25'000'000; -const std::uint64_t pre_check_error = 1000; using BlockHeaderProvider = std::function(uint64_t)>; using AccountReader = std::function>(const evmc::address&, uint64_t)>; diff --git a/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp b/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp index 3fcdcd207e..76d11a9278 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp @@ -101,8 +101,8 @@ TEST_CASE("estimate gas") { MockEstimateGasOracle estimate_gas_oracle{block_header_provider, account_reader, config, workers, *tx, tx_database}; SECTION("Call empty, always fails but success in last step") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) @@ -128,7 +128,7 @@ TEST_CASE("estimate gas") { } SECTION("Call empty, always succeeds") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)).Times(14).WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); const intx::uint256& estimate_gas = result.get(); @@ -136,8 +136,8 @@ TEST_CASE("estimate gas") { } SECTION("Call empty, alternatively fails and succeeds") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) .Times(14) .WillOnce(Return(expect_result_fail)) @@ -161,8 +161,8 @@ TEST_CASE("estimate gas") { } SECTION("Call empty, alternatively succeeds and fails") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) .Times(14) .WillOnce(Return(expect_result_ok)) @@ -187,8 +187,8 @@ TEST_CASE("estimate gas") { SECTION("Call with gas, always fails but succes last step") { call.gas = kTxGas * 4; - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) .Times(17) .WillOnce(Return(expect_result_fail)) @@ -216,7 +216,7 @@ TEST_CASE("estimate gas") { SECTION("Call with gas, always succeeds") { call.gas = kTxGas * 4; - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) .Times(15) .WillRepeatedly(Return(expect_result_ok)); @@ -227,8 +227,8 @@ TEST_CASE("estimate gas") { } SECTION("Call with gas_price, gas not capped") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; call.gas = kTxGas * 2; call.gas_price = intx::uint256{10'000}; @@ -257,8 +257,8 @@ TEST_CASE("estimate gas") { } SECTION("Call with gas_price, gas capped") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; call.gas = kTxGas * 2; call.gas_price = intx::uint256{40'000}; @@ -284,8 +284,8 @@ TEST_CASE("estimate gas") { } SECTION("Call with gas_price and value, gas not capped") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; call.gas = kTxGas * 2; call.gas_price = intx::uint256{10'000}; call.value = intx::uint256{500'000'000}; @@ -315,8 +315,8 @@ TEST_CASE("estimate gas") { } SECTION("Call with gas_price and value, gas capped") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; call.gas = kTxGas * 2; call.gas_price = intx::uint256{20'000}; call.value = intx::uint256{500'000'000}; @@ -343,7 +343,7 @@ TEST_CASE("estimate gas") { } SECTION("Call gas above allowance, always succeeds, gas capped") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; call.gas = kGasCap * 2; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)).Times(24).WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); @@ -353,7 +353,7 @@ TEST_CASE("estimate gas") { } SECTION("Call gas below minimum, always succeeds") { - ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS, .pre_check_error = std::nullopt}; + ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; call.gas = kTxGas / 2; EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)).Times(14).WillRepeatedly(Return(expect_result_ok)); @@ -364,7 +364,7 @@ TEST_CASE("estimate gas") { } SECTION("Call with too high value, exception") { - ExecutionResult expect_result_fail{.error_code = pre_check_error, .pre_check_error = "intrisic gas"}; + ExecutionResult expect_result_fail{.pre_check_error = "intrisic gas"}; call.value = intx::uint256{2'000'000'000}; try { @@ -383,7 +383,7 @@ TEST_CASE("estimate gas") { SECTION("Call fail, try exception") { ExecutionResult expect_result_fail_pre_check{.error_code = 4, .pre_check_error = "intrinsic gas"}; - ExecutionResult expect_result_fail{.error_code = 4, .pre_check_error = std::nullopt}; + ExecutionResult expect_result_fail{.error_code = 4}; call.gas = kTxGas * 2; call.gas_price = intx::uint256{20'000}; call.value = intx::uint256{500'000'000}; @@ -408,7 +408,7 @@ TEST_CASE("estimate gas") { SECTION("Call fail, try exception with data") { ExecutionResult expect_result_fail_pre_check{.error_code = 4, .pre_check_error = "intrinsic gas"}; auto data = *silkworm::from_hex("2ac3c1d3e24b45c6c310534bc2dd84b5ed576335"); - ExecutionResult expect_result_fail{.error_code = 4, .data = data, .pre_check_error = std::nullopt}; + ExecutionResult expect_result_fail{.error_code = 4, .data = data}; call.gas = kTxGas * 2; call.gas_price = intx::uint256{20'000}; call.value = intx::uint256{500'000'000}; diff --git a/silkworm/silkrpc/core/evm_debug.cpp b/silkworm/silkrpc/core/evm_debug.cpp index 4f7e4e0252..9c6e8b942f 100644 --- a/silkworm/silkrpc/core/evm_debug.cpp +++ b/silkworm/silkrpc/core/evm_debug.cpp @@ -376,15 +376,12 @@ awaitable DebugExecutor::execute(json::Stream& stream, const silkworm::Blo debug_tracer->flush_logs(); stream.close_array(); - if (execution_result.pre_check_error) { - SILK_DEBUG << "debug failed: " << execution_result.pre_check_error.value(); - - stream.write_field("failed", true); - } else { - stream.write_field("failed", execution_result.error_code != evmc_status_code::EVMC_SUCCESS); + stream.write_field("failed", !execution_result.success()); + if (!execution_result.pre_check_error) { stream.write_field("gas", txn.gas_limit - execution_result.gas_left); stream.write_field("returnValue", silkworm::to_hex(execution_result.data)); } + stream.close_object(); stream.close_object(); } @@ -442,12 +439,11 @@ awaitable DebugExecutor::execute(json::Stream& stream, uint64_t block_numb debug_tracer->flush_logs(); stream.close_array(); - if (execution_result.pre_check_error) { - SILK_DEBUG << "debug failed: " << execution_result.pre_check_error.value(); - stream.write_field("failed", true); - } else { - stream.write_field("failed", execution_result.error_code != evmc_status_code::EVMC_SUCCESS); + SILK_DEBUG << "debug return: " << execution_result.error_message(); + + stream.write_field("failed", !execution_result.success()); + if (!execution_result.pre_check_error) { stream.write_field("gas", transaction.gas_limit - execution_result.gas_left); stream.write_field("returnValue", silkworm::to_hex(execution_result.data)); } @@ -536,12 +532,10 @@ awaitable DebugExecutor::execute(json::Stream& stream, debug_tracer->flush_logs(); stream.close_array(); - if (execution_result.pre_check_error) { - SILK_DEBUG << "debug failed: " << execution_result.pre_check_error.value(); + SILK_DEBUG << "debug return: " << execution_result.error_message(); - stream.write_field("failed", true); - } else { - stream.write_field("failed", execution_result.error_code != evmc_status_code::EVMC_SUCCESS); + stream.write_field("failed", !execution_result.success()); + if (!execution_result.pre_check_error) { stream.write_field("gas", txn.gas_limit - execution_result.gas_left); stream.write_field("returnValue", silkworm::to_hex(execution_result.data)); } diff --git a/silkworm/silkrpc/core/evm_executor.cpp b/silkworm/silkrpc/core/evm_executor.cpp index 88d35c5ffa..9f337ddbf0 100644 --- a/silkworm/silkrpc/core/evm_executor.cpp +++ b/silkworm/silkrpc/core/evm_executor.cpp @@ -36,6 +36,16 @@ namespace silkworm::rpc { +std::string ExecutionResult::error_message(bool full_error) const { + if (pre_check_error) { + return *pre_check_error; + } + if (error_code) { + return silkworm::rpc::EVMExecutor::get_error_message(*error_code, data, full_error); + } + return ""; +} + static Bytes build_abi_selector(const std::string& signature) { const auto signature_hash = hash_of(byte_view_of_string(signature)); return {std::begin(signature_hash.bytes), std::begin(signature_hash.bytes) + 4}; @@ -231,7 +241,7 @@ ExecutionResult EVMExecutor::call( const auto error = pre_check(evm, txn, base_fee_per_gas, g0); if (error) { Bytes data{}; - return {1000, txn.gas_limit, data, *error}; + return {std::nullopt, txn.gas_limit, data, *error}; } intx::uint256 want; @@ -248,7 +258,7 @@ ExecutionResult EVMExecutor::call( Bytes data{}; std::string from = to_hex(*txn.from); std::string msg = "insufficient funds for gas * price + value: address 0x" + from + " have " + intx::to_string(have) + " want " + intx::to_string(want + txn.value); - return {1000, txn.gas_limit, data, msg}; + return {std::nullopt, txn.gas_limit, data, msg}; } } else { ibs_state_.subtract_from_balance(*txn.from, want); @@ -289,7 +299,7 @@ ExecutionResult EVMExecutor::call( ExecutionResult exec_result{result.status, gas_left, result.data}; - SILK_DEBUG << "EVMExecutor::call call_result: " << exec_result.error_code << " #data: " << exec_result.data.size() << " end"; + SILK_DEBUG << "EVMExecutor::call call_result: " << exec_result.error_message() << " #data: " << exec_result.data.size() << " end"; return exec_result; } diff --git a/silkworm/silkrpc/core/evm_executor.hpp b/silkworm/silkrpc/core/evm_executor.hpp index 29cc3a0f45..23f28ca19f 100644 --- a/silkworm/silkrpc/core/evm_executor.hpp +++ b/silkworm/silkrpc/core/evm_executor.hpp @@ -43,10 +43,16 @@ namespace silkworm::rpc { struct ExecutionResult { - int64_t error_code; + std::optional error_code; uint64_t gas_left; Bytes data; - std::optional pre_check_error{std::nullopt}; + std::optional pre_check_error; + + bool success() const { + return ((error_code == std::nullopt || *error_code == evmc_status_code::EVMC_SUCCESS) && pre_check_error == std::nullopt); + } + + std::string error_message(bool full_error = true) const; }; constexpr int kCacheSize = 32000; diff --git a/silkworm/silkrpc/core/evm_executor_test.cpp b/silkworm/silkrpc/core/evm_executor_test.cpp index 53b3cc28a1..daaaeaa00c 100644 --- a/silkworm/silkrpc/core/evm_executor_test.cpp +++ b/silkworm/silkrpc/core/evm_executor_test.cpp @@ -84,7 +84,7 @@ TEST_CASE("EVMExecutor") { auto result = executor.call(block, txn, {}); my_pool.stop(); my_pool.join(); - CHECK(result.error_code == 1000); + CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "intrinsic gas too low: have 0, want 53000"); } @@ -113,7 +113,7 @@ TEST_CASE("EVMExecutor") { auto result = executor.call(block, txn, {}); my_pool.stop(); my_pool.join(); - CHECK(result.error_code == 1000); + CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "fee cap less than block base fee: address 0xa872626373628737383927236382161739290870, gasFeeCap: 2 baseFee: 7"); } @@ -143,7 +143,7 @@ TEST_CASE("EVMExecutor") { auto result = executor.call(block, txn, {}); my_pool.stop(); my_pool.join(); - CHECK(result.error_code == 1000); + CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "tip higher than fee cap: address 0xa872626373628737383927236382161739290870, tip: 24 gasFeeCap: 2"); } @@ -173,7 +173,7 @@ TEST_CASE("EVMExecutor") { auto result = executor.call(block, txn, {}); my_pool.stop(); my_pool.join(); - CHECK(result.error_code == 1000); + CHECK(result.error_code == std::nullopt); CHECK(result.pre_check_error.value() == "insufficient funds for gas * price + value: address 0xa872626373628737383927236382161739290870 have 0 want 60000"); }