Add FlowDebugInfo to easily compare diffs between flow V1 and V2

This commit is contained in:
seelabs
2016-06-06 08:23:52 -04:00
parent 34d590d93a
commit f16d701a2c
11 changed files with 506 additions and 42 deletions

View File

@@ -1049,6 +1049,8 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\app\paths\impl\FlowDebugInfo.h">
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\paths\impl\PaySteps.cpp"> <ClCompile Include="..\..\src\ripple\app\paths\impl\PaySteps.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>

View File

@@ -1524,6 +1524,9 @@
<ClCompile Include="..\..\src\ripple\app\paths\impl\DirectStep.cpp"> <ClCompile Include="..\..\src\ripple\app\paths\impl\DirectStep.cpp">
<Filter>ripple\app\paths\impl</Filter> <Filter>ripple\app\paths\impl</Filter>
</ClCompile> </ClCompile>
<ClInclude Include="..\..\src\ripple\app\paths\impl\FlowDebugInfo.h">
<Filter>ripple\app\paths\impl</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ripple\app\paths\impl\PaySteps.cpp"> <ClCompile Include="..\..\src\ripple\app\paths\impl\PaySteps.cpp">
<Filter>ripple\app\paths\impl</Filter> <Filter>ripple\app\paths\impl</Filter>
</ClCompile> </ClCompile>

View File

@@ -65,7 +65,8 @@ flow (
bool ownerPaysTransferFee, bool ownerPaysTransferFee,
boost::optional<Quality> const& limitQuality, boost::optional<Quality> const& limitQuality,
boost::optional<STAmount> const& sendMax, boost::optional<STAmount> const& sendMax,
beast::Journal j) beast::Journal j,
path::detail::FlowDebugInfo* flowDebugInfo)
{ {
Issue const srcIssue = [&] { Issue const srcIssue = [&] {
if (sendMax) if (sendMax)
@@ -123,7 +124,7 @@ flow (
return finishFlow (sb, srcIssue, dstIssue, return finishFlow (sb, srcIssue, dstIssue,
flow<XRPAmount, XRPAmount> ( flow<XRPAmount, XRPAmount> (
sb, strands, asDeliver.xrp, defaultPaths, partialPayment, sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
limitQuality, sendMax, j)); limitQuality, sendMax, j, flowDebugInfo));
} }
if (srcIsXRP && !dstIsXRP) if (srcIsXRP && !dstIsXRP)
@@ -131,7 +132,7 @@ flow (
return finishFlow (sb, srcIssue, dstIssue, return finishFlow (sb, srcIssue, dstIssue,
flow<XRPAmount, IOUAmount> ( flow<XRPAmount, IOUAmount> (
sb, strands, asDeliver.iou, defaultPaths, partialPayment, sb, strands, asDeliver.iou, defaultPaths, partialPayment,
limitQuality, sendMax, j)); limitQuality, sendMax, j, flowDebugInfo));
} }
if (!srcIsXRP && dstIsXRP) if (!srcIsXRP && dstIsXRP)
@@ -139,14 +140,14 @@ flow (
return finishFlow (sb, srcIssue, dstIssue, return finishFlow (sb, srcIssue, dstIssue,
flow<IOUAmount, XRPAmount> ( flow<IOUAmount, XRPAmount> (
sb, strands, asDeliver.xrp, defaultPaths, partialPayment, sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
limitQuality, sendMax, j)); limitQuality, sendMax, j, flowDebugInfo));
} }
assert (!srcIsXRP && !dstIsXRP); assert (!srcIsXRP && !dstIsXRP);
return finishFlow (sb, srcIssue, dstIssue, return finishFlow (sb, srcIssue, dstIssue,
flow<IOUAmount, IOUAmount> ( flow<IOUAmount, IOUAmount> (
sb, strands, asDeliver.iou, defaultPaths, partialPayment, sb, strands, asDeliver.iou, defaultPaths, partialPayment,
limitQuality, sendMax, j)); limitQuality, sendMax, j, flowDebugInfo));
} }

View File

