Skip to content

Commit

Permalink
rpcdaemon: StateReader using only txn_number (#2623)
Browse files Browse the repository at this point in the history
  • Loading branch information
lupin012 authored Dec 24, 2024
1 parent 13718e4 commit 7923eb4
Show file tree
Hide file tree
Showing 12 changed files with 80 additions and 74 deletions.
20 changes: 4 additions & 16 deletions silkworm/db/kv/state_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,14 @@

namespace silkworm::db::kv {

StateReader::StateReader(kv::api::Transaction& tx, std::optional<BlockNum> block_num, std::optional<TxnId> 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<std::optional<Account>> 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<kv::api::Timestamp>(*txn_number_),
.timestamp = static_cast<kv::api::Timestamp>(txn_number_),
};
const auto result = co_await tx_.get_as_of(std::move(query));
if (!result.success) {
Expand All @@ -53,14 +48,10 @@ Task<std::optional<Account>> StateReader::read_account(const evmc::address& addr
Task<evmc::bytes32> 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<kv::api::Timestamp>(*txn_number_),
.timestamp = static_cast<kv::api::Timestamp>(txn_number_),
};
const auto result = co_await tx_.get_as_of(std::move(query));
if (!result.success) {
Expand All @@ -73,14 +64,11 @@ Task<std::optional<Bytes>> 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<kv::api::Timestamp>(*txn_number_),
.timestamp = static_cast<kv::api::Timestamp>(txn_number_),
};
const auto result = co_await tx_.get_as_of(std::move(query));
if (!result.success) {
Expand Down
5 changes: 2 additions & 3 deletions silkworm/db/kv/state_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ namespace silkworm::db::kv {

class StateReader {
public:
StateReader(kv::api::Transaction& tx, std::optional<BlockNum> block_num, std::optional<TxnId> txn_id = std::nullopt);
StateReader(kv::api::Transaction& tx, TxnId txn_id);

StateReader(const StateReader&) = delete;
StateReader& operator=(const StateReader&) = delete;
Expand All @@ -49,8 +49,7 @@ class StateReader {

private:
kv::api::Transaction& tx_;
std::optional<BlockNum> block_num_;
mutable std::optional<TxnId> txn_number_;
TxnId txn_number_;
};

} // namespace silkworm::db::kv
9 changes: 4 additions & 5 deletions silkworm/execution/remote_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,8 @@ class AsyncRemoteState {
explicit AsyncRemoteState(
db::kv::api::Transaction& tx,
const db::chain::ChainStorage& storage,
std::optional<BlockNum> block_num,
std::optional<TxnId> 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<std::optional<Account>> read_account(const evmc::address& address) const noexcept;

Expand Down Expand Up @@ -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<BlockNum> block_num, std::optional<TxnId> 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<Account> read_account(const evmc::address& address) const noexcept override;

Expand Down
45 changes: 23 additions & 22 deletions silkworm/execution/remote_state_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf
.WillRepeatedly(InvokeWithoutArgs([]() -> Task<Bytes> {
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());
}
Expand All @@ -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});
Expand All @@ -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<TxnId> {
co_return 0;
}));
EXPECT_CALL(transaction, get_as_of(_)).WillOnce(Invoke([=](Unused) -> Task<db::kv::api::GetAsOfResult> {
db::kv::api::GetAsOfResult response{
.success = true,
Expand Down Expand Up @@ -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);
}
Expand All @@ -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());
}
Expand All @@ -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);
}
Expand All @@ -335,36 +332,39 @@ 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<std::optional<intx::uint256>> { co_return std::nullopt; }));
const auto total_difficulty{spawn_and_wait(state.total_difficulty(block_num, block_hash))};
CHECK(total_difficulty == std::nullopt);
}

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<std::optional<BlockHeader>> { co_return std::nullopt; }));
const auto block_header{spawn_and_wait(state.read_header(block_num, block_hash))};
CHECK(block_header == std::nullopt);
}

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<bool> { co_return true; }));
Expand All @@ -378,10 +378,11 @@ TEST_CASE_METHOD(RemoteStateTest, "async remote buffer", "[rpc][core][remote_buf
.WillRepeatedly(InvokeWithoutArgs([=]() -> Task<Bytes> {
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<std::optional<Hash>> { 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);
}
Expand Down
2 changes: 1 addition & 1 deletion silkworm/execution/state_factory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ std::shared_ptr<State> StateFactory::create_state(
auto& local_tx = dynamic_cast<db::kv::api::LocalTransaction&>(tx);
return std::make_shared<LocalState>(std::nullopt, txn_id, local_tx.data_store());
} else { // NOLINT(readability-else-after-return)
return std::make_shared<RemoteState>(executor, tx, storage, std::nullopt, txn_id);
return std::make_shared<RemoteState>(executor, tx, storage, txn_id);
}
}

Expand Down
33 changes: 23 additions & 10 deletions silkworm/rpc/commands/eth_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -918,8 +918,8 @@ Task<void> 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<std::optional<Account>> {
StateReader state_reader{*tx, block_num + 1};
rpc::AccountReader account_reader = [&tx](const evmc::address& address, TxnId txn_id) -> Task<std::optional<Account>> {
StateReader state_reader{*tx, txn_id};
co_return co_await state_reader.read_account(address);
};

Expand Down Expand Up @@ -973,7 +973,10 @@ Task<void> 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<silkworm::Account> account{co_await state_reader.read_account(address)};

reply = make_json_content(request, "0x" + (account ? intx::hex(account->balance) : "0"));
Expand Down Expand Up @@ -1012,7 +1015,10 @@ Task<void> 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<silkworm::Account> account{co_await state_reader.read_account(address)};

if (account) {
Expand Down Expand Up @@ -1053,7 +1059,11 @@ Task<void> 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<silkworm::Account> account{co_await state_reader.read_account(address)};

if (account) {
Expand Down Expand Up @@ -1101,7 +1111,10 @@ Task<void> 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<silkworm::Account> account{co_await state_reader.read_account(address)};

if (account) {
Expand Down Expand Up @@ -1317,7 +1330,10 @@ Task<void> 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<uint64_t> nonce = std::nullopt;
evmc::address to{};
Expand Down Expand Up @@ -1347,9 +1363,6 @@ Task<void> 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) {
Expand Down
11 changes: 9 additions & 2 deletions silkworm/rpc/commands/ots_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include <silkworm/db/kv/state_reader.hpp>
#include <silkworm/db/kv/txn_num.hpp>
#include <silkworm/db/tables.hpp>
#include <silkworm/execution/state_factory.hpp>
#include <silkworm/infra/common/async_binary_search.hpp>
#include <silkworm/infra/common/log.hpp>
#include <silkworm/rpc/core/block_reader.hpp>
Expand Down Expand Up @@ -80,7 +81,10 @@ Task<void> 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<silkworm::Account> account{co_await state_reader.read_account(address)};

if (account) {
Expand Down Expand Up @@ -435,7 +439,10 @@ Task<void> 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<silkworm::Account> 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);
Expand Down
8 changes: 6 additions & 2 deletions silkworm/rpc/commands/parity_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <silkworm/db/kv/state_reader.hpp>
#include <silkworm/db/kv/txn_num.hpp>
#include <silkworm/db/tables.hpp>
#include <silkworm/execution/state_factory.hpp>
#include <silkworm/infra/common/log.hpp>
#include <silkworm/rpc/common/util.hpp>
#include <silkworm/rpc/core/block_reader.hpp>
Expand Down Expand Up @@ -67,11 +68,14 @@ Task<void> 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> 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) {
Expand Down
Loading

0 comments on commit 7923eb4

Please sign in to comment.