diff --git a/Builds/VisualStudio2015/RippleD.vcxproj b/Builds/VisualStudio2015/RippleD.vcxproj
index 4413ed27b..b3726c308 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj
+++ b/Builds/VisualStudio2015/RippleD.vcxproj
@@ -1049,6 +1049,8 @@
True
True
+
+
True
True
diff --git a/Builds/VisualStudio2015/RippleD.vcxproj.filters b/Builds/VisualStudio2015/RippleD.vcxproj.filters
index d436b05cf..816ce4bc0 100644
--- a/Builds/VisualStudio2015/RippleD.vcxproj.filters
+++ b/Builds/VisualStudio2015/RippleD.vcxproj.filters
@@ -1524,6 +1524,9 @@
ripple\app\paths\impl
+
+ ripple\app\paths\impl
+
ripple\app\paths\impl
diff --git a/src/ripple/app/paths/Flow.cpp b/src/ripple/app/paths/Flow.cpp
index 9e68f8e98..903c70629 100644
--- a/src/ripple/app/paths/Flow.cpp
+++ b/src/ripple/app/paths/Flow.cpp
@@ -65,7 +65,8 @@ flow (
bool ownerPaysTransferFee,
boost::optional const& limitQuality,
boost::optional const& sendMax,
- beast::Journal j)
+ beast::Journal j,
+ path::detail::FlowDebugInfo* flowDebugInfo)
{
Issue const srcIssue = [&] {
if (sendMax)
@@ -123,7 +124,7 @@ flow (
return finishFlow (sb, srcIssue, dstIssue,
flow (
sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
- limitQuality, sendMax, j));
+ limitQuality, sendMax, j, flowDebugInfo));
}
if (srcIsXRP && !dstIsXRP)
@@ -131,7 +132,7 @@ flow (
return finishFlow (sb, srcIssue, dstIssue,
flow (
sb, strands, asDeliver.iou, defaultPaths, partialPayment,
- limitQuality, sendMax, j));
+ limitQuality, sendMax, j, flowDebugInfo));
}
if (!srcIsXRP && dstIsXRP)
@@ -139,14 +140,14 @@ flow (
return finishFlow (sb, srcIssue, dstIssue,
flow (
sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
- limitQuality, sendMax, j));
+ limitQuality, sendMax, j, flowDebugInfo));
}
assert (!srcIsXRP && !dstIsXRP);
return finishFlow (sb, srcIssue, dstIssue,
flow (
sb, strands, asDeliver.iou, defaultPaths, partialPayment,
- limitQuality, sendMax, j));
+ limitQuality, sendMax, j, flowDebugInfo));
}
diff --git a/src/ripple/app/paths/Flow.h b/src/ripple/app/paths/Flow.h
index ecf622585..e6f9087ce 100644
--- a/src/ripple/app/paths/Flow.h
+++ b/src/ripple/app/paths/Flow.h
@@ -27,6 +27,12 @@
namespace ripple
{
+namespace path {
+namespace detail{
+struct FlowDebugInfo;
+}
+}
+
/**
Make a payment from the src account to the dst account
@@ -54,7 +60,8 @@ flow (PaymentSandbox& view,
bool ownerPaysTransferFee,
boost::optional const& limitQuality,
boost::optional const& sendMax,
- beast::Journal j);
+ beast::Journal j,
+ path::detail::FlowDebugInfo* flowDebugInfo=nullptr);
} // ripple
diff --git a/src/ripple/app/paths/RippleCalc.cpp b/src/ripple/app/paths/RippleCalc.cpp
index 6c5c930d1..7d6a6fda2 100644
--- a/src/ripple/app/paths/RippleCalc.cpp
+++ b/src/ripple/app/paths/RippleCalc.cpp
@@ -22,6 +22,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -70,19 +71,25 @@ RippleCalc::Output RippleCalc::rippleCalculate (
Config const& config,
Input const* const pInputs)
{
+ // call flow v1 and v2 so results may be compared
+ bool const compareFlowV1V2 =
+ view.rules ().enabled (featureCompareFlowV1V2, config.features);
+
bool const useFlowV1Output =
!flowV2Switchover (view.info ().parentCloseTime) &&
!view.rules ().enabled (featureFlowV2, config.features);
- // When flowV2 is enabled via rules, call old flow so results may be
- // compared
- bool const callFlowV1 = useFlowV1Output ||
- view.rules ().enabled (featureFlowV2, config.features);
- bool const callFlowV2 = !useFlowV1Output;
+ bool const callFlowV1 = useFlowV1Output || compareFlowV1V2;
+ bool const callFlowV2 = !useFlowV1Output || compareFlowV1V2;
Output flowV1Out;
PaymentSandbox flowV1SB (&view);
+
+ auto const inNative = saMaxAmountReq.native();
+ auto const outNative = saDstAmountReq.native();
+ detail::FlowDebugInfo flowV1FlowDebugInfo (inNative, outNative);
if (callFlowV1)
{
+ auto const timeIt = flowV1FlowDebugInfo.timeBlock ("main");
RippleCalc rc (
flowV1SB,
saMaxAmountReq,
@@ -96,7 +103,7 @@ RippleCalc::Output RippleCalc::rippleCalculate (
rc.inputFlags = *pInputs;
}
- auto result = rc.rippleCalculate ();
+ auto result = rc.rippleCalculate (compareFlowV1V2 ? &flowV1FlowDebugInfo : nullptr);
flowV1Out.setResult (result);
flowV1Out.actualAmountIn = rc.actualAmountIn_;
flowV1Out.actualAmountOut = rc.actualAmountOut_;
@@ -106,6 +113,8 @@ RippleCalc::Output RippleCalc::rippleCalculate (
Output flowV2Out;
PaymentSandbox flowV2SB (&view);
+ detail::FlowDebugInfo flowV2FlowDebugInfo (inNative, outNative);
+ auto j = l.journal ("Flow");
if (callFlowV2)
{
bool defaultPaths = true;
@@ -129,15 +138,15 @@ RippleCalc::Output RippleCalc::rippleCalculate (
sendMax.emplace (saMaxAmountReq);
}
- auto j = l.journal ("Flow");
-
try
{
bool const ownerPaysTransferFee =
view.rules ().enabled (featureOwnerPaysFee, config.features);
+ auto const timeIt = flowV2FlowDebugInfo.timeBlock ("main");
flowV2Out = flow (flowV2SB, saDstAmountReq, uSrcAccountID,
uDstAccountID, spsPaths, defaultPaths, partialPayment,
- ownerPaysTransferFee, limitQuality, sendMax, j);
+ ownerPaysTransferFee, limitQuality, sendMax, j,
+ compareFlowV1V2 ? &flowV2FlowDebugInfo : nullptr);
}
catch (std::exception& e)
{
@@ -145,32 +154,63 @@ RippleCalc::Output RippleCalc::rippleCalculate (
if (!useFlowV1Output)
Rethrow();
}
+ }
- if (j.debug())
+ if (j.debug())
+ {
+ using BalanceDiffs = detail::BalanceDiffs;
+ auto logResult = [&](std::string const& algoName,
+ Output const& result,
+ detail::FlowDebugInfo const& flowDebugInfo,
+ boost::optional const& balanceDiffs,
+ bool outputPassInfo,
+ bool outputBalanceDiffs) {
+ j.debug () << "RippleCalc Result> " <<
+ " actualIn: " << result.actualAmountIn <<
+ ", actualOut: " << result.actualAmountOut <<
+ ", result: " << result.result () <<
+ ", dstAmtReq: " << saDstAmountReq <<
+ ", sendMax: " << saMaxAmountReq <<
+ (compareFlowV1V2 ? ", " + flowDebugInfo.to_string (outputPassInfo): "") <<
+ (outputBalanceDiffs && balanceDiffs
+ ? ", " + detail::balanceDiffsToString(balanceDiffs) : "") <<
+ ", algo: " << algoName;
+ };
+ bool outputPassInfo = false;
+ bool outputBalanceDiffs = false;
+ boost::optional bdV1, bdV2;
+ if (compareFlowV1V2)
{
- auto logResult = [&] (std::string const& algoName, Output const& result)
+ auto const v1r = flowV1Out.result ();
+ auto const v2r = flowV2Out.result ();
+ if (v1r != v2r ||
+ (((v1r == tesSUCCESS) || (v1r == tecPATH_PARTIAL)) &&
+ ((flowV1Out.actualAmountIn !=
+ flowV2Out.actualAmountIn) ||
+ (flowV1Out.actualAmountOut !=
+ flowV2Out.actualAmountOut))))
{
- j.debug() << "RippleCalc Result> " <<
- " actualIn: " << result.actualAmountIn <<
- ", actualOut: " << result.actualAmountOut <<
- ", result: " << result.result () <<
- ", dstAmtReq: " << saDstAmountReq <<
- ", sendMax: " << saMaxAmountReq <<
- ", algo: " << algoName;
- };
- if (callFlowV1)
- {
- logResult ("V1", flowV1Out);
- }
- if (callFlowV2)
- {
- logResult ("V2", flowV2Out);
+ outputPassInfo = true;
}
+ bdV1 = detail::balanceDiffs (flowV1SB, view);
+ bdV2 = detail::balanceDiffs (flowV2SB, view);
+ outputBalanceDiffs = bdV1 != bdV2;
}
- JLOG (j.trace()) << "Using old flow: " << useFlowV1Output;
+ if (callFlowV1)
+ {
+ logResult ("V1", flowV1Out, flowV1FlowDebugInfo, bdV1,
+ outputPassInfo, outputBalanceDiffs);
+ }
+ if (callFlowV2)
+ {
+ logResult ("V2", flowV2Out, flowV2FlowDebugInfo, bdV2,
+ outputPassInfo, outputBalanceDiffs);
+ }
}
+ JLOG (j.trace()) << "Using old flow: " << useFlowV1Output;
+
if (!useFlowV1Output)
{
flowV2SB.apply (view);
@@ -232,7 +272,7 @@ bool RippleCalc::addPathState(STPath const& path, TER& resultCode)
// liquidity. No need to revisit path in the future if all liquidity is used.
// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed.
-TER RippleCalc::rippleCalculate ()
+TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo)
{
JLOG (j_.trace())
<< "rippleCalc>"
@@ -301,6 +341,7 @@ TER RippleCalc::rippleCalculate ()
// True, if ever computed multi-quality.
bool multiQuality = false;
+ if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
// Find the best path.
for (auto pathState : pathStateList_)
{
@@ -328,6 +369,11 @@ TER RippleCalc::rippleCalculate ()
<< " uQuality=" << pathState->quality()
<< " rate=" << amountFromRate (pathState->quality());
+ if (flowDebugInfo)
+ flowDebugInfo->pushLiquiditySrc (
+ toEitherAmount (pathState->inPass ()),
+ toEitherAmount (pathState->outPass ()));
+
if (!pathState->quality())
{
// Path was dry.
@@ -418,13 +464,16 @@ TER RippleCalc::rippleCalculate ()
// Apply best path.
auto pathState = pathStateList_[iBest];
- JLOG (j_.debug())
+ if (flowDebugInfo)
+ flowDebugInfo->pushPass (toEitherAmount (pathState->inPass ()),
+ toEitherAmount (pathState->outPass ()),
+ pathStateList_.size () - iDry);
+
+ JLOG (j_.debug ())
<< "rippleCalc: best:"
- << " uQuality="
- << amountFromRate (pathState->quality())
- << " inPass()=" << pathState->inPass()
- << " saOutPass=" << pathState->outPass()
- << " iBest=" << iBest;
+ << " uQuality=" << amountFromRate (pathState->quality ())
+ << " inPass()=" << pathState->inPass ()
+ << " saOutPass=" << pathState->outPass () << " iBest=" << iBest;
// Record best pass' offers that became unfunded for deletion on
// success.
diff --git a/src/ripple/app/paths/RippleCalc.h b/src/ripple/app/paths/RippleCalc.h
index ff235646e..4f5b53033 100644
--- a/src/ripple/app/paths/RippleCalc.h
+++ b/src/ripple/app/paths/RippleCalc.h
@@ -32,6 +32,10 @@ namespace ripple {
class Config;
namespace path {
+namespace detail {
+struct FlowDebugInfo;
+}
+
/** RippleCalc calculates the quality of a payment path.
Quality is the amount of input required to produce a given output along a
@@ -145,7 +149,7 @@ private:
}
/** Compute liquidity through these path sets. */
- TER rippleCalculate ();
+ TER rippleCalculate (detail::FlowDebugInfo* flowDebugInfo=nullptr);
/** Add a single PathState. Returns true on success.*/
bool addPathState(STPath const&, TER&);
diff --git a/src/ripple/app/paths/impl/AmountSpec.h b/src/ripple/app/paths/impl/AmountSpec.h
index 10ba1336b..57be8e009 100644
--- a/src/ripple/app/paths/impl/AmountSpec.h
+++ b/src/ripple/app/paths/impl/AmountSpec.h
@@ -182,6 +182,15 @@ toAmountSpec (STAmount const& amt)
return result;
}
+inline
+EitherAmount
+toEitherAmount (STAmount const& amt)
+{
+ if (isXRP (amt))
+ return EitherAmount{amt.xrp()};
+ return EitherAmount{amt.iou()};
+}
+
inline
AmountSpec
toAmountSpec (
diff --git a/src/ripple/app/paths/impl/FlowDebugInfo.h b/src/ripple/app/paths/impl/FlowDebugInfo.h
new file mode 100644
index 000000000..62d484190
--- /dev/null
+++ b/src/ripple/app/paths/impl/FlowDebugInfo.h
@@ -0,0 +1,372 @@
+//------------------------------------------------------------------------------
+/*
+ This file is part of rippled: https://github.com/ripple/rippled
+ Copyright (c) 2012, 2013 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_PATH_IMPL_FLOWDEBUGINFO_H_INCLUDED
+#define RIPPLE_PATH_IMPL_FLOWDEBUGINFO_H_INCLUDED
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+namespace ripple
+{
+namespace path
+{
+namespace detail
+{
+// Track performance information of a single payment
+struct FlowDebugInfo
+{
+ using clock = std::chrono::high_resolution_clock;
+ using time_point = clock::time_point;
+ boost::container::flat_map>
+ timePoints;
+ boost::container::flat_map counts;
+
+ struct PassInfo
+ {
+ PassInfo () = delete;
+ PassInfo (bool nativeIn_, bool nativeOut_)
+ : nativeIn (nativeIn_), nativeOut (nativeOut_)
+ {
+ }
+ bool const nativeIn;
+ bool const nativeOut;
+ std::vector in;
+ std::vector out;
+ std::vector numActive;
+
+ std::vector> liquiditySrcIn;
+ std::vector> liquiditySrcOut;
+
+ void
+ reserve (size_t s)
+ {
+ in.reserve (s);
+ out.reserve (s);
+ liquiditySrcIn.reserve(s);
+ liquiditySrcOut.reserve(s);
+ numActive.reserve (s);
+ }
+
+ size_t
+ size () const
+ {
+ return in.size ();
+ }
+
+ void
+ push_back (EitherAmount const& in_amt,
+ EitherAmount const& out_amt,
+ std::size_t active)
+ {
+ in.push_back (in_amt);
+ out.push_back (out_amt);
+ numActive.push_back (active);
+ }
+
+ void
+ pushLiquiditySrc (EitherAmount const& in, EitherAmount const& out)
+ {
+ assert(!liquiditySrcIn.empty());
+ liquiditySrcIn.back().push_back(in);
+ liquiditySrcOut.back().push_back(out);
+ }
+
+ void
+ newLiquidityPass()
+ {
+ auto const s = liquiditySrcIn.size();
+ size_t const r = !numActive.empty() ? numActive.back() : 16;
+ liquiditySrcIn.resize(s+1);
+ liquiditySrcIn.back().reserve(r);
+ liquiditySrcOut.resize(s+1);
+ liquiditySrcOut.back().reserve(r);
+ }
+ };
+
+ PassInfo passInfo;
+
+ FlowDebugInfo () = delete;
+ FlowDebugInfo (bool nativeIn, bool nativeOut)
+ : passInfo (nativeIn, nativeOut)
+ {
+ timePoints.reserve (16);
+ counts.reserve (16);
+ passInfo.reserve (64);
+ }
+
+ auto
+ duration (std::string const& tag) const
+ {
+ auto i = timePoints.find (tag);
+ if (i == timePoints.end ())
+ {
+ assert (0);
+ return std::chrono::duration(0);
+ }
+ auto const& t = i->second;
+ return std::chrono::duration_cast> (
+ t.second - t.first);
+ }
+
+ std::size_t
+ count (std::string const& tag) const
+ {
+ auto i = counts.find (tag);
+ if (i == counts.end ())
+ return 0;
+ return i->second;
+ }
+
+ // Time the duration of the existence of the result
+ auto
+ timeBlock (std::string name)
+ {
+ struct Stopper
+ {
+ std::string tag;
+ FlowDebugInfo* info;
+ Stopper (std::string name, FlowDebugInfo& pi)
+ : tag (std::move (name)), info (&pi)
+ {
+ auto const start = FlowDebugInfo::clock::now ();
+ info->timePoints.emplace (tag, std::make_pair (start, start));
+ }
+ ~Stopper ()
+ {
+ auto const end = FlowDebugInfo::clock::now ();
+ info->timePoints[tag].second = end;
+ }
+ };
+ return Stopper (std::move (name), *this);
+ }
+
+ void
+ inc (std::string const& tag)
+ {
+ auto i = counts.find (tag);
+ if (i == counts.end ())
+ {
+ counts[tag] = 1;
+ }
+ ++i->second;
+ }
+
+ void
+ setCount (std::string const& tag, std::size_t c)
+ {
+ counts[tag] = c;
+ }
+
+ std::size_t
+ passCount () const
+ {
+ return passInfo.size ();
+ }
+
+ void
+ pushPass (EitherAmount const& in,
+ EitherAmount const& out,
+ std::size_t activeStrands)
+ {
+ passInfo.push_back (in, out, activeStrands);
+ }
+
+ void
+ pushLiquiditySrc (EitherAmount const& in, EitherAmount const& out)
+ {
+ passInfo.pushLiquiditySrc (in, out);
+ }
+
+ void
+ newLiquidityPass ()
+ {
+ passInfo.newLiquidityPass ();
+ }
+
+ std::string
+ to_string (bool writePassInfo) const
+ {
+ std::ostringstream ostr;
+
+ auto const d = duration ("main");
+
+ ostr << "duration: " << d.count () << ", pass_count: " << passCount ();
+
+ if (writePassInfo)
+ {
+ auto write_list = [&ostr](auto const& vals, auto&& fun, char delim=';') {
+ ostr << '[';
+ if (!vals.empty ())
+ {
+ ostr << fun (vals[0]);
+ for (size_t i = 1, e = vals.size (); i < e; ++i)
+ ostr << delim << fun (vals[i]);
+ }
+ ostr << ']';
+ };
+ auto writeXrpAmtList = [&ostr, &write_list](
+ std::vector const& amts, char delim=';') {
+ auto get_val = [](EitherAmount const& a) -> std::string {
+ return ripple::to_string (a.xrp);
+ };
+ write_list (amts, get_val, delim);
+ };
+ auto writeIouAmtList = [&ostr, &write_list](
+ std::vector const& amts, char delim=';') {
+ auto get_val = [](EitherAmount const& a) -> std::string {
+ return ripple::to_string (a.iou);
+ };
+ write_list (amts, get_val, delim);
+ };
+ auto writeIntList = [&ostr, &write_list](
+ std::vector const& vals, char delim=';') {
+ auto get_val = [](
+ size_t const& v) -> size_t const& { return v; };
+ write_list (vals, get_val);
+ };
+ auto writeNestedIouAmtList = [&ostr, &writeIouAmtList](
+ std::vector> const& amts) {
+ ostr << '[';
+ if (!amts.empty ())
+ {
+ writeIouAmtList(amts[0], '|');
+ for (size_t i = 1, e = amts.size (); i < e; ++i)
+ {
+ ostr << ';';
+ writeIouAmtList(amts[i], '|');
+ }
+ }
+ ostr << ']';
+ };
+ auto writeNestedXrpAmtList = [&ostr, &writeXrpAmtList](
+ std::vector> const& amts) {
+ ostr << '[';
+ if (!amts.empty ())
+ {
+ writeXrpAmtList(amts[0], '|');
+ for (size_t i = 1, e = amts.size (); i < e; ++i)
+ {
+ ostr << ';';
+ writeXrpAmtList(amts[i], '|');
+ }
+ }
+ ostr << ']';
+ };
+
+ ostr << ", in_pass: ";
+ if (passInfo.nativeIn)
+ writeXrpAmtList (passInfo.in);
+ else
+ writeIouAmtList (passInfo.in);
+ ostr << ", out_pass: ";
+ if (passInfo.nativeOut)
+ writeXrpAmtList (passInfo.out);
+ else
+ writeIouAmtList (passInfo.out);
+ ostr << ", num_active: ";
+ writeIntList (passInfo.numActive);
+ if (!passInfo.liquiditySrcIn.empty () &&
+ !passInfo.liquiditySrcIn.back ().empty ())
+ {
+ ostr << ", l_src_in: ";
+ if (passInfo.nativeIn)
+ writeNestedXrpAmtList (passInfo.liquiditySrcIn);
+ else
+ writeNestedIouAmtList (passInfo.liquiditySrcIn);
+ ostr << ", l_src_out: ";
+ if (passInfo.nativeOut)
+ writeNestedXrpAmtList (passInfo.liquiditySrcOut);
+ else
+ writeNestedIouAmtList (passInfo.liquiditySrcOut);
+ }
+ }
+
+ return ostr.str ();
+ }
+};
+
+inline
+void
+writeDiffElement (std::ostringstream& ostr,
+ std::pair, STAmount> const& elem)
+{
+ using namespace std;
+ auto const k = elem.first;
+ auto const v = elem.second;
+ ostr << '[' << get<0> (k) << '|' << get<1> (k) << '|' << get<2> (k) << '|'
+ << v << ']';
+};
+
+template
+void
+writeDiffs (std::ostringstream& ostr, Iter begin, Iter end)
+{
+ ostr << '[';
+ if (begin != end)
+ {
+ writeDiffElement (ostr, *begin);
+ ++begin;
+ }
+ for (; begin != end; ++begin)
+ {
+ ostr << ';';
+ writeDiffElement (ostr, *begin);
+ }
+ ostr << ']';
+};
+
+using BalanceDiffs = std::pair<
+ std::map, STAmount>,
+ XRPAmount>;
+
+inline
+BalanceDiffs
+balanceDiffs(PaymentSandbox const& sb, ReadView const& rv)
+{
+ return {sb.balanceChanges (rv), sb.xrpDestroyed ()};
+}
+
+inline
+std::string
+balanceDiffsToString (boost::optional const& bd)
+{
+ if (!bd)
+ return std::string{};
+ auto const& diffs = bd->first;
+ auto const& xrpDestroyed = bd->second;
+ std::ostringstream ostr;
+ ostr << ", xrpDestroyed: " << to_string (xrpDestroyed);
+ ostr << ", balanceDiffs: ";
+ writeDiffs (ostr, diffs.begin (), diffs.end ());
+ return ostr.str ();
+};
+
+} // detail
+} // path
+} // ripple
+#endif
diff --git a/src/ripple/app/paths/impl/StrandFlow.h b/src/ripple/app/paths/impl/StrandFlow.h
index 464321dc4..9b1f62cfd 100644
--- a/src/ripple/app/paths/impl/StrandFlow.h
+++ b/src/ripple/app/paths/impl/StrandFlow.h
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -330,6 +331,11 @@ public:
{
return cur_.end ();
}
+
+ auto size () const
+ {
+ return cur_.size ();
+ }
};
/*
@@ -355,7 +361,8 @@ flow (PaymentSandbox const& baseView,
bool partialPayment,
boost::optional const& limitQuality,
boost::optional const& sendMaxST,
- beast::Journal j)
+ beast::Journal j,
+ path::detail::FlowDebugInfo* flowDebugInfo=nullptr)
{
using Result = FlowResult;
@@ -434,6 +441,7 @@ flow (PaymentSandbox const& baseView,
boost::container::flat_set ofrsToRm;
boost::optional best;
+ if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
for (auto strand : activeStrands)
{
auto f = flow (
@@ -446,6 +454,9 @@ flow (PaymentSandbox const& baseView,
if (f.ter != tesSUCCESS || f.out == beast::zero)
continue;
+ if (flowDebugInfo)
+ flowDebugInfo->pushLiquiditySrc(EitherAmount(f.in), EitherAmount(f.out));
+
assert (f.out <= remainingOut && f.sandbox &&
(!remainingIn || f.in <= *remainingIn));
@@ -483,6 +494,10 @@ flow (PaymentSandbox const& baseView,
if (sendMax)
remainingIn = *sendMax - sum (savedIns);
+ if (flowDebugInfo)
+ flowDebugInfo->pushPass (EitherAmount (best->in),
+ EitherAmount (best->out), activeStrands.size ());
+
JLOG (j.trace())
<< "Best path: in: " << to_string (best->in)
<< " out: " << to_string (best->out)
diff --git a/src/ripple/protocol/Feature.h b/src/ripple/protocol/Feature.h
index 7acd6daac..8b867f7a4 100644
--- a/src/ripple/protocol/Feature.h
+++ b/src/ripple/protocol/Feature.h
@@ -41,6 +41,7 @@ extern uint256 const featureTrustSetAuth;
extern uint256 const featureFeeEscalation;
extern uint256 const featureFlowV2;
extern uint256 const featureOwnerPaysFee;
+extern uint256 const featureCompareFlowV1V2;
} // ripple
diff --git a/src/ripple/protocol/impl/Feature.cpp b/src/ripple/protocol/impl/Feature.cpp
index dfe15456b..cd1b1f510 100644
--- a/src/ripple/protocol/impl/Feature.cpp
+++ b/src/ripple/protocol/impl/Feature.cpp
@@ -52,5 +52,6 @@ uint256 const featureTrustSetAuth = feature("TrustSetAuth");
uint256 const featureFeeEscalation = feature("FeeEscalation");
uint256 const featureFlowV2 = feature("FlowV2");
uint256 const featureOwnerPaysFee = feature("OwnerPaysFee");
+uint256 const featureCompareFlowV1V2 = feature("CompareFlowV1V2");
} // ripple