From 2f3f6dcb03661ae75c6607b4925c3c2e05e0eb28 Mon Sep 17 00:00:00 2001 From: drlongle Date: Thu, 30 Mar 2023 20:59:10 +0200 Subject: [PATCH] Fix ledger_data to return an empty list: (#4398) Change `ledger_data` to return an empty list when all entries are filtered out. When the `type` field is specified for the `ledger_data` method, it is possible that no objects of the specified type are found. This can even occur if those objects exist, but not in the section that the server checked while serving your request. Previously, the `state` field of the response has the value `null`, instead of an empty array `[]`. By changing this to an empty array, the response is the same data type so that clients can handle it consistently. For example, in Python, `for entry in state` should now work correctly. It would raise an exception if `state` is `null` (or `None`). This could break client code that explicitly checks for null. However, this fix aligns the response with the documentation, where the `state` field is an array. Fix #4392. --- src/ripple/rpc/handlers/LedgerData.cpp | 4 +++ src/test/rpc/LedgerData_test.cpp | 37 +++++++++++++++++++------- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/ripple/rpc/handlers/LedgerData.cpp b/src/ripple/rpc/handlers/LedgerData.cpp index 7392b505126..f5433945772 100644 --- a/src/ripple/rpc/handlers/LedgerData.cpp +++ b/src/ripple/rpc/handlers/LedgerData.cpp @@ -94,6 +94,10 @@ doLedgerData(RPC::JsonContext& context) return jvResult; } Json::Value& nodes = jvResult[jss::state]; + if (nodes.type() == Json::nullValue) + { + nodes = Json::Value(Json::arrayValue); + } auto e = lpLedger->sles.end(); for (auto i = lpLedger->sles.upper_bound(key); i != e; ++i) diff --git a/src/test/rpc/LedgerData_test.cpp b/src/test/rpc/LedgerData_test.cpp index ab520181c05..ae57d6dcf92 100644 --- a/src/test/rpc/LedgerData_test.cpp +++ b/src/test/rpc/LedgerData_test.cpp @@ -314,6 +314,34 @@ class LedgerData_test : public beast::unit_test::suite auto const USD = gw["USD"]; env.fund(XRP(100000), gw); + auto makeRequest = [&env](Json::StaticString const& type) { + Json::Value jvParams; + jvParams[jss::ledger_index] = "current"; + jvParams[jss::type] = type; + return env.rpc( + "json", + "ledger_data", + boost::lexical_cast(jvParams))[jss::result]; + }; + + // Assert that state is an empty array. + for (auto const& type : + {jss::amendments, + jss::check, + jss::directory, + jss::fee, + jss::offer, + jss::signer_list, + jss::state, + jss::ticket, + jss::escrow, + jss::payment_channel, + jss::deposit_preauth}) + { + auto const jrr = makeRequest(type); + BEAST_EXPECT(checkArraySize(jrr[jss::state], 0)); + } + int const num_accounts = 10; for (auto i = 0; i < num_accounts; i++) @@ -372,15 +400,6 @@ class LedgerData_test : public beast::unit_test::suite env.close(); // Now fetch each type - auto makeRequest = [&env](Json::StaticString t) { - Json::Value jvParams; - jvParams[jss::ledger_index] = "current"; - jvParams[jss::type] = t; - return env.rpc( - "json", - "ledger_data", - boost::lexical_cast(jvParams))[jss::result]; - }; { // jvParams[jss::type] = "account"; auto const jrr = makeRequest(jss::account);