mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 19:15:54 +00:00
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.
This commit is contained in:
@@ -3355,8 +3355,6 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\rpc\json_body.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\rpc\RipplePathFind.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\rpc\Role.h">
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\rpc\RPCHandler.h">
|
||||
|
||||
@@ -3813,9 +3813,6 @@
|
||||
<ClInclude Include="..\..\src\ripple\rpc\json_body.h">
|
||||
<Filter>ripple\rpc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\rpc\RipplePathFind.h">
|
||||
<Filter>ripple\rpc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\ripple\rpc\Role.h">
|
||||
<Filter>ripple\rpc</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -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 <InfoSub> const& subscriber,
|
||||
@@ -66,6 +69,8 @@ public:
|
||||
PathRequests&,
|
||||
beast::Journal journal);
|
||||
|
||||
// ripple_path_find semantics
|
||||
// Completion function is called
|
||||
PathRequest (
|
||||
Application& app,
|
||||
std::function <void (void)> const& completion,
|
||||
|
||||
@@ -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<ReadView const> const& inLedger,
|
||||
Json::Value const& request)
|
||||
{
|
||||
auto cache = std::make_shared<RippleLineCache> (inLedger);
|
||||
|
||||
auto req = std::make_shared<PathRequest> (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
|
||||
|
||||
@@ -49,11 +49,16 @@ public:
|
||||
std::shared_ptr<RippleLineCache> getLineCache (
|
||||
std::shared_ptr <ReadView const> const& ledger, bool authoritative);
|
||||
|
||||
// Create a new-style path request that pushes
|
||||
// updates to a subscriber
|
||||
Json::Value makePathRequest (
|
||||
std::shared_ptr <InfoSub> const& subscriber,
|
||||
std::shared_ptr<ReadView const> 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 <void (void)> completion,
|
||||
@@ -61,6 +66,13 @@ public:
|
||||
std::shared_ptr<ReadView const> 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<ReadView const> const& inLedger,
|
||||
Json::Value const& request);
|
||||
|
||||
void reportFast (std::chrono::milliseconds ms)
|
||||
{
|
||||
mFast.notify (ms);
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/impl/Tuning.h>
|
||||
#include <ripple/rpc/RipplePathFind.h>
|
||||
#include <ripple/rpc/RPCHandler.h>
|
||||
#include <ripple/test/jtx.h>
|
||||
#include <ripple/beast/unit_test.h>
|
||||
@@ -143,52 +142,6 @@ equal(STAmount const& sa1, STAmount const& sa2)
|
||||
sa1.issue().account == sa2.issue().account;
|
||||
}
|
||||
|
||||
std::tuple <STPathSet, STAmount, STAmount>
|
||||
find_paths(jtx::Env& env,
|
||||
jtx::Account const& src, jtx::Account const& dst,
|
||||
STAmount const& saDstAmount,
|
||||
boost::optional<STAmount> const& saSendMax = boost::none)
|
||||
{
|
||||
static int const level = 8;
|
||||
auto const& view = env.current();
|
||||
auto cache = std::make_shared<RippleLineCache>(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<std::runtime_error> (
|
||||
"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 <STPathSet, STAmount, STAmount>
|
||||
find_paths(jtx::Env& env,
|
||||
jtx::Account const& src, jtx::Account const& dst,
|
||||
STAmount const& saDstAmount,
|
||||
boost::optional<STAmount> 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()
|
||||
{
|
||||
|
||||
@@ -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 ()
|
||||
|
||||
@@ -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 <ripple/app/paths/RippleLineCache.h>
|
||||
#include <ripple/app/ledger/Ledger.h>
|
||||
|
||||
namespace ripple {
|
||||
|
||||
std::pair<bool, Json::Value>
|
||||
ripplePathFind (std::shared_ptr<RippleLineCache> const& cache,
|
||||
AccountID const& raSrc, AccountID const& raDst,
|
||||
STAmount const& saDstAmount,
|
||||
Json::Value const& jvSrcCurrencies,
|
||||
boost::optional<Json::Value> const& contextPaths,
|
||||
int const level, boost::optional<STAmount> saSendMax,
|
||||
bool convert_all, Application& app);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -18,7 +18,6 @@
|
||||
//==============================================================================
|
||||
|
||||
#include <BeastConfig.h>
|
||||
#include <ripple/rpc/RipplePathFind.h>
|
||||
#include <ripple/app/ledger/LedgerMaster.h>
|
||||
#include <ripple/app/main/Application.h>
|
||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||
@@ -39,7 +38,6 @@
|
||||
#include <ripple/protocol/types.h>
|
||||
#include <ripple/resource/Fees.h>
|
||||
#include <ripple/rpc/Context.h>
|
||||
#include <ripple/rpc/RipplePathFind.h>
|
||||
#include <ripple/rpc/impl/LegacyPathFind.h>
|
||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||
#include <ripple/rpc/impl/Tuning.h>
|
||||
@@ -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<RippleLineCache> cache;
|
||||
auto result = context.app.getPathRequests().doLegacyPathRequest (
|
||||
context.consumer, lpLedger, context.params);
|
||||
|
||||
if (lpLedger)
|
||||
{
|
||||
// The caller specified a ledger
|
||||
cache = std::make_shared<RippleLineCache>(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<STAmount> 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<Json::Value>(context.params[jss::paths]) :
|
||||
boost::optional<Json::Value>(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<Pathfinder> const&
|
||||
getPathFinder(std::shared_ptr<RippleLineCache> const& cache, AccountID const& raSrc,
|
||||
AccountID const& raDst, boost::optional<STAmount> saSendMax,
|
||||
hash_map<Currency, std::unique_ptr<Pathfinder>>& 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<Pathfinder>(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<bool, Json::Value>
|
||||
ripplePathFind (std::shared_ptr<RippleLineCache> const& cache,
|
||||
AccountID const& raSrc, AccountID const& raDst,
|
||||
STAmount const& saDstAmount, Json::Value const& jvSrcCurrencies,
|
||||
boost::optional<Json::Value> const& contextPaths,
|
||||
int const level, boost::optional<STAmount> saSendMax,
|
||||
bool convert_all, Application& app)
|
||||
{
|
||||
Json::Value jvArray(Json::arrayValue);
|
||||
hash_map<Currency, std::unique_ptr<Pathfinder>> 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<PaymentSandbox> 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
|
||||
|
||||
Reference in New Issue
Block a user