diff --git a/silkworm/db/kv/state_reader.cpp b/silkworm/db/kv/state_reader.cpp index e6831404bf..e3c0d92a20 100644 --- a/silkworm/db/kv/state_reader.cpp +++ b/silkworm/db/kv/state_reader.cpp @@ -26,19 +26,14 @@ namespace silkworm::db::kv { -StateReader::StateReader(kv::api::Transaction& tx, std::optional block_num, std::optional txn_id) : tx_(tx), block_num_(block_num), txn_number_(txn_id) { - SILKWORM_ASSERT((txn_id && !block_num) || (!txn_id && block_num)); +StateReader::StateReader(kv::api::Transaction& tx, TxnId txn_id) : tx_(tx), txn_number_(txn_id) { } Task> StateReader::read_account(const evmc::address& address) const { - if (!txn_number_) { - txn_number_ = co_await tx_.first_txn_num_in_block(*block_num_); - } - db::kv::api::GetAsOfQuery query{ .table = table::kAccountDomain, .key = db::account_domain_key(address), - .timestamp = static_cast(*txn_number_), + .timestamp = static_cast(txn_number_), }; const auto result = co_await tx_.get_as_of(std::move(query)); if (!result.success) { @@ -53,14 +48,10 @@ Task> StateReader::read_account(const evmc::address& addr Task StateReader::read_storage(const evmc::address& address, uint64_t /* incarnation */, const evmc::bytes32& location_hash) const { - if (!txn_number_) { - txn_number_ = co_await tx_.first_txn_num_in_block(*block_num_); - } - db::kv::api::GetAsOfQuery query{ .table = table::kStorageDomain, .key = db::storage_domain_key(address, location_hash), - .timestamp = static_cast(*txn_number_), + .timestamp = static_cast(txn_number_), }; const auto result = co_await tx_.get_as_of(std::move(query)); if (!result.success) { @@ -73,14 +64,11 @@ Task> StateReader::read_code(const evmc::address& address, if (code_hash == kEmptyHash) { co_return std::nullopt; } - if (!txn_number_) { - txn_number_ = co_await tx_.first_txn_num_in_block(*block_num_); - } db::kv::api::GetAsOfQuery query{ .table = table::kCodeDomain, .key = db::code_domain_key(address), - .timestamp = static_cast(*txn_number_), + .timestamp = static_cast(txn_number_), }; const auto result = co_await tx_.get_as_of(std::move(query)); if (!result.success) { diff --git a/silkworm/db/kv/state_reader.hpp b/silkworm/db/kv/state_reader.hpp index 184d372100..a2356cf5ae 100644 --- a/silkworm/db/kv/state_reader.hpp +++ b/silkworm/db/kv/state_reader.hpp @@ -34,7 +34,7 @@ namespace silkworm::db::kv { class StateReader { public: - StateReader(kv::api::Transaction& tx, std::optional block_num, std::optional txn_id = std::nullopt); + StateReader(kv::api::Transaction& tx, TxnId txn_id); StateReader(const StateReader&) = delete; StateReader& operator=(const StateReader&) = delete; @@ -49,8 +49,7 @@ class StateReader { private: kv::api::Transaction& tx_; - std::optional block_num_; - mutable std::optional txn_number_; + TxnId txn_number_; }; } // namespace silkworm::db::kv diff --git a/silkworm/execution/remote_state.hpp b/silkworm/execution/remote_state.hpp index 2b22ec9546..1ff6e8ddc9 100644 --- a/silkworm/execution/remote_state.hpp +++ b/silkworm/execution/remote_state.hpp @@ -39,9 +39,8 @@ class AsyncRemoteState { explicit AsyncRemoteState( db::kv::api::Transaction& tx, const db::chain::ChainStorage& storage, - std::optional block_num, - std::optional txn_id = std::nullopt) - : storage_(storage), state_reader_(tx, block_num ? *block_num + 1 : block_num, txn_id) {} + TxnId txn_id) + : storage_(storage), state_reader_(tx, txn_id) {} Task> read_account(const evmc::address& address) const noexcept; @@ -76,8 +75,8 @@ class RemoteState : public State { boost::asio::any_io_executor& executor, db::kv::api::Transaction& tx, const db::chain::ChainStorage& storage, - std::optional block_num, std::optional txn_id = std::nullopt) - : executor_(executor), async_state_{tx, storage, block_num, txn_id} {} + TxnId txn_id) + : executor_(executor), async_state_{tx, storage, txn_id} {} std::optional read_account(const evmc::address& address) const noexcept override; diff --git a/silkworm/execution/remote_state_test.cpp b/silkworm/execution/remote_state_test.cpp index 50ac87ee6d..822ad07338 100644 --- a/silkworm/execution/remote_state_test.cpp +++ b/silkworm/execution/remote_state_test.cpp @@ -54,8 +54,8 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf .WillRepeatedly(InvokeWithoutArgs([]() -> Task { co_return Bytes{}; })); - const BlockNum block_num = 1'000'000; - AsyncRemoteState state{transaction, chain_storage, block_num, std::nullopt}; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const auto code_read{spawn_and_wait(state.read_code(address, kEmptyHash))}; CHECK(code_read.empty()); } @@ -73,8 +73,8 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf co_return response; })); - const BlockNum block_num = 1'000'000; - AsyncRemoteState state{transaction, chain_storage, block_num}; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const evmc::bytes32 code_hash{0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_bytes32}; const auto code_read{spawn_and_wait(state.read_code(address, code_hash))}; CHECK(code_read == ByteView{kCode}); @@ -101,9 +101,6 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf } SECTION("read_storage with empty response from db") { - EXPECT_CALL(transaction, first_txn_num_in_block(1'000'001)).WillOnce(Invoke([]() -> Task { - co_return 0; - })); EXPECT_CALL(transaction, get_as_of(_)).WillOnce(Invoke([=](Unused) -> Task { db::kv::api::GetAsOfResult response{ .success = true, @@ -280,8 +277,8 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf .value = Bytes{}}; co_return response; })); - const BlockNum block_num = 1'000'000; - AsyncRemoteState state{transaction, chain_storage, block_num}; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const auto account_read{spawn_and_wait(state.read_account(address))}; CHECK(account_read == std::nullopt); } @@ -296,9 +293,9 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf .value = Bytes{}}; co_return response; })); - const BlockNum block_num = 1'000'000; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const evmc::bytes32 code_hash{0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_bytes32}; - AsyncRemoteState state{transaction, chain_storage, block_num}; const auto code_read{spawn_and_wait(state.read_code(address, code_hash))}; CHECK(code_read.empty()); } @@ -313,16 +310,16 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf .value = Bytes{}}; co_return response; })); - const BlockNum block_num = 1'000'000; const evmc::bytes32 location{0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_bytes32}; - AsyncRemoteState state{transaction, chain_storage, block_num}; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const auto storage_read{spawn_and_wait(state.read_storage(address, 0, location))}; CHECK(storage_read == 0x0000000000000000000000000000000000000000000000000000000000000000_bytes32); } SECTION("AsyncRemoteState::previous_incarnation returns ok") { - const BlockNum block_num = 1'000'000; - AsyncRemoteState state{transaction, chain_storage, block_num}; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const auto prev_incarnation{spawn_and_wait(state.previous_incarnation(address))}; CHECK(prev_incarnation == 0); } @@ -335,16 +332,17 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf } SECTION("AsyncRemoteState::current_canonical_block returns ok") { - const BlockNum block_num = 1'000'000; - AsyncRemoteState state{transaction, chain_storage, block_num}; + const TxnId txn_id = 244087591818874; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const auto current_canonical_block{spawn_and_wait(state.current_canonical_block())}; CHECK(current_canonical_block == 0); } SECTION("AsyncRemoteState::total_difficulty with empty response from chain storage") { + const TxnId txn_id = 244087591818874; const BlockNum block_num = 1'000'000; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const Hash block_hash{0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_bytes32}; - AsyncRemoteState state{transaction, chain_storage, block_num}; EXPECT_CALL(chain_storage, read_total_difficulty(block_hash, block_num)) .WillOnce(Invoke([](Unused, Unused) -> Task> { co_return std::nullopt; })); const auto total_difficulty{spawn_and_wait(state.total_difficulty(block_num, block_hash))}; @@ -352,9 +350,10 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf } SECTION("AsyncRemoteState::read_header with empty response from chain storage") { + const TxnId txn_id = 244087591818874; const BlockNum block_num = 1'000'000; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const Hash block_hash{0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_bytes32}; - AsyncRemoteState state{transaction, chain_storage, block_num}; EXPECT_CALL(chain_storage, read_header(block_num, block_hash)) .WillOnce(Invoke([](Unused, Unused) -> Task> { co_return std::nullopt; })); const auto block_header{spawn_and_wait(state.read_header(block_num, block_hash))}; @@ -362,9 +361,10 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf } SECTION("AsyncRemoteState::read_body with empty response from from chain storage") { - const BlockNum block_num = 1'000'000; const Hash block_hash{0x04491edcd115127caedbd478e2e7895ed80c7847e903431f94f9cfa579cad47f_bytes32}; - AsyncRemoteState state{transaction, chain_storage, block_num}; + const TxnId txn_id = 244087591818874; + const BlockNum block_num = 1'000'000; + AsyncRemoteState state{transaction, chain_storage, txn_id}; BlockBody body; EXPECT_CALL(chain_storage, read_body(block_hash, block_num, body)) .WillOnce(Invoke([](Unused, Unused, Unused) -> Task { co_return true; })); @@ -378,10 +378,11 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf .WillRepeatedly(InvokeWithoutArgs([=]() -> Task { co_return Bytes{}; })); + const TxnId txn_id = 244087591818874; const BlockNum block_num = 1'000'000; EXPECT_CALL(chain_storage, read_canonical_header_hash(block_num)) .WillOnce(Invoke([](Unused) -> Task> { co_return std::nullopt; })); - AsyncRemoteState state{transaction, chain_storage, block_num}; + AsyncRemoteState state{transaction, chain_storage, txn_id}; const auto canonical_hash{spawn_and_wait(state.canonical_hash(block_num))}; CHECK(canonical_hash == std::nullopt); } diff --git a/silkworm/execution/state_factory.cpp b/silkworm/execution/state_factory.cpp index 61d6eb0fac..1649acb2c6 100644 --- a/silkworm/execution/state_factory.cpp +++ b/silkworm/execution/state_factory.cpp @@ -32,7 +32,7 @@ std::shared_ptr StateFactory::create_state( auto& local_tx = dynamic_cast(tx); return std::make_shared(std::nullopt, txn_id, local_tx.data_store()); } else { // NOLINT(readability-else-after-return) - return std::make_shared(executor, tx, storage, std::nullopt, txn_id); + return std::make_shared(executor, tx, storage, txn_id); } } diff --git a/silkworm/rpc/commands/eth_api.cpp b/silkworm/rpc/commands/eth_api.cpp index 7a9a39986c..5cb6ee1199 100644 --- a/silkworm/rpc/commands/eth_api.cpp +++ b/silkworm/rpc/commands/eth_api.cpp @@ -918,8 +918,8 @@ Task EthereumRpcApi::handle_eth_estimate_gas(const nlohmann::json& request return chain_storage->read_canonical_header(block_num); }; - rpc::AccountReader account_reader = [&tx](const evmc::address& address, BlockNum block_num) -> Task> { - StateReader state_reader{*tx, block_num + 1}; + rpc::AccountReader account_reader = [&tx](const evmc::address& address, TxnId txn_id) -> Task> { + StateReader state_reader{*tx, txn_id}; co_return co_await state_reader.read_account(address); }; @@ -973,7 +973,10 @@ Task EthereumRpcApi::handle_eth_get_balance(const nlohmann::json& request, const auto [block_num, is_latest_block] = co_await block_reader.get_block_num(block_num_or_hash); tx->set_state_cache_enabled(is_latest_block); - StateReader state_reader{*tx, block_num + 1}; + execution::StateFactory state_factory{*tx}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_num + 1); + StateReader state_reader{*tx, txn_id}; + std::optional account{co_await state_reader.read_account(address)}; reply = make_json_content(request, "0x" + (account ? intx::hex(account->balance) : "0")); @@ -1012,7 +1015,10 @@ Task EthereumRpcApi::handle_eth_get_code(const nlohmann::json& request, nl const auto [block_num, is_latest_block] = co_await block_reader.get_block_num(block_id, /*latest_required=*/true); tx->set_state_cache_enabled(is_latest_block); - StateReader state_reader{*tx, block_num + 1}; + execution::StateFactory state_factory{*tx}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_num + 1); + + StateReader state_reader{*tx, txn_id}; std::optional account{co_await state_reader.read_account(address)}; if (account) { @@ -1053,7 +1059,11 @@ Task EthereumRpcApi::handle_eth_get_transaction_count(const nlohmann::json const auto [block_num, is_latest_block] = co_await block_reader.get_block_num(block_id, /*latest_required=*/true); tx->set_state_cache_enabled(is_latest_block); - StateReader state_reader{*tx, block_num + 1}; + execution::StateFactory state_factory{*tx}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_num + 1); + + StateReader state_reader{*tx, txn_id}; + std::optional account{co_await state_reader.read_account(address)}; if (account) { @@ -1101,7 +1111,10 @@ Task EthereumRpcApi::handle_eth_get_storage_at(const nlohmann::json& reque const auto [block_num, is_latest_block] = co_await block_reader.get_block_num(block_id, /*latest_required=*/true); tx->set_state_cache_enabled(is_latest_block); - StateReader state_reader{*tx, block_num + 1}; + execution::StateFactory state_factory{*tx}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_num + 1); + + StateReader state_reader{*tx, txn_id}; std::optional account{co_await state_reader.read_account(address)}; if (account) { @@ -1317,7 +1330,10 @@ Task EthereumRpcApi::handle_eth_create_access_list(const nlohmann::json& r const bool is_latest_block = co_await block_reader.get_latest_executed_block_num() == block_with_hash->block.header.number; tx->set_state_cache_enabled(/*cache_enabled=*/is_latest_block); - StateReader state_reader{*tx, block_with_hash->block.header.number + 1}; + execution::StateFactory state_factory{*tx}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_with_hash->block.header.number + 1); + + StateReader state_reader{*tx, txn_id}; std::optional nonce = std::nullopt; evmc::address to{}; @@ -1347,9 +1363,6 @@ Task EthereumRpcApi::handle_eth_create_access_list(const nlohmann::json& r auto txn = call.to_transaction(std::nullopt, nonce); AccessList saved_access_list = call.access_list; - execution::StateFactory state_factory{*tx}; - const auto txn_id = co_await state_factory.user_txn_id_at(block_with_hash->block.header.number + 1); - while (true) { const auto execution_result = co_await EVMExecutor::call( chain_config, *chain_storage, workers_, block_with_hash->block, txn, txn_id, [&](auto& io_executor, auto curr_txn_id, auto& storage) { diff --git a/silkworm/rpc/commands/ots_api.cpp b/silkworm/rpc/commands/ots_api.cpp index a539695daa..b629b42176 100644 --- a/silkworm/rpc/commands/ots_api.cpp +++ b/silkworm/rpc/commands/ots_api.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -80,7 +81,10 @@ Task OtsRpcApi::handle_ots_has_code(const nlohmann::json& request, nlohman tx->set_state_cache_enabled(is_latest_block); const auto block_num = co_await block_reader.get_block_num(block_id); - StateReader state_reader{*tx, block_num + 1}; + execution::StateFactory state_factory{*tx}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_num + 1); + + StateReader state_reader{*tx, txn_id}; std::optional account{co_await state_reader.read_account(address)}; if (account) { @@ -435,7 +439,10 @@ Task OtsRpcApi::handle_ots_get_contract_creator(const nlohmann::json& requ const auto chain_storage = tx->create_storage(); rpc::BlockReader block_reader{*chain_storage, *tx}; auto block_num = co_await block_reader.get_latest_block_num(); - StateReader state_reader{*tx, block_num}; + execution::StateFactory state_factory{*tx}; + const auto txn_number = co_await state_factory.user_txn_id_at(block_num); + + StateReader state_reader{*tx, txn_number}; std::optional account_opt{co_await state_reader.read_account(contract_address)}; if (!account_opt || account_opt.value().code_hash == kEmptyHash) { reply = make_json_content(request, nlohmann::detail::value_t::null); diff --git a/silkworm/rpc/commands/parity_api.cpp b/silkworm/rpc/commands/parity_api.cpp index ac734fa893..14f698b69a 100644 --- a/silkworm/rpc/commands/parity_api.cpp +++ b/silkworm/rpc/commands/parity_api.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -67,11 +68,14 @@ Task ParityRpcApi::handle_parity_list_storage_keys(const nlohmann::json& r const auto block_num = co_await block_reader.get_block_num(block_id); SILK_DEBUG << "read account with address: " << address << " block number: " << block_num; - StateReader state_reader{*tx, block_num}; + + execution::StateFactory state_factory{*tx}; + const auto txn_number = co_await state_factory.user_txn_id_at(block_num); + + StateReader state_reader{*tx, txn_number}; std::optional account = co_await state_reader.read_account(address); if (!account) throw std::domain_error{"account not found"}; - const auto txn_number = co_await tx->first_txn_num_in_block(block_num); auto from = db::code_domain_key(address); if (offset) { diff --git a/silkworm/rpc/core/block_reader.cpp b/silkworm/rpc/core/block_reader.cpp index 15a05573d1..fb09a9b5dc 100644 --- a/silkworm/rpc/core/block_reader.cpp +++ b/silkworm/rpc/core/block_reader.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -54,11 +55,14 @@ Task BlockReader::read_balance_changes(BlockCache& cache, const BlockNumOr SILK_TRACE << "read_balance_changes: block_num: " << block_num; - StateReader state_reader{transaction_, block_num + 1}; - const auto start_txn_number = co_await transaction_.first_txn_num_in_block(block_num); const auto end_txn_number = co_await transaction_.first_txn_num_in_block(block_num + 1); + execution::StateFactory state_factory{transaction_}; + const auto txn_id = co_await state_factory.user_txn_id_at(block_num + 1); + + StateReader state_reader{transaction_, txn_id}; + db::kv::api::HistoryRangeQuery query{ .table = db::table::kAccountDomain, .from_timestamp = static_cast(start_txn_number), diff --git a/silkworm/rpc/core/estimate_gas_oracle.cpp b/silkworm/rpc/core/estimate_gas_oracle.cpp index 6f399506ba..2edbe618cf 100644 --- a/silkworm/rpc/core/estimate_gas_oracle.cpp +++ b/silkworm/rpc/core/estimate_gas_oracle.cpp @@ -49,7 +49,7 @@ Task EstimateGasOracle::estimate_gas(const Call& call, const silk if (gas_price && gas_price != 0) { evmc::address from = call.from.value_or(evmc::address{0}); - std::optional account{co_await account_reader_(from, block_num + 1)}; + std::optional account{co_await account_reader_(from, txn_id)}; intx::uint256 balance = account->balance; SILK_DEBUG << "balance for address " << from << ": 0x" << intx::hex(balance); diff --git a/silkworm/rpc/core/estimate_gas_oracle.hpp b/silkworm/rpc/core/estimate_gas_oracle.hpp index 9b50ace24e..f77d99dfdd 100644 --- a/silkworm/rpc/core/estimate_gas_oracle.hpp +++ b/silkworm/rpc/core/estimate_gas_oracle.hpp @@ -37,7 +37,7 @@ inline constexpr std::uint64_t kTxGas = 21'000; inline constexpr std::uint64_t kGasCap = 50'000'000; using BlockHeaderProvider = std::function>(uint64_t)>; -using AccountReader = std::function>(const evmc::address&, uint64_t)>; +using AccountReader = std::function>(const evmc::address&, TxnId txn_id)>; struct EstimateGasException : public std::exception { public: diff --git a/silkworm/rpc/core/evm_executor_test.cpp b/silkworm/rpc/core/evm_executor_test.cpp index 385864f364..9f23251393 100644 --- a/silkworm/rpc/core/evm_executor_test.cpp +++ b/silkworm/rpc/core/evm_executor_test.cpp @@ -111,9 +111,6 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { SECTION("failed if transaction cost greater user amount") { auto cursor = std::make_shared(); - EXPECT_CALL(transaction, first_txn_num_in_block(6'000'001)).WillOnce(Invoke([]() -> Task { - co_return 244087591818873; - })); EXPECT_CALL(transaction, get_as_of(_)).WillOnce(Invoke([=](Unused) -> Task { db::kv::api::GetAsOfResult response{ .success = true, @@ -137,9 +134,6 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { SECTION("doesn't fail if transaction cost greater user amount && gasBailout == true") { auto cursor = std::make_shared(); - EXPECT_CALL(transaction, first_txn_num_in_block(6'000'001)).WillOnce(Invoke([]() -> Task { - co_return 244087591818873; - })); EXPECT_CALL(transaction, get_as_of(_)).WillRepeatedly(Invoke([=](Unused) -> Task { db::kv::api::GetAsOfResult response{ .success = true, @@ -172,9 +166,6 @@ TEST_CASE_METHOD(EVMExecutorTest, "EVMExecutor") { SECTION("call returns SUCCESS") { auto cursor = std::make_shared(); - EXPECT_CALL(transaction, first_txn_num_in_block(6'000'001)).WillOnce(Invoke([]() -> Task { - co_return 244087591818873; - })); EXPECT_CALL(transaction, get_as_of(_)).WillRepeatedly(Invoke([=](Unused) -> Task { db::kv::api::GetAsOfResult response{ .success = true,