Skip to content

Commit

Permalink
Support MPT in path finding
Browse files Browse the repository at this point in the history
  • Loading branch information
gregtatcam committed Mar 5, 2024
1 parent 95dea4f commit 6e13450
Show file tree
Hide file tree
Showing 10 changed files with 360 additions and 185 deletions.
172 changes: 108 additions & 64 deletions src/ripple/app/paths/PathRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,7 @@ PathRequest::parseJson(Json::Value const& jvParams)

convert_all_ = saDstAmount == STAmount(saDstAmount.asset(), 1u, 0, true);

auto invalidCurrency = [](STAmount const& amt) {
return amt.isIssue() &&
(!isConsistent(amt.issue()) || amt.getCurrency() == badCurrency());
};

if (invalidCurrency(saDstAmount) ||
if (!validAsset(saDstAmount.asset()) ||
(!convert_all_ && saDstAmount <= beast::zero))
{
jvStatus = rpcError(rpcDST_AMT_MALFORMED);
Expand All @@ -338,7 +333,7 @@ PathRequest::parseJson(Json::Value const& jvParams)

saSendMax.emplace();
if (!amountFromJsonNoThrow(*saSendMax, jvParams[jss::send_max]) ||
invalidCurrency(*saSendMax) ||
!validAsset(saSendMax->asset()) ||
(*saSendMax <= beast::zero &&
*saSendMax != STAmount(saSendMax->asset(), 1u, 0, true)))
{
Expand All @@ -357,47 +352,77 @@ PathRequest::parseJson(Json::Value const& jvParams)
return PFR_PJ_INVALID;
}

sciSourceCurrencies.clear();
sciSourceAssets.clear();

for (auto const& c : jvSrcCurrencies)
{
// Mandatory currency
Currency srcCurrencyID;
if (!c.isObject() || !c.isMember(jss::currency) ||
!c[jss::currency].isString() ||
!to_currency(srcCurrencyID, c[jss::currency].asString()))
// Mandatory currency or MPT
if ((!c.isMember(jss::currency) &&
!c.isMember(jss::mpt_issuance_id)) ||
(c.isMember(jss::currency) &&
c.isMember(jss::mpt_issuance_id)) ||
!c.isObject())
{
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
return PFR_PJ_INVALID;
}

PathAsset srcPathAsset;
if (c.isMember(jss::currency))
{
Currency currency;
if (!c[jss::currency].isString() ||
!to_currency(currency, c[jss::currency].asString()))
{
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
return PFR_PJ_INVALID;
}
srcPathAsset = currency;
}
else
{
uint192 u;
if (!c[jss::mpt_issuance_id].isString() ||
!u.parseHex(c[jss::mpt_issuance_id].asString()))
{
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
return PFR_PJ_INVALID;
}
srcPathAsset = u;
}

// Optional issuer
// TODO MPT is issuer valid in this case?
AccountID srcIssuerID;
if (c.isMember(jss::issuer) &&
(!c[jss::issuer].isString() ||
(c.isMember(jss::mpt_issuance_id) ||
!c[jss::issuer].isString() ||
!to_issuer(srcIssuerID, c[jss::issuer].asString())))
{
jvStatus = rpcError(rpcSRC_ISR_MALFORMED);
return PFR_PJ_INVALID;
}

if (srcCurrencyID.isZero())
if (srcPathAsset.isCurrency())
{
if (srcIssuerID.isNonZero())
if (srcPathAsset.currency().isZero())
{
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
return PFR_PJ_INVALID;
if (srcIssuerID.isNonZero())
{
jvStatus = rpcError(rpcSRC_CUR_MALFORMED);
return PFR_PJ_INVALID;
}
}
else if (srcIssuerID.isZero())
{
srcIssuerID = *raSrcAccount;
}
}
else if (srcIssuerID.isZero())
{
srcIssuerID = *raSrcAccount;
}

if (saSendMax)
{
// If the currencies don't match, ignore the source currency.
if (srcCurrencyID == saSendMax->getCurrency())
// If the assets don't match, ignore the source asset.
if (equalAssets(srcPathAsset, saSendMax->asset()))
{
// If neither is the source and they are not equal, then the
// source issuer is illegal.
Expand All @@ -411,26 +436,37 @@ PathRequest::parseJson(Json::Value const& jvParams)

// If both are the source, use the source.
// Otherwise, use the one that's not the source.
if (srcIssuerID != *raSrcAccount)
if (srcPathAsset.isCurrency())
{
sciSourceCurrencies.insert(
{srcCurrencyID, srcIssuerID});
}
else if (saSendMax->getIssuer() != *raSrcAccount)
{
sciSourceCurrencies.insert(
{srcCurrencyID, saSendMax->getIssuer()});
if (srcIssuerID != *raSrcAccount)
{
sciSourceAssets.insert(
Issue{srcPathAsset.currency(), srcIssuerID});
}
else if (saSendMax->getIssuer() != *raSrcAccount)
{
sciSourceAssets.insert(Issue{
srcPathAsset.currency(),
saSendMax->getIssuer()});
}
else
{
sciSourceAssets.insert(
Issue{srcPathAsset.currency(), *raSrcAccount});
}
}
else
{
sciSourceCurrencies.insert(
{srcCurrencyID, *raSrcAccount});
}
sciSourceAssets.insert(srcPathAsset.mpt());
}
}
else if (srcPathAsset.isCurrency())
{
sciSourceAssets.insert(
Issue{srcPathAsset.currency(), srcIssuerID});
}
else
{
sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID});
sciSourceAssets.insert(MPTIssue{srcPathAsset.mpt()});
}
}
}
Expand Down Expand Up @@ -467,20 +503,20 @@ PathRequest::doAborting() const
std::unique_ptr<Pathfinder> const&
PathRequest::getPathFinder(
std::shared_ptr<RippleLineCache> const& cache,
hash_map<Currency, std::unique_ptr<Pathfinder>>& currency_map,
Currency const& currency,
hash_map<PathAsset, std::unique_ptr<Pathfinder>>& pathasset_map,
PathAsset const& asset,
STAmount const& dst_amount,
int const level,
std::function<bool(void)> const& continueCallback)
{
auto i = currency_map.find(currency);
if (i != currency_map.end())
auto i = pathasset_map.find(asset);
if (i != pathasset_map.end())
return i->second;
auto pathfinder = std::make_unique<Pathfinder>(
cache,
*raSrcAccount,
*raDstAccount,
currency,
asset,
std::nullopt,
dst_amount,
saSendMax,
Expand All @@ -489,7 +525,7 @@ PathRequest::getPathFinder(
pathfinder->computePathRanks(max_paths_, continueCallback);
else
pathfinder.reset(); // It's a bad request - clear it.
return currency_map[currency] = std::move(pathfinder);
return pathasset_map[asset] = std::move(pathfinder);
}

bool
Expand All @@ -499,41 +535,43 @@ PathRequest::findPaths(
Json::Value& jvArray,
std::function<bool(void)> const& continueCallback)
{
auto sourceCurrencies = sciSourceCurrencies;
if (sourceCurrencies.empty() && saSendMax)
auto sourceAssets = sciSourceAssets;
if (sourceAssets.empty() && saSendMax)
{
sourceCurrencies.insert(saSendMax->asset());
sourceAssets.insert(saSendMax->asset());
}
if (sourceCurrencies.empty())
if (sourceAssets.empty())
{
// TODO MPT
auto currencies = accountSourceCurrencies(*raSrcAccount, cache, true);
bool const sameAccount = *raSrcAccount == *raDstAccount;
for (auto const& c : currencies)
{
if (!sameAccount || c != saDstAmount.getCurrency())
if (!sameAccount ||
(saDstAmount.isIssue() && c != saDstAmount.getCurrency()))
{
if (sourceCurrencies.size() >= RPC::Tuning::max_auto_src_cur)
if (sourceAssets.size() >= RPC::Tuning::max_auto_src_cur)
return false;
sourceCurrencies.insert(
sourceAssets.insert(
{c, c.isZero() ? xrpAccount() : *raSrcAccount});
}
}
}

auto const dst_amount = convertAmount(saDstAmount, convert_all_);
hash_map<Currency, std::unique_ptr<Pathfinder>> currency_map;
for (auto const& issue : sourceCurrencies)
hash_map<PathAsset, std::unique_ptr<Pathfinder>> pathasset_map;
for (auto const& asset : sourceAssets)
{
if (continueCallback && !continueCallback())
break;
JLOG(m_journal.debug())
<< iIdentifier
<< " Trying to find paths: " << STAmount(issue, 1).getFullText();
<< " Trying to find paths: " << STAmount(asset, 1).getFullText();

auto& pathfinder = getPathFinder(
cache,
currency_map,
issue.currency,
pathasset_map,
PathAsset::toPathAsset(asset),
dst_amount,
level,
continueCallback);
Expand All @@ -547,23 +585,29 @@ PathRequest::findPaths(
auto ps = pathfinder->getBestPaths(
max_paths_,
fullLiquidityPath,
mContext[issue],
issue.account,
mContext[asset],
asset.account(),
continueCallback);
mContext[issue] = ps;
mContext[asset] = ps;

auto const& sourceAccount = [&] {
if (!isXRP(issue.account))
return issue.account;
if (!isXRP(asset.account()))
return asset.account();

if (isXRP(issue.currency))
if (isXRP(asset))
return xrpAccount();

return *raSrcAccount;
}();

STAmount saMaxAmount = saSendMax.value_or(
STAmount(Issue{issue.currency, sourceAccount}, 1u, 0, true));
STAmount saMaxAmount = [&]() {
if (saSendMax)
return *saSendMax;
if (asset.isIssue())
return STAmount(
Issue{asset.issue().currency, sourceAccount}, 1u, 0, true);
return STAmount(asset.mptIssue(), 1u, 0, true);
}();

JLOG(m_journal.debug())
<< iIdentifier << " Paths found, calling rippleCalc";
Expand Down Expand Up @@ -648,7 +692,7 @@ PathRequest::findPaths(
The minimum cost is 50 and the maximum is 400. The cost increases
after four source currencies, 50 - (4 * 4) = 34.
*/
int const size = sourceCurrencies.size();
int const size = sourceAssets.size();
consumer_.charge({std::clamp(size * size + 34, 50, 400), "path update"});
return true;
}
Expand Down
9 changes: 5 additions & 4 deletions src/ripple/app/paths/PathRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <ripple/app/paths/RippleLineCache.h>
#include <ripple/json/json_value.h>
#include <ripple/net/InfoSub.h>
#include <ripple/protocol/Asset.h>
#include <ripple/protocol/UintTypes.h>
#include <map>
#include <mutex>
Expand Down Expand Up @@ -113,8 +114,8 @@ class PathRequest final : public InfoSubRequest,
std::unique_ptr<Pathfinder> const&
getPathFinder(
std::shared_ptr<RippleLineCache> const&,
hash_map<Currency, std::unique_ptr<Pathfinder>>&,
Currency const&,
hash_map<PathAsset, std::unique_ptr<Pathfinder>>&,
PathAsset const&,
STAmount const&,
int const,
std::function<bool(void)> const&);
Expand Down Expand Up @@ -152,8 +153,8 @@ class PathRequest final : public InfoSubRequest,
STAmount saDstAmount;
std::optional<STAmount> saSendMax;

std::set<Issue> sciSourceCurrencies;
std::map<Issue, STPathSet> mContext;
std::set<Asset> sciSourceAssets;
std::map<Asset, STPathSet> mContext;

bool convert_all_;

Expand Down
Loading

0 comments on commit 6e13450

Please sign in to comment.