#include #include #include #include #include #include #include #include #include #include #include namespace xrpl { namespace test { class LedgerRPC_test : public beast::unit_test::suite { void checkErrorValue( Json::Value const& jv, std::string const& err, std::string const& msg) { if (BEAST_EXPECT(jv.isMember(jss::status))) BEAST_EXPECT(jv[jss::status] == "error"); if (BEAST_EXPECT(jv.isMember(jss::error))) BEAST_EXPECT(jv[jss::error] == err); if (msg.empty()) { BEAST_EXPECT( jv[jss::error_message] == Json::nullValue || jv[jss::error_message] == ""); } else if (BEAST_EXPECT(jv.isMember(jss::error_message))) BEAST_EXPECTS( jv[jss::error_message] == msg, "Expected error message \"" + msg + "\", received \"" + jv[jss::error_message].asString() + "\""); } // Corrupt a valid address by replacing the 10th character with '!'. // '!' is not part of the ripple alphabet. std::string makeBadAddress(std::string good) { std::string ret = std::move(good); ret.replace(10, 1, 1, '!'); return ret; } void testLedgerRequest() { testcase("Basic Request"); using namespace test::jtx; Env env{*this}; env.close(); BEAST_EXPECT(env.current()->header().seq == 4); { Json::Value jvParams; // can be either numeric or quoted numeric jvParams[jss::ledger_index] = 1; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1"); } { Json::Value jvParams; jvParams[jss::ledger_index] = "1"; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::ledger][jss::closed] == true); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "1"); } { // using current identifier auto const jrr = env.rpc("ledger", "current")[jss::result]; BEAST_EXPECT(jrr[jss::ledger][jss::closed] == false); BEAST_EXPECT( jrr[jss::ledger][jss::ledger_index] == std::to_string(env.current()->header().seq)); BEAST_EXPECT( jrr[jss::ledger_current_index] == env.current()->header().seq); } } void testBadInput() { testcase("Bad Input"); using namespace test::jtx; Env env{*this}; Account const gw{"gateway"}; auto const USD = gw["USD"]; Account const bob{"bob"}; env.fund(XRP(10000), gw, bob); env.close(); env.trust(USD(1000), bob); env.close(); { // ask for an arbitrary string - index Json::Value jvParams; jvParams[jss::ledger_index] = "potato"; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; checkErrorValue( jrr, "invalidParams", "Invalid field 'ledger_index', not string or number."); } { // ask for a negative index Json::Value jvParams; jvParams[jss::ledger_index] = -1; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; checkErrorValue( jrr, "invalidParams", "Invalid field 'ledger_index', not string or number."); } { // ask for a bad ledger index Json::Value jvParams; jvParams[jss::ledger_index] = 10u; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound"); } { // unrecognized string arg -- error auto const jrr = env.rpc("ledger", "arbitrary_text")[jss::result]; checkErrorValue(jrr, "lgrNotFound", "ledgerNotFound"); } { // Request queue for closed ledger Json::Value jvParams; jvParams[jss::ledger_index] = "validated"; jvParams[jss::queue] = true; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; checkErrorValue(jrr, "invalidParams", "Invalid parameters."); } { // Request a ledger with a very large (double) sequence. auto const ret = env.rpc("json", "ledger", "{ \"ledger_index\" : 2e15 }"); BEAST_EXPECT(RPC::contains_error(ret)); BEAST_EXPECT(ret[jss::error_message] == "Invalid parameters."); } { // Request a ledger with very large (integer) sequence. auto const ret = env.rpc( "json", "ledger", "{ \"ledger_index\" : 1000000000000000 }"); checkErrorValue(ret, "invalidParams", "Invalid parameters."); } } void testLedgerCurrent() { testcase("ledger_current Request"); using namespace test::jtx; Env env{*this}; env.close(); BEAST_EXPECT(env.current()->header().seq == 4); { auto const jrr = env.rpc("ledger_current")[jss::result]; BEAST_EXPECT( jrr[jss::ledger_current_index] == env.current()->header().seq); } } void testLedgerFull() { testcase("Ledger Request, Full Option"); using namespace test::jtx; Env env{*this}; env.close(); Json::Value jvParams; jvParams[jss::ledger_index] = 3u; jvParams[jss::full] = true; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState)); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u); } void testLedgerFullNonAdmin() { testcase("Ledger Request, Full Option Without Admin"); using namespace test::jtx; Env env{*this, envconfig(no_admin)}; // env.close(); Json::Value jvParams; jvParams[jss::ledger_index] = 1u; jvParams[jss::full] = true; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; checkErrorValue( jrr, "noPermission", "You don't have permission for this command."); } void testLedgerAccounts() { testcase("Ledger Request, Accounts Option"); using namespace test::jtx; Env env{*this}; env.close(); Json::Value jvParams; jvParams[jss::ledger_index] = 3u; jvParams[jss::accounts] = true; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState)); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].size() == 3u); } /// @brief ledger RPC requests as a way to drive /// input options to lookupLedger. The point of this test is /// coverage for lookupLedger, not so much the ledger /// RPC request. void testLookupLedger() { testcase("Lookup ledger"); using namespace test::jtx; auto cfg = envconfig(); cfg->FEES.reference_fee = 10; Env env{ *this, std::move(cfg), FeatureBitset{}}; // hashes requested below // assume no amendments env.fund(XRP(10000), "alice"); env.close(); env.fund(XRP(10000), "bob"); env.close(); env.fund(XRP(10000), "jim"); env.close(); env.fund(XRP(10000), "jill"); { // access via the legacy ledger field, keyword index values Json::Value jvParams; jvParams[jss::ledger] = "closed"; auto jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5"); jvParams[jss::ledger] = "validated"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5"); jvParams[jss::ledger] = "current"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6"); // ask for a bad ledger keyword jvParams[jss::ledger] = "invalid"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT( jrr[jss::error_message] == "Invalid field 'ledger', not string or number."); // numeric index jvParams[jss::ledger] = 4; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "4"); // numeric index - out of range jvParams[jss::ledger] = 20; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "lgrNotFound"); BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound"); } { std::string const hash3{ "0F1A9E0C109ADEF6DA2BDE19217C12BBEC57174CBDBD212B0EBDC1CEDB8531" "85"}; // access via the ledger_hash field Json::Value jvParams; jvParams[jss::ledger_hash] = hash3; auto jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "3"); // extra leading hex chars in hash are not allowed jvParams[jss::ledger_hash] = "DEADBEEF" + hash3; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT( jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); // request with non-string ledger_hash jvParams[jss::ledger_hash] = 2; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT( jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); // malformed (non hex) hash jvParams[jss::ledger_hash] = "2E81FC6EC0DD943197EGC7E3FBE9AE30" "7F2775F2F7485BB37307984C3C0F2340"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT( jrr[jss::error_message] == "Invalid field 'ledger_hash', not hex string."); // properly formed, but just doesn't exist jvParams[jss::ledger_hash] = "8C3EEDB3124D92E49E75D81A8826A2E6" "5A75FD71FC3FD6F36FEB803C5F1D812D"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "lgrNotFound"); BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound"); } { // access via the ledger_index field, keyword index values Json::Value jvParams; jvParams[jss::ledger_index] = "closed"; auto jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5"); BEAST_EXPECT(jrr.isMember(jss::ledger_index)); jvParams[jss::ledger_index] = "validated"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "5"); jvParams[jss::ledger_index] = "current"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); BEAST_EXPECT(jrr[jss::ledger][jss::ledger_index] == "6"); BEAST_EXPECT(jrr.isMember(jss::ledger_current_index)); // ask for a bad ledger keyword jvParams[jss::ledger_index] = "invalid"; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "invalidParams"); BEAST_EXPECT( jrr[jss::error_message] == "Invalid field 'ledger_index', not string or number."); // numeric index for (auto i : {1, 2, 3, 4, 5, 6}) { jvParams[jss::ledger_index] = i; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr.isMember(jss::ledger)); if (i < 6) BEAST_EXPECT(jrr.isMember(jss::ledger_hash)); BEAST_EXPECT( jrr[jss::ledger][jss::ledger_index] == std::to_string(i)); } // numeric index - out of range jvParams[jss::ledger_index] = 7; jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::error] == "lgrNotFound"); BEAST_EXPECT(jrr[jss::error_message] == "ledgerNotFound"); } } void testNoQueue() { testcase("Ledger with queueing disabled"); using namespace test::jtx; Env env{*this}; Json::Value jv; jv[jss::ledger_index] = "current"; jv[jss::queue] = true; jv[jss::expand] = true; auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; BEAST_EXPECT(!jrr.isMember(jss::queue_data)); } void testQueue() { testcase("Ledger with Queued Transactions"); using namespace test::jtx; auto cfg = envconfig([](std::unique_ptr cfg) { auto& section = cfg->section("transaction_queue"); section.set("minimum_txn_in_ledger_standalone", "3"); section.set("normal_consensus_increase_percent", "0"); return cfg; }); cfg->FEES.reference_fee = 10; Env env(*this, std::move(cfg)); Json::Value jv; jv[jss::ledger_index] = "current"; jv[jss::queue] = true; jv[jss::expand] = true; Account const alice{"alice"}; Account const bob{"bob"}; Account const charlie{"charlie"}; Account const daria{"daria"}; env.fund(XRP(10000), alice); env.fund(XRP(10000), bob); env.close(); env.fund(XRP(10000), charlie); env.fund(XRP(10000), daria); env.close(); auto jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; BEAST_EXPECT(!jrr.isMember(jss::queue_data)); // Fill the open ledger for (;;) { auto metrics = env.app().getTxQ().getMetrics(*env.current()); if (metrics.openLedgerFeeLevel > metrics.minProcessingFeeLevel) break; env(noop(alice)); } BEAST_EXPECT(env.current()->header().seq == 5); // Put some txs in the queue // Alice auto aliceSeq = env.seq(alice); env(pay(alice, "george", XRP(1000)), last_ledger_seq(7), ter(terQUEUED)); env(offer(alice, XRP(50000), alice["USD"](5000)), seq(aliceSeq + 1), ter(terQUEUED)); env(noop(alice), seq(aliceSeq + 2), ter(terQUEUED)); // Bob auto batch = [&env](Account a) { auto aSeq = env.seq(a); // Enough fee to get in front of alice in the queue for (int i = 0; i < 10; ++i) { env(noop(a), fee(1000 + i), seq(aSeq + i), ter(terQUEUED)); } }; batch(bob); // Charlie batch(charlie); // Daria batch(daria); jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; BEAST_EXPECT(jrr[jss::queue_data].size() == 33); // Close enough ledgers so that alice's first tx expires. env.close(); env.close(); env.close(); BEAST_EXPECT(env.current()->header().seq == 8); jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; BEAST_EXPECT(jrr[jss::queue_data].size() == 11); env.close(); jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; std::string const txid0 = [&]() { auto const& parentHash = env.current()->header().parentHash; if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2)) { std::string const txid1 = [&]() { auto const& txj = jrr[jss::queue_data][1u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj["retries_remaining"] == 10); BEAST_EXPECT(txj.isMember(jss::tx)); auto const& tx = txj[jss::tx]; BEAST_EXPECT(tx[jss::Account] == alice.human()); BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet); return tx[jss::hash].asString(); }(); auto const& txj = jrr[jss::queue_data][0u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj["retries_remaining"] == 10); BEAST_EXPECT(txj.isMember(jss::tx)); auto const& tx = txj[jss::tx]; BEAST_EXPECT(tx[jss::Account] == alice.human()); BEAST_EXPECT(tx[jss::TransactionType] == jss::OfferCreate); auto const txid0 = tx[jss::hash].asString(); uint256 tx0, tx1; BEAST_EXPECT(tx0.parseHex(txid0)); BEAST_EXPECT(tx1.parseHex(txid1)); BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash)); return txid0; } return std::string{}; }(); env.close(); jv[jss::expand] = false; jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2)) { auto const& parentHash = env.current()->header().parentHash; auto const txid1 = [&]() { auto const& txj = jrr[jss::queue_data][1u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj.isMember(jss::tx)); return txj[jss::tx].asString(); }(); auto const& txj = jrr[jss::queue_data][0u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj["retries_remaining"] == 9); BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ"); BEAST_EXPECT(txj.isMember(jss::tx)); BEAST_EXPECT(txj[jss::tx] == txid0); uint256 tx0, tx1; BEAST_EXPECT(tx0.parseHex(txid0)); BEAST_EXPECT(tx1.parseHex(txid1)); BEAST_EXPECT((tx0 ^ parentHash) < (tx1 ^ parentHash)); } env.close(); jv[jss::expand] = true; jv[jss::binary] = true; jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; if (BEAST_EXPECT(jrr[jss::queue_data].size() == 2)) { auto const& txj = jrr[jss::queue_data][1u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj["retries_remaining"] == 8); BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ"); BEAST_EXPECT(txj.isMember(jss::tx)); BEAST_EXPECT(txj[jss::tx].isMember(jss::tx_blob)); auto const& txj2 = jrr[jss::queue_data][0u]; BEAST_EXPECT(txj2[jss::account] == alice.human()); BEAST_EXPECT(txj2[jss::fee_level] == "256"); BEAST_EXPECT(txj2["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj2["retries_remaining"] == 10); BEAST_EXPECT(!txj2.isMember("last_result")); BEAST_EXPECT(txj2.isMember(jss::tx)); BEAST_EXPECT(txj2[jss::tx].isMember(jss::tx_blob)); } for (int i = 0; i != 9; ++i) { env.close(); } jv[jss::expand] = false; jv[jss::binary] = false; jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; std::string const txid2 = [&]() { if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1)) { auto const& txj = jrr[jss::queue_data][0u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj["retries_remaining"] == 1); BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ"); BEAST_EXPECT(txj.isMember(jss::tx)); BEAST_EXPECT(txj[jss::tx] != txid0); return txj[jss::tx].asString(); } return std::string{}; }(); jv[jss::full] = true; jrr = env.rpc("json", "ledger", to_string(jv))[jss::result]; if (BEAST_EXPECT(jrr[jss::queue_data].size() == 1)) { auto const& txj = jrr[jss::queue_data][0u]; BEAST_EXPECT(txj[jss::account] == alice.human()); BEAST_EXPECT(txj[jss::fee_level] == "256"); BEAST_EXPECT(txj["preflight_result"] == "tesSUCCESS"); BEAST_EXPECT(txj["retries_remaining"] == 1); BEAST_EXPECT(txj["last_result"] == "terPRE_SEQ"); BEAST_EXPECT(txj.isMember(jss::tx)); auto const& tx = txj[jss::tx]; BEAST_EXPECT(tx[jss::Account] == alice.human()); BEAST_EXPECT(tx[jss::TransactionType] == jss::AccountSet); BEAST_EXPECT(tx[jss::hash] == txid2); } } void testLedgerAccountsOption() { testcase("Ledger Request, Accounts Hashes"); using namespace test::jtx; Env env{*this}; env.close(); std::string index; int hashesLedgerEntryIndex = -1; { Json::Value jvParams; jvParams[jss::ledger_index] = 3u; jvParams[jss::accounts] = true; jvParams[jss::expand] = true; jvParams[jss::type] = "hashes"; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState)); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); for (auto i = 0; i < jrr[jss::ledger][jss::accountState].size(); i++) if (jrr[jss::ledger][jss::accountState][i]["LedgerEntryType"] == jss::LedgerHashes) { index = jrr[jss::ledger][jss::accountState][i]["index"] .asString(); hashesLedgerEntryIndex = i; } for (auto const& object : jrr[jss::ledger][jss::accountState]) if (object["LedgerEntryType"] == jss::LedgerHashes) index = object["index"].asString(); // jss::type is a deprecated field BEAST_EXPECT( jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() && jrr[jss::warnings].size() == 1 && jrr[jss::warnings][0u][jss::id].asInt() == warnRPC_FIELDS_DEPRECATED); } { Json::Value jvParams; jvParams[jss::ledger_index] = 3u; jvParams[jss::accounts] = true; jvParams[jss::expand] = false; jvParams[jss::type] = "hashes"; auto const jrr = env.rpc("json", "ledger", to_string(jvParams))[jss::result]; BEAST_EXPECT(jrr[jss::ledger].isMember(jss::accountState)); BEAST_EXPECT(jrr[jss::ledger][jss::accountState].isArray()); BEAST_EXPECT( hashesLedgerEntryIndex > 0 && jrr[jss::ledger][jss::accountState][hashesLedgerEntryIndex] == index); // jss::type is a deprecated field BEAST_EXPECT( jrr.isMember(jss::warnings) && jrr[jss::warnings].isArray() && jrr[jss::warnings].size() == 1 && jrr[jss::warnings][0u][jss::id].asInt() == warnRPC_FIELDS_DEPRECATED); } } public: void run() override { testLedgerRequest(); testBadInput(); testLedgerCurrent(); testLedgerFull(); testLedgerFullNonAdmin(); testLedgerAccounts(); testLookupLedger(); testNoQueue(); testQueue(); testLedgerAccountsOption(); } }; BEAST_DEFINE_TESTSUITE(LedgerRPC, rpc, xrpl); } // namespace test } // namespace xrpl