@@ -27,6 +27,12 @@
namespace ripple namespace ripple
{ {
namespace path {
namespace detail{
struct FlowDebugInfo;
}
}
/** /**
Make a payment from the src account to the dst account Make a payment from the src account to the dst account
@@ -54,7 +60,8 @@ flow (PaymentSandbox& view,
bool ownerPaysTransferFee, bool ownerPaysTransferFee,
boost::optional<Quality> const& limitQuality, boost::optional<Quality> const& limitQuality,
boost::optional<STAmount> const& sendMax, boost::optional<STAmount> const& sendMax,
beast::Journal j); beast::Journal j,
path::detail::FlowDebugInfo* flowDebugInfo=nullptr);
} // ripple } // ripple

View File

@@ -22,6 +22,7 @@
#include <ripple/app/paths/RippleCalc.h> #include <ripple/app/paths/RippleCalc.h>
#include <ripple/app/paths/Tuning.h> #include <ripple/app/paths/Tuning.h>
#include <ripple/app/paths/cursor/PathCursor.h> #include <ripple/app/paths/cursor/PathCursor.h>
#include <ripple/app/paths/impl/FlowDebugInfo.h>
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
#include <ripple/core/Config.h> #include <ripple/core/Config.h>
#include <ripple/ledger/View.h> #include <ripple/ledger/View.h>
@@ -70,19 +71,25 @@ RippleCalc::Output RippleCalc::rippleCalculate (
Config const& config, Config const& config,
Input const* const pInputs) 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 = bool const useFlowV1Output =
!flowV2Switchover (view.info ().parentCloseTime) && !flowV2Switchover (view.info ().parentCloseTime) &&
!view.rules ().enabled (featureFlowV2, config.features); !view.rules ().enabled (featureFlowV2, config.features);
// When flowV2 is enabled via rules, call old flow so results may be bool const callFlowV1 = useFlowV1Output || compareFlowV1V2;
// compared bool const callFlowV2 = !useFlowV1Output || compareFlowV1V2;
bool const callFlowV1 = useFlowV1Output ||
view.rules ().enabled (featureFlowV2, config.features);
bool const callFlowV2 = !useFlowV1Output;
Output flowV1Out; Output flowV1Out;
PaymentSandbox flowV1SB (&view); PaymentSandbox flowV1SB (&view);
auto const inNative = saMaxAmountReq.native();
auto const outNative = saDstAmountReq.native();
detail::FlowDebugInfo flowV1FlowDebugInfo (inNative, outNative);
if (callFlowV1) if (callFlowV1)
{ {
auto const timeIt = flowV1FlowDebugInfo.timeBlock ("main");
RippleCalc rc ( RippleCalc rc (
flowV1SB, flowV1SB,
saMaxAmountReq, saMaxAmountReq,
@@ -96,7 +103,7 @@ RippleCalc::Output RippleCalc::rippleCalculate (
rc.inputFlags = *pInputs; rc.inputFlags = *pInputs;
} }
auto result = rc.rippleCalculate (); auto result = rc.rippleCalculate (compareFlowV1V2 ? &flowV1FlowDebugInfo : nullptr);
flowV1Out.setResult (result); flowV1Out.setResult (result);
flowV1Out.actualAmountIn = rc.actualAmountIn_; flowV1Out.actualAmountIn = rc.actualAmountIn_;
flowV1Out.actualAmountOut = rc.actualAmountOut_; flowV1Out.actualAmountOut = rc.actualAmountOut_;
@@ -106,6 +113,8 @@ RippleCalc::Output RippleCalc::rippleCalculate (
Output flowV2Out; Output flowV2Out;
PaymentSandbox flowV2SB (&view); PaymentSandbox flowV2SB (&view);
detail::FlowDebugInfo flowV2FlowDebugInfo (inNative, outNative);
auto j = l.journal ("Flow");
if (callFlowV2) if (callFlowV2)
{ {
bool defaultPaths = true; bool defaultPaths = true;
@@ -129,15 +138,15 @@ RippleCalc::Output RippleCalc::rippleCalculate (
sendMax.emplace (saMaxAmountReq); sendMax.emplace (saMaxAmountReq);
} }
auto j = l.journal ("Flow");
try try
{ {
bool const ownerPaysTransferFee = bool const ownerPaysTransferFee =
view.rules ().enabled (featureOwnerPaysFee, config.features); view.rules ().enabled (featureOwnerPaysFee, config.features);
auto const timeIt = flowV2FlowDebugInfo.timeBlock ("main");
flowV2Out = flow (flowV2SB, saDstAmountReq, uSrcAccountID, flowV2Out = flow (flowV2SB, saDstAmountReq, uSrcAccountID,
uDstAccountID, spsPaths, defaultPaths, partialPayment, uDstAccountID, spsPaths, defaultPaths, partialPayment,
ownerPaysTransferFee, limitQuality, sendMax, j); ownerPaysTransferFee, limitQuality, sendMax, j,
compareFlowV1V2 ? &flowV2FlowDebugInfo : nullptr);
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@@ -145,32 +154,63 @@ RippleCalc::Output RippleCalc::rippleCalculate (
if (!useFlowV1Output) if (!useFlowV1Output)
Rethrow(); 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<BalanceDiffs> 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<BalanceDiffs> 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> " << outputPassInfo = true;
" 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);
} }
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) if (!useFlowV1Output)
{ {
flowV2SB.apply (view); 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. // liquidity. No need to revisit path in the future if all liquidity is used.
// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed. // <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed.
TER RippleCalc::rippleCalculate () TER RippleCalc::rippleCalculate (detail::FlowDebugInfo* flowDebugInfo)
{ {
JLOG (j_.trace()) JLOG (j_.trace())
<< "rippleCalc>" << "rippleCalc>"
@@ -301,6 +341,7 @@ TER RippleCalc::rippleCalculate ()
// True, if ever computed multi-quality. // True, if ever computed multi-quality.
bool multiQuality = false; bool multiQuality = false;
if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
// Find the best path. // Find the best path.
for (auto pathState : pathStateList_) for (auto pathState : pathStateList_)
{ {
@@ -328,6 +369,11 @@ TER RippleCalc::rippleCalculate ()
<< " uQuality=" << pathState->quality() << " uQuality=" << pathState->quality()
<< " rate=" << amountFromRate (pathState->quality()); << " rate=" << amountFromRate (pathState->quality());
if (flowDebugInfo)
flowDebugInfo->pushLiquiditySrc (
toEitherAmount (pathState->inPass ()),
toEitherAmount (pathState->outPass ()));
if (!pathState->quality()) if (!pathState->quality())
{ {
// Path was dry. // Path was dry.
@@ -418,13 +464,16 @@ TER RippleCalc::rippleCalculate ()
// Apply best path. // Apply best path.
auto pathState = pathStateList_[iBest]; 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:" << "rippleCalc: best:"
<< " uQuality=" << " uQuality=" << amountFromRate (pathState->quality ())
<< amountFromRate (pathState->quality()) << " inPass()=" << pathState->inPass ()
<< " inPass()=" << pathState->inPass() << " saOutPass=" << pathState->outPass () << " iBest=" << iBest;
<< " saOutPass=" << pathState->outPass()
<< " iBest=" << iBest;
// Record best pass' offers that became unfunded for deletion on // Record best pass' offers that became unfunded for deletion on
// success. // success.

View File

@@ -32,6 +32,10 @@ namespace ripple {
class Config; class Config;
namespace path { namespace path {
namespace detail {
struct FlowDebugInfo;
}
/** RippleCalc calculates the quality of a payment path. /** RippleCalc calculates the quality of a payment path.
Quality is the amount of input required to produce a given output along a 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. */ /** Compute liquidity through these path sets. */
TER rippleCalculate (); TER rippleCalculate (detail::FlowDebugInfo* flowDebugInfo=nullptr);
/** Add a single PathState. Returns true on success.*/ /** Add a single PathState. Returns true on success.*/
bool addPathState(STPath const&, TER&); bool addPathState(STPath const&, TER&);

View File

@@ -182,6 +182,15 @@ toAmountSpec (STAmount const& amt)
return result; return result;
} }
inline
EitherAmount
toEitherAmount (STAmount const& amt)
{
if (isXRP (amt))
return EitherAmount{amt.xrp()};
return EitherAmount{amt.iou()};
}
inline inline
AmountSpec AmountSpec
toAmountSpec ( toAmountSpec (

View File

@@ -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 <ripple/app/paths/impl/AmountSpec.h>
#include <ripple/ledger/PaymentSandbox.h>
#include <ripple/protocol/IOUAmount.h>
#include <ripple/protocol/XRPAmount.h>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <chrono>
#include <sstream>
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<std::string, std::pair<time_point, time_point>>
timePoints;
boost::container::flat_map<std::string, std::size_t> counts;
struct PassInfo
{
PassInfo () = delete;
PassInfo (bool nativeIn_, bool nativeOut_)
: nativeIn (nativeIn_), nativeOut (nativeOut_)
{
}
bool const nativeIn;
bool const nativeOut;
std::vector<EitherAmount> in;
std::vector<EitherAmount> out;
std::vector<size_t> numActive;
std::vector<std::vector<EitherAmount>> liquiditySrcIn;
std::vector<std::vector<EitherAmount>> 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<double>(0);
}
auto const& t = i->second;
return std::chrono::duration_cast<std::chrono::duration<double>> (
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<EitherAmount> 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<EitherAmount> 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<size_t> 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<std::vector<EitherAmount>> 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<std::vector<EitherAmount>> 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<std::tuple<AccountID, AccountID, Currency>, 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<class Iter>
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<std::tuple<AccountID, AccountID, Currency>, STAmount>,
XRPAmount>;
inline
BalanceDiffs
balanceDiffs(PaymentSandbox const& sb, ReadView const& rv)
{
return {sb.balanceChanges (rv), sb.xrpDestroyed ()};
}
inline
std::string
balanceDiffsToString (boost::optional<BalanceDiffs> 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

View File

@@ -24,6 +24,7 @@
#include <ripple/app/paths/Credit.h> #include <ripple/app/paths/Credit.h>
#include <ripple/app/paths/Flow.h> #include <ripple/app/paths/Flow.h>
#include <ripple/app/paths/impl/AmountSpec.h> #include <ripple/app/paths/impl/AmountSpec.h>
#include <ripple/app/paths/impl/FlowDebugInfo.h>
#include <ripple/app/paths/impl/Steps.h> #include <ripple/app/paths/impl/Steps.h>
#include <ripple/basics/Log.h> #include <ripple/basics/Log.h>
#include <ripple/protocol/IOUAmount.h> #include <ripple/protocol/IOUAmount.h>
@@ -330,6 +331,11 @@ public:
{ {
return cur_.end (); return cur_.end ();
} }
auto size () const
{
return cur_.size ();
}
}; };
/* /*
@@ -355,7 +361,8 @@ flow (PaymentSandbox const& baseView,
bool partialPayment, bool partialPayment,
boost::optional<Quality> const& limitQuality, boost::optional<Quality> const& limitQuality,
boost::optional<STAmount> const& sendMaxST, boost::optional<STAmount> const& sendMaxST,
beast::Journal j) beast::Journal j,
path::detail::FlowDebugInfo* flowDebugInfo=nullptr)
{ {
using Result = FlowResult<TInAmt, TOutAmt>; using Result = FlowResult<TInAmt, TOutAmt>;
@@ -434,6 +441,7 @@ flow (PaymentSandbox const& baseView,
boost::container::flat_set<uint256> ofrsToRm; boost::container::flat_set<uint256> ofrsToRm;
boost::optional<BestStrand> best; boost::optional<BestStrand> best;
if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
for (auto strand : activeStrands) for (auto strand : activeStrands)
{ {
auto f = flow<TInAmt, TOutAmt> ( auto f = flow<TInAmt, TOutAmt> (
@@ -446,6 +454,9 @@ flow (PaymentSandbox const& baseView,
if (f.ter != tesSUCCESS || f.out == beast::zero) if (f.ter != tesSUCCESS || f.out == beast::zero)
continue; continue;
if (flowDebugInfo)
flowDebugInfo->pushLiquiditySrc(EitherAmount(f.in), EitherAmount(f.out));
assert (f.out <= remainingOut && f.sandbox && assert (f.out <= remainingOut && f.sandbox &&
(!remainingIn || f.in <= *remainingIn)); (!remainingIn || f.in <= *remainingIn));
@@ -483,6 +494,10 @@ flow (PaymentSandbox const& baseView,
if (sendMax) if (sendMax)
remainingIn = *sendMax - sum (savedIns); remainingIn = *sendMax - sum (savedIns);
if (flowDebugInfo)
flowDebugInfo->pushPass (EitherAmount (best->in),
EitherAmount (best->out), activeStrands.size ());
JLOG (j.trace()) JLOG (j.trace())
<< "Best path: in: " << to_string (best->in) << "Best path: in: " << to_string (best->in)
<< " out: " << to_string (best->out) << " out: " << to_string (best->out)

View File

@@ -41,6 +41,7 @@ extern uint256 const featureTrustSetAuth;
extern uint256 const featureFeeEscalation; extern uint256 const featureFeeEscalation;
extern uint256 const featureFlowV2; extern uint256 const featureFlowV2;
extern uint256 const featureOwnerPaysFee; extern uint256 const featureOwnerPaysFee;
extern uint256 const featureCompareFlowV1V2;
} // ripple } // ripple

View File

@@ -52,5 +52,6 @@ uint256 const featureTrustSetAuth = feature("TrustSetAuth");
uint256 const featureFeeEscalation = feature("FeeEscalation"); uint256 const featureFeeEscalation = feature("FeeEscalation");
uint256 const featureFlowV2 = feature("FlowV2"); uint256 const featureFlowV2 = feature("FlowV2");
uint256 const featureOwnerPaysFee = feature("OwnerPaysFee"); uint256 const featureOwnerPaysFee = feature("OwnerPaysFee");
uint256 const featureCompareFlowV1V2 = feature("CompareFlowV1V2");
} // ripple } // ripple