From 21936ef6bf2107a7710451ca9e250aee3b2e8c0d Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Mon, 1 Apr 2024 02:14:18 +0200 Subject: [PATCH 01/11] rpcdaemon: extract awaitable async task run by executor --- silkworm/rpc/common/async_task.hpp | 85 +++++++++++++++++++ silkworm/rpc/common/async_task_test.cpp | 105 ++++++++++++++++++++++++ 2 files changed, 190 insertions(+) create mode 100644 silkworm/rpc/common/async_task.hpp create mode 100644 silkworm/rpc/common/async_task_test.cpp diff --git a/silkworm/rpc/common/async_task.hpp b/silkworm/rpc/common/async_task.hpp new file mode 100644 index 0000000000..f3b9806dd0 --- /dev/null +++ b/silkworm/rpc/common/async_task.hpp @@ -0,0 +1,85 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include +#include + +namespace silkworm::rpc { + +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) because of https://github.com/llvm/llvm-project/issues/68105 +Task> async_task(Executor runner, F&& fn, Args&&... args) { + auto this_executor = co_await ThisTask::executor; + co_return co_await boost::asio::async_compose)>( + [&this_executor, &runner, fn = std::forward(fn), ...args = std::forward(args)](auto& self) { + boost::asio::post(runner, [&, self = std::move(self)]() mutable { + try { + auto result = std::invoke(fn, args...); + boost::asio::post(this_executor, [result = std::move(result), self = std::move(self)]() mutable { + if constexpr (std::is_void_v>) + self.complete({}); + else + self.complete({}, result); + }); + } catch (...) { + std::exception_ptr eptr = std::current_exception(); + boost::asio::post(this_executor, [eptr, self = std::move(self)]() mutable { + if constexpr (std::is_void_v>) + self.complete(eptr); + else + self.complete(eptr, {}); + }); + } + }); + }, + boost::asio::use_awaitable); +} + +template +requires std::is_void_v> +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) because of https://github.com/llvm/llvm-project/issues/68105 +Task async_task(Executor runner, F&& fn, Args&&... args) { + auto this_executor = co_await ThisTask::executor; + co_return co_await boost::asio::async_compose( + [&this_executor, &runner, fn = std::forward(fn), ...args = std::forward(args)](auto& self) { + boost::asio::post(runner, [&, self = std::move(self)]() mutable { + try { + std::invoke(fn, args...); + boost::asio::post(this_executor, [self = std::move(self)]() mutable { + self.complete({}); + }); + } catch (...) { + std::exception_ptr eptr = std::current_exception(); + boost::asio::post(this_executor, [eptr, self = std::move(self)]() mutable { + self.complete(eptr); + }); + } + }); + }, + boost::asio::use_awaitable); +} + +} // namespace silkworm::rpc diff --git a/silkworm/rpc/common/async_task_test.cpp b/silkworm/rpc/common/async_task_test.cpp new file mode 100644 index 0000000000..a7a06c187e --- /dev/null +++ b/silkworm/rpc/common/async_task_test.cpp @@ -0,0 +1,105 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "async_task.hpp" + +#include +#include + +#include +#include + +#include + +namespace silkworm::rpc { + +struct AsyncTaskTest : test::ContextTestBase { +}; + +const static std::vector> kTestData = { + {0, 1}, + {1, 1}, + {9, 362'880}, + {10, 3'628'800}, +}; + +std::size_t recursive_factorial(std::size_t n) { + return n == 0 ? 1 : n * recursive_factorial(n - 1); +} + +template +Task async_factorial(Executor runner, std::size_t number) { + co_return co_await async_task(runner, recursive_factorial, number); +} + +TEST_CASE_METHOD(AsyncTaskTest, "async_task: factorial", "[rpc][common][async_task]") { + boost::asio::thread_pool workers; + for (std::size_t i{0}; i < kTestData.size(); ++i) { + const auto [n, r] = kTestData[i]; + SECTION("factorial " + std::to_string(n)) { + std::cout << n << "! = " << spawn_and_wait(async_factorial(workers.get_executor(), n)) << "\n"; + CHECK(spawn_and_wait(async_factorial(workers.get_executor(), n)) == r); + CHECK(spawn_and_wait(async_task(workers.get_executor(), recursive_factorial, n)) == r); + } + } +} + +void raise_exception() { + throw std::runtime_error{""}; +} + +void raise_exception_with_args(int i) { + if (i > 0) { + throw std::runtime_error{""}; + } +} + +template +Task async_raise_exception(Executor runner) { + co_await async_task(runner, raise_exception); + co_return; +} + +template +Task async_raise_exception_with_args(Executor runner, int i) { + co_await async_task(runner, raise_exception_with_args, i); + co_return; +} + +template +Task async_lambda_raise_exception(Executor runner) { + co_await async_task(runner, []() { throw std::runtime_error{""}; }); + co_return; +} + +template +Task async_lambda_raise_exception_with_args(Executor runner, int i) { + co_await async_task(runner, [](auto ii) { if (ii > 0) throw std::runtime_error{""}; }, i); + co_return; +} + +TEST_CASE_METHOD(AsyncTaskTest, "async_task: exception", "[rpc][common][async_task]") { + boost::asio::thread_pool workers; + CHECK_THROWS_AS(spawn_and_wait(async_task(workers.get_executor(), raise_exception)), std::runtime_error); + CHECK_THROWS_AS(spawn_and_wait(async_raise_exception(workers.get_executor())), std::runtime_error); + CHECK_THROWS_AS(spawn_and_wait(async_lambda_raise_exception(workers.get_executor())), std::runtime_error); + + CHECK_THROWS_AS(spawn_and_wait(async_task(workers.get_executor(), raise_exception_with_args, 1)), std::runtime_error); + CHECK_THROWS_AS(spawn_and_wait(async_raise_exception_with_args(workers.get_executor(), 1)), std::runtime_error); + CHECK_THROWS_AS(spawn_and_wait(async_lambda_raise_exception_with_args(workers.get_executor(), 1)), std::runtime_error); +} + +} // namespace silkworm::rpc From bee6b2013a5cc4b8157aa7bde5763829d889beb7 Mon Sep 17 00:00:00 2001 From: GitHub Date: Mon, 1 Apr 2024 00:14:42 +0000 Subject: [PATCH 02/11] make fmt --- silkworm/rpc/common/async_task.hpp | 6 +++--- silkworm/rpc/common/async_task_test.cpp | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/silkworm/rpc/common/async_task.hpp b/silkworm/rpc/common/async_task.hpp index f3b9806dd0..5a82d41e74 100644 --- a/silkworm/rpc/common/async_task.hpp +++ b/silkworm/rpc/common/async_task.hpp @@ -34,7 +34,7 @@ template Task> async_task(Executor runner, F&& fn, Args&&... args) { auto this_executor = co_await ThisTask::executor; co_return co_await boost::asio::async_compose)>( - [&this_executor, &runner, fn = std::forward(fn), ...args = std::forward(args)](auto& self) { + [&this_executor, &runner, fn = std::forward(fn), ... args = std::forward(args)](auto& self) { boost::asio::post(runner, [&, self = std::move(self)]() mutable { try { auto result = std::invoke(fn, args...); @@ -59,12 +59,12 @@ Task> async_task(Executor runner, F&& fn, Args& } template -requires std::is_void_v> + requires std::is_void_v> // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) because of https://github.com/llvm/llvm-project/issues/68105 Task async_task(Executor runner, F&& fn, Args&&... args) { auto this_executor = co_await ThisTask::executor; co_return co_await boost::asio::async_compose( - [&this_executor, &runner, fn = std::forward(fn), ...args = std::forward(args)](auto& self) { + [&this_executor, &runner, fn = std::forward(fn), ... args = std::forward(args)](auto& self) { boost::asio::post(runner, [&, self = std::move(self)]() mutable { try { std::invoke(fn, args...); diff --git a/silkworm/rpc/common/async_task_test.cpp b/silkworm/rpc/common/async_task_test.cpp index a7a06c187e..50b8e1b25a 100644 --- a/silkworm/rpc/common/async_task_test.cpp +++ b/silkworm/rpc/common/async_task_test.cpp @@ -50,7 +50,7 @@ TEST_CASE_METHOD(AsyncTaskTest, "async_task: factorial", "[rpc][common][async_ta for (std::size_t i{0}; i < kTestData.size(); ++i) { const auto [n, r] = kTestData[i]; SECTION("factorial " + std::to_string(n)) { - std::cout << n << "! = " << spawn_and_wait(async_factorial(workers.get_executor(), n)) << "\n"; + std::cout << n << "! = " << spawn_and_wait(async_factorial(workers.get_executor(), n)) << "\n"; CHECK(spawn_and_wait(async_factorial(workers.get_executor(), n)) == r); CHECK(spawn_and_wait(async_task(workers.get_executor(), recursive_factorial, n)) == r); } @@ -87,7 +87,8 @@ Task async_lambda_raise_exception(Executor runner) { template Task async_lambda_raise_exception_with_args(Executor runner, int i) { - co_await async_task(runner, [](auto ii) { if (ii > 0) throw std::runtime_error{""}; }, i); + co_await async_task( + runner, [](auto ii) { if (ii > 0) throw std::runtime_error{""}; }, i); co_return; } From 628019fe62b14d51ec79be40510f7eabb1e37925 Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:13:11 +0200 Subject: [PATCH 03/11] refactor to better handle task functions returning void --- silkworm/rpc/common/async_task.hpp | 57 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/silkworm/rpc/common/async_task.hpp b/silkworm/rpc/common/async_task.hpp index 5a82d41e74..af593b5cf4 100644 --- a/silkworm/rpc/common/async_task.hpp +++ b/silkworm/rpc/common/async_task.hpp @@ -29,21 +29,42 @@ namespace silkworm::rpc { +//! Helper trait for any completion handler signature +template +struct CompletionHandler { + using type = void(std::exception_ptr, R); +}; + +//! Partial specialization for \code void return type +template +struct CompletionHandler { + using type = void(std::exception_ptr); +}; + +//! Alias helper trait for the completion handler signature of any task +template +using TaskCompletionHandler = typename CompletionHandler, F, Args...>::type; + +//! Asynchronous \code co_await-able task executing function \code fn with arguments \code args in \code runner executor template // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) because of https://github.com/llvm/llvm-project/issues/68105 Task> async_task(Executor runner, F&& fn, Args&&... args) { auto this_executor = co_await ThisTask::executor; - co_return co_await boost::asio::async_compose)>( + co_return co_await boost::asio::async_compose>( [&this_executor, &runner, fn = std::forward(fn), ... args = std::forward(args)](auto& self) { boost::asio::post(runner, [&, self = std::move(self)]() mutable { try { - auto result = std::invoke(fn, args...); - boost::asio::post(this_executor, [result = std::move(result), self = std::move(self)]() mutable { - if constexpr (std::is_void_v>) + if constexpr (std::is_void_v>) { + std::invoke(fn, args...); + boost::asio::post(this_executor, [self = std::move(self)]() mutable { self.complete({}); - else + }); + } else { + auto result = std::invoke(fn, args...); + boost::asio::post(this_executor, [result = std::move(result), self = std::move(self)]() mutable { self.complete({}, result); - }); + }); + } } catch (...) { std::exception_ptr eptr = std::current_exception(); boost::asio::post(this_executor, [eptr, self = std::move(self)]() mutable { @@ -58,28 +79,4 @@ Task> async_task(Executor runner, F&& fn, Args& boost::asio::use_awaitable); } -template - requires std::is_void_v> -// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) because of https://github.com/llvm/llvm-project/issues/68105 -Task async_task(Executor runner, F&& fn, Args&&... args) { - auto this_executor = co_await ThisTask::executor; - co_return co_await boost::asio::async_compose( - [&this_executor, &runner, fn = std::forward(fn), ... args = std::forward(args)](auto& self) { - boost::asio::post(runner, [&, self = std::move(self)]() mutable { - try { - std::invoke(fn, args...); - boost::asio::post(this_executor, [self = std::move(self)]() mutable { - self.complete({}); - }); - } catch (...) { - std::exception_ptr eptr = std::current_exception(); - boost::asio::post(this_executor, [eptr, self = std::move(self)]() mutable { - self.complete(eptr); - }); - } - }); - }, - boost::asio::use_awaitable); -} - } // namespace silkworm::rpc From 986a449891d5f9592dd9deda3f1b3facaf0cdb47 Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:35:35 +0200 Subject: [PATCH 04/11] refactor using async_task --- silkworm/rpc/commands/debug_api.cpp | 79 ++-- silkworm/rpc/core/call_many.cpp | 27 +- silkworm/rpc/core/estimate_gas_oracle.cpp | 73 ++-- silkworm/rpc/core/evm_debug.cpp | 250 +++++------ silkworm/rpc/core/evm_executor.cpp | 29 +- silkworm/rpc/core/evm_trace.cpp | 480 ++++++++++------------ silkworm/rpc/http/connection.cpp | 31 +- 7 files changed, 416 insertions(+), 553 deletions(-) diff --git a/silkworm/rpc/commands/debug_api.cpp b/silkworm/rpc/commands/debug_api.cpp index 79c9ba364d..60e6898baf 100644 --- a/silkworm/rpc/commands/debug_api.cpp +++ b/silkworm/rpc/commands/debug_api.cpp @@ -24,8 +24,6 @@ #include #include -#include -#include #include #include @@ -36,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -47,7 +46,6 @@ #include #include #include -#include #include #include #include @@ -254,10 +252,10 @@ Task DebugRpcApi::handle_debug_storage_range_at(const nlohmann::json& requ co_await storage_walker.storage_range_at(block_number, address, start_key, max_result, collector); nlohmann::json result = {{"storage", storage}}; - if (next_key.length() > 0) { - result["nextKey"] = "0x" + silkworm::to_hex(next_key); - } else { + if (next_key.empty()) { result["nextKey"] = nlohmann::json(); + } else { + result["nextKey"] = "0x" + silkworm::to_hex(next_key); } reply = make_json_content(request, result); @@ -314,46 +312,39 @@ Task DebugRpcApi::handle_debug_account_at(const nlohmann::json& request, n auto chain_config_ptr = co_await chain_storage->read_chain_config(); ensure(chain_config_ptr.has_value(), "cannot read chain config"); + auto this_executor = co_await boost::asio::this_coro::executor; + auto result = co_await async_task(workers_.executor(), [&]() -> nlohmann::json { + auto state = tx->create_state(this_executor, tx_database, *chain_storage, block_number - 1); + auto account_opt = state->read_account(address); + account_opt.value_or(silkworm::Account{}); + + EVMExecutor executor{*chain_config_ptr, workers_, state}; - auto result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx->create_state(this_executor, tx_database, *chain_storage, block_number - 1); - auto account_opt = state->read_account(address); - account_opt.value_or(silkworm::Account{}); - - EVMExecutor executor{*chain_config_ptr, workers_, state}; - - uint64_t index = std::min(static_cast(transactions.size()), tx_index); - for (uint64_t idx{0}; idx < index; idx++) { - rpc::Transaction txn{transactions[idx]}; - executor.call(block, txn); - } - - const auto& ibs = executor.get_ibs_state(); - - nlohmann::json json_result; - if (ibs.exists(address)) { - std::ostringstream oss; - oss << std::hex << ibs.get_nonce(address); - json_result["nonce"] = "0x" + oss.str(); - json_result["balance"] = "0x" + intx::to_string(ibs.get_balance(address), 16); - json_result["codeHash"] = ibs.get_code_hash(address); - json_result["code"] = "0x" + silkworm::to_hex(ibs.get_code(address)); - } else { - json_result["balance"] = "0x0"; - json_result["code"] = "0x"; - json_result["codeHash"] = "0x0000000000000000000000000000000000000000000000000000000000000000"; - json_result["nonce"] = "0x0"; - } - - boost::asio::post(this_executor, [json_result, self = std::move(self)]() mutable { - self.complete(json_result); - }); - }); - }, - boost::asio::use_awaitable); + uint64_t index = std::min(static_cast(transactions.size()), tx_index); + for (uint64_t idx{0}; idx < index; idx++) { + rpc::Transaction txn{transactions[idx]}; + executor.call(block, txn); + } + + const auto& ibs = executor.get_ibs_state(); + + nlohmann::json json_result; + if (ibs.exists(address)) { + std::ostringstream oss; + oss << std::hex << ibs.get_nonce(address); + json_result["nonce"] = "0x" + oss.str(); + json_result["balance"] = "0x" + intx::to_string(ibs.get_balance(address), 16); + json_result["codeHash"] = ibs.get_code_hash(address); + json_result["code"] = "0x" + silkworm::to_hex(ibs.get_code(address)); + } else { + json_result["balance"] = "0x0"; + json_result["code"] = "0x"; + json_result["codeHash"] = "0x0000000000000000000000000000000000000000000000000000000000000000"; + json_result["nonce"] = "0x0"; + } + return json_result; + }); reply = make_json_content(request, result); } catch (const std::invalid_argument& e) { diff --git a/silkworm/rpc/core/call_many.cpp b/silkworm/rpc/core/call_many.cpp index b04eace6c8..f55f685c5a 100644 --- a/silkworm/rpc/core/call_many.cpp +++ b/silkworm/rpc/core/call_many.cpp @@ -20,13 +20,11 @@ #include #include -#include -#include -#include #include #include #include +#include #include #include #include @@ -143,7 +141,7 @@ Task CallExecutor::execute( const Bundles& bundles, const SimulationContext& context, const AccountsOverrides& accounts_overrides, - std::optional opt_timeout) { + std::optional timeout) { ethdb::TransactionDatabase tx_database{transaction_}; const auto chain_storage{transaction_.create_storage(tx_database, backend_)}; @@ -174,16 +172,17 @@ Task CallExecutor::execute( } auto this_executor = co_await boost::asio::this_coro::executor; - result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - result = executes_all_bundles(*chain_config_ptr, *chain_storage, block_with_hash, tx_database, bundles, opt_timeout, accounts_overrides, transaction_index, this_executor); - boost::asio::post(this_executor, [result, self = std::move(self)]() mutable { - self.complete(result); - }); - }); - }, - boost::asio::use_awaitable); + result = co_await async_task(workers_.executor(), [&]() -> CallManyResult { + return executes_all_bundles(*chain_config_ptr, + *chain_storage, + block_with_hash, + tx_database, + bundles, + timeout, + accounts_overrides, + transaction_index, + this_executor); + }); co_return result; } diff --git a/silkworm/rpc/core/estimate_gas_oracle.cpp b/silkworm/rpc/core/estimate_gas_oracle.cpp index 9557ce91fb..8b299d7a62 100644 --- a/silkworm/rpc/core/estimate_gas_oracle.cpp +++ b/silkworm/rpc/core/estimate_gas_oracle.cpp @@ -18,12 +18,9 @@ #include -#include -#include -#include - #include #include +#include #include namespace silkworm::rpc { @@ -83,48 +80,42 @@ Task EstimateGasOracle::estimate_gas(const Call& call, const silk SILK_DEBUG << "hi: " << hi << ", lo: " << lo << ", cap: " << cap; auto this_executor = co_await boost::asio::this_coro::executor; - auto exec_result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = transaction_.create_state(this_executor, tx_database_, storage_, block_number); - EVMExecutor executor{config_, workers_, state}; - - ExecutionResult result{evmc_status_code::EVMC_SUCCESS}; - silkworm::Transaction transaction{call.to_transaction()}; - while (lo + 1 < hi) { - auto mid = (hi + lo) / 2; - transaction.gas_limit = mid; - - result = try_execution(executor, block, transaction); - if (result.success()) { - hi = mid; - } else { - lo = mid; - if (result.pre_check_error == std::nullopt) { - break; - } - } + auto exec_result = co_await async_task(workers_.executor(), [&]() -> ExecutionResult { + auto state = transaction_.create_state(this_executor, tx_database_, storage_, block_number); + EVMExecutor executor{config_, workers_, state}; + + ExecutionResult result{evmc_status_code::EVMC_SUCCESS}; + silkworm::Transaction transaction{call.to_transaction()}; + while (lo + 1 < hi) { + auto mid = (hi + lo) / 2; + transaction.gas_limit = mid; + + result = try_execution(executor, block, transaction); + if (result.success()) { + hi = mid; + } else { + lo = mid; + if (result.pre_check_error == std::nullopt) { + break; } + } + } - if (hi == cap) { - 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 == std::nullopt) { - result.pre_check_error = std::nullopt; - result.error_code = evmc_status_code::EVMC_SUCCESS; - } + if (hi == cap) { + 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 == std::nullopt) { + result.pre_check_error = std::nullopt; + result.error_code = evmc_status_code::EVMC_SUCCESS; + } - SILK_DEBUG << "EstimateGasOracle::estimate_gas returns " << hi; + SILK_DEBUG << "EstimateGasOracle::estimate_gas returns " << hi; - boost::asio::post(this_executor, [result, self = std::move(self)]() mutable { - self.complete(result); - }); - }); - }, - boost::asio::use_awaitable); + return result; + }); - if (exec_result.success() == false) { + if (!exec_result.success()) { throw_exception(exec_result, cap); } co_return hi; diff --git a/silkworm/rpc/core/evm_debug.cpp b/silkworm/rpc/core/evm_debug.cpp index 1c486abd1d..f81218676c 100644 --- a/silkworm/rpc/core/evm_debug.cpp +++ b/silkworm/rpc/core/evm_debug.cpp @@ -19,12 +19,8 @@ #include #include -#include -#include -#include #include #include -#include #include #include @@ -32,11 +28,10 @@ #include #include #include +#include #include #include #include -#include -#include #include namespace silkworm::rpc::debug { @@ -395,47 +390,39 @@ Task DebugExecutor::execute(json::Stream& stream, const ChainStorage& stor const auto chain_config_ptr = co_await storage.read_chain_config(); ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + co_await async_task(workers_.executor(), [&]() -> void { + auto state = tx_.create_state(current_executor, database_reader_, storage, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, state}; - co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, storage, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, state}; + for (std::uint64_t idx = 0; idx < transactions.size(); idx++) { + rpc::Transaction txn{block.transactions[idx]}; + SILK_DEBUG << "processing transaction: idx: " << idx << " txn: " << txn; - for (std::uint64_t idx = 0; idx < transactions.size(); idx++) { - rpc::Transaction txn{block.transactions[idx]}; - SILK_DEBUG << "processing transaction: idx: " << idx << " txn: " << txn; + auto debug_tracer = std::make_shared(stream, config_); - auto debug_tracer = std::make_shared(stream, config_); + stream.open_object(); + stream.write_field("result"); + stream.open_object(); + stream.write_field("structLogs"); + stream.open_array(); - stream.open_object(); - stream.write_field("result"); - stream.open_object(); - stream.write_field("structLogs"); - stream.open_array(); + Tracers tracers{debug_tracer}; + const auto execution_result = executor.call(block, txn, tracers, /* refund */ false, /* gasBailout */ false); - Tracers tracers{debug_tracer}; - const auto execution_result = executor.call(block, txn, tracers, /* refund */ false, /* gasBailout */ false); + debug_tracer->flush_logs(); + stream.close_array(); - debug_tracer->flush_logs(); - stream.close_array(); - - stream.write_json_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.write_json_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.write_field("txHash", txn.hash()); - stream.close_object(); - } - boost::asio::post(current_executor, [self = std::move(self)]() mutable { - self.complete(); - }); - }); - }, - boost::asio::use_awaitable); + stream.close_object(); + stream.write_field("txHash", txn.hash()); + stream.close_object(); + } + }); co_return; } @@ -462,43 +449,35 @@ Task DebugExecutor::execute( const auto chain_config_ptr = co_await storage.read_chain_config(); ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + co_await async_task(workers_.executor(), [&]() { + auto state = tx_.create_state(current_executor, database_reader_, storage, block_number); + EVMExecutor executor{*chain_config_ptr, workers_, state}; - co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, storage, block_number); - EVMExecutor executor{*chain_config_ptr, workers_, state}; + for (auto idx{0}; idx < index; idx++) { + silkworm::Transaction txn{block.transactions[std::size_t(idx)]}; + executor.call(block, txn); + } + executor.reset(); - for (auto idx{0}; idx < index; idx++) { - silkworm::Transaction txn{block.transactions[std::size_t(idx)]}; - executor.call(block, txn); - } - executor.reset(); + auto debug_tracer = std::make_shared(stream, config_); - auto debug_tracer = std::make_shared(stream, config_); + stream.write_field("structLogs"); + stream.open_array(); - stream.write_field("structLogs"); - stream.open_array(); - - Tracers tracers{debug_tracer}; - const auto execution_result = executor.call(block, transaction, tracers); + Tracers tracers{debug_tracer}; + const auto execution_result = executor.call(block, transaction, tracers); - debug_tracer->flush_logs(); - stream.close_array(); + debug_tracer->flush_logs(); + stream.close_array(); - SILK_DEBUG << "debug return: " << execution_result.error_message(); + SILK_DEBUG << "debug return: " << execution_result.error_message(); - stream.write_json_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)); - } - boost::asio::post(current_executor, [self = std::move(self)]() mutable { - self.complete(); - }); - }); - }, - boost::asio::use_awaitable); + stream.write_json_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)); + } + }); co_return; } @@ -523,76 +502,69 @@ Task DebugExecutor::execute( ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; - co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, storage, block.header.number); - EVMExecutor executor{*chain_config_ptr, workers_, state}; - - for (auto idx{0}; idx < transaction_index; idx++) { - silkworm::Transaction txn{block_transactions[std::size_t(idx)]}; - executor.call(block, txn); - } - executor.reset(); - - for (const auto& bundle : bundles) { - const auto& block_override = bundle.block_override; - - rpc::Block blockContext{{block_with_hash}}; - if (block_override.block_number) { - blockContext.block_with_hash->block.header.number = block_override.block_number.value(); - } - if (block_override.coin_base) { - blockContext.block_with_hash->block.header.beneficiary = block_override.coin_base.value(); - } - if (block_override.timestamp) { - blockContext.block_with_hash->block.header.timestamp = block_override.timestamp.value(); - } - if (block_override.difficulty) { - blockContext.block_with_hash->block.header.difficulty = block_override.difficulty.value(); - } - if (block_override.gas_limit) { - blockContext.block_with_hash->block.header.gas_limit = block_override.gas_limit.value(); - } - if (block_override.base_fee) { - blockContext.block_with_hash->block.header.base_fee_per_gas = block_override.base_fee; - } - - stream.open_array(); - - for (const auto& call : bundle.transactions) { - silkworm::Transaction txn{call.to_transaction()}; - - stream.open_object(); - stream.write_field("structLogs"); - stream.open_array(); - - auto debug_tracer = std::make_shared(stream, config_); - Tracers tracers{debug_tracer}; - - const auto execution_result = executor.call(blockContext.block_with_hash->block, txn, tracers, /* refund */ false, /* gasBailout */ false); - - debug_tracer->flush_logs(); - stream.close_array(); - - SILK_DEBUG << "debug return: " << execution_result.error_message(); - - stream.write_json_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_array(); + co_await async_task(workers_.executor(), [&]() { + auto state = tx_.create_state(current_executor, database_reader_, storage, block.header.number); + EVMExecutor executor{*chain_config_ptr, workers_, state}; + + for (auto idx{0}; idx < transaction_index; idx++) { + silkworm::Transaction txn{block_transactions[std::size_t(idx)]}; + executor.call(block, txn); + } + executor.reset(); + + for (const auto& bundle : bundles) { + const auto& block_override = bundle.block_override; + + rpc::Block blockContext{{block_with_hash}}; + if (block_override.block_number) { + blockContext.block_with_hash->block.header.number = block_override.block_number.value(); + } + if (block_override.coin_base) { + blockContext.block_with_hash->block.header.beneficiary = block_override.coin_base.value(); + } + if (block_override.timestamp) { + blockContext.block_with_hash->block.header.timestamp = block_override.timestamp.value(); + } + if (block_override.difficulty) { + blockContext.block_with_hash->block.header.difficulty = block_override.difficulty.value(); + } + if (block_override.gas_limit) { + blockContext.block_with_hash->block.header.gas_limit = block_override.gas_limit.value(); + } + if (block_override.base_fee) { + blockContext.block_with_hash->block.header.base_fee_per_gas = block_override.base_fee; + } + + stream.open_array(); + + for (const auto& call : bundle.transactions) { + silkworm::Transaction txn{call.to_transaction()}; + + stream.open_object(); + stream.write_field("structLogs"); + stream.open_array(); + + auto debug_tracer = std::make_shared(stream, config_); + Tracers tracers{debug_tracer}; + + const auto execution_result = executor.call(blockContext.block_with_hash->block, txn, tracers, /* refund */ false, /* gasBailout */ false); + + debug_tracer->flush_logs(); + stream.close_array(); + + SILK_DEBUG << "debug return: " << execution_result.error_message(); + + stream.write_json_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)); } - boost::asio::post(current_executor, [self = std::move(self)]() mutable { - self.complete(); - }); - }); - }, - boost::asio::use_awaitable); + stream.close_object(); + } + + stream.close_array(); + } + }); co_return; } diff --git a/silkworm/rpc/core/evm_executor.cpp b/silkworm/rpc/core/evm_executor.cpp index 443fcf6f88..9104bb6f17 100644 --- a/silkworm/rpc/core/evm_executor.cpp +++ b/silkworm/rpc/core/evm_executor.cpp @@ -20,9 +20,6 @@ #include #include -#include -#include -#include #include #include @@ -31,8 +28,8 @@ #include #include #include +#include #include -#include #include namespace silkworm::rpc { @@ -192,9 +189,8 @@ std::optional EVMExecutor::pre_check(const EVM& evm, const silkworm if (txn.max_fee_per_gas > 0 || txn.max_priority_fee_per_gas > 0) { if (txn.max_fee_per_gas < base_fee_per_gas) { const std::string from = address_to_hex(*txn.sender()); - const std::string error = "fee cap less than block base fee: address " + from + ", gasFeeCap: " + - intx::to_string(txn.max_fee_per_gas) + " baseFee: " + intx::to_string(base_fee_per_gas); - return error; + return "fee cap less than block base fee: address " + from + ", gasFeeCap: " + + intx::to_string(txn.max_fee_per_gas) + " baseFee: " + intx::to_string(base_fee_per_gas); } if (txn.max_fee_per_gas < txn.max_priority_fee_per_gas) { @@ -245,7 +241,7 @@ ExecutionResult EVMExecutor::call( const auto error = pre_check(evm, txn, base_fee_per_gas, g0); if (error) { Bytes data{}; - return {std::nullopt, txn.gas_limit, data, *error}; + return {std::nullopt, txn.gas_limit, data, error}; } intx::uint256 want; @@ -330,18 +326,11 @@ Task EVMExecutor::call( bool refund, bool gas_bailout) { auto this_executor = co_await boost::asio::this_coro::executor; - const auto execution_result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers, [&, self = std::move(self)]() mutable { - auto state = state_factory(this_executor, block.header.number, chain_storage); - EVMExecutor executor{config, workers, state}; - auto exec_result = executor.call(block, txn, tracers, refund, gas_bailout); - boost::asio::post(this_executor, [exec_result, self = std::move(self)]() mutable { - self.complete(exec_result); - }); - }); - }, - boost::asio::use_awaitable); + const auto execution_result = co_await async_task(workers.executor(), [&]() -> ExecutionResult { + auto state = state_factory(this_executor, block.header.number, chain_storage); + EVMExecutor executor{config, workers, state}; + return executor.call(block, txn, tracers, refund, gas_bailout); + }); co_return execution_result; } diff --git a/silkworm/rpc/core/evm_trace.cpp b/silkworm/rpc/core/evm_trace.cpp index 075cf6d521..cdf4a85399 100644 --- a/silkworm/rpc/core/evm_trace.cpp +++ b/silkworm/rpc/core/evm_trace.cpp @@ -22,13 +22,10 @@ #include #include -#include -#include #include #include #include #include -#include #include #include @@ -39,9 +36,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -1130,7 +1127,7 @@ void StateDiffTracer::on_reward_granted(const silkworm::CallResult& result, cons } } } -}; +} void IntraBlockStateTracer::on_reward_granted(const silkworm::CallResult& result, const silkworm::IntraBlockState& intra_block_state) noexcept { SILK_DEBUG @@ -1241,7 +1238,7 @@ Task> TraceCallExecutor::trace_block(const BlockWithHash& blo for (auto& ommer_reward : block_rewards.ommers) { RewardAction action; - action.author = block_with_hash.block.header.beneficiary; /* to be fix */ + action.author = block_with_hash.block.header.beneficiary; /* to be fixed */ action.reward_type = "block"; action.value = ommer_reward; @@ -1276,60 +1273,53 @@ Task> TraceCallExecutor::trace_block_transactions(c ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + const auto call_result = co_await async_task(workers_.executor(), [&]() -> std::vector { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + IntraBlockState initial_ibs{*state}; - const auto call_result = co_await boost::asio::async_compose)>( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - IntraBlockState initial_ibs{*state}; - - StateAddresses state_addresses(initial_ibs); - std::shared_ptr ibs_tracer = std::make_shared(state_addresses); + StateAddresses state_addresses(initial_ibs); + std::shared_ptr ibs_tracer = std::make_shared(state_addresses); - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - std::vector trace_call_result(transactions.size()); - for (std::uint64_t index = 0; index < transactions.size(); index++) { - const silkworm::Transaction& transaction{block.transactions[index]}; + std::vector trace_call_result(transactions.size()); + for (std::uint64_t index = 0; index < transactions.size(); index++) { + const silkworm::Transaction& transaction{block.transactions[index]}; - auto& result = trace_call_result.at(index); - TraceCallTraces& traces = result.traces; - traces.transaction_hash = transaction.hash(); + auto& result = trace_call_result.at(index); + TraceCallTraces& traces = result.traces; + traces.transaction_hash = transaction.hash(); - Tracers tracers; - if (config.vm_trace) { - traces.vm_trace.emplace(); - std::shared_ptr tracer = std::make_shared(traces.vm_trace.value(), index); - tracers.push_back(tracer); - } - if (config.trace) { - std::shared_ptr tracer = std::make_shared(traces.trace, initial_ibs); - tracers.push_back(tracer); - } - if (config.state_diff) { - traces.state_diff.emplace(); + Tracers tracers; + if (config.vm_trace) { + traces.vm_trace.emplace(); + std::shared_ptr tracer = std::make_shared(traces.vm_trace.value(), index); + tracers.push_back(tracer); + } + if (config.trace) { + std::shared_ptr tracer = std::make_shared(traces.trace, initial_ibs); + tracers.push_back(tracer); + } + if (config.state_diff) { + traces.state_diff.emplace(); - std::shared_ptr tracer = std::make_shared(traces.state_diff.value(), state_addresses); - tracers.push_back(tracer); - } + std::shared_ptr tracer = std::make_shared(traces.state_diff.value(), state_addresses); + tracers.push_back(tracer); + } - tracers.push_back(ibs_tracer); + tracers.push_back(ibs_tracer); - auto execution_result = executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); - if (execution_result.pre_check_error) { - result.pre_check_error = execution_result.pre_check_error.value(); - } else { - traces.output = "0x" + silkworm::to_hex(execution_result.data); - } - executor.reset(); - } - boost::asio::post(current_executor, [trace_call_result, self = std::move(self)]() mutable { - self.complete(trace_call_result); - }); - }); - }, - boost::asio::use_awaitable); + auto execution_result = executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + if (execution_result.pre_check_error) { + result.pre_check_error = execution_result.pre_check_error.value(); + } else { + traces.output = "0x" + silkworm::to_hex(execution_result.data); + } + executor.reset(); + } + return trace_call_result; + }); co_return call_result; } @@ -1350,63 +1340,57 @@ Task TraceCallExecutor::trace_calls(const silkworm::Block& ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; - const auto ret_result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); - silkworm::IntraBlockState initial_ibs{*state}; - StateAddresses state_addresses(initial_ibs); - - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); - EVMExecutor executor{*chain_config_ptr, workers_, state}; - - std::shared_ptr ibs_tracer = std::make_shared(state_addresses); - - TraceManyCallResult result; - for (std::size_t index{0}; index < calls.size(); index++) { - const auto& config = calls[index].trace_config; - - silkworm::Transaction transaction{calls[index].call.to_transaction()}; - - Tracers tracers; - TraceCallTraces traces; - if (config.vm_trace) { - traces.vm_trace.emplace(); - std::shared_ptr tracer = std::make_shared(traces.vm_trace.value(), index); - tracers.push_back(tracer); - } - if (config.trace) { - std::shared_ptr tracer = std::make_shared(traces.trace, initial_ibs); - tracers.push_back(tracer); - } - if (config.state_diff) { - traces.state_diff.emplace(); + const auto trace_calls_result = co_await async_task(workers_.executor(), [&]() -> TraceManyCallResult { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); + silkworm::IntraBlockState initial_ibs{*state}; + StateAddresses state_addresses(initial_ibs); - std::shared_ptr tracer = std::make_shared(traces.state_diff.value(), state_addresses); - tracers.push_back(tracer); - } - tracers.push_back(ibs_tracer); + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); + EVMExecutor executor{*chain_config_ptr, workers_, state}; - auto execution_result = executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + std::shared_ptr ibs_tracer = std::make_shared(state_addresses); - if (execution_result.pre_check_error) { - result.pre_check_error = "first run for txIndex " + std::to_string(index) + " error: " + execution_result.pre_check_error.value(); - result.traces.clear(); - break; - } - traces.output = "0x" + silkworm::to_hex(execution_result.data); - result.traces.push_back(traces); + TraceManyCallResult result; + for (std::size_t index{0}; index < calls.size(); index++) { + const auto& config = calls[index].trace_config; - executor.reset(); - } - boost::asio::post(current_executor, [result, self = std::move(self)]() mutable { - self.complete(result); - }); - }); - }, - boost::asio::use_awaitable); - - co_return ret_result; + silkworm::Transaction transaction{calls[index].call.to_transaction()}; + + Tracers tracers; + TraceCallTraces traces; + if (config.vm_trace) { + traces.vm_trace.emplace(); + std::shared_ptr tracer = std::make_shared(traces.vm_trace.value(), index); + tracers.push_back(tracer); + } + if (config.trace) { + std::shared_ptr tracer = std::make_shared(traces.trace, initial_ibs); + tracers.push_back(tracer); + } + if (config.state_diff) { + traces.state_diff.emplace(); + + std::shared_ptr tracer = std::make_shared(traces.state_diff.value(), state_addresses); + tracers.push_back(tracer); + } + tracers.push_back(ibs_tracer); + + auto execution_result = executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + + if (execution_result.pre_check_error) { + result.pre_check_error = "first run for txIndex " + std::to_string(index) + " error: " + execution_result.pre_check_error.value(); + result.traces.clear(); + break; + } + traces.output = "0x" + silkworm::to_hex(execution_result.data); + result.traces.push_back(traces); + + executor.reset(); + } + return result; + }); + + co_return trace_calls_result; } Task TraceCallExecutor::trace_deploy_transaction(const silkworm::Block& block, const evmc::address& contract_address) { @@ -1419,38 +1403,29 @@ Task TraceCallExecutor::trace_deploy_transaction(const silkwo ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; - - const auto deploy_result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - silkworm::IntraBlockState initial_ibs{*state}; - - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - - TraceDeployResult result; - - auto create_tracer = std::make_shared(contract_address, initial_ibs); - - Tracers tracers{create_tracer}; - - for (std::uint64_t index = 0; index < transactions.size(); index++) { - const silkworm::Transaction& transaction{block.transactions[index]}; - executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); - executor.reset(); - if (create_tracer->found()) { - result.transaction_hash = transaction.hash(); - result.contract_creator = transaction.sender(); - break; - } - } - boost::asio::post(current_executor, [result, self = std::move(self)]() mutable { - self.complete(result); - }); - }); - }, - boost::asio::use_awaitable); + const auto deploy_result = co_await async_task(workers_.executor(), [&]() -> TraceDeployResult { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + silkworm::IntraBlockState initial_ibs{*state}; + + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; + + TraceDeployResult result; + + auto create_tracer = std::make_shared(contract_address, initial_ibs); + Tracers tracers{create_tracer}; + for (std::uint64_t index = 0; index < transactions.size(); index++) { + const silkworm::Transaction& transaction{block.transactions[index]}; + executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + executor.reset(); + if (create_tracer->found()) { + result.transaction_hash = transaction.hash(); + result.contract_creator = transaction.sender(); + break; + } + } + return result; + }); co_return deploy_result; } @@ -1485,30 +1460,20 @@ Task TraceCallExecutor::trace_transaction_entries(const Tran ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + const auto trace_result = co_await async_task(workers_.executor(), [&]() -> TraceEntriesResult { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + silkworm::IntraBlockState initial_ibs{*state}; + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - const auto ret_entry_tracer = co_await boost::asio::async_compose)>( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - silkworm::IntraBlockState initial_ibs{*state}; + auto entry_tracer = std::make_shared(initial_ibs); + Tracers tracers{entry_tracer}; - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; + executor.call(transaction_with_block.block_with_hash->block, transaction_with_block.transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + return entry_tracer->result(); + }); - auto entry_tracer = std::make_shared(initial_ibs); - - Tracers tracers{entry_tracer}; - - executor.call(transaction_with_block.block_with_hash->block, transaction_with_block.transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); - - boost::asio::post(current_executor, [entry_tracer, self = std::move(self)]() mutable { - self.complete(entry_tracer); - }); - }); - }, - boost::asio::use_awaitable); - - co_return ret_entry_tracer->result(); + co_return trace_result; } Task TraceCallExecutor::trace_transaction_error(const TransactionWithBlock& transaction_with_block) { @@ -1518,31 +1483,27 @@ Task TraceCallExecutor::trace_transaction_error(const TransactionWi ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + const auto result = co_await async_task(workers_.executor(), [&]() -> std::string { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + silkworm::IntraBlockState initial_ibs{*state}; + + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; + Tracers tracers{}; + + const auto execution_result = executor.call(transaction_with_block.block_with_hash->block, + transaction_with_block.transaction, + tracers, + /*refund=*/true, /*gas_bailout=*/true); + + std::string result = "0x"; + if (execution_result.error_code != evmc_status_code::EVMC_SUCCESS) { + result = "0x" + silkworm::to_hex(execution_result.data); + } + return result; + }); - const auto ret_result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - silkworm::IntraBlockState initial_ibs{*state}; - - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - Tracers tracers{}; - - auto execution_result = executor.call(transaction_with_block.block_with_hash->block, transaction_with_block.transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); - - std::string result = "0x"; - if (execution_result.error_code != evmc_status_code::EVMC_SUCCESS) { - result = "0x" + silkworm::to_hex(execution_result.data); - } - boost::asio::post(current_executor, [result, self = std::move(self)]() mutable { - self.complete(result); - }); - }); - }, - boost::asio::use_awaitable); - - co_return ret_result; + co_return result; } Task TraceCallExecutor::trace_operations(const TransactionWithBlock& transaction_with_block) { @@ -1552,30 +1513,21 @@ Task TraceCallExecutor::trace_operations(const Transactio ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + const auto trace_op_result = co_await async_task(workers_.executor(), [&]() -> TraceOperationsResult { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + silkworm::IntraBlockState initial_ibs{*state}; - const auto ret_entry_tracer = co_await boost::asio::async_compose)>( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - silkworm::IntraBlockState initial_ibs{*state}; + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; + auto entry_tracer = std::make_shared(initial_ibs); + Tracers tracers{entry_tracer}; - auto entry_tracer = std::make_shared(initial_ibs); + executor.call(transaction_with_block.block_with_hash->block, transaction_with_block.transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + return entry_tracer->result(); + }); - Tracers tracers{entry_tracer}; - - executor.call(transaction_with_block.block_with_hash->block, transaction_with_block.transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); - - boost::asio::post(current_executor, [entry_tracer, self = std::move(self)]() mutable { - self.complete(entry_tracer); - }); - }); - }, - boost::asio::use_awaitable); - - co_return ret_entry_tracer->result(); + co_return trace_op_result; } Task TraceCallExecutor::trace_touch_transaction(const silkworm::Block& block, const silkworm::Transaction& txn, const evmc::address& address) { @@ -1585,29 +1537,21 @@ Task TraceCallExecutor::trace_touch_transaction(const silkworm::Block& blo ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + const bool result = co_await async_task(workers_.executor(), [&]() -> bool { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + silkworm::IntraBlockState initial_ibs{*state}; - const auto ret_entry_tracer = co_await boost::asio::async_compose)>( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - silkworm::IntraBlockState initial_ibs{*state}; - - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - - auto tracer = std::make_shared(address, initial_ibs); - Tracers tracers{tracer}; + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - executor.call(block, txn, tracers, /*refund=*/true, /*gas_bailout=*/true); + auto tracer = std::make_shared(address, initial_ibs); + Tracers tracers{tracer}; - boost::asio::post(current_executor, [tracer, self = std::move(self)]() mutable { - self.complete(tracer); - }); - }); - }, - boost::asio::use_awaitable); + executor.call(block, txn, tracers, /*refund=*/true, /*gas_bailout=*/true); + return tracer->found(); + }); - co_return ret_entry_tracer->found(); + co_return result; } Task TraceCallExecutor::trace_filter(const TraceFilter& trace_filter, const ChainStorage& storage, json::Stream& stream) { @@ -1685,60 +1629,54 @@ Task TraceCallExecutor::execute( ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; + const auto trace_call_result = co_await async_task(workers_.executor(), [&]() -> TraceCallResult { + auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); + silkworm::IntraBlockState initial_ibs{*state}; + + Tracers tracers; + StateAddresses state_addresses(initial_ibs); + std::shared_ptr tracer = std::make_shared(state_addresses); + tracers.push_back(tracer); + + auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); + EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; + for (std::size_t idx{0}; idx < transaction.transaction_index; idx++) { + silkworm::Transaction txn{block.transactions[idx]}; + const auto execution_result = executor.call(block, txn, tracers, /*refund=*/true, /*gas_bailout=*/true); + if (execution_result.pre_check_error) { + SILK_ERROR << "execution failed for tx " << idx << " due to pre-check error: " << *execution_result.pre_check_error; + } + executor.reset(); + } - const auto trace_call_result = co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); - silkworm::IntraBlockState initial_ibs{*state}; - - Tracers tracers; - StateAddresses state_addresses(initial_ibs); - std::shared_ptr tracer = std::make_shared(state_addresses); - tracers.push_back(tracer); - - auto curr_state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number); - EVMExecutor executor{*chain_config_ptr, workers_, curr_state}; - for (std::size_t idx{0}; idx < transaction.transaction_index; idx++) { - silkworm::Transaction txn{block.transactions[idx]}; - const auto execution_result = executor.call(block, txn, tracers, /*refund=*/true, /*gas_bailout=*/true); - if (execution_result.pre_check_error) { - SILK_ERROR << "execution failed for tx " << idx << " due to pre-check error: " << *execution_result.pre_check_error; - } - executor.reset(); - } + tracers.clear(); - tracers.clear(); - TraceCallResult result; - TraceCallTraces& traces = result.traces; - if (config.vm_trace) { - traces.vm_trace.emplace(); - tracers.push_back(std::make_shared(traces.vm_trace.value(), index)); - } - if (config.trace) { - tracers.push_back(std::make_shared(traces.trace, initial_ibs)); - } - if (config.state_diff) { - traces.state_diff.emplace(); + TraceCallResult result; + TraceCallTraces& traces = result.traces; + if (config.vm_trace) { + traces.vm_trace.emplace(); + tracers.push_back(std::make_shared(traces.vm_trace.value(), index)); + } + if (config.trace) { + tracers.push_back(std::make_shared(traces.trace, initial_ibs)); + } + if (config.state_diff) { + traces.state_diff.emplace(); - tracers.push_back(std::make_shared(traces.state_diff.value(), state_addresses)); - } - if (index != -1) { - traces.transaction_hash = transaction.hash(); // to have same behaviour as erigon, should be done PR on erigon - } - const auto execution_result = executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); + tracers.push_back(std::make_shared(traces.state_diff.value(), state_addresses)); + } + if (index != -1) { + traces.transaction_hash = transaction.hash(); // to have same behaviour as erigon, should be done PR on erigon + } + const auto execution_result = executor.call(block, transaction, tracers, /*refund=*/true, /*gas_bailout=*/true); - if (execution_result.pre_check_error) { - result.pre_check_error = execution_result.pre_check_error.value(); - } else { - traces.output = "0x" + silkworm::to_hex(execution_result.data); - } - boost::asio::post(current_executor, [result, self = std::move(self)]() mutable { - self.complete(result); - }); - }); - }, - boost::asio::use_awaitable); + if (execution_result.pre_check_error) { + result.pre_check_error = execution_result.pre_check_error.value(); + } else { + traces.output = "0x" + silkworm::to_hex(execution_result.data); + } + return result; + }); co_return trace_call_result; } diff --git a/silkworm/rpc/http/connection.cpp b/silkworm/rpc/http/connection.cpp index 5baa336fa3..c804fbc90a 100644 --- a/silkworm/rpc/http/connection.cpp +++ b/silkworm/rpc/http/connection.cpp @@ -21,9 +21,7 @@ #include #include -#include #include -#include #include #include #include @@ -35,6 +33,7 @@ #include #include +#include #include namespace silkworm::rpc::http { @@ -420,29 +419,13 @@ std::string Connection::get_date_time() { return ss.str(); } -Task Connection::compress( - const std::string& clear_data, - std::string& compressed_data) { - auto this_executor = co_await boost::asio::this_coro::executor; +Task Connection::compress(const std::string& clear_data, std::string& compressed_data) { boost::iostreams::filtering_ostream out; - co_await boost::asio::async_compose( - [&](auto& self) { - boost::asio::post(workers_, [&, self = std::move(self)]() mutable { - std::exception_ptr eptr; - try { - out.push(boost::iostreams::gzip_compressor()); - out.push(boost::iostreams::back_inserter(compressed_data)); - boost::iostreams::copy(boost::make_iterator_range(clear_data), out); - } catch (const std::exception& e) { - SILK_ERROR << "Connection::compress cannot compress exception: " << e.what(); - eptr = std::current_exception(); - } - boost::asio::post(this_executor, [eptr, self = std::move(self)]() mutable { - self.complete(eptr); - }); - }); - }, - boost::asio::use_awaitable); + co_await async_task(workers_.executor(), [&]() -> void { + out.push(boost::iostreams::gzip_compressor()); + out.push(boost::iostreams::back_inserter(compressed_data)); + boost::iostreams::copy(boost::make_iterator_range(clear_data), out); + }); } } // namespace silkworm::rpc::http From b78fb23577c2938e894ba0d98f4cea39ac8a6ce5 Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Tue, 2 Apr 2024 00:51:06 +0200 Subject: [PATCH 05/11] fix shadowing --- silkworm/rpc/core/evm_trace.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/silkworm/rpc/core/evm_trace.cpp b/silkworm/rpc/core/evm_trace.cpp index cdf4a85399..f87de51802 100644 --- a/silkworm/rpc/core/evm_trace.cpp +++ b/silkworm/rpc/core/evm_trace.cpp @@ -1483,7 +1483,7 @@ Task TraceCallExecutor::trace_transaction_error(const TransactionWi ensure(chain_config_ptr.has_value(), "cannot read chain config"); auto current_executor = co_await boost::asio::this_coro::executor; - const auto result = co_await async_task(workers_.executor(), [&]() -> std::string { + const auto trace_error = co_await async_task(workers_.executor(), [&]() -> std::string { auto state = tx_.create_state(current_executor, database_reader_, chain_storage_, block_number - 1); silkworm::IntraBlockState initial_ibs{*state}; @@ -1503,7 +1503,7 @@ Task TraceCallExecutor::trace_transaction_error(const TransactionWi return result; }); - co_return result; + co_return trace_error; } Task TraceCallExecutor::trace_operations(const TransactionWithBlock& transaction_with_block) { From e6c6720a7c82abd625bbcaa1d44b8d83331d15e6 Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Tue, 2 Apr 2024 09:58:09 +0200 Subject: [PATCH 06/11] comment out test case --- silkworm/rpc/common/async_task_test.cpp | 1 - silkworm/rpc/core/evm_debug_test.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/silkworm/rpc/common/async_task_test.cpp b/silkworm/rpc/common/async_task_test.cpp index 50b8e1b25a..a28e85470c 100644 --- a/silkworm/rpc/common/async_task_test.cpp +++ b/silkworm/rpc/common/async_task_test.cpp @@ -50,7 +50,6 @@ TEST_CASE_METHOD(AsyncTaskTest, "async_task: factorial", "[rpc][common][async_ta for (std::size_t i{0}; i < kTestData.size(); ++i) { const auto [n, r] = kTestData[i]; SECTION("factorial " + std::to_string(n)) { - std::cout << n << "! = " << spawn_and_wait(async_factorial(workers.get_executor(), n)) << "\n"; CHECK(spawn_and_wait(async_factorial(workers.get_executor(), n)) == r); CHECK(spawn_and_wait(async_task(workers.get_executor(), recursive_factorial, n)) == r); } diff --git a/silkworm/rpc/core/evm_debug_test.cpp b/silkworm/rpc/core/evm_debug_test.cpp index 51f513c750..addc2b7ad5 100644 --- a/silkworm/rpc/core/evm_debug_test.cpp +++ b/silkworm/rpc/core/evm_debug_test.cpp @@ -75,7 +75,7 @@ class TestDebugExecutor : DebugExecutor { }; #ifndef SILKWORM_SANITIZE -TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute precompiled") { +/*TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute precompiled") { static Bytes kAccountHistoryKey1{*silkworm::from_hex("0a6bb546b9208cfab9e8fa2b9b2c042b18df703000000000009db707")}; static Bytes kAccountHistoryKey2{*silkworm::from_hex("000000000000000000000000000000000000000900000000009db707")}; static Bytes kAccountHistoryKey3{*silkworm::from_hex("000000000000000000000000000000000000000000000000009db707")}; @@ -163,7 +163,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute precompiled") { "structLogs":[] })"_json); } -} +}*/ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { static Bytes kAccountHistoryKey1{*silkworm::from_hex("e0a2bd4258d2768837baa26a28fe71dc079f84c700000000005279a8")}; From 50555cf4ae203bf45763ff3b86d4aff09aca835b Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Sat, 6 Apr 2024 01:41:44 +0200 Subject: [PATCH 07/11] fix captured function lifetime in lambda sent to task runner --- silkworm/rpc/common/async_task.hpp | 4 ++-- silkworm/rpc/core/evm_debug_test.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/silkworm/rpc/common/async_task.hpp b/silkworm/rpc/common/async_task.hpp index af593b5cf4..f3de0154ca 100644 --- a/silkworm/rpc/common/async_task.hpp +++ b/silkworm/rpc/common/async_task.hpp @@ -51,8 +51,8 @@ template Task> async_task(Executor runner, F&& fn, Args&&... args) { auto this_executor = co_await ThisTask::executor; co_return co_await boost::asio::async_compose>( - [&this_executor, &runner, fn = std::forward(fn), ... args = std::forward(args)](auto& self) { - boost::asio::post(runner, [&, self = std::move(self)]() mutable { + [&this_executor, &runner, fn = std::forward(fn), ... args = std::forward(args)](auto& self) mutable { + boost::asio::post(runner, [&, fn = std::forward(fn), ... args = std::forward(args), self = std::move(self)]() mutable { try { if constexpr (std::is_void_v>) { std::invoke(fn, args...); diff --git a/silkworm/rpc/core/evm_debug_test.cpp b/silkworm/rpc/core/evm_debug_test.cpp index addc2b7ad5..51f513c750 100644 --- a/silkworm/rpc/core/evm_debug_test.cpp +++ b/silkworm/rpc/core/evm_debug_test.cpp @@ -75,7 +75,7 @@ class TestDebugExecutor : DebugExecutor { }; #ifndef SILKWORM_SANITIZE -/*TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute precompiled") { +TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute precompiled") { static Bytes kAccountHistoryKey1{*silkworm::from_hex("0a6bb546b9208cfab9e8fa2b9b2c042b18df703000000000009db707")}; static Bytes kAccountHistoryKey2{*silkworm::from_hex("000000000000000000000000000000000000000900000000009db707")}; static Bytes kAccountHistoryKey3{*silkworm::from_hex("000000000000000000000000000000000000000000000000009db707")}; @@ -163,7 +163,7 @@ class TestDebugExecutor : DebugExecutor { "structLogs":[] })"_json); } -}*/ +} TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { static Bytes kAccountHistoryKey1{*silkworm::from_hex("e0a2bd4258d2768837baa26a28fe71dc079f84c700000000005279a8")}; From eaa477bcbf8e58cbb070c66cfe86e6e368a5f22c Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Sat, 6 Apr 2024 21:32:13 +0200 Subject: [PATCH 08/11] run perf workflow on PR avoid clang tidy warning in test --- .github/workflows/rpc-performance-tests.yml | 7 +++++++ silkworm/rpc/core/evm_debug_test.cpp | 22 ++++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/rpc-performance-tests.yml b/.github/workflows/rpc-performance-tests.yml index 42b819ff72..49aab1af13 100644 --- a/.github/workflows/rpc-performance-tests.yml +++ b/.github/workflows/rpc-performance-tests.yml @@ -1,6 +1,13 @@ name: QA - RPC Performance Tests on: + pull_request: + branches: + - master + types: + - opened + - ready_for_review + - synchronize schedule: - cron: '0 0 * * *' # Run every day at 00:00 AM UTC diff --git a/silkworm/rpc/core/evm_debug_test.cpp b/silkworm/rpc/core/evm_debug_test.cpp index 51f513c750..3e0e20a429 100644 --- a/silkworm/rpc/core/evm_debug_test.cpp +++ b/silkworm/rpc/core/evm_debug_test.cpp @@ -69,7 +69,7 @@ class TestDebugExecutor : DebugExecutor { TestDebugExecutor(const TestDebugExecutor&) = delete; TestDebugExecutor& operator=(const TestDebugExecutor&) = delete; - Task execute(json::Stream& stream, const ChainStorage& storage, const silkworm::Block& block, const Call& call) { + Task exec(json::Stream& stream, const ChainStorage& storage, const silkworm::Block& block, const Call& call) { return DebugExecutor::execute(stream, storage, block, call); } }; @@ -150,7 +150,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute precompiled") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -305,7 +305,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -368,7 +368,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -479,7 +479,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -581,7 +581,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -688,7 +688,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -796,7 +796,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -891,7 +891,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 1") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -1102,7 +1102,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call 2") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); @@ -1263,7 +1263,7 @@ TEST_CASE_METHOD(DebugExecutorTest, "DebugExecutor::execute call with error") { const RemoteChainStorage storage{db_reader, backend.get()}; stream.open_object(); - spawn_and_wait(executor.execute(stream, storage, block, call)); + spawn_and_wait(executor.exec(stream, storage, block, call)); stream.close_object(); spawn_and_wait(stream.close()); From b64d23b9ebe9b1c21d11629629426232e61b0f5d Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Mon, 8 Apr 2024 15:25:38 +0200 Subject: [PATCH 09/11] add benchmark tests --- silkworm/rpc/common/async_task_benchmark.cpp | 92 ++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 silkworm/rpc/common/async_task_benchmark.cpp diff --git a/silkworm/rpc/common/async_task_benchmark.cpp b/silkworm/rpc/common/async_task_benchmark.cpp new file mode 100644 index 0000000000..299dd9117f --- /dev/null +++ b/silkworm/rpc/common/async_task_benchmark.cpp @@ -0,0 +1,92 @@ +/* + Copyright 2024 The Silkworm Authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +#include "async_task.hpp" + +#include +#include + +#include + +namespace silkworm::rpc { + +std::size_t recursive_factorial(std::size_t n) { + return n == 0 ? 1 : n * recursive_factorial(n - 1); +} + +struct AsyncTaskBenchTest : test::ContextTestBase { +}; + +template +Task async_compose_factorial(const Executor runner, const std::size_t number) { + const auto this_executor = co_await ThisTask::executor; + co_return co_await boost::asio::async_compose( + [&](auto& self) { + boost::asio::post(runner, [&, self = std::move(self)]() mutable { + try { + const auto result = recursive_factorial(number); + boost::asio::post(this_executor, [result, self = std::move(self)]() mutable { + self.complete({}, result); + }); + } catch (...) { + std::exception_ptr eptr = std::current_exception(); + boost::asio::post(this_executor, [eptr, self = std::move(self)]() mutable { + self.complete(eptr, {}); + }); + } + }); + }, + boost::asio::use_awaitable); +} + +static void benchmark_async_compose(benchmark::State& state) { + const auto n = static_cast(state.range(0)); + + boost::asio::thread_pool workers{}; + AsyncTaskBenchTest test; + for ([[maybe_unused]] auto _ : state) { + const auto result = test.spawn_and_wait(async_compose_factorial(workers.get_executor(), n)); + benchmark::DoNotOptimize(result); + } +} + +BENCHMARK(benchmark_async_compose)->Arg(10); +BENCHMARK(benchmark_async_compose)->Arg(100); +BENCHMARK(benchmark_async_compose)->Arg(1'000); +BENCHMARK(benchmark_async_compose)->Arg(10'000); + +template +Task async_task_factorial(Executor runner, std::size_t number) { + co_return co_await async_task(runner, recursive_factorial, number); +} + +static void benchmark_async_task(benchmark::State& state) { + const auto n = static_cast(state.range(0)); + + boost::asio::thread_pool workers{}; + AsyncTaskBenchTest test; + for ([[maybe_unused]] auto _ : state) { + const auto result = test.spawn_and_wait(async_task_factorial(workers.get_executor(), n)); + benchmark::DoNotOptimize(result); + } +} + +BENCHMARK(benchmark_async_task)->Arg(10); +BENCHMARK(benchmark_async_task)->Arg(100); +BENCHMARK(benchmark_async_task)->Arg(1'000); +BENCHMARK(benchmark_async_task)->Arg(10'000); + +} // namespace silkworm::rpc From 99038708e1635de8a65821ca896b9cbf2890a697 Mon Sep 17 00:00:00 2001 From: GitHub Date: Mon, 8 Apr 2024 13:26:03 +0000 Subject: [PATCH 10/11] make fmt --- silkworm/rpc/common/async_task_benchmark.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/silkworm/rpc/common/async_task_benchmark.cpp b/silkworm/rpc/common/async_task_benchmark.cpp index 299dd9117f..bc38d1c7d9 100644 --- a/silkworm/rpc/common/async_task_benchmark.cpp +++ b/silkworm/rpc/common/async_task_benchmark.cpp @@ -14,13 +14,13 @@ limitations under the License. */ -#include "async_task.hpp" - -#include #include +#include #include +#include "async_task.hpp" + namespace silkworm::rpc { std::size_t recursive_factorial(std::size_t n) { From d61c46b3240fbf61faf170d94e611ce4b6d0c217 Mon Sep 17 00:00:00 2001 From: canepat <16927169+canepat@users.noreply.github.com> Date: Mon, 8 Apr 2024 17:50:32 +0200 Subject: [PATCH 11/11] user trigger for perf workflow --- .github/workflows/rpc-performance-tests.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/rpc-performance-tests.yml b/.github/workflows/rpc-performance-tests.yml index 49aab1af13..3ad792755b 100644 --- a/.github/workflows/rpc-performance-tests.yml +++ b/.github/workflows/rpc-performance-tests.yml @@ -1,13 +1,7 @@ name: QA - RPC Performance Tests on: - pull_request: - branches: - - master - types: - - opened - - ready_for_review - - synchronize + workflow_dispatch: schedule: - cron: '0 0 * * *' # Run every day at 00:00 AM UTC