mirror of
https://github.com/Xahau/xahaud.git
synced 2025-11-20 10:35:50 +00:00
Add FlowDebugInfo to easily compare diffs between flow V1 and V2
This commit is contained in:
@@ -1049,6 +1049,8 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClInclude Include="..\..\src\ripple\app\paths\impl\FlowDebugInfo.h">
|
||||
</ClInclude>
|
||||
<ClCompile Include="..\..\src\ripple\app\paths\impl\PaySteps.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='debug|x64'">True</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='release|x64'">True</ExcludedFromBuild>
|
||||
|
||||
@@ -1524,6 +1524,9 @@
|
||||
<ClCompile Include="..\..\src\ripple\app\paths\impl\DirectStep.cpp">
|
||||
<Filter>ripple\app\paths\impl</Filter>
|
||||
</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">
|
||||
<Filter>ripple\app\paths\impl</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@@ -65,7 +65,8 @@ flow (
|
||||
bool ownerPaysTransferFee,
|
||||
boost::optional<Quality> const& limitQuality,
|
||||
boost::optional<STAmount> 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<XRPAmount, XRPAmount> (
|
||||
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<XRPAmount, IOUAmount> (
|
||||
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<IOUAmount, XRPAmount> (
|
||||
sb, strands, asDeliver.xrp, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j));
|
||||
limitQuality, sendMax, j, flowDebugInfo));
|
||||
}
|
||||
|
||||
assert (!srcIsXRP && !dstIsXRP);
|
||||
return finishFlow (sb, srcIssue, dstIssue,
|
||||
flow<IOUAmount, IOUAmount> (
|
||||
sb, strands, asDeliver.iou, defaultPaths, partialPayment,
|
||||
limitQuality, sendMax, j));
|
||||
limitQuality, sendMax, j, flowDebugInfo));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Quality> const& limitQuality,
|
||||
boost::optional<STAmount> const& sendMax,
|
||||
beast::Journal j);
|
||||
beast::Journal j,
|
||||
path::detail::FlowDebugInfo* flowDebugInfo=nullptr);
|
||||
|
||||
} // ripple
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <ripple/app/paths/RippleCalc.h>
|
||||
#include <ripple/app/paths/Tuning.h>
|
||||
#include <ripple/app/paths/cursor/PathCursor.h>
|
||||
#include <ripple/app/paths/impl/FlowDebugInfo.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/core/Config.h>
|
||||
#include <ripple/ledger/View.h>
|
||||
@@ -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,31 +154,62 @@ RippleCalc::Output RippleCalc::rippleCalculate (
|
||||
if (!useFlowV1Output)
|
||||
Rethrow();
|
||||
}
|
||||
}
|
||||
|
||||
if (j.debug())
|
||||
{
|
||||
auto logResult = [&] (std::string const& algoName, Output const& result)
|
||||
{
|
||||
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 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))))
|
||||
{
|
||||
outputPassInfo = true;
|
||||
}
|
||||
bdV1 = detail::balanceDiffs (flowV1SB, view);
|
||||
bdV2 = detail::balanceDiffs (flowV2SB, view);
|
||||
outputBalanceDiffs = bdV1 != bdV2;
|
||||
}
|
||||
|
||||
if (callFlowV1)
|
||||
{
|
||||
logResult ("V1", flowV1Out);
|
||||
logResult ("V1", flowV1Out, flowV1FlowDebugInfo, bdV1,
|
||||
outputPassInfo, outputBalanceDiffs);
|
||||
}
|
||||
if (callFlowV2)
|
||||
{
|
||||
logResult ("V2", flowV2Out);
|
||||
logResult ("V2", flowV2Out, flowV2FlowDebugInfo, bdV2,
|
||||
outputPassInfo, outputBalanceDiffs);
|
||||
}
|
||||
}
|
||||
|
||||
JLOG (j.trace()) << "Using old flow: " << useFlowV1Output;
|
||||
}
|
||||
|
||||
if (!useFlowV1Output)
|
||||
{
|
||||
@@ -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];
|
||||
|
||||
if (flowDebugInfo)
|
||||
flowDebugInfo->pushPass (toEitherAmount (pathState->inPass ()),
|
||||
toEitherAmount (pathState->outPass ()),
|
||||
pathStateList_.size () - iDry);
|
||||
|
||||
JLOG (j_.debug ())
|
||||
<< "rippleCalc: best:"
|
||||
<< " uQuality="
|
||||
<< amountFromRate (pathState->quality())
|
||||
<< " uQuality=" << amountFromRate (pathState->quality ())
|
||||
<< " inPass()=" << pathState->inPass ()
|
||||
<< " saOutPass=" << pathState->outPass()
|
||||
<< " iBest=" << iBest;
|
||||
<< " saOutPass=" << pathState->outPass () << " iBest=" << iBest;
|
||||
|
||||
// Record best pass' offers that became unfunded for deletion on
|
||||
// success.
|
||||
|
||||
@@ -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&);
|
||||
|
||||
@@ -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 (
|
||||
|
||||
372
src/ripple/app/paths/impl/FlowDebugInfo.h
Normal file
372
src/ripple/app/paths/impl/FlowDebugInfo.h
Normal 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
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <ripple/app/paths/Credit.h>
|
||||
#include <ripple/app/paths/Flow.h>
|
||||
#include <ripple/app/paths/impl/AmountSpec.h>
|
||||
#include <ripple/app/paths/impl/FlowDebugInfo.h>
|
||||
#include <ripple/app/paths/impl/Steps.h>
|
||||
#include <ripple/basics/Log.h>
|
||||
#include <ripple/protocol/IOUAmount.h>
|
||||
@@ -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<Quality> const& limitQuality,
|
||||
boost::optional<STAmount> const& sendMaxST,
|
||||
beast::Journal j)
|
||||
beast::Journal j,
|
||||
path::detail::FlowDebugInfo* flowDebugInfo=nullptr)
|
||||
{
|
||||
using Result = FlowResult<TInAmt, TOutAmt>;
|
||||
|
||||
@@ -434,6 +441,7 @@ flow (PaymentSandbox const& baseView,
|
||||
|
||||
boost::container::flat_set<uint256> ofrsToRm;
|
||||
boost::optional<BestStrand> best;
|
||||
if (flowDebugInfo) flowDebugInfo->newLiquidityPass();
|
||||
for (auto strand : activeStrands)
|
||||
{
|
||||
auto f = flow<TInAmt, TOutAmt> (
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user