diff --git a/silkworm/silkrpc/commands/debug_api_test.cpp b/silkworm/silkrpc/commands/debug_api_test.cpp index 3bb67cec..f0cf3fd7 100644 --- a/silkworm/silkrpc/commands/debug_api_test.cpp +++ b/silkworm/silkrpc/commands/debug_api_test.cpp @@ -99,6 +99,32 @@ class DummyCursor : public ethdb::CursorDupSort { co_return kv; } + awaitable prev() override { + KeyValue out; + + if (itr_ != table_.begin() && table_.begin() != table_.end()) { + --itr_; + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + + awaitable last() override { + KeyValue out; + + if (table_.begin() != table_.end()) { + itr_ = --table_.end(); + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + awaitable next() override { KeyValue out; diff --git a/silkworm/silkrpc/commands/eth_api.cpp b/silkworm/silkrpc/commands/eth_api.cpp index df15cda3..93df8271 100644 --- a/silkworm/silkrpc/commands/eth_api.cpp +++ b/silkworm/silkrpc/commands/eth_api.cpp @@ -62,6 +62,8 @@ #include #include +#include + namespace silkworm::rpc::commands { awaitable> get_block_numbers(const Filter& filter, const core::rawdb::DatabaseReader& reader) { @@ -1137,12 +1139,14 @@ awaitable EthereumRpcApi::handle_eth_call(const nlohmann::json& request, s silkworm::Transaction txn{call.to_transaction()}; if(!txn.from.has_value()) txn.from = evmc::address{0}; + const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block.header); + const core::rawdb::DatabaseReader& db_reader = is_latest_block ? static_cast(cached_database) : static_cast(tx_database); const auto execution_result = co_await EVMExecutor::call( *chain_config_ptr, workers_, block_with_hash->block, txn, [&](auto& io_executor, auto block_num) { return tx->create_state(io_executor, db_reader, block_num); - }); + }, {}, true, false, eos_evm_version, gas_params); if (execution_result.success()) { make_glaze_json_content(reply, request["id"], execution_result.data); @@ -1284,6 +1288,7 @@ awaitable EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js const auto block_with_hash = co_await core::read_block_by_number_or_hash(*block_cache_, tx_database, block_number_or_hash); const auto chain_id = co_await core::rawdb::read_chain_id(tx_database); const auto chain_config_ptr = lookup_chain_config(chain_id); + const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block.header); const bool is_latest_block = co_await core::get_latest_executed_block_number(tx_database) == block_with_hash->block.header.number; const core::rawdb::DatabaseReader& db_reader = @@ -1325,7 +1330,7 @@ awaitable EthereumRpcApi::handle_eth_create_access_list(const nlohmann::js *chain_config_ptr, workers_, block_with_hash->block, txn, [&](auto& io_executor, auto block_num) { return tx->create_state(io_executor, db_reader, block_num); }, - std::move(tracers), /* refund */ true, /* gasBailout */ false); + std::move(tracers), /* refund */ true, /* gasBailout */ false, eos_evm_version, gas_params); if (execution_result.pre_check_error) { reply = make_json_error(request["id"], -32000, execution_result.pre_check_error.value()); @@ -1383,12 +1388,13 @@ awaitable EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req auto tx = co_await database_->begin(); try { - ethdb::kv::CachedDatabase tx_database{block_number_or_hash, *tx, *state_cache_}; + ethdb::TransactionDatabase tx_database{*tx}; ethdb::kv::CachedDatabase cached_database{block_number_or_hash, *tx, *state_cache_}; const auto block_with_hash = co_await core::read_block_by_number_or_hash(*block_cache_, tx_database, block_number_or_hash); const auto chain_id = co_await core::rawdb::read_chain_id(tx_database); const auto chain_config_ptr = lookup_chain_config(chain_id); + const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database, chain_config_ptr, block_with_hash->block.header); const bool is_latest_block = co_await core::get_latest_executed_block_number(tx_database) == block_with_hash->block.header.number; const core::rawdb::DatabaseReader& db_reader = @@ -1414,7 +1420,7 @@ awaitable EthereumRpcApi::handle_eth_call_bundle(const nlohmann::json& req const auto execution_result = co_await EVMExecutor::call( *chain_config_ptr, workers_, block_with_hash->block, tx_with_block->transaction, [&](auto& io_executor, auto block_num) { return tx->create_state(io_executor, db_reader, block_num); - }); + }, {}, true, false, eos_evm_version, gas_params); if (execution_result.pre_check_error) { reply = make_json_error(request["id"], -32000, execution_result.pre_check_error.value()); error = true; diff --git a/silkworm/silkrpc/commands/eth_api.hpp b/silkworm/silkrpc/commands/eth_api.hpp index 80e3853b..34fac1dc 100644 --- a/silkworm/silkrpc/commands/eth_api.hpp +++ b/silkworm/silkrpc/commands/eth_api.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include diff --git a/silkworm/silkrpc/core/account_dumper_test.cpp b/silkworm/silkrpc/core/account_dumper_test.cpp index 0e2917d4..cf98d2fb 100644 --- a/silkworm/silkrpc/core/account_dumper_test.cpp +++ b/silkworm/silkrpc/core/account_dumper_test.cpp @@ -93,6 +93,32 @@ class DummyCursor : public ethdb::CursorDupSort { co_return kv; } + boost::asio::awaitable prev() override { + KeyValue out; + + if (itr_ != table_.begin() && table_.begin() != table_.end()) { + --itr_; + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + + boost::asio::awaitable last() override { + KeyValue out; + + if (table_.begin() != table_.end()) { + itr_ = --table_.end(); + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + boost::asio::awaitable next() override { KeyValue out; diff --git a/silkworm/silkrpc/core/account_walker_test.cpp b/silkworm/silkrpc/core/account_walker_test.cpp index c31bfdc9..edf03e57 100644 --- a/silkworm/silkrpc/core/account_walker_test.cpp +++ b/silkworm/silkrpc/core/account_walker_test.cpp @@ -91,6 +91,32 @@ class DummyCursor : public ethdb::CursorDupSort { co_return kv; } + boost::asio::awaitable prev() override { + KeyValue out; + + if (itr_ != table_.begin() && table_.begin() != table_.end()) { + --itr_; + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + + boost::asio::awaitable last() override { + KeyValue out; + + if (table_.begin() != table_.end()) { + itr_ = --table_.end(); + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + boost::asio::awaitable next() override { KeyValue out; diff --git a/silkworm/silkrpc/core/estimate_gas_oracle.cpp b/silkworm/silkrpc/core/estimate_gas_oracle.cpp index f987cb77..18f1d15c 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle.cpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle.cpp @@ -15,6 +15,7 @@ */ #include "estimate_gas_oracle.hpp" +#include #include @@ -77,6 +78,8 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call SILK_DEBUG << "hi: " << hi << ", lo: " << lo << ", cap: " << cap; + const auto [eos_evm_version, gas_params] = co_await load_gas_parameters(tx_database_, &config_, block.header); + auto this_executor = co_await boost::asio::this_coro::executor; auto exec_result = co_await boost::asio::async_compose( [&](auto&& self) { @@ -91,7 +94,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call auto mid = (hi + lo) / 2; transaction.gas_limit = mid; - result = try_execution(executor, block, transaction); + result = try_execution(executor, block, transaction, eos_evm_version, gas_params); if(result.pre_check_error && !result.pre_check_error.value().starts_with("intrinsic gas too low")) { boost::asio::post(this_executor, [result, self = std::move(self)]() mutable { @@ -109,7 +112,7 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call if (hi == cap) { transaction.gas_limit = hi; - result = try_execution(executor, block, transaction); + result = try_execution(executor, block, transaction, eos_evm_version, gas_params); SILK_DEBUG << "HI == cap tested again with " << (result.success() ? "succeed" : "failed"); } else { result.pre_check_error = std::nullopt; @@ -131,9 +134,9 @@ boost::asio::awaitable EstimateGasOracle::estimate_gas(const Call co_return hi; } -ExecutionResult EstimateGasOracle::try_execution(EVMExecutor& executor, const silkworm::Block& block, const silkworm::Transaction& transaction) { +ExecutionResult EstimateGasOracle::try_execution(EVMExecutor& executor, const silkworm::Block& block, const silkworm::Transaction& transaction, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params) { executor.reset_all(); - return executor.call(block, transaction); + return executor.call(block, transaction, {}, true, false, eos_evm_version, gas_params); } void EstimateGasOracle::throw_exception(ExecutionResult& result, uint64_t cap) { diff --git a/silkworm/silkrpc/core/estimate_gas_oracle.hpp b/silkworm/silkrpc/core/estimate_gas_oracle.hpp index a61860c5..23c29c1e 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle.hpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle.hpp @@ -89,7 +89,7 @@ class EstimateGasOracle { boost::asio::awaitable estimate_gas(const Call& call, const silkworm::Block& latest_block); protected: - virtual ExecutionResult try_execution(EVMExecutor& executor, const silkworm::Block& _block, const silkworm::Transaction& transaction); + virtual ExecutionResult try_execution(EVMExecutor& executor, const silkworm::Block& _block, const silkworm::Transaction& transaction, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params); private: void throw_exception(ExecutionResult& result, uint64_t cap); diff --git a/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp b/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp index 6497cd52..7beaf175 100644 --- a/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp +++ b/silkworm/silkrpc/core/estimate_gas_oracle_test.cpp @@ -103,7 +103,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, always fails but success in last step") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -129,7 +129,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, always succeeds") { 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)); + 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(); CHECK(estimate_gas == kTxGas); @@ -138,7 +138,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, alternatively fails and succeeds") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(14) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_ok)) @@ -163,7 +163,7 @@ TEST_CASE("estimate gas") { SECTION("Call empty, alternatively succeeds and fails") { ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(14) .WillOnce(Return(expect_result_ok)) .WillOnce(Return(expect_result_fail)) @@ -189,7 +189,7 @@ TEST_CASE("estimate gas") { call.gas = kTxGas * 4; ExecutionResult expect_result_ok{.error_code = evmc_status_code::EVMC_SUCCESS}; ExecutionResult expect_result_fail{.pre_check_error = "intrinsic gas too low"}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(17) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -217,7 +217,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}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(15) .WillRepeatedly(Return(expect_result_ok)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); @@ -232,7 +232,7 @@ TEST_CASE("estimate gas") { call.gas = kTxGas * 2; call.gas_price = intx::uint256{10'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -262,7 +262,7 @@ TEST_CASE("estimate gas") { call.gas = kTxGas * 2; call.gas_price = intx::uint256{40'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(13) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -290,7 +290,7 @@ TEST_CASE("estimate gas") { call.gas_price = intx::uint256{10'000}; call.value = intx::uint256{500'000'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(16) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -321,7 +321,7 @@ TEST_CASE("estimate gas") { call.gas_price = intx::uint256{20'000}; call.value = intx::uint256{500'000'000}; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(13) .WillOnce(Return(expect_result_fail)) .WillOnce(Return(expect_result_fail)) @@ -345,7 +345,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}; call.gas = kGasCap * 2; - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)).Times(24).WillRepeatedly(Return(expect_result_ok)); + 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); const intx::uint256& estimate_gas = result.get(); @@ -356,7 +356,7 @@ TEST_CASE("estimate gas") { 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)); + 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(); @@ -368,7 +368,7 @@ TEST_CASE("estimate gas") { call.value = intx::uint256{2'000'000'000}; try { - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)).Times(16).WillRepeatedly(Return(expect_result_fail)); + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)).Times(16).WillRepeatedly(Return(expect_result_fail)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); result.get(); CHECK(false); @@ -389,7 +389,7 @@ TEST_CASE("estimate gas") { call.value = intx::uint256{500'000'000}; try { - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(1) .WillOnce(Return(expect_result_fail_pre_check)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); @@ -413,7 +413,7 @@ TEST_CASE("estimate gas") { call.value = intx::uint256{500'000'000}; try { - EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _)) + EXPECT_CALL(estimate_gas_oracle, try_execution(_, _, _, _, _)) .Times(1) .WillOnce(Return(expect_result_fail_pre_check)); auto result = boost::asio::co_spawn(pool, estimate_gas_oracle.estimate_gas(call, block), boost::asio::use_future); diff --git a/silkworm/silkrpc/core/evm_executor.cpp b/silkworm/silkrpc/core/evm_executor.cpp index b6fd2667..c609466e 100644 --- a/silkworm/silkrpc/core/evm_executor.cpp +++ b/silkworm/silkrpc/core/evm_executor.cpp @@ -222,7 +222,9 @@ ExecutionResult EVMExecutor::call( const silkworm::Transaction& txn, Tracers tracers, bool refund, - bool gas_bailout) { + bool gas_bailout, + uint64_t eos_evm_version, + const evmone::gas_parameters& gas_params) { SILK_DEBUG << "EVMExecutor::call: " << block.header.number << " gasLimit: " << txn.gas_limit << " refund: " << refund << " gasBailout: " << gas_bailout; SILK_DEBUG << "EVMExecutor::call: transaction: " << &txn; @@ -248,7 +250,7 @@ ExecutionResult EVMExecutor::call( const evmc_revision rev{evm.revision()}; const intx::uint256 base_fee_per_gas{evm.block().header.base_fee_per_gas.value_or(0)}; - const intx::uint128 g0{protocol::intrinsic_gas(txn, rev)}; + const intx::uint128 g0{protocol::intrinsic_gas(txn, rev, eos_evm_version, gas_params)}; SILKWORM_ASSERT(g0 <= UINT64_MAX); // true due to the precondition (transaction must be valid) const auto error = pre_check(evm, txn, base_fee_per_gas, g0); @@ -334,14 +336,16 @@ awaitable EVMExecutor::call(const silkworm::ChainConfig& config StateFactory state_factory, Tracers tracers, bool refund, - bool gas_bailout) { + bool gas_bailout, + uint64_t eos_evm_version, + const evmone::gas_parameters& gas_params) { 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); EVMExecutor executor{config, workers, state}; - auto exec_result = executor.call(block, txn, tracers, refund, gas_bailout); + auto exec_result = executor.call(block, txn, tracers, refund, gas_bailout, eos_evm_version, gas_params); boost::asio::post(this_executor, [exec_result, self = std::move(self)]() mutable { self.complete(exec_result); }); diff --git a/silkworm/silkrpc/core/evm_executor.hpp b/silkworm/silkrpc/core/evm_executor.hpp index 82878ba4..6a088fba 100644 --- a/silkworm/silkrpc/core/evm_executor.hpp +++ b/silkworm/silkrpc/core/evm_executor.hpp @@ -86,7 +86,9 @@ class EVMExecutor { StateFactory state_factory, Tracers tracers = {}, bool refund = true, - bool gas_bailout = false); + bool gas_bailout = false, + uint64_t eos_evm_version = 0, + const evmone::gas_parameters& gas_params = {}); static std::string get_error_message(int64_t error_code, const Bytes& error_data, bool full_error = true); EVMExecutor(const silkworm::ChainConfig& config, boost::asio::thread_pool& workers, std::shared_ptr& state) @@ -105,7 +107,7 @@ class EVMExecutor { EVMExecutor(const EVMExecutor&) = delete; EVMExecutor& operator=(const EVMExecutor&) = delete; - ExecutionResult call(const silkworm::Block& block, const silkworm::Transaction& txn, Tracers tracers = {}, bool refund = true, bool gas_bailout = false); + ExecutionResult call(const silkworm::Block& block, const silkworm::Transaction& txn, Tracers tracers = {}, bool refund = true, bool gas_bailout = false, uint64_t eos_evm_version = 0, const evmone::gas_parameters& gas_params={}); void reset(); void reset_all(); diff --git a/silkworm/silkrpc/core/evm_executor_test.cpp b/silkworm/silkrpc/core/evm_executor_test.cpp index daaaeaa0..179aa073 100644 --- a/silkworm/silkrpc/core/evm_executor_test.cpp +++ b/silkworm/silkrpc/core/evm_executor_test.cpp @@ -50,6 +50,9 @@ TEST_CASE("EVMExecutor") { [[nodiscard]] awaitable get_one(const std::string& /*table*/, silkworm::ByteView /*key*/) const override { co_return silkworm::Bytes{}; } + [[nodiscard]] awaitable get_exact_or_previous(const std::string& /*table*/, silkworm::ByteView /*key*/) const override { + co_return silkworm::Bytes{}; + } [[nodiscard]] awaitable> get_both_range(const std::string& /*table*/, silkworm::ByteView /*key*/, silkworm::ByteView /*subkey*/) const override { co_return silkworm::Bytes{}; } diff --git a/silkworm/silkrpc/core/rawdb/accessors.hpp b/silkworm/silkrpc/core/rawdb/accessors.hpp index e3ec1307..4604da44 100644 --- a/silkworm/silkrpc/core/rawdb/accessors.hpp +++ b/silkworm/silkrpc/core/rawdb/accessors.hpp @@ -38,6 +38,8 @@ class DatabaseReader { [[nodiscard]] virtual awaitable get(const std::string& table, silkworm::ByteView key) const = 0; + [[nodiscard]] virtual awaitable get_exact_or_previous(const std::string& table, ByteView key) const = 0; + [[nodiscard]] virtual awaitable get_one(const std::string& table, silkworm::ByteView key) const = 0; [[nodiscard]] virtual awaitable> get_both_range(const std::string& table, silkworm::ByteView key, silkworm::ByteView subkey) const = 0; diff --git a/silkworm/silkrpc/core/rawdb/chain.cpp b/silkworm/silkrpc/core/rawdb/chain.cpp index aac40420..ce33e169 100644 --- a/silkworm/silkrpc/core/rawdb/chain.cpp +++ b/silkworm/silkrpc/core/rawdb/chain.cpp @@ -435,4 +435,13 @@ boost::asio::awaitable read_cumulative_gas_used(const core::rawdb co_return cumulative_gas_index; } +boost::asio::awaitable> read_consensus_parameters(const core::rawdb::DatabaseReader& reader, uint64_t block_number) { + const auto block_key = silkworm::db::block_key(block_number); + const auto value = co_await reader.get_exact_or_previous(db::table::kConsensusParametersName, block_key); + if (value.empty()) { + co_return std::nullopt; + } + co_return eosevm::ConsensusParameters::decode(value); +} + } // namespace silkworm::rpc::core::rawdb diff --git a/silkworm/silkrpc/core/rawdb/chain.hpp b/silkworm/silkrpc/core/rawdb/chain.hpp index d5e4ac02..9db3654e 100644 --- a/silkworm/silkrpc/core/rawdb/chain.hpp +++ b/silkworm/silkrpc/core/rawdb/chain.hpp @@ -89,4 +89,6 @@ boost::asio::awaitable read_total_burnt(const core::rawdb::Databa boost::asio::awaitable read_cumulative_gas_used(const core::rawdb::DatabaseReader& reader, uint64_t block_number); +boost::asio::awaitable> read_consensus_parameters(const core::rawdb::DatabaseReader& reader, uint64_t block_number); + } // namespace silkworm::rpc::core::rawdb diff --git a/silkworm/silkrpc/core/remote_state_test.cpp b/silkworm/silkrpc/core/remote_state_test.cpp index 11c3a082..eef242fb 100644 --- a/silkworm/silkrpc/core/remote_state_test.cpp +++ b/silkworm/silkrpc/core/remote_state_test.cpp @@ -49,6 +49,9 @@ TEST_CASE("async remote buffer", "[silkrpc][core][remote_buffer]") { [[nodiscard]] boost::asio::awaitable get_one(const std::string& /*table*/, silkworm::ByteView /*key*/) const override { co_return value_; } + [[nodiscard]] boost::asio::awaitable get_exact_or_previous(const std::string& /*table*/, silkworm::ByteView /*key*/) const override { + co_return value_; + } [[nodiscard]] boost::asio::awaitable> get_both_range(const std::string& /*table*/, silkworm::ByteView /*key*/, silkworm::ByteView /*subkey*/) const override { co_return silkworm::Bytes{}; } diff --git a/silkworm/silkrpc/core/storage_walker_test.cpp b/silkworm/silkrpc/core/storage_walker_test.cpp index daf5b4e3..ff09aa56 100644 --- a/silkworm/silkrpc/core/storage_walker_test.cpp +++ b/silkworm/silkrpc/core/storage_walker_test.cpp @@ -90,6 +90,32 @@ class DummyCursor : public ethdb::CursorDupSort { co_return kv; } + boost::asio::awaitable prev() override { + KeyValue out; + + if (itr_ != table_.begin() && table_.begin() != table_.end()) { + --itr_; + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + + boost::asio::awaitable last() override { + KeyValue out; + + if (table_.begin() != table_.end()) { + itr_ = --table_.end(); + auto key{*silkworm::from_hex(itr_.key())}; + auto value{*silkworm::from_hex(itr_.value().get())}; + out = KeyValue{key, value}; + } + + co_return out; + } + boost::asio::awaitable next() override { KeyValue out; diff --git a/silkworm/silkrpc/ethdb/cursor.hpp b/silkworm/silkrpc/ethdb/cursor.hpp index d20efa39..76fb7d20 100644 --- a/silkworm/silkrpc/ethdb/cursor.hpp +++ b/silkworm/silkrpc/ethdb/cursor.hpp @@ -44,6 +44,10 @@ class Cursor { virtual boost::asio::awaitable seek_exact(silkworm::ByteView key) = 0; + virtual boost::asio::awaitable prev() = 0; + + virtual boost::asio::awaitable last() = 0; + virtual boost::asio::awaitable next() = 0; virtual boost::asio::awaitable close_cursor() = 0; diff --git a/silkworm/silkrpc/ethdb/file/local_cursor.cpp b/silkworm/silkrpc/ethdb/file/local_cursor.cpp index 9db341f0..c1e53ea7 100644 --- a/silkworm/silkrpc/ethdb/file/local_cursor.cpp +++ b/silkworm/silkrpc/ethdb/file/local_cursor.cpp @@ -67,6 +67,38 @@ boost::asio::awaitable LocalCursor::seek_exact(ByteView key) { co_return KeyValue{}; } +boost::asio::awaitable LocalCursor::prev() { + SILK_DEBUG << "LocalCursor::prev: " << cursor_id_; + + const auto result = db_cursor_.to_previous(/*throw_notfound=*/false); + SILK_DEBUG << "LocalCursor::prev result: " << db::detail::dump_mdbx_result(result); + + if (result) { + SILK_DEBUG << "LocalCursor::prev: " + << " key: " << byte_view_of_string(result.key.as_string()) << " value: " << byte_view_of_string(result.value.as_string()); + co_return KeyValue{bytes_of_string(result.key.as_string()), bytes_of_string(result.value.as_string())}; + } else { + SILK_ERROR << "LocalCursor::prev !result"; + } + co_return KeyValue{}; +} + +boost::asio::awaitable LocalCursor::last() { + SILK_DEBUG << "LocalCursor::last: " << cursor_id_; + + const auto result = db_cursor_.to_last(/*throw_notfound=*/false); + SILK_DEBUG << "LocalCursor::last result: " << db::detail::dump_mdbx_result(result); + + if (result) { + SILK_DEBUG << "LocalCursor::last: " + << " key: " << byte_view_of_string(result.key.as_string()) << " value: " << byte_view_of_string(result.value.as_string()); + co_return KeyValue{bytes_of_string(result.key.as_string()), bytes_of_string(result.value.as_string())}; + } else { + SILK_ERROR << "LocalCursor::last !result"; + } + co_return KeyValue{}; +} + boost::asio::awaitable LocalCursor::next() { SILK_DEBUG << "LocalCursor::next: " << cursor_id_; diff --git a/silkworm/silkrpc/ethdb/file/local_cursor.hpp b/silkworm/silkrpc/ethdb/file/local_cursor.hpp index 4ceb79a8..ac0291dc 100644 --- a/silkworm/silkrpc/ethdb/file/local_cursor.hpp +++ b/silkworm/silkrpc/ethdb/file/local_cursor.hpp @@ -48,6 +48,10 @@ class LocalCursor : public CursorDupSort { boost::asio::awaitable seek_exact(silkworm::ByteView key) override; + boost::asio::awaitable prev() override; + + boost::asio::awaitable last() override; + boost::asio::awaitable next() override; boost::asio::awaitable next_dup() override; diff --git a/silkworm/silkrpc/ethdb/kv/cached_database.cpp b/silkworm/silkrpc/ethdb/kv/cached_database.cpp index acfc35cb..517e209f 100644 --- a/silkworm/silkrpc/ethdb/kv/cached_database.cpp +++ b/silkworm/silkrpc/ethdb/kv/cached_database.cpp @@ -30,6 +30,10 @@ boost::asio::awaitable CachedDatabase::get(const std::string& table, s co_return co_await txn_database_.get(table, key); } +boost::asio::awaitable CachedDatabase::get_exact_or_previous(const std::string& table, ByteView key) const { + co_return co_await txn_database_.get_exact_or_previous(table, key); +} + boost::asio::awaitable CachedDatabase::get_one(const std::string& table, silkworm::ByteView key) const { // Just PlainState and Code tables are present in state cache if (table == db::table::kPlainStateName) { diff --git a/silkworm/silkrpc/ethdb/kv/cached_database.hpp b/silkworm/silkrpc/ethdb/kv/cached_database.hpp index 11ddf41b..0e1330e5 100644 --- a/silkworm/silkrpc/ethdb/kv/cached_database.hpp +++ b/silkworm/silkrpc/ethdb/kv/cached_database.hpp @@ -37,6 +37,8 @@ class CachedDatabase : public core::rawdb::DatabaseReader { boost::asio::awaitable get(const std::string& table, silkworm::ByteView key) const override; + boost::asio::awaitable get_exact_or_previous(const std::string& table, ByteView key) const override; + boost::asio::awaitable get_one(const std::string& table, silkworm::ByteView key) const override; boost::asio::awaitable> get_both_range(const std::string& table, silkworm::ByteView key, diff --git a/silkworm/silkrpc/ethdb/kv/remote_cursor.cpp b/silkworm/silkrpc/ethdb/kv/remote_cursor.cpp index 6c7739d2..f5f968da 100644 --- a/silkworm/silkrpc/ethdb/kv/remote_cursor.cpp +++ b/silkworm/silkrpc/ethdb/kv/remote_cursor.cpp @@ -67,6 +67,30 @@ boost::asio::awaitable RemoteCursor::seek_exact(silkworm::ByteView key co_return KeyValue{k, v}; } +boost::asio::awaitable RemoteCursor::prev() { + const auto start_time = clock_time::now(); + auto next_message = remote::Cursor{}; + next_message.set_op(remote::Op::PREV); + next_message.set_cursor(cursor_id_); + auto next_pair = co_await tx_rpc_.write_and_read(next_message); + const auto k = silkworm::bytes_of_string(next_pair.k()); + const auto v = silkworm::bytes_of_string(next_pair.v()); + SILK_DEBUG << "RemoteCursor::prev k: " << k << " v: " << v << " c=" << cursor_id_ << " t=" << clock_time::since(start_time); + co_return KeyValue{k, v}; +} + +boost::asio::awaitable RemoteCursor::last() { + const auto start_time = clock_time::now(); + auto next_message = remote::Cursor{}; + next_message.set_op(remote::Op::LAST); + next_message.set_cursor(cursor_id_); + auto next_pair = co_await tx_rpc_.write_and_read(next_message); + const auto k = silkworm::bytes_of_string(next_pair.k()); + const auto v = silkworm::bytes_of_string(next_pair.v()); + SILK_DEBUG << "RemoteCursor::last k: " << k << " v: " << v << " c=" << cursor_id_ << " t=" << clock_time::since(start_time); + co_return KeyValue{k, v}; +} + boost::asio::awaitable RemoteCursor::next() { const auto start_time = clock_time::now(); auto next_message = remote::Cursor{}; diff --git a/silkworm/silkrpc/ethdb/kv/remote_cursor.hpp b/silkworm/silkrpc/ethdb/kv/remote_cursor.hpp index 4d19e53c..e3f19d80 100644 --- a/silkworm/silkrpc/ethdb/kv/remote_cursor.hpp +++ b/silkworm/silkrpc/ethdb/kv/remote_cursor.hpp @@ -45,6 +45,10 @@ class RemoteCursor : public CursorDupSort { boost::asio::awaitable seek_exact(silkworm::ByteView key) override; + boost::asio::awaitable prev() override; + + boost::asio::awaitable last() override; + boost::asio::awaitable next() override; boost::asio::awaitable next_dup() override; diff --git a/silkworm/silkrpc/ethdb/transaction_database.cpp b/silkworm/silkrpc/ethdb/transaction_database.cpp index 101a5d40..06ca9023 100644 --- a/silkworm/silkrpc/ethdb/transaction_database.cpp +++ b/silkworm/silkrpc/ethdb/transaction_database.cpp @@ -31,6 +31,18 @@ awaitable TransactionDatabase::get(const std::string& table, ByteView co_return kv_pair; } +awaitable TransactionDatabase::get_exact_or_previous(const std::string& table, ByteView key) const { + const auto cursor = co_await tx_.cursor(table); + SILK_TRACE << "TransactionDatabase::get_exact_or_previous cursor_id: " << cursor->cursor_id(); + auto kv_pair = co_await cursor->seek(key); + if(!kv_pair.key.size()) { + kv_pair = co_await cursor->last(); + } else if( kv_pair.key > key ) { + kv_pair = co_await cursor->prev(); + } + co_return kv_pair.value; +} + awaitable TransactionDatabase::get_one(const std::string& table, ByteView key) const { const auto cursor = co_await tx_.cursor(table); SILK_TRACE << "TransactionDatabase::get_one cursor_id: " << cursor->cursor_id(); diff --git a/silkworm/silkrpc/ethdb/transaction_database.hpp b/silkworm/silkrpc/ethdb/transaction_database.hpp index f7ff7fec..370dc76d 100644 --- a/silkworm/silkrpc/ethdb/transaction_database.hpp +++ b/silkworm/silkrpc/ethdb/transaction_database.hpp @@ -36,6 +36,8 @@ class TransactionDatabase : public core::rawdb::DatabaseReader { [[nodiscard]] awaitable get(const std::string& table, ByteView key) const override; + [[nodiscard]] awaitable get_exact_or_previous(const std::string& table, ByteView key) const override; + [[nodiscard]] awaitable get_one(const std::string& table, ByteView key) const override; [[nodiscard]] awaitable> get_both_range(const std::string& table, ByteView key, ByteView subkey) const override; diff --git a/silkworm/silkrpc/test/mock_cursor.hpp b/silkworm/silkrpc/test/mock_cursor.hpp index f853ddad..daabb71d 100644 --- a/silkworm/silkrpc/test/mock_cursor.hpp +++ b/silkworm/silkrpc/test/mock_cursor.hpp @@ -33,6 +33,8 @@ class MockCursor : public ethdb::Cursor { MOCK_METHOD((boost::asio::awaitable), open_cursor, (const std::string& table_name, bool is_dup_sorted)); MOCK_METHOD((boost::asio::awaitable), seek, (silkworm::ByteView key)); MOCK_METHOD((boost::asio::awaitable), seek_exact, (silkworm::ByteView key)); + MOCK_METHOD((boost::asio::awaitable), prev, ()); + MOCK_METHOD((boost::asio::awaitable), last, ()); MOCK_METHOD((boost::asio::awaitable), next, ()); MOCK_METHOD((boost::asio::awaitable), close_cursor, ()); }; @@ -43,6 +45,8 @@ class MockCursorDupSort : public ethdb::CursorDupSort { MOCK_METHOD((boost::asio::awaitable), open_cursor, (const std::string& table_name, bool is_dup_sorted)); MOCK_METHOD((boost::asio::awaitable), seek, (silkworm::ByteView key)); MOCK_METHOD((boost::asio::awaitable), seek_exact, (silkworm::ByteView key)); + MOCK_METHOD((boost::asio::awaitable), prev, ()); + MOCK_METHOD((boost::asio::awaitable), last, ()); MOCK_METHOD((boost::asio::awaitable), next, ()); MOCK_METHOD((boost::asio::awaitable), next_dup, ()); MOCK_METHOD((boost::asio::awaitable), close_cursor, ()); diff --git a/silkworm/silkrpc/test/mock_database_reader.hpp b/silkworm/silkrpc/test/mock_database_reader.hpp index 7e46f89d..ac342d43 100644 --- a/silkworm/silkrpc/test/mock_database_reader.hpp +++ b/silkworm/silkrpc/test/mock_database_reader.hpp @@ -31,6 +31,7 @@ namespace silkworm::rpc::test { class MockDatabaseReader : public core::rawdb::DatabaseReader { public: MOCK_METHOD((boost::asio::awaitable), get, (const std::string&, silkworm::ByteView), (const)); + MOCK_METHOD((boost::asio::awaitable), get_exact_or_previous, (const std::string&, silkworm::ByteView), (const)); MOCK_METHOD((boost::asio::awaitable), get_one, (const std::string&, silkworm::ByteView), (const)); MOCK_METHOD((boost::asio::awaitable>), get_both_range, (const std::string&, silkworm::ByteView, silkworm::ByteView), (const)); diff --git a/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp b/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp index 4484003c..6d4d1ddc 100644 --- a/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp +++ b/silkworm/silkrpc/test/mock_estimate_gas_oracle.hpp @@ -34,7 +34,7 @@ class MockEstimateGasOracle : public EstimateGasOracle { const silkworm::ChainConfig& config, boost::asio::thread_pool& workers, ethdb::Transaction& tx, ethdb::TransactionDatabase& tx_database) : EstimateGasOracle(block_header_provider, account_reader, config, workers, tx, tx_database) {} - MOCK_METHOD((ExecutionResult), try_execution, (EVMExecutor&, const silkworm::Block&, const silkworm::Transaction&), (override)); + MOCK_METHOD((ExecutionResult), try_execution, (EVMExecutor&, const silkworm::Block&, const silkworm::Transaction&, uint64_t eos_evm_version, const evmone::gas_parameters& gas_params), (override)); }; } // namespace silkworm::rpc