From d9c27da529a6cd75f30de145069b32610564dc3d Mon Sep 17 00:00:00 2001 From: Mayukha Vadari Date: Wed, 19 Nov 2025 02:14:39 +0530 Subject: [PATCH] refactor: split up `RPCHelpers.h` into two (#6047) This PR splits `RPCHelpers.h` into two files, by moving out all the ledger-fetching-related functions into a separate file, `RPCLedgerHelpers.h`. It also moves `getAccountObjects` to `AccountObjects.h`, since it is only used in that one place. --- src/xrpld/app/rdb/RelationalDatabase.h | 2 +- src/xrpld/rpc/detail/RPCHelpers.cpp | 625 ------------------ src/xrpld/rpc/detail/RPCHelpers.h | 80 --- src/xrpld/rpc/detail/RPCLedgerHelpers.cpp | 458 +++++++++++++ src/xrpld/rpc/detail/RPCLedgerHelpers.h | 96 +++ src/xrpld/rpc/handlers/AMMInfo.cpp | 1 + src/xrpld/rpc/handlers/AccountChannels.cpp | 1 + .../rpc/handlers/AccountCurrenciesHandler.cpp | 2 +- src/xrpld/rpc/handlers/AccountInfo.cpp | 1 + src/xrpld/rpc/handlers/AccountLines.cpp | 1 + src/xrpld/rpc/handlers/AccountObjects.cpp | 195 +++++- src/xrpld/rpc/handlers/AccountOffers.cpp | 1 + src/xrpld/rpc/handlers/BookOffers.cpp | 1 + src/xrpld/rpc/handlers/DepositAuthorized.cpp | 2 +- src/xrpld/rpc/handlers/GatewayBalances.cpp | 2 +- src/xrpld/rpc/handlers/GetAggregatePrice.cpp | 2 +- src/xrpld/rpc/handlers/LedgerData.cpp | 1 + src/xrpld/rpc/handlers/LedgerDiff.cpp | 2 +- src/xrpld/rpc/handlers/LedgerEntry.cpp | 2 +- src/xrpld/rpc/handlers/LedgerHandler.cpp | 2 +- src/xrpld/rpc/handlers/LedgerHeader.cpp | 2 +- src/xrpld/rpc/handlers/LedgerRequest.cpp | 2 +- src/xrpld/rpc/handlers/NFTOffers.cpp | 1 + src/xrpld/rpc/handlers/NoRippleCheck.cpp | 1 + src/xrpld/rpc/handlers/RipplePathFind.cpp | 2 +- src/xrpld/rpc/handlers/TransactionEntry.cpp | 2 +- src/xrpld/rpc/handlers/VaultInfo.cpp | 2 +- 27 files changed, 770 insertions(+), 719 deletions(-) create mode 100644 src/xrpld/rpc/detail/RPCLedgerHelpers.cpp create mode 100644 src/xrpld/rpc/detail/RPCLedgerHelpers.h diff --git a/src/xrpld/app/rdb/RelationalDatabase.h b/src/xrpld/app/rdb/RelationalDatabase.h index 570bce2b95..18a5536cf6 100644 --- a/src/xrpld/app/rdb/RelationalDatabase.h +++ b/src/xrpld/app/rdb/RelationalDatabase.h @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include diff --git a/src/xrpld/rpc/detail/RPCHelpers.cpp b/src/xrpld/rpc/detail/RPCHelpers.cpp index d9f640d537..6d1314ff51 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.cpp +++ b/src/xrpld/rpc/detail/RPCHelpers.cpp @@ -130,513 +130,6 @@ isRelatedToAccount( return false; } -bool -getAccountObjects( - ReadView const& ledger, - AccountID const& account, - std::optional> const& typeFilter, - uint256 dirIndex, - uint256 entryIndex, - std::uint32_t const limit, - Json::Value& jvResult) -{ - // check if dirIndex is valid - if (!dirIndex.isZero() && !ledger.read({ltDIR_NODE, dirIndex})) - return false; - - auto typeMatchesFilter = [](std::vector const& typeFilter, - LedgerEntryType ledgerType) { - auto it = std::find(typeFilter.begin(), typeFilter.end(), ledgerType); - return it != typeFilter.end(); - }; - - // if dirIndex != 0, then all NFTs have already been returned. only - // iterate NFT pages if the filter says so AND dirIndex == 0 - bool iterateNFTPages = - (!typeFilter.has_value() || - typeMatchesFilter(typeFilter.value(), ltNFTOKEN_PAGE)) && - dirIndex == beast::zero; - - Keylet const firstNFTPage = keylet::nftpage_min(account); - - // we need to check the marker to see if it is an NFTTokenPage index. - if (iterateNFTPages && entryIndex != beast::zero) - { - // if it is we will try to iterate the pages up to the limit - // and then change over to the owner directory - - if (firstNFTPage.key != (entryIndex & ~nft::pageMask)) - iterateNFTPages = false; - } - - auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue); - - // this is a mutable version of limit, used to seamlessly switch - // to iterating directory entries when nftokenpages are exhausted - uint32_t mlimit = limit; - - // iterate NFTokenPages preferentially - if (iterateNFTPages) - { - Keylet const first = entryIndex == beast::zero - ? firstNFTPage - : Keylet{ltNFTOKEN_PAGE, entryIndex}; - - Keylet const last = keylet::nftpage_max(account); - - // current key - uint256 ck = ledger.succ(first.key, last.key.next()).value_or(last.key); - - // current page - auto cp = ledger.read(Keylet{ltNFTOKEN_PAGE, ck}); - - while (cp) - { - jvObjects.append(cp->getJson(JsonOptions::none)); - auto const npm = (*cp)[~sfNextPageMin]; - if (npm) - cp = ledger.read(Keylet(ltNFTOKEN_PAGE, *npm)); - else - cp = nullptr; - - if (--mlimit == 0) - { - if (cp) - { - jvResult[jss::limit] = limit; - jvResult[jss::marker] = std::string("0,") + to_string(ck); - return true; - } - } - - if (!npm) - break; - - ck = *npm; - } - - // if execution reaches here then we're about to transition - // to iterating the root directory (and the conventional - // behaviour of this RPC function.) Therefore we should - // zero entryIndex so as not to terribly confuse things. - entryIndex = beast::zero; - } - - auto const root = keylet::ownerDir(account); - auto found = false; - - if (dirIndex.isZero()) - { - dirIndex = root.key; - found = true; - } - - auto dir = ledger.read({ltDIR_NODE, dirIndex}); - if (!dir) - { - // it's possible the user had nftoken pages but no - // directory entries. If there's no nftoken page, we will - // give empty array for account_objects. - if (mlimit >= limit) - jvResult[jss::account_objects] = Json::arrayValue; - - // non-zero dirIndex validity was checked in the beginning of this - // function; by this point, it should be zero. This function returns - // true regardless of nftoken page presence; if absent, account_objects - // is already set as an empty array. Notice we will only return false in - // this function when entryIndex can not be found, indicating an invalid - // marker error. - return true; - } - - std::uint32_t i = 0; - for (;;) - { - auto const& entries = dir->getFieldV256(sfIndexes); - auto iter = entries.begin(); - - if (!found) - { - iter = std::find(iter, entries.end(), entryIndex); - if (iter == entries.end()) - return false; - - found = true; - } - - // it's possible that the returned NFTPages exactly filled the - // response. Check for that condition. - if (i == mlimit && mlimit < limit) - { - jvResult[jss::limit] = limit; - jvResult[jss::marker] = - to_string(dirIndex) + ',' + to_string(*iter); - return true; - } - - for (; iter != entries.end(); ++iter) - { - auto const sleNode = ledger.read(keylet::child(*iter)); - - if (!typeFilter.has_value() || - typeMatchesFilter(typeFilter.value(), sleNode->getType())) - { - jvObjects.append(sleNode->getJson(JsonOptions::none)); - } - - if (++i == mlimit) - { - if (++iter != entries.end()) - { - jvResult[jss::limit] = limit; - jvResult[jss::marker] = - to_string(dirIndex) + ',' + to_string(*iter); - return true; - } - - break; - } - } - - auto const nodeIndex = dir->getFieldU64(sfIndexNext); - if (nodeIndex == 0) - return true; - - dirIndex = keylet::page(root, nodeIndex).key; - dir = ledger.read({ltDIR_NODE, dirIndex}); - if (!dir) - return true; - - if (i == mlimit) - { - auto const& e = dir->getFieldV256(sfIndexes); - if (!e.empty()) - { - jvResult[jss::limit] = limit; - jvResult[jss::marker] = - to_string(dirIndex) + ',' + to_string(*e.begin()); - } - - return true; - } - } -} - -namespace { - -bool -isValidatedOld(LedgerMaster& ledgerMaster, bool standalone) -{ - if (standalone) - return false; - - return ledgerMaster.getValidatedLedgerAge() > Tuning::maxValidatedLedgerAge; -} - -template -Status -ledgerFromRequest(T& ledger, JsonContext& context) -{ - ledger.reset(); - - auto& params = context.params; - - auto indexValue = params[jss::ledger_index]; - auto hashValue = params[jss::ledger_hash]; - - // We need to support the legacy "ledger" field. - auto& legacyLedger = params[jss::ledger]; - if (legacyLedger) - { - if (legacyLedger.asString().size() > 12) - hashValue = legacyLedger; - else - indexValue = legacyLedger; - } - - if (!hashValue.isNull()) - { - if (!hashValue.isString()) - return {rpcINVALID_PARAMS, "ledgerHashNotString"}; - - uint256 ledgerHash; - if (!ledgerHash.parseHex(hashValue.asString())) - return {rpcINVALID_PARAMS, "ledgerHashMalformed"}; - return getLedger(ledger, ledgerHash, context); - } - - if (!indexValue.isConvertibleTo(Json::stringValue)) - return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; - - auto const index = indexValue.asString(); - - if (index == "current" || index.empty()) - return getLedger(ledger, LedgerShortcut::CURRENT, context); - - if (index == "validated") - return getLedger(ledger, LedgerShortcut::VALIDATED, context); - - if (index == "closed") - return getLedger(ledger, LedgerShortcut::CLOSED, context); - - std::uint32_t val; - if (!beast::lexicalCastChecked(val, index)) - return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; - - return getLedger(ledger, val, context); -} -} // namespace - -template -Status -ledgerFromRequest(T& ledger, GRPCContext& context) -{ - R& request = context.params; - return ledgerFromSpecifier(ledger, request.ledger(), context); -} - -// explicit instantiation of above function -template Status -ledgerFromRequest<>( - std::shared_ptr&, - GRPCContext&); - -// explicit instantiation of above function -template Status -ledgerFromRequest<>( - std::shared_ptr&, - GRPCContext&); - -// explicit instantiation of above function -template Status -ledgerFromRequest<>( - std::shared_ptr&, - GRPCContext&); - -template -Status -ledgerFromSpecifier( - T& ledger, - org::xrpl::rpc::v1::LedgerSpecifier const& specifier, - Context& context) -{ - ledger.reset(); - - using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase; - LedgerCase ledgerCase = specifier.ledger_case(); - switch (ledgerCase) - { - case LedgerCase::kHash: { - if (auto hash = uint256::fromVoidChecked(specifier.hash())) - { - return getLedger(ledger, *hash, context); - } - return {rpcINVALID_PARAMS, "ledgerHashMalformed"}; - } - case LedgerCase::kSequence: - return getLedger(ledger, specifier.sequence(), context); - case LedgerCase::kShortcut: - [[fallthrough]]; - case LedgerCase::LEDGER_NOT_SET: { - auto const shortcut = specifier.shortcut(); - if (shortcut == - org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED) - { - return getLedger(ledger, LedgerShortcut::VALIDATED, context); - } - else - { - if (shortcut == - org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT || - shortcut == - org::xrpl::rpc::v1::LedgerSpecifier:: - SHORTCUT_UNSPECIFIED) - { - return getLedger(ledger, LedgerShortcut::CURRENT, context); - } - else if ( - shortcut == - org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED) - { - return getLedger(ledger, LedgerShortcut::CLOSED, context); - } - } - } - } - - return Status::OK; -} - -template -Status -getLedger(T& ledger, uint256 const& ledgerHash, Context& context) -{ - ledger = context.ledgerMaster.getLedgerByHash(ledgerHash); - if (ledger == nullptr) - return {rpcLGR_NOT_FOUND, "ledgerNotFound"}; - return Status::OK; -} - -template -Status -getLedger(T& ledger, uint32_t ledgerIndex, Context& context) -{ - ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex); - if (ledger == nullptr) - { - auto cur = context.ledgerMaster.getCurrentLedger(); - if (cur->info().seq == ledgerIndex) - { - ledger = cur; - } - } - - if (ledger == nullptr) - return {rpcLGR_NOT_FOUND, "ledgerNotFound"}; - - if (ledger->info().seq > context.ledgerMaster.getValidLedgerIndex() && - isValidatedOld(context.ledgerMaster, context.app.config().standalone())) - { - ledger.reset(); - if (context.apiVersion == 1) - return {rpcNO_NETWORK, "InsufficientNetworkMode"}; - return {rpcNOT_SYNCED, "notSynced"}; - } - - return Status::OK; -} - -template -Status -getLedger(T& ledger, LedgerShortcut shortcut, Context& context) -{ - if (isValidatedOld(context.ledgerMaster, context.app.config().standalone())) - { - if (context.apiVersion == 1) - return {rpcNO_NETWORK, "InsufficientNetworkMode"}; - return {rpcNOT_SYNCED, "notSynced"}; - } - - if (shortcut == LedgerShortcut::VALIDATED) - { - ledger = context.ledgerMaster.getValidatedLedger(); - if (ledger == nullptr) - { - if (context.apiVersion == 1) - return {rpcNO_NETWORK, "InsufficientNetworkMode"}; - return {rpcNOT_SYNCED, "notSynced"}; - } - - XRPL_ASSERT( - !ledger->open(), "ripple::RPC::getLedger : validated is not open"); - } - else - { - if (shortcut == LedgerShortcut::CURRENT) - { - ledger = context.ledgerMaster.getCurrentLedger(); - XRPL_ASSERT( - ledger->open(), "ripple::RPC::getLedger : current is open"); - } - else if (shortcut == LedgerShortcut::CLOSED) - { - ledger = context.ledgerMaster.getClosedLedger(); - XRPL_ASSERT( - !ledger->open(), "ripple::RPC::getLedger : closed is not open"); - } - else - { - return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; - } - - if (ledger == nullptr) - { - if (context.apiVersion == 1) - return {rpcNO_NETWORK, "InsufficientNetworkMode"}; - return {rpcNOT_SYNCED, "notSynced"}; - } - - static auto const minSequenceGap = 10; - - if (ledger->info().seq + minSequenceGap < - context.ledgerMaster.getValidLedgerIndex()) - { - ledger.reset(); - if (context.apiVersion == 1) - return {rpcNO_NETWORK, "InsufficientNetworkMode"}; - return {rpcNOT_SYNCED, "notSynced"}; - } - } - return Status::OK; -} - -// Explicit instantiation of above three functions -template Status -getLedger<>(std::shared_ptr&, uint32_t, Context&); - -template Status -getLedger<>( - std::shared_ptr&, - LedgerShortcut shortcut, - Context&); - -template Status -getLedger<>(std::shared_ptr&, uint256 const&, Context&); - -// The previous version of the lookupLedger command would accept the -// "ledger_index" argument as a string and silently treat it as a request to -// return the current ledger which, while not strictly wrong, could cause a lot -// of confusion. -// -// The code now robustly validates the input and ensures that the only possible -// values for the "ledger_index" parameter are the index of a ledger passed as -// an integer or one of the strings "current", "closed" or "validated". -// Additionally, the code ensures that the value passed in "ledger_hash" is a -// string and a valid hash. Invalid values will return an appropriate error -// code. -// -// In the absence of the "ledger_hash" or "ledger_index" parameters, the code -// assumes that "ledger_index" has the value "current". -// -// Returns a Json::objectValue. If there was an error, it will be in that -// return value. Otherwise, the object contains the field "validated" and -// optionally the fields "ledger_hash", "ledger_index" and -// "ledger_current_index", if they are defined. -Status -lookupLedger( - std::shared_ptr& ledger, - JsonContext& context, - Json::Value& result) -{ - if (auto status = ledgerFromRequest(ledger, context)) - return status; - - auto& info = ledger->info(); - - if (!ledger->open()) - { - result[jss::ledger_hash] = to_string(info.hash); - result[jss::ledger_index] = info.seq; - } - else - { - result[jss::ledger_current_index] = info.seq; - } - - result[jss::validated] = context.ledgerMaster.isValidated(*ledger); - return Status::OK; -} - -Json::Value -lookupLedger(std::shared_ptr& ledger, JsonContext& context) -{ - Json::Value result; - if (auto status = lookupLedger(ledger, context, result)) - status.inject(result); - - return result; -} - hash_set parseAccountIds(Json::Value const& jvArray) { @@ -988,123 +481,5 @@ isAccountObjectsValidType(LedgerEntryType const& type) } } -std::variant, Json::Value> -getLedgerByContext(RPC::JsonContext& context) -{ - auto const hasHash = context.params.isMember(jss::ledger_hash); - auto const hasIndex = context.params.isMember(jss::ledger_index); - std::uint32_t ledgerIndex = 0; - - auto& ledgerMaster = context.app.getLedgerMaster(); - LedgerHash ledgerHash; - - if ((hasHash && hasIndex) || !(hasHash || hasIndex)) - { - return RPC::make_param_error( - "Exactly one of ledger_hash and ledger_index can be set."); - } - - context.loadType = Resource::feeHeavyBurdenRPC; - - if (hasHash) - { - auto const& jsonHash = context.params[jss::ledger_hash]; - if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString())) - return RPC::invalid_field_error(jss::ledger_hash); - } - else - { - auto const& jsonIndex = context.params[jss::ledger_index]; - if (!jsonIndex.isInt()) - return RPC::invalid_field_error(jss::ledger_index); - - // We need a validated ledger to get the hash from the sequence - if (ledgerMaster.getValidatedLedgerAge() > - RPC::Tuning::maxValidatedLedgerAge) - { - if (context.apiVersion == 1) - return rpcError(rpcNO_CURRENT); - return rpcError(rpcNOT_SYNCED); - } - - ledgerIndex = jsonIndex.asInt(); - auto ledger = ledgerMaster.getValidatedLedger(); - - if (ledgerIndex >= ledger->info().seq) - return RPC::make_param_error("Ledger index too large"); - if (ledgerIndex <= 0) - return RPC::make_param_error("Ledger index too small"); - - auto const j = context.app.journal("RPCHandler"); - // Try to get the hash of the desired ledger from the validated - // ledger - auto neededHash = hashOfSeq(*ledger, ledgerIndex, j); - if (!neededHash) - { - // Find a ledger more likely to have the hash of the desired - // ledger - auto const refIndex = getCandidateLedger(ledgerIndex); - auto refHash = hashOfSeq(*ledger, refIndex, j); - XRPL_ASSERT( - refHash, - "ripple::RPC::getLedgerByContext : nonzero ledger hash"); - - ledger = ledgerMaster.getLedgerByHash(*refHash); - if (!ledger) - { - // We don't have the ledger we need to figure out which - // ledger they want. Try to get it. - - if (auto il = context.app.getInboundLedgers().acquire( - *refHash, refIndex, InboundLedger::Reason::GENERIC)) - { - Json::Value jvResult = RPC::make_error( - rpcLGR_NOT_FOUND, - "acquiring ledger containing requested index"); - jvResult[jss::acquiring] = - getJson(LedgerFill(*il, &context)); - return jvResult; - } - - if (auto il = context.app.getInboundLedgers().find(*refHash)) - { - Json::Value jvResult = RPC::make_error( - rpcLGR_NOT_FOUND, - "acquiring ledger containing requested index"); - jvResult[jss::acquiring] = il->getJson(0); - return jvResult; - } - - // Likely the app is shutting down - return Json::Value(); - } - - neededHash = hashOfSeq(*ledger, ledgerIndex, j); - } - XRPL_ASSERT( - neededHash, - "ripple::RPC::getLedgerByContext : nonzero needed hash"); - ledgerHash = neededHash ? *neededHash : beast::zero; // kludge - } - - // Try to get the desired ledger - // Verify all nodes even if we think we have it - auto ledger = context.app.getInboundLedgers().acquire( - ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); - - // In standalone mode, accept the ledger from the ledger cache - if (!ledger && context.app.config().standalone()) - ledger = ledgerMaster.getLedgerByHash(ledgerHash); - - if (ledger) - return ledger; - - if (auto il = context.app.getInboundLedgers().find(ledgerHash)) - return il->getJson(0); - - return RPC::make_error( - rpcNOT_READY, "findCreate failed to return an inbound ledger"); -} - } // namespace RPC } // namespace ripple diff --git a/src/xrpld/rpc/detail/RPCHelpers.h b/src/xrpld/rpc/detail/RPCHelpers.h index 9ee4376411..808b975de3 100644 --- a/src/xrpld/rpc/detail/RPCHelpers.h +++ b/src/xrpld/rpc/detail/RPCHelpers.h @@ -73,81 +73,6 @@ isRelatedToAccount( std::shared_ptr const& sle, AccountID const& accountID); -/** Gathers all objects for an account in a ledger. - @param ledger Ledger to search account objects. - @param account AccountID to find objects for. - @param typeFilter Gathers objects of these types. empty gathers all types. - @param dirIndex Begin gathering account objects from this directory. - @param entryIndex Begin gathering objects from this directory node. - @param limit Maximum number of objects to find. - @param jvResult A JSON result that holds the request objects. -*/ -bool -getAccountObjects( - ReadView const& ledger, - AccountID const& account, - std::optional> const& typeFilter, - uint256 dirIndex, - uint256 entryIndex, - std::uint32_t const limit, - Json::Value& jvResult); - -/** Get ledger by hash - If there is no error in the return value, the ledger pointer will have - been filled -*/ -template -Status -getLedger(T& ledger, uint256 const& ledgerHash, Context& context); - -/** Get ledger by sequence - If there is no error in the return value, the ledger pointer will have - been filled -*/ -template -Status -getLedger(T& ledger, uint32_t ledgerIndex, Context& context); - -enum LedgerShortcut { CURRENT, CLOSED, VALIDATED }; -/** Get ledger specified in shortcut. - If there is no error in the return value, the ledger pointer will have - been filled -*/ -template -Status -getLedger(T& ledger, LedgerShortcut shortcut, Context& context); - -/** Look up a ledger from a request and fill a Json::Result with either - an error, or data representing a ledger. - - If there is no error in the return value, then the ledger pointer will have - been filled. -*/ -Json::Value -lookupLedger(std::shared_ptr&, JsonContext&); - -/** Look up a ledger from a request and fill a Json::Result with the data - representing a ledger. - - If the returned Status is OK, the ledger pointer will have been filled. -*/ -Status -lookupLedger( - std::shared_ptr&, - JsonContext&, - Json::Value& result); - -template -Status -ledgerFromRequest(T& ledger, GRPCContext& context); - -template -Status -ledgerFromSpecifier( - T& ledger, - org::xrpl::rpc::v1::LedgerSpecifier const& specifier, - Context& context); - hash_set parseAccountIds(Json::Value const& jvArray); @@ -194,11 +119,6 @@ chooseLedgerEntryType(Json::Value const& params); bool isAccountObjectsValidType(LedgerEntryType const& type); -/** Return a ledger based on ledger_hash or ledger_index, - or an RPC error */ -std::variant, Json::Value> -getLedgerByContext(RPC::JsonContext& context); - std::optional> keypairForSignature( Json::Value const& params, diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp new file mode 100644 index 0000000000..8ba7eeddcd --- /dev/null +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.cpp @@ -0,0 +1,458 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace ripple { +namespace RPC { + +namespace { + +bool +isValidatedOld(LedgerMaster& ledgerMaster, bool standalone) +{ + if (standalone) + return false; + + return ledgerMaster.getValidatedLedgerAge() > Tuning::maxValidatedLedgerAge; +} + +template +Status +ledgerFromRequest(T& ledger, JsonContext& context) +{ + ledger.reset(); + + auto& params = context.params; + + auto indexValue = params[jss::ledger_index]; + auto hashValue = params[jss::ledger_hash]; + + // We need to support the legacy "ledger" field. + auto& legacyLedger = params[jss::ledger]; + if (legacyLedger) + { + if (legacyLedger.asString().size() > 12) + hashValue = legacyLedger; + else + indexValue = legacyLedger; + } + + if (!hashValue.isNull()) + { + if (!hashValue.isString()) + return {rpcINVALID_PARAMS, "ledgerHashNotString"}; + + uint256 ledgerHash; + if (!ledgerHash.parseHex(hashValue.asString())) + return {rpcINVALID_PARAMS, "ledgerHashMalformed"}; + return getLedger(ledger, ledgerHash, context); + } + + if (!indexValue.isConvertibleTo(Json::stringValue)) + return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + + auto const index = indexValue.asString(); + + if (index == "current" || index.empty()) + return getLedger(ledger, LedgerShortcut::CURRENT, context); + + if (index == "validated") + return getLedger(ledger, LedgerShortcut::VALIDATED, context); + + if (index == "closed") + return getLedger(ledger, LedgerShortcut::CLOSED, context); + + std::uint32_t val; + if (!beast::lexicalCastChecked(val, index)) + return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + + return getLedger(ledger, val, context); +} +} // namespace + +template +Status +ledgerFromRequest(T& ledger, GRPCContext& context) +{ + R& request = context.params; + return ledgerFromSpecifier(ledger, request.ledger(), context); +} + +// explicit instantiation of above function +template Status +ledgerFromRequest<>( + std::shared_ptr&, + GRPCContext&); + +// explicit instantiation of above function +template Status +ledgerFromRequest<>( + std::shared_ptr&, + GRPCContext&); + +// explicit instantiation of above function +template Status +ledgerFromRequest<>( + std::shared_ptr&, + GRPCContext&); + +template +Status +ledgerFromSpecifier( + T& ledger, + org::xrpl::rpc::v1::LedgerSpecifier const& specifier, + Context& context) +{ + ledger.reset(); + + using LedgerCase = org::xrpl::rpc::v1::LedgerSpecifier::LedgerCase; + LedgerCase ledgerCase = specifier.ledger_case(); + switch (ledgerCase) + { + case LedgerCase::kHash: { + if (auto hash = uint256::fromVoidChecked(specifier.hash())) + { + return getLedger(ledger, *hash, context); + } + return {rpcINVALID_PARAMS, "ledgerHashMalformed"}; + } + case LedgerCase::kSequence: + return getLedger(ledger, specifier.sequence(), context); + case LedgerCase::kShortcut: + [[fallthrough]]; + case LedgerCase::LEDGER_NOT_SET: { + auto const shortcut = specifier.shortcut(); + if (shortcut == + org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_VALIDATED) + { + return getLedger(ledger, LedgerShortcut::VALIDATED, context); + } + else + { + if (shortcut == + org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CURRENT || + shortcut == + org::xrpl::rpc::v1::LedgerSpecifier:: + SHORTCUT_UNSPECIFIED) + { + return getLedger(ledger, LedgerShortcut::CURRENT, context); + } + else if ( + shortcut == + org::xrpl::rpc::v1::LedgerSpecifier::SHORTCUT_CLOSED) + { + return getLedger(ledger, LedgerShortcut::CLOSED, context); + } + } + } + } + + return Status::OK; +} + +template +Status +getLedger(T& ledger, uint256 const& ledgerHash, Context& context) +{ + ledger = context.ledgerMaster.getLedgerByHash(ledgerHash); + if (ledger == nullptr) + return {rpcLGR_NOT_FOUND, "ledgerNotFound"}; + return Status::OK; +} + +template +Status +getLedger(T& ledger, uint32_t ledgerIndex, Context& context) +{ + ledger = context.ledgerMaster.getLedgerBySeq(ledgerIndex); + if (ledger == nullptr) + { + auto cur = context.ledgerMaster.getCurrentLedger(); + if (cur->info().seq == ledgerIndex) + { + ledger = cur; + } + } + + if (ledger == nullptr) + return {rpcLGR_NOT_FOUND, "ledgerNotFound"}; + + if (ledger->info().seq > context.ledgerMaster.getValidLedgerIndex() && + isValidatedOld(context.ledgerMaster, context.app.config().standalone())) + { + ledger.reset(); + if (context.apiVersion == 1) + return {rpcNO_NETWORK, "InsufficientNetworkMode"}; + return {rpcNOT_SYNCED, "notSynced"}; + } + + return Status::OK; +} + +template +Status +getLedger(T& ledger, LedgerShortcut shortcut, Context& context) +{ + if (isValidatedOld(context.ledgerMaster, context.app.config().standalone())) + { + if (context.apiVersion == 1) + return {rpcNO_NETWORK, "InsufficientNetworkMode"}; + return {rpcNOT_SYNCED, "notSynced"}; + } + + if (shortcut == LedgerShortcut::VALIDATED) + { + ledger = context.ledgerMaster.getValidatedLedger(); + if (ledger == nullptr) + { + if (context.apiVersion == 1) + return {rpcNO_NETWORK, "InsufficientNetworkMode"}; + return {rpcNOT_SYNCED, "notSynced"}; + } + + XRPL_ASSERT( + !ledger->open(), "ripple::RPC::getLedger : validated is not open"); + } + else + { + if (shortcut == LedgerShortcut::CURRENT) + { + ledger = context.ledgerMaster.getCurrentLedger(); + XRPL_ASSERT( + ledger->open(), "ripple::RPC::getLedger : current is open"); + } + else if (shortcut == LedgerShortcut::CLOSED) + { + ledger = context.ledgerMaster.getClosedLedger(); + XRPL_ASSERT( + !ledger->open(), "ripple::RPC::getLedger : closed is not open"); + } + else + { + return {rpcINVALID_PARAMS, "ledgerIndexMalformed"}; + } + + if (ledger == nullptr) + { + if (context.apiVersion == 1) + return {rpcNO_NETWORK, "InsufficientNetworkMode"}; + return {rpcNOT_SYNCED, "notSynced"}; + } + + static auto const minSequenceGap = 10; + + if (ledger->info().seq + minSequenceGap < + context.ledgerMaster.getValidLedgerIndex()) + { + ledger.reset(); + if (context.apiVersion == 1) + return {rpcNO_NETWORK, "InsufficientNetworkMode"}; + return {rpcNOT_SYNCED, "notSynced"}; + } + } + return Status::OK; +} + +// Explicit instantiation of above three functions +template Status +getLedger<>(std::shared_ptr&, uint32_t, Context&); + +template Status +getLedger<>( + std::shared_ptr&, + LedgerShortcut shortcut, + Context&); + +template Status +getLedger<>(std::shared_ptr&, uint256 const&, Context&); + +// The previous version of the lookupLedger command would accept the +// "ledger_index" argument as a string and silently treat it as a request to +// return the current ledger which, while not strictly wrong, could cause a lot +// of confusion. +// +// The code now robustly validates the input and ensures that the only possible +// values for the "ledger_index" parameter are the index of a ledger passed as +// an integer or one of the strings "current", "closed" or "validated". +// Additionally, the code ensures that the value passed in "ledger_hash" is a +// string and a valid hash. Invalid values will return an appropriate error +// code. +// +// In the absence of the "ledger_hash" or "ledger_index" parameters, the code +// assumes that "ledger_index" has the value "current". +// +// Returns a Json::objectValue. If there was an error, it will be in that +// return value. Otherwise, the object contains the field "validated" and +// optionally the fields "ledger_hash", "ledger_index" and +// "ledger_current_index", if they are defined. +Status +lookupLedger( + std::shared_ptr& ledger, + JsonContext& context, + Json::Value& result) +{ + if (auto status = ledgerFromRequest(ledger, context)) + return status; + + auto& info = ledger->info(); + + if (!ledger->open()) + { + result[jss::ledger_hash] = to_string(info.hash); + result[jss::ledger_index] = info.seq; + } + else + { + result[jss::ledger_current_index] = info.seq; + } + + result[jss::validated] = context.ledgerMaster.isValidated(*ledger); + return Status::OK; +} + +Json::Value +lookupLedger(std::shared_ptr& ledger, JsonContext& context) +{ + Json::Value result; + if (auto status = lookupLedger(ledger, context, result)) + status.inject(result); + + return result; +} + +std::variant, Json::Value> +getLedgerByContext(RPC::JsonContext& context) +{ + auto const hasHash = context.params.isMember(jss::ledger_hash); + auto const hasIndex = context.params.isMember(jss::ledger_index); + std::uint32_t ledgerIndex = 0; + + auto& ledgerMaster = context.app.getLedgerMaster(); + LedgerHash ledgerHash; + + if ((hasHash && hasIndex) || !(hasHash || hasIndex)) + { + return RPC::make_param_error( + "Exactly one of ledger_hash and ledger_index can be set."); + } + + context.loadType = Resource::feeHeavyBurdenRPC; + + if (hasHash) + { + auto const& jsonHash = context.params[jss::ledger_hash]; + if (!jsonHash.isString() || !ledgerHash.parseHex(jsonHash.asString())) + return RPC::invalid_field_error(jss::ledger_hash); + } + else + { + auto const& jsonIndex = context.params[jss::ledger_index]; + if (!jsonIndex.isInt()) + return RPC::invalid_field_error(jss::ledger_index); + + // We need a validated ledger to get the hash from the sequence + if (ledgerMaster.getValidatedLedgerAge() > + RPC::Tuning::maxValidatedLedgerAge) + { + if (context.apiVersion == 1) + return rpcError(rpcNO_CURRENT); + return rpcError(rpcNOT_SYNCED); + } + + ledgerIndex = jsonIndex.asInt(); + auto ledger = ledgerMaster.getValidatedLedger(); + + if (ledgerIndex >= ledger->info().seq) + return RPC::make_param_error("Ledger index too large"); + if (ledgerIndex <= 0) + return RPC::make_param_error("Ledger index too small"); + + auto const j = context.app.journal("RPCHandler"); + // Try to get the hash of the desired ledger from the validated + // ledger + auto neededHash = hashOfSeq(*ledger, ledgerIndex, j); + if (!neededHash) + { + // Find a ledger more likely to have the hash of the desired + // ledger + auto const refIndex = getCandidateLedger(ledgerIndex); + auto refHash = hashOfSeq(*ledger, refIndex, j); + XRPL_ASSERT( + refHash, + "ripple::RPC::getLedgerByContext : nonzero ledger hash"); + + ledger = ledgerMaster.getLedgerByHash(*refHash); + if (!ledger) + { + // We don't have the ledger we need to figure out which + // ledger they want. Try to get it. + + if (auto il = context.app.getInboundLedgers().acquire( + *refHash, refIndex, InboundLedger::Reason::GENERIC)) + { + Json::Value jvResult = RPC::make_error( + rpcLGR_NOT_FOUND, + "acquiring ledger containing requested index"); + jvResult[jss::acquiring] = + getJson(LedgerFill(*il, &context)); + return jvResult; + } + + if (auto il = context.app.getInboundLedgers().find(*refHash)) + { + Json::Value jvResult = RPC::make_error( + rpcLGR_NOT_FOUND, + "acquiring ledger containing requested index"); + jvResult[jss::acquiring] = il->getJson(0); + return jvResult; + } + + // Likely the app is shutting down + return Json::Value(); + } + + neededHash = hashOfSeq(*ledger, ledgerIndex, j); + } + XRPL_ASSERT( + neededHash, + "ripple::RPC::getLedgerByContext : nonzero needed hash"); + ledgerHash = neededHash ? *neededHash : beast::zero; // kludge + } + + // Try to get the desired ledger + // Verify all nodes even if we think we have it + auto ledger = context.app.getInboundLedgers().acquire( + ledgerHash, ledgerIndex, InboundLedger::Reason::GENERIC); + + // In standalone mode, accept the ledger from the ledger cache + if (!ledger && context.app.config().standalone()) + ledger = ledgerMaster.getLedgerByHash(ledgerHash); + + if (ledger) + return ledger; + + if (auto il = context.app.getInboundLedgers().find(ledgerHash)) + return il->getJson(0); + + return RPC::make_error( + rpcNOT_READY, "findCreate failed to return an inbound ledger"); +} + +} // namespace RPC +} // namespace ripple diff --git a/src/xrpld/rpc/detail/RPCLedgerHelpers.h b/src/xrpld/rpc/detail/RPCLedgerHelpers.h new file mode 100644 index 0000000000..17382d3c53 --- /dev/null +++ b/src/xrpld/rpc/detail/RPCLedgerHelpers.h @@ -0,0 +1,96 @@ +#ifndef XRPL_RPC_RPCLEDGERHELPERS_H_INCLUDED +#define XRPL_RPC_RPCLEDGERHELPERS_H_INCLUDED + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace Json { +class Value; +} + +namespace ripple { + +class ReadView; +class Transaction; + +namespace RPC { + +struct JsonContext; + +/** Get ledger by hash + If there is no error in the return value, the ledger pointer will have + been filled +*/ +template +Status +getLedger(T& ledger, uint256 const& ledgerHash, Context& context); + +/** Get ledger by sequence + If there is no error in the return value, the ledger pointer will have + been filled +*/ +template +Status +getLedger(T& ledger, uint32_t ledgerIndex, Context& context); + +enum LedgerShortcut { CURRENT, CLOSED, VALIDATED }; +/** Get ledger specified in shortcut. + If there is no error in the return value, the ledger pointer will have + been filled +*/ +template +Status +getLedger(T& ledger, LedgerShortcut shortcut, Context& context); + +/** Look up a ledger from a request and fill a Json::Result with either + an error, or data representing a ledger. + + If there is no error in the return value, then the ledger pointer will have + been filled. +*/ +Json::Value +lookupLedger(std::shared_ptr&, JsonContext&); + +/** Look up a ledger from a request and fill a Json::Result with the data + representing a ledger. + + If the returned Status is OK, the ledger pointer will have been filled. +*/ +Status +lookupLedger( + std::shared_ptr&, + JsonContext&, + Json::Value& result); + +template +Status +ledgerFromRequest(T& ledger, GRPCContext& context); + +template +Status +ledgerFromSpecifier( + T& ledger, + org::xrpl::rpc::v1::LedgerSpecifier const& specifier, + Context& context); + +/** Return a ledger based on ledger_hash or ledger_index, + or an RPC error */ +std::variant, Json::Value> +getLedgerByContext(RPC::JsonContext& context); + +} // namespace RPC + +} // namespace ripple + +#endif diff --git a/src/xrpld/rpc/handlers/AMMInfo.cpp b/src/xrpld/rpc/handlers/AMMInfo.cpp index 01e9f0cd16..dda30fcfa7 100644 --- a/src/xrpld/rpc/handlers/AMMInfo.cpp +++ b/src/xrpld/rpc/handlers/AMMInfo.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/AccountChannels.cpp b/src/xrpld/rpc/handlers/AccountChannels.cpp index e1f86630c5..281b0ec8c4 100644 --- a/src/xrpld/rpc/handlers/AccountChannels.cpp +++ b/src/xrpld/rpc/handlers/AccountChannels.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp b/src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp index ca165e7592..10f22ad654 100644 --- a/src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp +++ b/src/xrpld/rpc/handlers/AccountCurrenciesHandler.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/AccountInfo.cpp b/src/xrpld/rpc/handlers/AccountInfo.cpp index 8c07f20581..3caafc411b 100644 --- a/src/xrpld/rpc/handlers/AccountInfo.cpp +++ b/src/xrpld/rpc/handlers/AccountInfo.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/AccountLines.cpp b/src/xrpld/rpc/handlers/AccountLines.cpp index 22ca1bc2f9..b28f4d7483 100644 --- a/src/xrpld/rpc/handlers/AccountLines.cpp +++ b/src/xrpld/rpc/handlers/AccountLines.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/AccountObjects.cpp b/src/xrpld/rpc/handlers/AccountObjects.cpp index f68ab5dfae..82ff1782e4 100644 --- a/src/xrpld/rpc/handlers/AccountObjects.cpp +++ b/src/xrpld/rpc/handlers/AccountObjects.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -158,6 +159,198 @@ doAccountNFTs(RPC::JsonContext& context) return result; } +bool +getAccountObjects( + ReadView const& ledger, + AccountID const& account, + std::optional> const& typeFilter, + uint256 dirIndex, + uint256 entryIndex, + std::uint32_t const limit, + Json::Value& jvResult) +{ + // check if dirIndex is valid + if (!dirIndex.isZero() && !ledger.read({ltDIR_NODE, dirIndex})) + return false; + + auto typeMatchesFilter = [](std::vector const& typeFilter, + LedgerEntryType ledgerType) { + auto it = std::find(typeFilter.begin(), typeFilter.end(), ledgerType); + return it != typeFilter.end(); + }; + + // if dirIndex != 0, then all NFTs have already been returned. only + // iterate NFT pages if the filter says so AND dirIndex == 0 + bool iterateNFTPages = + (!typeFilter.has_value() || + typeMatchesFilter(typeFilter.value(), ltNFTOKEN_PAGE)) && + dirIndex == beast::zero; + + Keylet const firstNFTPage = keylet::nftpage_min(account); + + // we need to check the marker to see if it is an NFTTokenPage index. + if (iterateNFTPages && entryIndex != beast::zero) + { + // if it is we will try to iterate the pages up to the limit + // and then change over to the owner directory + + if (firstNFTPage.key != (entryIndex & ~nft::pageMask)) + iterateNFTPages = false; + } + + auto& jvObjects = (jvResult[jss::account_objects] = Json::arrayValue); + + // this is a mutable version of limit, used to seamlessly switch + // to iterating directory entries when nftokenpages are exhausted + uint32_t mlimit = limit; + + // iterate NFTokenPages preferentially + if (iterateNFTPages) + { + Keylet const first = entryIndex == beast::zero + ? firstNFTPage + : Keylet{ltNFTOKEN_PAGE, entryIndex}; + + Keylet const last = keylet::nftpage_max(account); + + // current key + uint256 ck = ledger.succ(first.key, last.key.next()).value_or(last.key); + + // current page + auto cp = ledger.read(Keylet{ltNFTOKEN_PAGE, ck}); + + while (cp) + { + jvObjects.append(cp->getJson(JsonOptions::none)); + auto const npm = (*cp)[~sfNextPageMin]; + if (npm) + cp = ledger.read(Keylet(ltNFTOKEN_PAGE, *npm)); + else + cp = nullptr; + + if (--mlimit == 0) + { + if (cp) + { + jvResult[jss::limit] = limit; + jvResult[jss::marker] = std::string("0,") + to_string(ck); + return true; + } + } + + if (!npm) + break; + + ck = *npm; + } + + // if execution reaches here then we're about to transition + // to iterating the root directory (and the conventional + // behaviour of this RPC function.) Therefore we should + // zero entryIndex so as not to terribly confuse things. + entryIndex = beast::zero; + } + + auto const root = keylet::ownerDir(account); + auto found = false; + + if (dirIndex.isZero()) + { + dirIndex = root.key; + found = true; + } + + auto dir = ledger.read({ltDIR_NODE, dirIndex}); + if (!dir) + { + // it's possible the user had nftoken pages but no + // directory entries. If there's no nftoken page, we will + // give empty array for account_objects. + if (mlimit >= limit) + jvResult[jss::account_objects] = Json::arrayValue; + + // non-zero dirIndex validity was checked in the beginning of this + // function; by this point, it should be zero. This function returns + // true regardless of nftoken page presence; if absent, account_objects + // is already set as an empty array. Notice we will only return false in + // this function when entryIndex can not be found, indicating an invalid + // marker error. + return true; + } + + std::uint32_t i = 0; + for (;;) + { + auto const& entries = dir->getFieldV256(sfIndexes); + auto iter = entries.begin(); + + if (!found) + { + iter = std::find(iter, entries.end(), entryIndex); + if (iter == entries.end()) + return false; + + found = true; + } + + // it's possible that the returned NFTPages exactly filled the + // response. Check for that condition. + if (i == mlimit && mlimit < limit) + { + jvResult[jss::limit] = limit; + jvResult[jss::marker] = + to_string(dirIndex) + ',' + to_string(*iter); + return true; + } + + for (; iter != entries.end(); ++iter) + { + auto const sleNode = ledger.read(keylet::child(*iter)); + + if (!typeFilter.has_value() || + typeMatchesFilter(typeFilter.value(), sleNode->getType())) + { + jvObjects.append(sleNode->getJson(JsonOptions::none)); + } + + if (++i == mlimit) + { + if (++iter != entries.end()) + { + jvResult[jss::limit] = limit; + jvResult[jss::marker] = + to_string(dirIndex) + ',' + to_string(*iter); + return true; + } + + break; + } + } + + auto const nodeIndex = dir->getFieldU64(sfIndexNext); + if (nodeIndex == 0) + return true; + + dirIndex = keylet::page(root, nodeIndex).key; + dir = ledger.read({ltDIR_NODE, dirIndex}); + if (!dir) + return true; + + if (i == mlimit) + { + auto const& e = dir->getFieldV256(sfIndexes); + if (!e.empty()) + { + jvResult[jss::limit] = limit; + jvResult[jss::marker] = + to_string(dirIndex) + ',' + to_string(*e.begin()); + } + + return true; + } + } +} + Json::Value doAccountObjects(RPC::JsonContext& context) { @@ -265,7 +458,7 @@ doAccountObjects(RPC::JsonContext& context) return RPC::invalid_field_error(jss::marker); } - if (!RPC::getAccountObjects( + if (!getAccountObjects( *ledger, accountID, typeFilter, diff --git a/src/xrpld/rpc/handlers/AccountOffers.cpp b/src/xrpld/rpc/handlers/AccountOffers.cpp index acba153554..e8bc2bcf79 100644 --- a/src/xrpld/rpc/handlers/AccountOffers.cpp +++ b/src/xrpld/rpc/handlers/AccountOffers.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/BookOffers.cpp b/src/xrpld/rpc/handlers/BookOffers.cpp index d0544460d1..e82b2fa345 100644 --- a/src/xrpld/rpc/handlers/BookOffers.cpp +++ b/src/xrpld/rpc/handlers/BookOffers.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/DepositAuthorized.cpp b/src/xrpld/rpc/handlers/DepositAuthorized.cpp index 7490dbea94..707db63e36 100644 --- a/src/xrpld/rpc/handlers/DepositAuthorized.cpp +++ b/src/xrpld/rpc/handlers/DepositAuthorized.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/GatewayBalances.cpp b/src/xrpld/rpc/handlers/GatewayBalances.cpp index 3e614c2a24..88411b020e 100644 --- a/src/xrpld/rpc/handlers/GatewayBalances.cpp +++ b/src/xrpld/rpc/handlers/GatewayBalances.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/GetAggregatePrice.cpp b/src/xrpld/rpc/handlers/GetAggregatePrice.cpp index a1121e260b..c74a4331fd 100644 --- a/src/xrpld/rpc/handlers/GetAggregatePrice.cpp +++ b/src/xrpld/rpc/handlers/GetAggregatePrice.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/LedgerData.cpp b/src/xrpld/rpc/handlers/LedgerData.cpp index d5f27a138e..c5e39f69b4 100644 --- a/src/xrpld/rpc/handlers/LedgerData.cpp +++ b/src/xrpld/rpc/handlers/LedgerData.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/LedgerDiff.cpp b/src/xrpld/rpc/handlers/LedgerDiff.cpp index d713f43bfd..3628138329 100644 --- a/src/xrpld/rpc/handlers/LedgerDiff.cpp +++ b/src/xrpld/rpc/handlers/LedgerDiff.cpp @@ -1,5 +1,5 @@ #include -#include +#include namespace ripple { std::pair diff --git a/src/xrpld/rpc/handlers/LedgerEntry.cpp b/src/xrpld/rpc/handlers/LedgerEntry.cpp index 78655f1c9f..992c5551c3 100644 --- a/src/xrpld/rpc/handlers/LedgerEntry.cpp +++ b/src/xrpld/rpc/handlers/LedgerEntry.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/LedgerHandler.cpp b/src/xrpld/rpc/handlers/LedgerHandler.cpp index f282d0ddd3..4483838fa0 100644 --- a/src/xrpld/rpc/handlers/LedgerHandler.cpp +++ b/src/xrpld/rpc/handlers/LedgerHandler.cpp @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/LedgerHeader.cpp b/src/xrpld/rpc/handlers/LedgerHeader.cpp index 193ca355d3..ebbd6beb01 100644 --- a/src/xrpld/rpc/handlers/LedgerHeader.cpp +++ b/src/xrpld/rpc/handlers/LedgerHeader.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/LedgerRequest.cpp b/src/xrpld/rpc/handlers/LedgerRequest.cpp index ea3208605f..fd06ab30ba 100644 --- a/src/xrpld/rpc/handlers/LedgerRequest.cpp +++ b/src/xrpld/rpc/handlers/LedgerRequest.cpp @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/NFTOffers.cpp b/src/xrpld/rpc/handlers/NFTOffers.cpp index 6d11ff5b0a..51290a404c 100644 --- a/src/xrpld/rpc/handlers/NFTOffers.cpp +++ b/src/xrpld/rpc/handlers/NFTOffers.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/NoRippleCheck.cpp b/src/xrpld/rpc/handlers/NoRippleCheck.cpp index e5c1170969..9ff2757a40 100644 --- a/src/xrpld/rpc/handlers/NoRippleCheck.cpp +++ b/src/xrpld/rpc/handlers/NoRippleCheck.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include diff --git a/src/xrpld/rpc/handlers/RipplePathFind.cpp b/src/xrpld/rpc/handlers/RipplePathFind.cpp index 1cfea8dd5c..771c1cb95f 100644 --- a/src/xrpld/rpc/handlers/RipplePathFind.cpp +++ b/src/xrpld/rpc/handlers/RipplePathFind.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/TransactionEntry.cpp b/src/xrpld/rpc/handlers/TransactionEntry.cpp index 5dd8d685e1..9553c2d41e 100644 --- a/src/xrpld/rpc/handlers/TransactionEntry.cpp +++ b/src/xrpld/rpc/handlers/TransactionEntry.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/xrpld/rpc/handlers/VaultInfo.cpp b/src/xrpld/rpc/handlers/VaultInfo.cpp index 3e8226352a..7e040fef82 100644 --- a/src/xrpld/rpc/handlers/VaultInfo.cpp +++ b/src/xrpld/rpc/handlers/VaultInfo.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include