Skip to content

Commit

Permalink
rpcdaemon: debug_getModifiedAccountsBy[Number|Hash] for E3 (#2468)
Browse files Browse the repository at this point in the history
  • Loading branch information
lupin012 authored Nov 7, 2024
1 parent 8c149fc commit 04edec7
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 31 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rpc-integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
- name: Checkout RPC Tests Repository & Install Requirements
run: |
rm -rf ${{runner.workspace}}/rpc-tests
git -c advice.detachedHead=false clone --depth 1 --branch v1.10.0 https://github.com/erigontech/rpc-tests ${{runner.workspace}}/rpc-tests
git -c advice.detachedHead=false clone --depth 1 --branch v1.11.0 https://github.com/erigontech/rpc-tests ${{runner.workspace}}/rpc-tests
cd ${{runner.workspace}}/rpc-tests
pip3 install -r requirements.txt
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/run_integration_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ rm -rf ./mainnet/results/

# eth_getBlockReceipts/test_07.json new blobFields
# debug_accountRange: new algo using TKV
# debug_getModifiedAccounts: new algo using TKV
# debug_storageRangeAt: new algo using TKV
# debug_traceCall/test_02.json: requested is_latest fix to support ethbackend
# erigon_getBalanceChangesInBlock: new algo using TKV
Expand All @@ -28,7 +27,6 @@ rm -rf ./mainnet/results/

python3 ./run_tests.py --continue --blockchain mainnet --jwt "$2" --display-only-fail --json-diff --port 51515 --transport_type http,websocket -x \
debug_accountRange,\
debug_getModifiedAccounts,\
debug_storageRangeAt,\
debug_traceBlockByHash/test_05,\
debug_traceBlockByHash/test_08,\
Expand Down
14 changes: 14 additions & 0 deletions silkworm/db/kv/api/endpoint/temporal_range.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,22 @@ struct HistoryRangeQuery {
int64_t limit{kUnlimited};
uint32_t page_size{0};
std::string page_token;

// TODO(canepat) we need clang >= 17 to use spaceship operator instead of hand-made operator== below
// auto operator<=>(const HistoryRangeQuery&) const = default;
};

inline bool operator==(const HistoryRangeQuery& lhs, const HistoryRangeQuery& rhs) {
return (lhs.tx_id == rhs.tx_id) &&
(lhs.table == rhs.table) &&
(lhs.from_timestamp == rhs.from_timestamp) &&
(lhs.to_timestamp == rhs.to_timestamp) &&
(lhs.ascending_order == rhs.ascending_order) &&
(lhs.limit == rhs.limit) &&
(lhs.page_size == rhs.page_size) &&
(lhs.page_token == rhs.page_token);
}

using HistoryRangeResult = RangeResult;

struct DomainRangeQuery {
Expand Down
2 changes: 1 addition & 1 deletion silkworm/infra/grpc/client/call.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Task<Response> unary_rpc(
agrpc::GrpcContext& grpc_context,
boost::asio::cancellation_slot* cancellation_slot = nullptr) {
grpc::ClientContext client_context;
client_context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(10));
client_context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(60));
if (cancellation_slot) {
cancellation_slot->assign([&client_context](boost::asio::cancellation_type /*type*/) {
client_context.TryCancel();
Expand Down
43 changes: 27 additions & 16 deletions silkworm/rpc/commands/debug_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,13 @@ Task<void> DebugRpcApi::handle_debug_get_modified_accounts_by_number(const nlohm
const auto start_block_number = co_await core::get_block_number(start_block_id, *tx);
const auto end_block_number = co_await core::get_block_number(end_block_id, *tx);

const auto addresses = co_await get_modified_accounts(*tx, start_block_number, end_block_number);
if (end_block_number < start_block_number) {
std::stringstream msg;
msg << "start block (" << start_block_number << ") must be less or equal to end block (" << end_block_number << ")";
throw std::invalid_argument(msg.str());
}

const auto addresses = co_await get_modified_accounts(*tx, start_block_number, end_block_number + 1);
reply = make_json_content(request, addresses);
} catch (const std::invalid_argument& e) {
SILK_ERROR << "exception: " << e.what() << " processing request: " << request.dump();
Expand Down Expand Up @@ -173,7 +179,7 @@ Task<void> DebugRpcApi::handle_debug_get_modified_accounts_by_hash(const nlohman
if (!end_block_number) {
throw std::invalid_argument("end block " + silkworm::to_hex(end_hash) + " not found");
}
const auto addresses = co_await get_modified_accounts(*tx, *start_block_number, *end_block_number);
const auto addresses = co_await get_modified_accounts(*tx, *start_block_number, *end_block_number + 1);

reply = make_json_content(request, addresses);
} catch (const std::invalid_argument& e) {
Expand Down Expand Up @@ -634,28 +640,33 @@ Task<std::set<evmc::address>> get_modified_accounts(db::kv::api::Transaction& tx

SILK_DEBUG << "latest: " << latest_block_number << " start: " << start_block_number << " end: " << end_block_number;

std::set<evmc::address> addresses;
if (start_block_number > latest_block_number) {
std::stringstream msg;
msg << "start block (" << start_block_number << ") is later than the latest block (" << latest_block_number << ")";
throw std::invalid_argument(msg.str());
}
if (start_block_number <= end_block_number) {
auto walker = [&](const silkworm::Bytes& key, const silkworm::Bytes& value) {
auto block_number = static_cast<BlockNum>(std::stol(silkworm::to_hex(key), nullptr, 16));
if (block_number <= end_block_number) {
auto address = bytes_to_address(value.substr(0, kAddressLength));

SILK_TRACE << "Walker: processing block " << block_number << " address " << address;
addresses.insert(address);
}
return block_number <= end_block_number;
};
if (end_block_number > latest_block_number) {
std::stringstream msg;
msg << "end block (" << end_block_number << ") is later than the latest block (" << latest_block_number << ")";
throw std::invalid_argument(msg.str());
}

const auto start_txn_number = co_await tx.first_txn_num_in_block(start_block_number);
const auto end_txn_number = co_await tx.first_txn_num_in_block(end_block_number == start_block_number ? end_block_number + 1 : end_block_number) - 1;

const auto key = silkworm::db::block_key(start_block_number);
SILK_TRACE << "Ready to walk starting from key: " << silkworm::to_hex(key);
db::kv::api::HistoryRangeQuery query{
.table = db::table::kAccountsHistory,
.from_timestamp = static_cast<db::kv::api::Timestamp>(start_txn_number),
.to_timestamp = static_cast<db::kv::api::Timestamp>(end_txn_number),
.ascending_order = true};

co_await walk(tx, db::table::kAccountChangeSetName, key, 0, walker);
auto paginated_result = co_await tx.history_range(std::move(query));
auto it = co_await paginated_result.begin();

std::set<evmc::address> addresses;
while (const auto value = co_await it.next()) {
addresses.insert(bytes_to_address(value->first));
}

co_return addresses;
Expand Down
63 changes: 52 additions & 11 deletions silkworm/rpc/commands/debug_api_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@
#include <silkworm/db/chain/chain_storage.hpp>
#include <silkworm/db/kv/api/base_transaction.hpp>
#include <silkworm/db/kv/api/cursor.hpp>
#include <silkworm/db/kv/api/endpoint/paginated_sequence.hpp>
#include <silkworm/db/kv/api/endpoint/temporal_range.hpp>
#include <silkworm/db/kv/api/state_cache.hpp>
#include <silkworm/db/test_util/mock_transaction.hpp>
#include <silkworm/infra/common/log.hpp>
#include <silkworm/infra/concurrency/shared_service.hpp>
#include <silkworm/infra/test_util/log.hpp>
Expand All @@ -46,6 +49,17 @@ using db::chain::ChainStorage;
using db::kv::api::Cursor;
using db::kv::api::CursorDupSort;
using db::kv::api::KeyValue;
using testing::_;
using testing::Invoke;
using testing::InvokeWithoutArgs;
using testing::Unused;
using namespace evmc::literals;

using PaginatedKV = db::kv::api::PaginatedSequencePair<Bytes, Bytes>;
using PaginatorKV = PaginatedKV::Paginator;
using PageK = PaginatedKV::KPage;
using PageV = PaginatedKV::VPage;
using PageResultKV = PaginatedKV::PageResult;

static const nlohmann::json kEmpty;
static const std::string kZeros = "00000000000000000000000000000000000000000000000000000000000000000000000000000000";
Expand Down Expand Up @@ -366,27 +380,53 @@ TEST_CASE("get_modified_accounts") {
{"000000000052a057", "f3a3956d084e3f2a24add02c35c8afd09e3e9bf5030105080c9eea7771667e25"}, // NOLINT
{"000000000052a058", "053eafe07f12033715d31e1599bbf27dd1c05fb2030105080ddd58b6af8be86e"} // NOLINT
};
// std::cout << "json: " << json << "\n" << std::flush;

auto database = DummyDatabase{json};
auto begin_result = boost::asio::co_spawn(pool, database.begin(), boost::asio::use_future);
auto tx = begin_result.get();
db::test_util::MockTransaction transaction;

auto& tx = transaction;
SECTION("end == start") {
auto result = boost::asio::co_spawn(pool, get_modified_accounts(*tx, 0x52a010, 0x52a010), boost::asio::use_future);
auto accounts = result.get();

CHECK(accounts.size() == 1);

EXPECT_CALL(transaction, get(db::table::kLastForkchoiceName, _)).WillOnce(InvokeWithoutArgs([]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, silkworm::Bytes{}};
}));
EXPECT_CALL(transaction, get(db::table::kSyncStageProgressName, ByteView{stages::kExecution}))
.WillOnce(InvokeWithoutArgs([]() -> Task<KeyValue> {
co_return KeyValue{silkworm::Bytes{}, *silkworm::from_hex("000000000052a010")};
}));
EXPECT_CALL(transaction, first_txn_num_in_block(0x52a010))
.WillOnce(InvokeWithoutArgs([]() -> Task<TxnId> {
co_return 0;
}));
EXPECT_CALL(transaction, first_txn_num_in_block(0x52a011))
.WillOnce(InvokeWithoutArgs([]() -> Task<TxnId> {
co_return 20;
}));
db::kv::api::HistoryRangeQuery query{
.table = db::table::kAccountsHistory,
.from_timestamp = static_cast<db::kv::api::Timestamp>(0),
.to_timestamp = static_cast<db::kv::api::Timestamp>(19),
.ascending_order = true};
EXPECT_CALL(transaction, history_range(std::move(query))).WillOnce(Invoke([=](Unused) -> Task<db::kv::api::PaginatedKeysValues> {
PaginatorKV paginator = [](auto next_page_token) -> Task<PageResultKV> {
co_return PageResultKV{
.keys = {*from_hex("07aaec0b237ccf56b03a7c43c1c7a783da5606420501010101")},
.values = {Bytes{}}, // encoded account value doesn't care
.next_page_token = std::move(next_page_token)};
};
db::kv::api::PaginatedKeysValues result{paginator};
co_return result;
}));

auto result = boost::asio::co_spawn(pool, get_modified_accounts(tx, 0x52a010, 0x52a010), boost::asio::use_future);
const auto accounts = result.get();
nlohmann::json j = accounts;
CHECK(j == R"([
"0x07aaec0b237ccf56b03a7c43c1c7a783da560642"
])"_json);
}

#ifdef notdef
SECTION("end == start + 1") {
auto result = boost::asio::co_spawn(pool, get_modified_accounts(*tx, 0x52a010, 0x52a011), boost::asio::use_future);
auto accounts = result.get();
std::cout << "size2: " << accounts.size() << "\n";

CHECK(accounts.size() == 2);

Expand Down Expand Up @@ -489,6 +529,7 @@ TEST_CASE("get_modified_accounts") {
auto result = boost::asio::co_spawn(pool, get_modified_accounts(*tx, 0x52a061, 0x52a061), boost::asio::use_future);
CHECK_THROWS_AS(result.get(), std::invalid_argument);
}
#endif
}
#endif // !defined(__clang__)

Expand Down

0 comments on commit 04edec7

Please sign in to comment.