mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 02:55:50 +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>
|
||||||
<ClInclude Include="..\..\src\ripple\rpc\json_body.h">
|
<ClInclude Include="..\..\src\ripple\rpc\json_body.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\rpc\RipplePathFind.h">
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\ripple\rpc\Role.h">
|
<ClInclude Include="..\..\src\ripple\rpc\Role.h">
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\rpc\RPCHandler.h">
|
<ClInclude Include="..\..\src\ripple\rpc\RPCHandler.h">
|
||||||
|
|||||||
@@ -3813,9 +3813,6 @@
|
|||||||
<ClInclude Include="..\..\src\ripple\rpc\json_body.h">
|
<ClInclude Include="..\..\src\ripple\rpc\json_body.h">
|
||||||
<Filter>ripple\rpc</Filter>
|
<Filter>ripple\rpc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\src\ripple\rpc\RipplePathFind.h">
|
|
||||||
<Filter>ripple\rpc</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\..\src\ripple\rpc\Role.h">
|
<ClInclude Include="..\..\src\ripple\rpc\Role.h">
|
||||||
<Filter>ripple\rpc</Filter>
|
<Filter>ripple\rpc</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// VFALCO TODO Break the cyclic dependency on InfoSub
|
// VFALCO TODO Break the cyclic dependency on InfoSub
|
||||||
|
|
||||||
|
// path_find semantics
|
||||||
|
// Subscriber is updated
|
||||||
PathRequest (
|
PathRequest (
|
||||||
Application& app,
|
Application& app,
|
||||||
std::shared_ptr <InfoSub> const& subscriber,
|
std::shared_ptr <InfoSub> const& subscriber,
|
||||||
@@ -66,6 +69,8 @@ public:
|
|||||||
PathRequests&,
|
PathRequests&,
|
||||||
beast::Journal journal);
|
beast::Journal journal);
|
||||||
|
|
||||||
|
// ripple_path_find semantics
|
||||||
|
// Completion function is called
|
||||||
PathRequest (
|
PathRequest (
|
||||||
Application& app,
|
Application& app,
|
||||||
std::function <void (void)> const& completion,
|
std::function <void (void)> const& completion,
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ PathRequests::makePathRequest(
|
|||||||
insertPathRequest (req);
|
insertPathRequest (req);
|
||||||
app_.getLedgerMaster().newPathRequest();
|
app_.getLedgerMaster().newPathRequest();
|
||||||
}
|
}
|
||||||
return result.second;
|
return std::move (result.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make an old-style ripple_path_find request
|
// Make an old-style ripple_path_find request
|
||||||
@@ -249,7 +249,24 @@ PathRequests::makeLegacyPathRequest(
|
|||||||
app_.getLedgerMaster().newPathRequest();
|
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
|
} // ripple
|
||||||
|
|||||||
@@ -49,11 +49,16 @@ public:
|
|||||||
std::shared_ptr<RippleLineCache> getLineCache (
|
std::shared_ptr<RippleLineCache> getLineCache (
|
||||||
std::shared_ptr <ReadView const> const& ledger, bool authoritative);
|
std::shared_ptr <ReadView const> const& ledger, bool authoritative);
|
||||||
|
|
||||||
|
// Create a new-style path request that pushes
|
||||||
|
// updates to a subscriber
|
||||||
Json::Value makePathRequest (
|
Json::Value makePathRequest (
|
||||||
std::shared_ptr <InfoSub> const& subscriber,
|
std::shared_ptr <InfoSub> const& subscriber,
|
||||||
std::shared_ptr<ReadView const> const& ledger,
|
std::shared_ptr<ReadView const> const& ledger,
|
||||||
Json::Value const& request);
|
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 (
|
Json::Value makeLegacyPathRequest (
|
||||||
PathRequest::pointer& req,
|
PathRequest::pointer& req,
|
||||||
std::function <void (void)> completion,
|
std::function <void (void)> completion,
|
||||||
@@ -61,6 +66,13 @@ public:
|
|||||||
std::shared_ptr<ReadView const> const& inLedger,
|
std::shared_ptr<ReadView const> const& inLedger,
|
||||||
Json::Value const& request);
|
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)
|
void reportFast (std::chrono::milliseconds ms)
|
||||||
{
|
{
|
||||||
mFast.notify (ms);
|
mFast.notify (ms);
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include <ripple/resource/Fees.h>
|
#include <ripple/resource/Fees.h>
|
||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/impl/Tuning.h>
|
#include <ripple/rpc/impl/Tuning.h>
|
||||||
#include <ripple/rpc/RipplePathFind.h>
|
|
||||||
#include <ripple/rpc/RPCHandler.h>
|
#include <ripple/rpc/RPCHandler.h>
|
||||||
#include <ripple/test/jtx.h>
|
#include <ripple/test/jtx.h>
|
||||||
#include <ripple/beast/unit_test.h>
|
#include <ripple/beast/unit_test.h>
|
||||||
@@ -143,52 +142,6 @@ equal(STAmount const& sa1, STAmount const& sa2)
|
|||||||
sa1.issue().account == sa2.issue().account;
|
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
|
Json::Value
|
||||||
rpf(jtx::Account const& src, jtx::Account const& dst, std::uint32_t num_src)
|
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
|
void
|
||||||
source_currencies_limit()
|
source_currencies_limit()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -98,8 +98,12 @@ Disposition Consumer::disposition() const
|
|||||||
|
|
||||||
Disposition Consumer::charge (Charge const& what)
|
Disposition Consumer::charge (Charge const& what)
|
||||||
{
|
{
|
||||||
assert (m_entry != nullptr);
|
Disposition d = ok;
|
||||||
return m_logic->charge (*m_entry, what);
|
|
||||||
|
if (m_logic && m_entry)
|
||||||
|
d = m_logic->charge (*m_entry, what);
|
||||||
|
|
||||||
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Consumer::warn ()
|
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 <BeastConfig.h>
|
||||||
#include <ripple/rpc/RipplePathFind.h>
|
|
||||||
#include <ripple/app/ledger/LedgerMaster.h>
|
#include <ripple/app/ledger/LedgerMaster.h>
|
||||||
#include <ripple/app/main/Application.h>
|
#include <ripple/app/main/Application.h>
|
||||||
#include <ripple/app/misc/LoadFeeTrack.h>
|
#include <ripple/app/misc/LoadFeeTrack.h>
|
||||||
@@ -39,7 +38,6 @@
|
|||||||
#include <ripple/protocol/types.h>
|
#include <ripple/protocol/types.h>
|
||||||
#include <ripple/resource/Fees.h>
|
#include <ripple/resource/Fees.h>
|
||||||
#include <ripple/rpc/Context.h>
|
#include <ripple/rpc/Context.h>
|
||||||
#include <ripple/rpc/RipplePathFind.h>
|
|
||||||
#include <ripple/rpc/impl/LegacyPathFind.h>
|
#include <ripple/rpc/impl/LegacyPathFind.h>
|
||||||
#include <ripple/rpc/impl/RPCHelpers.h>
|
#include <ripple/rpc/impl/RPCHelpers.h>
|
||||||
#include <ripple/rpc/impl/Tuning.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_index) &&
|
||||||
! context.params.isMember(jss::ledger_hash))
|
! context.params.isMember(jss::ledger_hash))
|
||||||
{
|
{
|
||||||
|
// No ledger specified, use pathfinding defaults
|
||||||
|
// and dispatch to pathfinding engine
|
||||||
if (context.app.getLedgerMaster().getValidatedLedgerAge() >
|
if (context.app.getLedgerMaster().getValidatedLedgerAge() >
|
||||||
RPC::Tuning::maxValidatedLedgerAge)
|
RPC::Tuning::maxValidatedLedgerAge)
|
||||||
{
|
{
|
||||||
@@ -95,380 +95,13 @@ Json::Value doRipplePathFind (RPC::Context& context)
|
|||||||
if (! lpf.isOk ())
|
if (! lpf.isOk ())
|
||||||
return rpcError (rpcTOO_BUSY);
|
return rpcError (rpcTOO_BUSY);
|
||||||
|
|
||||||
AccountID raSrc;
|
auto result = context.app.getPathRequests().doLegacyPathRequest (
|
||||||
AccountID raDst;
|
context.consumer, lpLedger, context.params);
|
||||||
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;
|
|
||||||
|
|
||||||
if (lpLedger)
|
for (auto &fieldName : jvResult.getMemberNames ())
|
||||||
{
|
result[fieldName] = std::move (jvResult[fieldName]);
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Json::Value jvSrcCurrencies;
|
return result;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
Reference in New Issue
Block a user