From fc73fbd0509aacc9d3531f76ab75b5a974d8c673 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Thu, 11 Aug 2016 01:06:32 -0700 Subject: [PATCH] Remove old-style pathfinding code All cases that still used the old RPF code now use new-style pathfinding. This includes unit tests, RPF requests with a ledger specified, and RPF requests in standalone mode. --- Builds/VisualStudio2015/RippleD.vcxproj | 2 - .../VisualStudio2015/RippleD.vcxproj.filters | 3 - src/ripple/app/paths/PathRequest.h | 5 + src/ripple/app/paths/PathRequests.cpp | 21 +- src/ripple/app/paths/PathRequests.h | 12 + src/ripple/app/tests/Path_test.cpp | 120 +++--- src/ripple/resource/impl/Consumer.cpp | 8 +- src/ripple/rpc/RipplePathFind.h | 39 -- src/ripple/rpc/handlers/RipplePathFind.cpp | 381 +----------------- 9 files changed, 122 insertions(+), 469 deletions(-) delete mode 100644 src/ripple/rpc/RipplePathFind.h diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj index 8944f2dc30..4e0cef95be 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj +++ b/Builds/VisualStudio2015/RippleD.vcxproj @@ -3355,8 +3355,6 @@ - - diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters index 5a2880c876..429e3f8e0c 100644 --- a/Builds/VisualStudio2015/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters @@ -3813,9 +3813,6 @@ ripple\rpc - - ripple\rpc - ripple\rpc diff --git a/src/ripple/app/paths/PathRequest.h b/src/ripple/app/paths/PathRequest.h index 372cbe5679..4d2fa1ecf7 100644 --- a/src/ripple/app/paths/PathRequest.h +++ b/src/ripple/app/paths/PathRequest.h @@ -59,6 +59,9 @@ public: public: // VFALCO TODO Break the cyclic dependency on InfoSub + + // path_find semantics + // Subscriber is updated PathRequest ( Application& app, std::shared_ptr const& subscriber, @@ -66,6 +69,8 @@ public: PathRequests&, beast::Journal journal); + // ripple_path_find semantics + // Completion function is called PathRequest ( Application& app, std::function const& completion, diff --git a/src/ripple/app/paths/PathRequests.cpp b/src/ripple/app/paths/PathRequests.cpp index fc57364095..14726d364e 100644 --- a/src/ripple/app/paths/PathRequests.cpp +++ b/src/ripple/app/paths/PathRequests.cpp @@ -218,7 +218,7 @@ PathRequests::makePathRequest( insertPathRequest (req); app_.getLedgerMaster().newPathRequest(); } - return result.second; + return std::move (result.second); } // Make an old-style ripple_path_find request @@ -249,7 +249,24 @@ PathRequests::makeLegacyPathRequest( app_.getLedgerMaster().newPathRequest(); } - return result.second; + return std::move (result.second); +} + +Json::Value +PathRequests::doLegacyPathRequest ( + Resource::Consumer& consumer, + std::shared_ptr const& inLedger, + Json::Value const& request) +{ + auto cache = std::make_shared (inLedger); + + auto req = std::make_shared (app_, []{}, + consumer, ++mLastIdentifier, *this, mJournal); + + auto result = req->doCreate (cache, request); + if (result.first) + result.second = req->doUpdate (cache, false); + return std::move (result.second); } } // ripple diff --git a/src/ripple/app/paths/PathRequests.h b/src/ripple/app/paths/PathRequests.h index eb8749d2f4..c8359566fb 100644 --- a/src/ripple/app/paths/PathRequests.h +++ b/src/ripple/app/paths/PathRequests.h @@ -49,11 +49,16 @@ public: std::shared_ptr getLineCache ( std::shared_ptr const& ledger, bool authoritative); + // Create a new-style path request that pushes + // updates to a subscriber Json::Value makePathRequest ( std::shared_ptr const& subscriber, std::shared_ptr const& ledger, Json::Value const& request); + // Create an old-style path request that is + // managed by a coroutine and updated by + // the path engine Json::Value makeLegacyPathRequest ( PathRequest::pointer& req, std::function completion, @@ -61,6 +66,13 @@ public: std::shared_ptr const& inLedger, Json::Value const& request); + // Execute an old-style path request immediately + // with the ledger specified by the caller + Json::Value doLegacyPathRequest ( + Resource::Consumer& consumer, + std::shared_ptr const& inLedger, + Json::Value const& request); + void reportFast (std::chrono::milliseconds ms) { mFast.notify (ms); diff --git a/src/ripple/app/tests/Path_test.cpp b/src/ripple/app/tests/Path_test.cpp index 3cf14e05c5..f4a562e329 100644 --- a/src/ripple/app/tests/Path_test.cpp +++ b/src/ripple/app/tests/Path_test.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -143,52 +142,6 @@ equal(STAmount const& sa1, STAmount const& sa2) sa1.issue().account == sa2.issue().account; } -std::tuple -find_paths(jtx::Env& env, - jtx::Account const& src, jtx::Account const& dst, - STAmount const& saDstAmount, - boost::optional const& saSendMax = boost::none) -{ - static int const level = 8; - auto const& view = env.current(); - auto cache = std::make_shared(view); - auto currencies = accountSourceCurrencies(src, cache, true); - auto jvSrcCurrencies = Json::Value(Json::arrayValue); - for (auto const& c : currencies) - { - Json::Value jvCurrency = Json::objectValue; - jvCurrency[jss::currency] = to_string(c); - jvSrcCurrencies.append(jvCurrency); - } - auto const convert_all = - saDstAmount == STAmount(saDstAmount.issue(), 1u, 0, true); - auto result = ripplePathFind(cache, src.id(), dst.id(), - (convert_all ? STAmount(saDstAmount.issue(), STAmount::cMaxValue, - STAmount::cMaxOffset) : saDstAmount), jvSrcCurrencies, boost::none, - level, saSendMax, convert_all, env.app()); - if (! result.first) - { - Throw ( - "Path_test::findPath: ripplePathFind find failed"); - } - auto const& jv = result.second[0u]; - Json::Value paths; - paths["Paths"] = jv["paths_computed"]; - STParsedJSONObject stp("generic", paths); - - STAmount sa; - if (jv.isMember(jss::source_amount)) - sa = amountFromJson(sfGeneric, jv[jss::source_amount]); - - STAmount da; - if (jv.isMember(jss::destination_amount)) - da = amountFromJson(sfGeneric, jv[jss::destination_amount]); - - return std::make_tuple( - std::move(stp.object->getFieldPathSet(sfPaths)), - std::move(sa), std::move(da)); -} - Json::Value rpf(jtx::Account const& src, jtx::Account const& dst, std::uint32_t num_src) { @@ -252,6 +205,79 @@ public: } }; + std::tuple + find_paths(jtx::Env& env, + jtx::Account const& src, jtx::Account const& dst, + STAmount const& saDstAmount, + boost::optional const& saSendMax = boost::none) + { + using namespace jtx; + + auto& app = env.app(); + Resource::Charge loadType = Resource::feeReferenceRPC; + Resource::Consumer c; + RPC::Context context {beast::Journal(), {}, app, loadType, + app.getOPs(), app.getLedgerMaster(), c, Role::USER, {}}; + + Json::Value params = Json::objectValue; + params[jss::command] = "ripple_path_find"; + params[jss::source_account] = toBase58 (src); + params[jss::destination_account] = toBase58 (dst); + params[jss::destination_amount] = saDstAmount.getJson(0); + if (saSendMax) + params[jss::send_max] = saSendMax->getJson(0); + + Json::Value result; + gate g; + app.getJobQueue().postCoro(jtCLIENT, "RPC-Client", + [&](auto const& coro) + { + context.params = std::move (params); + context.jobCoro = coro; + RPC::doCommand (context, result); + g.signal(); + }); + + BEAST_EXPECT(g.wait_for(5s)); + BEAST_EXPECT(! result.isMember(jss::error)); + + STAmount da; + if (result.isMember(jss::destination_amount)) + da = amountFromJson(sfGeneric, + result[jss::destination_amount]); + + STAmount sa; + STPathSet paths; + if (result.isMember(jss::alternatives)) + { + auto const& alts = result[jss::alternatives]; + if (alts.size() > 0) + { + auto const& path = alts[0u]; + + if (path.isMember(jss::source_amount)) + sa = amountFromJson(sfGeneric, + path[jss::source_amount]); + + + if (path.isMember(jss::destination_amount)) + da = amountFromJson(sfGeneric, + path[jss::destination_amount]); + + if (path.isMember(jss::paths_computed)) + { + Json::Value p; + p["Paths"] = path[jss::paths_computed]; + STParsedJSONObject po("generic", p); + paths = po.object->getFieldPathSet (sfPaths); + } + } + } + + return std::make_tuple( + std::move(paths), std::move(sa), std::move(da)); + } + void source_currencies_limit() { diff --git a/src/ripple/resource/impl/Consumer.cpp b/src/ripple/resource/impl/Consumer.cpp index cd8654a97f..45eaae2683 100644 --- a/src/ripple/resource/impl/Consumer.cpp +++ b/src/ripple/resource/impl/Consumer.cpp @@ -98,8 +98,12 @@ Disposition Consumer::disposition() const Disposition Consumer::charge (Charge const& what) { - assert (m_entry != nullptr); - return m_logic->charge (*m_entry, what); + Disposition d = ok; + + if (m_logic && m_entry) + d = m_logic->charge (*m_entry, what); + + return d; } bool Consumer::warn () diff --git a/src/ripple/rpc/RipplePathFind.h b/src/ripple/rpc/RipplePathFind.h deleted file mode 100644 index 4e23843d59..0000000000 --- a/src/ripple/rpc/RipplePathFind.h +++ /dev/null @@ -1,39 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2015 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#ifndef RIPPLE_RPC_RIPPLEPATHFIND_H_INCLUDED -#define RIPPLE_RPC_RIPPLEPATHFIND_H_INCLUDED - -#include -#include - -namespace ripple { - -std::pair -ripplePathFind (std::shared_ptr const& cache, - AccountID const& raSrc, AccountID const& raDst, - STAmount const& saDstAmount, - Json::Value const& jvSrcCurrencies, - boost::optional const& contextPaths, - int const level, boost::optional saSendMax, - bool convert_all, Application& app); - -} - -#endif diff --git a/src/ripple/rpc/handlers/RipplePathFind.cpp b/src/ripple/rpc/handlers/RipplePathFind.cpp index dc1b30ceac..c3998262fc 100644 --- a/src/ripple/rpc/handlers/RipplePathFind.cpp +++ b/src/ripple/rpc/handlers/RipplePathFind.cpp @@ -18,7 +18,6 @@ //============================================================================== #include -#include #include #include #include @@ -39,7 +38,6 @@ #include #include #include -#include #include #include #include @@ -65,6 +63,8 @@ Json::Value doRipplePathFind (RPC::Context& context) ! context.params.isMember(jss::ledger_index) && ! context.params.isMember(jss::ledger_hash)) { + // No ledger specified, use pathfinding defaults + // and dispatch to pathfinding engine if (context.app.getLedgerMaster().getValidatedLedgerAge() > RPC::Tuning::maxValidatedLedgerAge) { @@ -95,380 +95,13 @@ Json::Value doRipplePathFind (RPC::Context& context) if (! lpf.isOk ()) return rpcError (rpcTOO_BUSY); - AccountID raSrc; - AccountID raDst; - STAmount saDstAmount; - if (! context.params.isMember (jss::source_account)) - { - jvResult = rpcError (rpcSRC_ACT_MISSING); - } - else if (! deprecatedParseBase58(raSrc, - context.params[jss::source_account])) - { - jvResult = rpcError (rpcSRC_ACT_MALFORMED); - } - else if (!context.params.isMember (jss::destination_account)) - { - jvResult = rpcError (rpcDST_ACT_MISSING); - } - else if (! deprecatedParseBase58 (raDst, - context.params[jss::destination_account])) - { - jvResult = rpcError (rpcDST_ACT_MALFORMED); - } - else if ( - // Parse saDstAmount. - !context.params.isMember (jss::destination_amount) - || ! amountFromJsonNoThrow(saDstAmount, context.params[jss::destination_amount]) - || (saDstAmount <= zero && saDstAmount != - STAmount(saDstAmount.issue(), 1u, 0, true)) - || (!isXRP(saDstAmount.getCurrency ()) - && (!saDstAmount.getIssuer () || - noAccount() == saDstAmount.getIssuer ()))) - { - JLOG (context.j.info()) << "Bad destination_amount."; - jvResult = rpcError (rpcINVALID_PARAMS); - } - else if ( - // Checks on source_currencies. - context.params.isMember(jss::source_currencies) && - (! context.params[jss::source_currencies].isArray() || - context.params[jss::source_currencies].size() == 0 || - context.params[jss::source_currencies].size() > - RPC::Tuning::max_src_cur)) - { - JLOG (context.j.info()) << "Bad source_currencies."; - jvResult = rpcError(rpcINVALID_PARAMS); - } - else - { - std::shared_ptr cache; + auto result = context.app.getPathRequests().doLegacyPathRequest ( + context.consumer, lpLedger, context.params); - if (lpLedger) - { - // The caller specified a ledger - cache = std::make_shared(lpLedger); - } - else - { - // The closed ledger is recent and any nodes made resident - // have the best chance to persist - lpLedger = context.ledgerMaster.getClosedLedger(); - cache = context.app.getPathRequests().getLineCache(lpLedger, false); - } + for (auto &fieldName : jvResult.getMemberNames ()) + result[fieldName] = std::move (jvResult[fieldName]); - Json::Value jvSrcCurrencies; - if (context.params.isMember (jss::source_currencies)) - { - jvSrcCurrencies = context.params[jss::source_currencies]; - } - else - { - auto currencies = accountSourceCurrencies(raSrc, cache, true); - if (currencies.size() > RPC::Tuning::max_auto_src_cur) - return rpcError(rpcINTERNAL); - auto jvSrcCurrencies = Json::Value(Json::arrayValue); - for (auto const& c : currencies) - { - Json::Value jvCurrency(Json::objectValue); - jvCurrency[jss::currency] = to_string(c); - jvSrcCurrencies.append(jvCurrency); - } - } - - // Fill in currencies destination will accept - Json::Value jvDestCur (Json::arrayValue); - - // TODO(tom): this could be optimized the same way that - // PathRequest::doUpdate() is - if we don't obsolete this code first. - auto usDestCurrID = accountDestCurrencies (raDst, cache, true); - for (auto const& uCurrency: usDestCurrID) - jvDestCur.append (to_string (uCurrency)); - - jvResult[jss::destination_currencies] = jvDestCur; - jvResult[jss::destination_account] = context.app.accountIDCache().toBase58(raDst); - - int level = context.app.config().PATH_SEARCH_OLD; - if ((context.app.config().PATH_SEARCH_MAX > level) - && !context.app.getFeeTrack().isLoadedLocal()) - { - ++level; - } - - if (context.params.isMember(jss::search_depth) - && context.params[jss::search_depth].isIntegral()) - { - int rLev = context.params[jss::search_depth].asInt (); - if ((rLev < level) || (isUnlimited (context.role))) - level = rLev; - } - - auto const convert_all = - saDstAmount == STAmount(saDstAmount.issue(), 1u, 0, true); - - boost::optional saSendMax; - if (context.params.isMember(jss::send_max)) - { - // Send_max requires destination amount to be -1. - if (! convert_all) - return rpcError(rpcDST_AMT_MALFORMED); - - saSendMax.emplace(); - if (!amountFromJsonNoThrow( - *saSendMax, context.params[jss::send_max]) || - (saSendMax->getCurrency().isZero() && - saSendMax->getIssuer().isNonZero()) || - (saSendMax->getCurrency() == badCurrency()) || - (*saSendMax <= zero && - *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true))) - { - return rpcError(rpcSENDMAX_MALFORMED); - } - } - - auto contextPaths = context.params.isMember(jss::paths) ? - boost::optional(context.params[jss::paths]) : - boost::optional(boost::none); - auto pathFindResult = ripplePathFind( - cache, raSrc, raDst, (convert_all ? STAmount(saDstAmount.issue(), - STAmount::cMaxValue, STAmount::cMaxOffset) : saDstAmount), - jvSrcCurrencies, contextPaths, level, saSendMax, - convert_all, context.app); - if (!pathFindResult.first) - return pathFindResult.second; - - // Each alternative differs by source currency. - jvResult[jss::alternatives] = pathFindResult.second; - } - - JLOG (context.j.debug()) - << "ripple_path_find< " << jvResult; - - return jvResult; -} - -std::unique_ptr const& -getPathFinder(std::shared_ptr const& cache, AccountID const& raSrc, - AccountID const& raDst, boost::optional saSendMax, - hash_map>& currency_map, - Currency const& currency, STAmount const& dst_amount, - int const level, Application& app) -{ - auto i = currency_map.find(currency); - if (i != currency_map.end()) - return i->second; - auto pathfinder = std::make_unique(cache, raSrc, raDst, - currency, boost::none, dst_amount, saSendMax, app); - if (pathfinder->findPaths(level)) - pathfinder->computePathRanks(max_paths); - else - pathfinder.reset(); // It's a bad request - clear it. - return currency_map[currency] = std::move(pathfinder); -} - -std::pair -ripplePathFind (std::shared_ptr const& cache, - AccountID const& raSrc, AccountID const& raDst, - STAmount const& saDstAmount, Json::Value const& jvSrcCurrencies, - boost::optional const& contextPaths, - int const level, boost::optional saSendMax, - bool convert_all, Application& app) -{ - Json::Value jvArray(Json::arrayValue); - hash_map> currency_map; - - auto j = app.journal ("RPCHandler"); - - for (auto const& c : jvSrcCurrencies) - { - if (! c.isObject()) - return std::make_pair(false, rpcError(rpcINVALID_PARAMS)); - - // Mandatory currency - Currency srcCurrencyID; - if (! c.isMember(jss::currency) || - ! to_currency(srcCurrencyID, c[jss::currency].asString())) - { - JLOG (j.info()) << "Bad currency."; - return std::make_pair(false, rpcError(rpcSRC_CUR_MALFORMED)); - } - - // Optional issuer - AccountID srcIssuerID; - if (c.isMember (jss::issuer) && - (! c[jss::issuer].isString() || - ! to_issuer(srcIssuerID, c[jss::issuer].asString()) || - srcIssuerID.isZero() != srcCurrencyID.isZero() || - noAccount() == srcIssuerID)) - { - JLOG (j.info()) << "Bad issuer."; - return std::make_pair(false, rpcError(rpcSRC_ISR_MALFORMED)); - } - - if (srcIssuerID.isZero()) - srcIssuerID = raSrc; - - auto issue = Issue(srcCurrencyID, srcIssuerID); - if (saSendMax) - { - // If the currencies don't match, ignore the source currency. - if (srcCurrencyID != saSendMax->getCurrency()) - continue; - - // If neither is the source and they are not equal, then the - // source issuer is illegal. - if (srcIssuerID != raSrc && saSendMax->getIssuer() != raSrc && - srcIssuerID != saSendMax->getIssuer()) - { - return std::make_pair(false, rpcError(rpcSRC_ISR_MALFORMED)); - } - - // If both are the source, use the source. - // Otherwise, use the one that's not the source. - if (srcIssuerID == raSrc) - { - issue.account = saSendMax->getIssuer() != raSrc ? - saSendMax->getIssuer() : raSrc; - } - } - - STPathSet spsComputed; - if (contextPaths) - { - Json::Value pathSet = Json::objectValue; - pathSet[jss::Paths] = contextPaths.get(); - STParsedJSONObject paths("pathSet", pathSet); - if (! paths.object) - return std::make_pair(false, paths.error); - - spsComputed = paths.object->getFieldPathSet(sfPaths); - JLOG (j.trace()) << "ripple_path_find: Paths: " << - spsComputed.getJson(0); - } - - auto& pathfinder = getPathFinder(cache, raSrc, raDst, saSendMax, - currency_map, issue.currency, saDstAmount, level, app); - if (! pathfinder) - { - JLOG (j.warn()) << "ripple_path_find: No paths found."; - continue; - } - - STPath fullLiquidityPath; - auto ps = pathfinder->getBestPaths(max_paths, - fullLiquidityPath, spsComputed, issue.account); - - STAmount saMaxAmount; - if (saSendMax) - { - saMaxAmount = *saSendMax; - } - else - { - AccountID issuerID; - if (isXRP(srcIssuerID)) - { - // Default to source account. - if(isXRP(srcCurrencyID)) - issuerID = xrpAccount(); - else - issuerID = raSrc; - } - else - { - // Use specifed issuer. - issuerID = srcIssuerID; - } - - saMaxAmount = STAmount( - {srcCurrencyID, issuerID}, 1u, 0, true); - } - - boost::optional sandbox; - sandbox.emplace(&*cache->getLedger(), tapNONE); - assert(sandbox->open()); - - path::RippleCalc::Input rcInput; - if (convert_all) - rcInput.partialPaymentAllowed = true; - - auto rc = path::RippleCalc::rippleCalculate( - *sandbox, - saMaxAmount, // --> Amount to send is unlimited - // to get an estimate. - saDstAmount, // --> Amount to deliver. - raDst, // --> Account to deliver to. - raSrc, // --> Account sending from. - ps, // --> Path set. - app.logs(), - app.config(), - &rcInput); - - JLOG(j.info()) - << "ripple_path_find:" - << " saMaxAmount=" << saMaxAmount - << " saDstAmount=" << saDstAmount - << " saMaxAmountAct=" << rc.actualAmountIn - << " saDstAmountAct=" << rc.actualAmountOut; - - if (! convert_all && - ! fullLiquidityPath.empty() && - (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) - { - auto jpr = app.journal("PathRequest"); - JLOG(jpr.debug()) - << "Trying with an extra path element"; - ps.push_back(fullLiquidityPath); - sandbox.emplace(&*cache->getLedger(), tapNONE); - assert(sandbox->open()); - rc = path::RippleCalc::rippleCalculate( - *sandbox, - saMaxAmount, // --> Amount to send is unlimited - // to get an estimate. - saDstAmount, // --> Amount to deliver. - raDst, // --> Account to deliver to. - raSrc, // --> Account sending from. - ps, // --> Path set. - app.logs(), - app.config()); - JLOG(jpr.debug()) - << "Extra path element gives " - << transHuman(rc.result()); - } - - if (rc.result() == tesSUCCESS) - { - Json::Value jvEntry(Json::objectValue); - - STPathSet spsCanonical; - - // Reuse the expanded as it would need to be calcuated - // anyway to produce the canonical. (At least unless we - // make a direct canonical.) - - jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(0); - jvEntry[jss::paths_canonical] = Json::arrayValue; - jvEntry[jss::paths_computed] = ps.getJson(0); - - if (convert_all) - jvEntry[jss::destination_amount] = rc.actualAmountOut.getJson(0); - - jvArray.append(jvEntry); - } - else - { - std::string strToken; - std::string strHuman; - transResultInfo(rc.result(), strToken, strHuman); - JLOG (j.debug()) - << "ripple_path_find: " - << strToken << " " - << strHuman << " " - << spsComputed.getJson(0); - } - } - - return std::make_pair(true, std::move(jvArray)); + return result; } } // ripple