mirror of
https://github.com/Xahau/xahaud.git
synced 2025-12-06 17:27:52 +00:00
Refactor the structure of RippleCalc
* Split code into multiple files * Rename and move things
This commit is contained in:
111
src/ripple_app/paths/CalcNode.cpp
Normal file
111
src/ripple_app/paths/CalcNode.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "CalcState.h"
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
TER calcNodeFwd (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex, PathState& pathState,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
auto const& node = pathState.vpnNodes[nodeIndex];
|
||||
auto const nodeIsAccount = isAccount(node);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeFwd> nodeIndex=" << nodeIndex;
|
||||
|
||||
TER errorCode = nodeIsAccount
|
||||
? calcNodeAccountFwd (rippleCalc, nodeIndex, pathState, bMultiQuality)
|
||||
: calcNodeOfferFwd (rippleCalc, nodeIndex, pathState, bMultiQuality);
|
||||
|
||||
if (errorCode == tesSUCCESS && nodeIndex + 1 != pathState.vpnNodes.size ())
|
||||
errorCode = calcNodeFwd (rippleCalc, nodeIndex + 1, pathState, bMultiQuality);
|
||||
|
||||
if (errorCode == tesSUCCESS && !(pathState.saInPass && pathState.saOutPass))
|
||||
errorCode = tecPATH_DRY;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeFwd<"
|
||||
<< " nodeIndex:" << nodeIndex
|
||||
<< " errorCode:" << errorCode;
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
// Calculate a node and its previous nodes.
|
||||
//
|
||||
// From the destination work in reverse towards the source calculating how much
|
||||
// must be asked for.
|
||||
//
|
||||
// Then work forward, figuring out how much can actually be delivered.
|
||||
// <-- errorCode: tesSUCCESS or tecPATH_DRY
|
||||
// <-> pnNodes:
|
||||
// --> [end]saWanted.mAmount
|
||||
// --> [all]saWanted.mCurrency
|
||||
// --> [all]saAccount
|
||||
// <-> [0]saWanted.mAmount : --> limit, <-- actual
|
||||
TER calcNodeRev (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex, PathState& pathState,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
PathState::Node& node = pathState.vpnNodes[nodeIndex];
|
||||
bool const nodeIsAccount
|
||||
= is_bit_set (node.uFlags, STPathElement::typeAccount);
|
||||
TER errorCode;
|
||||
|
||||
node.saTransferRate = STAmount::saFromRate (
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uIssuerID));
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRev>"
|
||||
<< " nodeIndex=" << nodeIndex
|
||||
<< " nodeIsAccount=" << nodeIsAccount
|
||||
<< " uIssuerID=" << RippleAddress::createHumanAccountID (node.uIssuerID)
|
||||
<< " saTransferRate=" << node.saTransferRate;
|
||||
|
||||
errorCode = nodeIsAccount
|
||||
? calcNodeAccountRev (rippleCalc, nodeIndex, pathState, bMultiQuality)
|
||||
: calcNodeOfferRev (rippleCalc, nodeIndex, pathState, bMultiQuality);
|
||||
|
||||
// Do previous.
|
||||
if (errorCode != tesSUCCESS)
|
||||
// Error, don't continue.
|
||||
nothing ();
|
||||
else if (nodeIndex)
|
||||
// Continue in reverse. TODO(tom): remove unnecessary recursion.
|
||||
errorCode = calcNodeRev (rippleCalc, nodeIndex - 1, pathState, bMultiQuality);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRev< "
|
||||
<< "nodeIndex=" << nodeIndex
|
||||
<< " errorCode=%s" << transToken (errorCode)
|
||||
<< "/" << errorCode;
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
462
src/ripple_app/paths/CalcNodeAccountFwd.cpp
Normal file
462
src/ripple_app/paths/CalcNodeAccountFwd.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// The reverse pass has been narrowing by credit available and inflating by fees
|
||||
// as it worked backwards. Now, for the current account node, take the actual
|
||||
// amount from previous and adjust forward balances.
|
||||
//
|
||||
// Perform balance adjustments between previous and current node.
|
||||
// - The previous node: specifies what to push through to current.
|
||||
// - All of previous output is consumed.
|
||||
//
|
||||
// Then, compute current node's output for next node.
|
||||
// - Current node: specify what to push through to next.
|
||||
// - Output to next node is computed as input minus quality or transfer fee.
|
||||
// - If next node is an offer and output is non-XRP then we are the issuer and
|
||||
// do not need to push funds.
|
||||
// - If next node is an offer and output is XRP then we need to deliver funds to
|
||||
// limbo.
|
||||
TER calcNodeAccountFwd (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex, // 0 <= nodeIndex <= lastNodeIndex
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
TER errorCode = tesSUCCESS;
|
||||
const unsigned int lastNodeIndex = pathState.vpnNodes.size () - 1;
|
||||
|
||||
std::uint64_t uRateMax = 0;
|
||||
|
||||
auto& previousNode = pathState.vpnNodes[nodeIndex ? nodeIndex - 1 : 0];
|
||||
auto& node = pathState.vpnNodes[nodeIndex];
|
||||
auto& nextNode = pathState.vpnNodes[nodeIndex == lastNodeIndex ? lastNodeIndex : nodeIndex + 1];
|
||||
|
||||
const bool previousNodeIsAccount
|
||||
= is_bit_set (previousNode.uFlags, STPathElement::typeAccount);
|
||||
const bool nextNodeIsAccount
|
||||
= is_bit_set (nextNode.uFlags, STPathElement::typeAccount);
|
||||
|
||||
const uint160& previousAccountID
|
||||
= previousNodeIsAccount ? previousNode.uAccountID : node.uAccountID;
|
||||
// Offers are always issue.
|
||||
const uint160& nextAccountID
|
||||
= nextNodeIsAccount ? nextNode.uAccountID : node.uAccountID;
|
||||
|
||||
std::uint32_t uQualityIn = nodeIndex
|
||||
? rippleCalc.mActiveLedger.rippleQualityIn (
|
||||
node.uAccountID, previousAccountID, node.uCurrencyID)
|
||||
: QUALITY_ONE;
|
||||
std::uint32_t uQualityOut = (nodeIndex == lastNodeIndex)
|
||||
? rippleCalc.mActiveLedger.rippleQualityOut (
|
||||
node.uAccountID, nextAccountID, node.uCurrencyID)
|
||||
: QUALITY_ONE;
|
||||
|
||||
// When looking backward (prv) for req we care about what we just
|
||||
// calculated: use fwd.
|
||||
// When looking forward (cur) for req we care about what was desired: use
|
||||
// rev.
|
||||
|
||||
// For nextNodeIsAccount
|
||||
STAmount saPrvRedeemAct (
|
||||
previousNode.saFwdRedeem.getCurrency (),
|
||||
previousNode.saFwdRedeem.getIssuer ());
|
||||
|
||||
STAmount saPrvIssueAct (
|
||||
previousNode.saFwdIssue.getCurrency (),
|
||||
previousNode.saFwdIssue.getIssuer ());
|
||||
|
||||
// For !previousNodeIsAccount
|
||||
STAmount saPrvDeliverAct (
|
||||
previousNode.saFwdDeliver.getCurrency (),
|
||||
previousNode.saFwdDeliver.getIssuer ());
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd> "
|
||||
<< "nodeIndex=" << nodeIndex << "/" << lastNodeIndex
|
||||
<< " previousNode.saFwdRedeem:" << previousNode.saFwdRedeem
|
||||
<< " saPrvIssueReq:" << previousNode.saFwdIssue
|
||||
<< " previousNode.saFwdDeliver:" << previousNode.saFwdDeliver
|
||||
<< " node.saRevRedeem:" << node.saRevRedeem
|
||||
<< " node.saRevIssue:" << node.saRevIssue
|
||||
<< " node.saRevDeliver:" << node.saRevDeliver;
|
||||
|
||||
// Ripple through account.
|
||||
|
||||
if (previousNodeIsAccount && nextNodeIsAccount)
|
||||
{
|
||||
// Next is an account, must be rippling.
|
||||
|
||||
if (!nodeIndex)
|
||||
{
|
||||
// ^ --> ACCOUNT --> account
|
||||
|
||||
// For the first node, calculate amount to ripple based on what is
|
||||
// available.
|
||||
node.saFwdRedeem = node.saRevRedeem;
|
||||
|
||||
if (pathState.saInReq >= zero)
|
||||
{
|
||||
// Limit by send max.
|
||||
node.saFwdRedeem = std::min (
|
||||
node.saFwdRedeem, pathState.saInReq - pathState.saInAct);
|
||||
}
|
||||
|
||||
pathState.saInPass = node.saFwdRedeem;
|
||||
|
||||
node.saFwdIssue = node.saFwdRedeem == node.saRevRedeem
|
||||
// Fully redeemed.
|
||||
? node.saRevIssue : STAmount (node.saRevIssue);
|
||||
|
||||
if (!!node.saFwdIssue && pathState.saInReq >= zero)
|
||||
{
|
||||
// Limit by send max.
|
||||
node.saFwdIssue = std::min (
|
||||
node.saFwdIssue,
|
||||
pathState.saInReq - pathState.saInAct - node.saFwdRedeem);
|
||||
}
|
||||
|
||||
pathState.saInPass += node.saFwdIssue;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: ^ --> ACCOUNT --> account :"
|
||||
<< " saInReq=" << pathState.saInReq
|
||||
<< " saInAct=" << pathState.saInAct
|
||||
<< " node.saFwdRedeem:" << node.saFwdRedeem
|
||||
<< " node.saRevIssue:" << node.saRevIssue
|
||||
<< " node.saFwdIssue:" << node.saFwdIssue
|
||||
<< " pathState.saInPass:" << pathState.saInPass;
|
||||
}
|
||||
else if (nodeIndex == lastNodeIndex)
|
||||
{
|
||||
// account --> ACCOUNT --> $
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: account --> ACCOUNT --> $ :"
|
||||
<< " previousAccountID="
|
||||
<< RippleAddress::createHumanAccountID (previousAccountID)
|
||||
<< " node.uAccountID="
|
||||
<< RippleAddress::createHumanAccountID (node.uAccountID)
|
||||
<< " previousNode.saFwdRedeem:" << previousNode.saFwdRedeem
|
||||
<< " previousNode.saFwdIssue:" << previousNode.saFwdIssue;
|
||||
|
||||
// Last node. Accept all funds. Calculate amount actually to credit.
|
||||
|
||||
STAmount& saCurReceive = pathState.saOutPass;
|
||||
|
||||
STAmount saIssueCrd = uQualityIn >= QUALITY_ONE
|
||||
? previousNode.saFwdIssue // No fee.
|
||||
: STAmount::mulRound (
|
||||
previousNode.saFwdIssue,
|
||||
STAmount (CURRENCY_ONE, ACCOUNT_ONE, uQualityIn, -9),
|
||||
true); // Amount to credit.
|
||||
|
||||
// Amount to credit. Credit for less than received as a surcharge.
|
||||
saCurReceive = previousNode.saFwdRedeem + saIssueCrd;
|
||||
|
||||
if (saCurReceive)
|
||||
{
|
||||
// Actually receive.
|
||||
errorCode = rippleCalc.mActiveLedger.rippleCredit (
|
||||
previousAccountID, node.uAccountID,
|
||||
previousNode.saFwdRedeem + previousNode.saFwdIssue, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// After applying quality, total payment was microscopic.
|
||||
errorCode = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// account --> ACCOUNT --> account
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: account --> ACCOUNT --> account";
|
||||
|
||||
node.saFwdRedeem.clear (node.saRevRedeem);
|
||||
node.saFwdIssue.clear (node.saRevIssue);
|
||||
|
||||
// Previous redeem part 1: redeem -> redeem
|
||||
if (previousNode.saFwdRedeem && node.saRevRedeem)
|
||||
// Previous wants to redeem.
|
||||
{
|
||||
// Rate : 1.0 : quality out
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE, uQualityOut, previousNode.saFwdRedeem, node.saRevRedeem,
|
||||
saPrvRedeemAct, node.saFwdRedeem, uRateMax);
|
||||
}
|
||||
|
||||
// Previous issue part 1: issue -> redeem
|
||||
if (previousNode.saFwdIssue != saPrvIssueAct
|
||||
// Previous wants to issue.
|
||||
&& node.saRevRedeem != node.saFwdRedeem)
|
||||
// Current has more to redeem to next.
|
||||
{
|
||||
// Rate: quality in : quality out
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, uQualityOut, previousNode.saFwdIssue, node.saRevRedeem,
|
||||
saPrvIssueAct, node.saFwdRedeem, uRateMax);
|
||||
}
|
||||
|
||||
// Previous redeem part 2: redeem -> issue.
|
||||
if (previousNode.saFwdRedeem != saPrvRedeemAct
|
||||
// Previous still wants to redeem.
|
||||
&& node.saRevRedeem == node.saFwdRedeem
|
||||
// Current redeeming is done can issue.
|
||||
&& node.saRevIssue)
|
||||
// Current wants to issue.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc, QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
previousNode.saFwdRedeem, node.saRevIssue, saPrvRedeemAct,
|
||||
node.saFwdIssue, uRateMax);
|
||||
}
|
||||
|
||||
// Previous issue part 2 : issue -> issue
|
||||
if (previousNode.saFwdIssue != saPrvIssueAct
|
||||
// Previous wants to issue.
|
||||
&& node.saRevRedeem == node.saFwdRedeem
|
||||
// Current redeeming is done can issue.
|
||||
&& node.saRevIssue)
|
||||
// Current wants to issue.
|
||||
{
|
||||
// Rate: quality in : 1.0
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevIssue,
|
||||
saPrvIssueAct, node.saFwdIssue, uRateMax);
|
||||
}
|
||||
|
||||
STAmount saProvide = node.saFwdRedeem + node.saFwdIssue;
|
||||
|
||||
// Adjust prv --> cur balance : take all inbound
|
||||
errorCode = saProvide
|
||||
? rippleCalc.mActiveLedger.rippleCredit (
|
||||
previousAccountID, node.uAccountID,
|
||||
previousNode.saFwdRedeem + previousNode.saFwdIssue, false)
|
||||
: tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
else if (previousNodeIsAccount && !nextNodeIsAccount)
|
||||
{
|
||||
// Current account is issuer to next offer.
|
||||
// Determine deliver to offer amount.
|
||||
// Don't adjust outbound balances- keep funds with issuer as limbo.
|
||||
// If issuer hold's an offer owners inbound IOUs, there is no fee and
|
||||
// redeem/issue will transparently happen.
|
||||
|
||||
if (nodeIndex)
|
||||
{
|
||||
// Non-XRP, current node is the issuer.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: account --> ACCOUNT --> offer";
|
||||
|
||||
node.saFwdDeliver.clear (node.saRevDeliver);
|
||||
|
||||
// redeem -> issue/deliver.
|
||||
// Previous wants to redeem.
|
||||
// Current is issuing to an offer so leave funds in account as
|
||||
// "limbo".
|
||||
if (previousNode.saFwdRedeem)
|
||||
// Previous wants to redeem.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
// XXX Is having the transfer rate here correct?
|
||||
calcNodeRipple (
|
||||
rippleCalc, QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
previousNode.saFwdRedeem, node.saRevDeliver, saPrvRedeemAct,
|
||||
node.saFwdDeliver, uRateMax);
|
||||
}
|
||||
|
||||
// issue -> issue/deliver
|
||||
if (previousNode.saFwdRedeem == saPrvRedeemAct
|
||||
// Previous done redeeming: Previous has no IOUs.
|
||||
&& previousNode.saFwdIssue)
|
||||
// Previous wants to issue. To next must be ok.
|
||||
{
|
||||
// Rate: quality in : 1.0
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, QUALITY_ONE, previousNode.saFwdIssue, node.saRevDeliver,
|
||||
saPrvIssueAct, node.saFwdDeliver, uRateMax);
|
||||
}
|
||||
|
||||
// Adjust prv --> cur balance : take all inbound
|
||||
errorCode = node.saFwdDeliver
|
||||
? rippleCalc.mActiveLedger.rippleCredit (
|
||||
previousAccountID, node.uAccountID,
|
||||
previousNode.saFwdRedeem + previousNode.saFwdIssue, false)
|
||||
: tecPATH_DRY; // Didn't actually deliver anything.
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delivering amount requested from downstream.
|
||||
node.saFwdDeliver = node.saRevDeliver;
|
||||
|
||||
// If limited, then limit by send max and available.
|
||||
if (pathState.saInReq >= zero)
|
||||
{
|
||||
// Limit by send max.
|
||||
node.saFwdDeliver = std::min (node.saFwdDeliver,
|
||||
pathState.saInReq - pathState.saInAct);
|
||||
|
||||
// Limit XRP by available. No limit for non-XRP as issuer.
|
||||
if (node.uCurrencyID.isZero ())
|
||||
node.saFwdDeliver = std::min (
|
||||
node.saFwdDeliver,
|
||||
rippleCalc.mActiveLedger.accountHolds (
|
||||
node.uAccountID, CURRENCY_XRP, ACCOUNT_XRP));
|
||||
|
||||
}
|
||||
|
||||
// Record amount sent for pass.
|
||||
pathState.saInPass = node.saFwdDeliver;
|
||||
|
||||
if (!node.saFwdDeliver)
|
||||
{
|
||||
errorCode = tecPATH_DRY;
|
||||
}
|
||||
else if (!!node.uCurrencyID)
|
||||
{
|
||||
// Non-XRP, current node is the issuer.
|
||||
// We could be delivering to multiple accounts, so we don't know
|
||||
// which ripple balance will be adjusted. Assume just issuing.
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: ^ --> ACCOUNT -- !XRP --> offer";
|
||||
|
||||
// As the issuer, would only issue.
|
||||
// Don't need to actually deliver. As from delivering leave in
|
||||
// the issuer as limbo.
|
||||
nothing ();
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: ^ --> ACCOUNT -- XRP --> offer";
|
||||
|
||||
// Deliver XRP to limbo.
|
||||
errorCode = rippleCalc.mActiveLedger.accountSend (
|
||||
node.uAccountID, ACCOUNT_XRP, node.saFwdDeliver);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!previousNodeIsAccount && nextNodeIsAccount)
|
||||
{
|
||||
if (nodeIndex == lastNodeIndex)
|
||||
{
|
||||
// offer --> ACCOUNT --> $
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: offer --> ACCOUNT --> $ : "
|
||||
<< previousNode.saFwdDeliver;
|
||||
|
||||
STAmount& saCurReceive = pathState.saOutPass;
|
||||
|
||||
// Amount to credit.
|
||||
saCurReceive = previousNode.saFwdDeliver;
|
||||
|
||||
// No income balance adjustments necessary. The paying side inside
|
||||
// the offer paid to this account.
|
||||
}
|
||||
else
|
||||
{
|
||||
// offer --> ACCOUNT --> account
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: offer --> ACCOUNT --> account";
|
||||
|
||||
node.saFwdRedeem.clear (node.saRevRedeem);
|
||||
node.saFwdIssue.clear (node.saRevIssue);
|
||||
|
||||
// deliver -> redeem
|
||||
if (previousNode.saFwdDeliver && node.saRevRedeem)
|
||||
// Previous wants to deliver and can current redeem.
|
||||
{
|
||||
// Rate : 1.0 : quality out
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE, uQualityOut, previousNode.saFwdDeliver, node.saRevRedeem,
|
||||
saPrvDeliverAct, node.saFwdRedeem, uRateMax);
|
||||
}
|
||||
|
||||
// deliver -> issue
|
||||
// Wants to redeem and current would and can issue.
|
||||
if (previousNode.saFwdDeliver != saPrvDeliverAct
|
||||
// Previous still wants to deliver.
|
||||
&& node.saRevRedeem == node.saFwdRedeem
|
||||
// Current has more to redeem to next.
|
||||
&& node.saRevIssue)
|
||||
// Current wants issue.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc, QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
previousNode.saFwdDeliver, node.saRevIssue, saPrvDeliverAct,
|
||||
node.saFwdIssue, uRateMax);
|
||||
}
|
||||
|
||||
// No income balance adjustments necessary. The paying side inside
|
||||
// the offer paid and the next link will receive.
|
||||
STAmount saProvide = node.saFwdRedeem + node.saFwdIssue;
|
||||
|
||||
if (!saProvide)
|
||||
errorCode = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// offer --> ACCOUNT --> offer
|
||||
// deliver/redeem -> deliver/issue.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountFwd: offer --> ACCOUNT --> offer";
|
||||
|
||||
node.saFwdDeliver.clear (node.saRevDeliver);
|
||||
|
||||
if (previousNode.saFwdDeliver
|
||||
// Previous wants to deliver
|
||||
&& node.saRevIssue)
|
||||
// Current wants issue.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc, QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
previousNode.saFwdDeliver, node.saRevDeliver, saPrvDeliverAct,
|
||||
node.saFwdDeliver, uRateMax);
|
||||
}
|
||||
|
||||
// No income balance adjustments necessary. The paying side inside the
|
||||
// offer paid and the next link will receive.
|
||||
if (!node.saFwdDeliver)
|
||||
errorCode = tecPATH_DRY;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
500
src/ripple_app/paths/CalcNodeAccountRev.cpp
Normal file
500
src/ripple_app/paths/CalcNodeAccountRev.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur, based on
|
||||
// required deliverable, propagate redeem, issue, and deliver requests to the
|
||||
// previous node.
|
||||
//
|
||||
// Inflate amount requested by required fees.
|
||||
// Reedems are limited based on IOUs previous has on hand.
|
||||
// Issues are limited based on credit limits and amount owed.
|
||||
//
|
||||
// No account balance adjustments as we don't know how much is going to actually
|
||||
// be pushed through yet.
|
||||
//
|
||||
// <-- tesSUCCESS or tecPATH_DRY
|
||||
|
||||
TER calcNodeAccountRev (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex, PathState& pathState,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
TER terResult = tesSUCCESS;
|
||||
auto const lastNodeIndex = pathState.vpnNodes.size () - 1;
|
||||
auto const isFinalNode = (nodeIndex == lastNodeIndex);
|
||||
|
||||
std::uint64_t uRateMax = 0;
|
||||
|
||||
auto& previousNode = pathState.vpnNodes[nodeIndex ? nodeIndex - 1 : 0];
|
||||
auto& node = pathState.vpnNodes[nodeIndex];
|
||||
auto& nextNode = pathState.vpnNodes[isFinalNode ? lastNodeIndex : nodeIndex + 1];
|
||||
|
||||
// Current is allowed to redeem to next.
|
||||
const bool previousNodeIsAccount = !nodeIndex ||
|
||||
is_bit_set (previousNode.uFlags, STPathElement::typeAccount);
|
||||
const bool nextNodeIsAccount = isFinalNode ||
|
||||
is_bit_set (nextNode.uFlags, STPathElement::typeAccount);
|
||||
|
||||
const uint160& previousAccountID = previousNodeIsAccount
|
||||
? previousNode.uAccountID : node.uAccountID;
|
||||
const uint160& nextAccountID = nextNodeIsAccount ? nextNode.uAccountID
|
||||
: node.uAccountID; // Offers are always issue.
|
||||
|
||||
// XXX Don't look up quality for XRP
|
||||
const std::uint32_t uQualityIn = nodeIndex
|
||||
? rippleCalc.mActiveLedger.rippleQualityIn (
|
||||
node.uAccountID, previousAccountID, node.uCurrencyID)
|
||||
: QUALITY_ONE;
|
||||
const std::uint32_t uQualityOut = (nodeIndex != lastNodeIndex)
|
||||
? rippleCalc.mActiveLedger.rippleQualityOut (
|
||||
node.uAccountID, nextAccountID, node.uCurrencyID)
|
||||
: QUALITY_ONE;
|
||||
|
||||
// For previousNodeIsAccount:
|
||||
// Previous account is owed.
|
||||
const STAmount saPrvOwed = (previousNodeIsAccount && nodeIndex)
|
||||
? rippleCalc.mActiveLedger.rippleOwed (
|
||||
node.uAccountID, previousAccountID, node.uCurrencyID)
|
||||
: STAmount (node.uCurrencyID, node.uAccountID);
|
||||
|
||||
// Previous account may owe.
|
||||
const STAmount saPrvLimit = (previousNodeIsAccount && nodeIndex)
|
||||
? rippleCalc.mActiveLedger.rippleLimit (
|
||||
node.uAccountID, previousAccountID, node.uCurrencyID)
|
||||
: STAmount (node.uCurrencyID, node.uAccountID);
|
||||
|
||||
// Next account is owed.
|
||||
const STAmount saNxtOwed = (nextNodeIsAccount && nodeIndex != lastNodeIndex)
|
||||
? rippleCalc.mActiveLedger.rippleOwed (
|
||||
node.uAccountID, nextAccountID, node.uCurrencyID)
|
||||
: STAmount (node.uCurrencyID, node.uAccountID);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev>"
|
||||
<< " nodeIndex=%d/%d" << nodeIndex << "/" << lastNodeIndex
|
||||
<< " previousAccountID="
|
||||
<< RippleAddress::createHumanAccountID (previousAccountID)
|
||||
<< " node.uAccountID="
|
||||
<< RippleAddress::createHumanAccountID (node.uAccountID)
|
||||
<< " nextAccountID="
|
||||
<< RippleAddress::createHumanAccountID (nextAccountID)
|
||||
<< " uCurrencyID="
|
||||
<< STAmount::createHumanCurrency (node.uCurrencyID)
|
||||
<< " uQualityIn=" << uQualityIn
|
||||
<< " uQualityOut=" << uQualityOut
|
||||
<< " saPrvOwed=" << saPrvOwed
|
||||
<< " saPrvLimit=" << saPrvLimit;
|
||||
|
||||
// Previous can redeem the owed IOUs it holds.
|
||||
const STAmount saPrvRedeemReq = (saPrvOwed > zero)
|
||||
? saPrvOwed
|
||||
: STAmount (saPrvOwed.getCurrency (), saPrvOwed.getIssuer ());
|
||||
STAmount& saPrvRedeemAct = previousNode.saRevRedeem;
|
||||
|
||||
// Previous can issue up to limit minus whatever portion of limit already
|
||||
// used (not including redeemable amount).
|
||||
const STAmount saPrvIssueReq = (saPrvOwed < zero)
|
||||
? saPrvLimit + saPrvOwed : saPrvLimit;
|
||||
STAmount& saPrvIssueAct = previousNode.saRevIssue;
|
||||
|
||||
// For !previousNodeIsAccount
|
||||
auto deliverCurrency = previousNode.saRevDeliver.getCurrency ();
|
||||
const STAmount saPrvDeliverReq (
|
||||
deliverCurrency, previousNode.saRevDeliver.getIssuer (), -1);
|
||||
// Unlimited.
|
||||
|
||||
STAmount& saPrvDeliverAct = previousNode.saRevDeliver;
|
||||
|
||||
// For nextNodeIsAccount
|
||||
const STAmount& saCurRedeemReq = node.saRevRedeem;
|
||||
STAmount saCurRedeemAct (
|
||||
saCurRedeemReq.getCurrency (), saCurRedeemReq.getIssuer ());
|
||||
|
||||
const STAmount& saCurIssueReq = node.saRevIssue;
|
||||
// Track progress.
|
||||
STAmount saCurIssueAct (
|
||||
saCurIssueReq.getCurrency (), saCurIssueReq.getIssuer ());
|
||||
|
||||
// For !nextNodeIsAccount
|
||||
const STAmount& saCurDeliverReq = node.saRevDeliver;
|
||||
STAmount saCurDeliverAct (
|
||||
saCurDeliverReq.getCurrency (), saCurDeliverReq.getIssuer ());
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev:"
|
||||
<< " saPrvRedeemReq:" << saPrvRedeemReq
|
||||
<< " saPrvIssueReq:" << saPrvIssueReq
|
||||
<< " saPrvDeliverAct:" << saPrvDeliverAct
|
||||
<< " saPrvDeliverReq:" << saPrvDeliverReq
|
||||
<< " saCurRedeemReq:" << saCurRedeemReq
|
||||
<< " saCurIssueReq:" << saCurIssueReq
|
||||
<< " saNxtOwed:" << saNxtOwed;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << pathState.getJson ();
|
||||
|
||||
// Current redeem req can't be more than IOUs on hand.
|
||||
assert (!saCurRedeemReq || (-saNxtOwed) >= saCurRedeemReq);
|
||||
assert (!saCurIssueReq // If not issuing, fine.
|
||||
|| saNxtOwed >= zero
|
||||
// saNxtOwed >= 0: Sender not holding next IOUs, saNxtOwed < 0:
|
||||
// Sender holding next IOUs.
|
||||
|| -saNxtOwed == saCurRedeemReq);
|
||||
// If issue req, then redeem req must consume all owed.
|
||||
|
||||
if (!nodeIndex)
|
||||
{
|
||||
// ^ --> ACCOUNT --> account|offer
|
||||
// Nothing to do, there is no previous to adjust.
|
||||
|
||||
nothing ();
|
||||
}
|
||||
else if (previousNodeIsAccount && nextNodeIsAccount)
|
||||
{
|
||||
if (isFinalNode)
|
||||
{
|
||||
// account --> ACCOUNT --> $
|
||||
// Overall deliverable.
|
||||
// If previous is an account, limit.
|
||||
const STAmount saCurWantedReq = std::min (
|
||||
pathState.saOutReq - pathState.saOutAct, saPrvLimit + saPrvOwed);
|
||||
STAmount saCurWantedAct (
|
||||
saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: account --> ACCOUNT --> $ :"
|
||||
<< " saCurWantedReq=" << saCurWantedReq;
|
||||
|
||||
// Calculate redeem
|
||||
if (saPrvRedeemReq) // Previous has IOUs to redeem.
|
||||
{
|
||||
// Redeem at 1:1
|
||||
|
||||
saCurWantedAct = std::min (saPrvRedeemReq, saCurWantedReq);
|
||||
saPrvRedeemAct = saCurWantedAct;
|
||||
|
||||
uRateMax = STAmount::uRateOne;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: Redeem at 1:1"
|
||||
<< " saPrvRedeemReq=" << saPrvRedeemReq
|
||||
<< " (available) saPrvRedeemAct=" << saPrvRedeemAct
|
||||
<< " uRateMax="
|
||||
<< STAmount::saFromRate (uRateMax).getText ();
|
||||
}
|
||||
else
|
||||
{
|
||||
saPrvRedeemAct.clear (saPrvRedeemReq);
|
||||
}
|
||||
|
||||
// Calculate issuing.
|
||||
saPrvIssueAct.clear (saPrvIssueReq);
|
||||
|
||||
if (saCurWantedReq != saCurWantedAct // Need more.
|
||||
&& saPrvIssueReq) // Will accept IOUs from prevous.
|
||||
{
|
||||
// Rate: quality in : 1.0
|
||||
|
||||
// If we previously redeemed and this has a poorer rate, this
|
||||
// won't be included the current increment.
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq,
|
||||
saPrvIssueAct, saCurWantedAct, uRateMax);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: Issuing: Rate: quality in : 1.0"
|
||||
<< " saPrvIssueAct:" << saPrvIssueAct
|
||||
<< " saCurWantedAct:" << saCurWantedAct;
|
||||
}
|
||||
|
||||
if (!saCurWantedAct)
|
||||
{
|
||||
// Must have processed something.
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// ^|account --> ACCOUNT --> account
|
||||
saPrvRedeemAct.clear (saPrvRedeemReq);
|
||||
saPrvIssueAct.clear (saPrvIssueReq);
|
||||
|
||||
// redeem (part 1) -> redeem
|
||||
if (saCurRedeemReq // Next wants IOUs redeemed.
|
||||
&& saPrvRedeemReq) // Previous has IOUs to redeem.
|
||||
{
|
||||
// Rate : 1.0 : quality out
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq,
|
||||
saPrvRedeemAct, saCurRedeemAct, uRateMax);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: Rate : 1.0 : quality out"
|
||||
<< " saPrvRedeemAct:" << saPrvRedeemAct
|
||||
<< " saCurRedeemAct:" << saCurRedeemAct;
|
||||
}
|
||||
|
||||
// issue (part 1) -> redeem
|
||||
if (saCurRedeemReq != saCurRedeemAct
|
||||
// Next wants more IOUs redeemed.
|
||||
&& saPrvRedeemAct == saPrvRedeemReq)
|
||||
// Previous has no IOUs to redeem remaining.
|
||||
{
|
||||
// Rate: quality in : quality out
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq,
|
||||
saPrvIssueAct, saCurRedeemAct, uRateMax);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: Rate: quality in : quality out:"
|
||||
<< " saPrvIssueAct:" << saPrvIssueAct
|
||||
<< " saCurRedeemAct:" << saCurRedeemAct;
|
||||
}
|
||||
|
||||
// redeem (part 2) -> issue.
|
||||
if (saCurIssueReq // Next wants IOUs issued.
|
||||
&& saCurRedeemAct == saCurRedeemReq
|
||||
// Can only issue if completed redeeming.
|
||||
&& saPrvRedeemAct != saPrvRedeemReq)
|
||||
// Did not complete redeeming previous IOUs.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct,
|
||||
saCurIssueAct, uRateMax);
|
||||
|
||||
WriteLog (lsDEBUG, RippleCalc)
|
||||
<< "calcNodeAccountRev: Rate : 1.0 : transfer_rate:"
|
||||
<< " saPrvRedeemAct:" << saPrvRedeemAct
|
||||
<< " saCurIssueAct:" << saCurIssueAct;
|
||||
}
|
||||
|
||||
// issue (part 2) -> issue
|
||||
if (saCurIssueReq != saCurIssueAct
|
||||
// Need wants more IOUs issued.
|
||||
&& saCurRedeemAct == saCurRedeemReq
|
||||
// Can only issue if completed redeeming.
|
||||
&& saPrvRedeemReq == saPrvRedeemAct
|
||||
// Previously redeemed all owed IOUs.
|
||||
&& saPrvIssueReq)
|
||||
// Previous can issue.
|
||||
{
|
||||
// Rate: quality in : 1.0
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq,
|
||||
saPrvIssueAct, saCurIssueAct, uRateMax);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: Rate: quality in : 1.0:"
|
||||
<< " saPrvIssueAct:" << saPrvIssueAct
|
||||
<< " saCurIssueAct:" << saCurIssueAct;
|
||||
}
|
||||
|
||||
if (!saCurRedeemAct && !saCurIssueAct)
|
||||
{
|
||||
// Did not make progress.
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: ^|account --> ACCOUNT --> account :"
|
||||
<< " saCurRedeemReq:" << saCurRedeemReq
|
||||
<< " saCurIssueReq:" << saCurIssueReq
|
||||
<< " saPrvOwed:" << saPrvOwed
|
||||
<< " saCurRedeemAct:" << saCurRedeemAct
|
||||
<< " saCurIssueAct:" << saCurIssueAct;
|
||||
}
|
||||
}
|
||||
else if (previousNodeIsAccount && !nextNodeIsAccount)
|
||||
{
|
||||
// account --> ACCOUNT --> offer
|
||||
// Note: deliver is always issue as ACCOUNT is the issuer for the offer
|
||||
// input.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: account --> ACCOUNT --> offer";
|
||||
|
||||
saPrvRedeemAct.clear (saPrvRedeemReq);
|
||||
saPrvIssueAct.clear (saPrvIssueReq);
|
||||
|
||||
// redeem -> deliver/issue.
|
||||
if (saPrvOwed > zero // Previous has IOUs to redeem.
|
||||
&& saCurDeliverReq) // Need some issued.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc, QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct,
|
||||
saCurDeliverAct, uRateMax);
|
||||
}
|
||||
|
||||
// issue -> deliver/issue
|
||||
if (saPrvRedeemReq == saPrvRedeemAct // Previously redeemed all owed.
|
||||
&& saCurDeliverReq != saCurDeliverAct) // Still need some issued.
|
||||
{
|
||||
// Rate: quality in : 1.0
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq,
|
||||
saPrvIssueAct, saCurDeliverAct, uRateMax);
|
||||
}
|
||||
|
||||
if (!saCurDeliverAct)
|
||||
{
|
||||
// Must want something.
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: "
|
||||
<< " saCurDeliverReq:" << saCurDeliverReq
|
||||
<< " saCurDeliverAct:" << saCurDeliverAct
|
||||
<< " saPrvOwed:" << saPrvOwed;
|
||||
}
|
||||
else if (!previousNodeIsAccount && nextNodeIsAccount)
|
||||
{
|
||||
if (isFinalNode)
|
||||
{
|
||||
// offer --> ACCOUNT --> $
|
||||
// Previous is an offer, no limit: redeem own IOUs.
|
||||
const STAmount& saCurWantedReq = pathState.saOutReq - pathState.saOutAct;
|
||||
STAmount saCurWantedAct (
|
||||
saCurWantedReq.getCurrency (), saCurWantedReq.getIssuer ());
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: offer --> ACCOUNT --> $ :"
|
||||
<< " saCurWantedReq:" << saCurWantedReq
|
||||
<< " saOutAct:" << pathState.saOutAct
|
||||
<< " saOutReq:" << pathState.saOutReq;
|
||||
|
||||
if (saCurWantedReq <= zero)
|
||||
{
|
||||
// TEMPORARY emergency fix
|
||||
WriteLog (lsFATAL, RippleCalc) << "CurWantReq was not positive";
|
||||
return tefEXCEPTION;
|
||||
}
|
||||
|
||||
assert (saCurWantedReq > zero); // FIXME: We got one of these
|
||||
// TODO(tom): can only be a race condition if true!
|
||||
|
||||
// Rate: quality in : 1.0
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq,
|
||||
saPrvDeliverAct, saCurWantedAct, uRateMax);
|
||||
|
||||
if (!saCurWantedAct)
|
||||
{
|
||||
// Must have processed something.
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev:"
|
||||
<< " saPrvDeliverAct:" << saPrvDeliverAct
|
||||
<< " saPrvDeliverReq:" << saPrvDeliverReq
|
||||
<< " saCurWantedAct:" << saCurWantedAct
|
||||
<< " saCurWantedReq:" << saCurWantedReq;
|
||||
}
|
||||
else
|
||||
{
|
||||
// offer --> ACCOUNT --> account
|
||||
// Note: offer is always delivering(redeeming) as account is issuer.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: offer --> ACCOUNT --> account :"
|
||||
<< " saCurRedeemReq:" << saCurRedeemReq
|
||||
<< " saCurIssueReq:" << saCurIssueReq;
|
||||
|
||||
// deliver -> redeem
|
||||
if (saCurRedeemReq) // Next wants us to redeem.
|
||||
{
|
||||
// Rate : 1.0 : quality out
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq,
|
||||
saPrvDeliverAct, saCurRedeemAct, uRateMax);
|
||||
}
|
||||
|
||||
// deliver -> issue.
|
||||
if (saCurRedeemReq == saCurRedeemAct
|
||||
// Can only issue if previously redeemed all.
|
||||
&& saCurIssueReq)
|
||||
// Need some issued.
|
||||
{
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct,
|
||||
saCurIssueAct, uRateMax);
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev:"
|
||||
<< " saCurRedeemAct:" << saCurRedeemAct
|
||||
<< " saCurRedeemReq:" << saCurRedeemReq
|
||||
<< " saPrvDeliverAct:" << saPrvDeliverAct
|
||||
<< " saCurIssueReq:" << saCurIssueReq;
|
||||
|
||||
if (!saPrvDeliverAct)
|
||||
{
|
||||
// Must want something.
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// offer --> ACCOUNT --> offer
|
||||
// deliver/redeem -> deliver/issue.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAccountRev: offer --> ACCOUNT --> offer";
|
||||
|
||||
// Rate : 1.0 : transfer_rate
|
||||
calcNodeRipple (
|
||||
rippleCalc,
|
||||
QUALITY_ONE,
|
||||
rippleCalc.mActiveLedger.rippleTransferRate (node.uAccountID),
|
||||
saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct,
|
||||
saCurDeliverAct, uRateMax);
|
||||
|
||||
if (!saCurDeliverAct)
|
||||
{
|
||||
// Must want something.
|
||||
terResult = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
|
||||
return terResult;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
427
src/ripple_app/paths/CalcNodeAdvance.cpp
Normal file
427
src/ripple_app/paths/CalcNodeAdvance.cpp
Normal file
@@ -0,0 +1,427 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// OPTIMIZE: When calculating path increment, note if increment consumes all
|
||||
// liquidity. No need to revisit path in the future if all liquidity is used.
|
||||
//
|
||||
|
||||
|
||||
// VFALCO TODO Update the comment for this function, the argument list no
|
||||
// resembles the comment
|
||||
//
|
||||
// Provide a better explanation for what this function does.
|
||||
|
||||
// If needed, advance to next funded offer.
|
||||
// - Automatically advances to first offer.
|
||||
// --> bEntryAdvance: true, to advance to next entry. false, recalculate.
|
||||
// <-- uOfferIndex : 0=end of list.
|
||||
TER calcNodeAdvance (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex,
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality,
|
||||
const bool bReverse)
|
||||
{
|
||||
auto& previousNode = pathState.vpnNodes[nodeIndex - 1];
|
||||
auto& node = pathState.vpnNodes[nodeIndex];
|
||||
|
||||
uint256& uDirectTip = node.uDirectTip;
|
||||
uint256& uDirectEnd = node.uDirectEnd;
|
||||
bool& bDirectAdvance = node.bDirectAdvance;
|
||||
bool& bDirectRestart = node.bDirectRestart;
|
||||
SLE::pointer& sleDirectDir = node.sleDirectDir;
|
||||
STAmount& saOfrRate = node.saOfrRate;
|
||||
|
||||
bool& bEntryAdvance = node.bEntryAdvance;
|
||||
unsigned int& uEntry = node.uEntry;
|
||||
uint256& uOfferIndex = node.uOfferIndex;
|
||||
SLE::pointer& sleOffer = node.sleOffer;
|
||||
uint160& uOfrOwnerID = node.uOfrOwnerID;
|
||||
STAmount& saOfferFunds = node.saOfferFunds;
|
||||
STAmount& saTakerPays = node.saTakerPays;
|
||||
STAmount& saTakerGets = node.saTakerGets;
|
||||
bool& bFundsDirty = node.bFundsDirty;
|
||||
|
||||
TER errorCode = tesSUCCESS;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: TakerPays:"
|
||||
<< saTakerPays << " TakerGets:" << saTakerGets;
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
do
|
||||
{
|
||||
// VFALCO NOTE Why not use a for() loop?
|
||||
// VFALCO TODO The limit on loop iterations puts an
|
||||
// upper limit on the number of different quality
|
||||
// levels (ratio of pay:get) that will be considered for one path.
|
||||
// Changing this value has repercusssions on validation and consensus.
|
||||
//
|
||||
if (++loopCount > NODE_ADVANCE_MAX_LOOPS)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) << "Loop count exceeded";
|
||||
return tefEXCEPTION;
|
||||
}
|
||||
|
||||
bool bDirectDirDirty = false;
|
||||
|
||||
if (!uDirectTip)
|
||||
{
|
||||
// Need to initialize current node.
|
||||
|
||||
uDirectTip = Ledger::getBookBase (
|
||||
previousNode.uCurrencyID, previousNode.uIssuerID,
|
||||
node.uCurrencyID,
|
||||
node.uIssuerID);
|
||||
uDirectEnd = Ledger::getQualityNext (uDirectTip);
|
||||
|
||||
sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, uDirectTip);
|
||||
|
||||
// Associated vars are dirty, if found it.
|
||||
bDirectDirDirty = !!sleDirectDir;
|
||||
|
||||
// Advance, if didn't find it. Normal not to be unable to lookup
|
||||
// firstdirectory. Maybe even skip this lookup.
|
||||
bDirectAdvance = !sleDirectDir;
|
||||
bDirectRestart = false;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: Initialize node:"
|
||||
<< " uDirectTip=" << uDirectTip
|
||||
<<" uDirectEnd=" << uDirectEnd
|
||||
<< " bDirectAdvance=" << bDirectAdvance;
|
||||
}
|
||||
|
||||
if (bDirectAdvance || bDirectRestart)
|
||||
{
|
||||
// Get next quality.
|
||||
if (bDirectAdvance)
|
||||
{
|
||||
uDirectTip = rippleCalc.mActiveLedger.getNextLedgerIndex (
|
||||
uDirectTip, uDirectEnd);
|
||||
}
|
||||
|
||||
bDirectDirDirty = true;
|
||||
bDirectAdvance = false;
|
||||
bDirectRestart = false;
|
||||
|
||||
if (!!uDirectTip)
|
||||
{
|
||||
// Have another quality directory.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: Quality advance: uDirectTip="
|
||||
<< uDirectTip;
|
||||
|
||||
sleDirectDir = rippleCalc.mActiveLedger.entryCache (ltDIR_NODE, uDirectTip);
|
||||
}
|
||||
else if (bReverse)
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: No more offers.";
|
||||
|
||||
uOfferIndex = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more offers. Should be done rather than fall off end of
|
||||
// book.
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeAdvance: Unreachable: "
|
||||
<< "Fell off end of order book.";
|
||||
// FIXME: why?
|
||||
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
|
||||
tecFAILED_PROCESSING;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDirectDirDirty)
|
||||
{
|
||||
saOfrRate = STAmount::setRate (Ledger::getQuality (uDirectTip));
|
||||
// For correct ratio
|
||||
uEntry = 0;
|
||||
bEntryAdvance = true;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: directory dirty: saOfrRate="
|
||||
<< saOfrRate;
|
||||
}
|
||||
|
||||
if (!bEntryAdvance)
|
||||
{
|
||||
if (bFundsDirty)
|
||||
{
|
||||
// We were called again probably merely to update structure
|
||||
// variables.
|
||||
saTakerPays = sleOffer->getFieldAmount (sfTakerPays);
|
||||
saTakerGets = sleOffer->getFieldAmount (sfTakerGets);
|
||||
|
||||
saOfferFunds = rippleCalc.mActiveLedger.accountFunds (
|
||||
uOfrOwnerID, saTakerGets);
|
||||
// Funds left.
|
||||
bFundsDirty = false;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: funds dirty: saOfrRate="
|
||||
<< saOfrRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc) << "calcNodeAdvance: as is";
|
||||
}
|
||||
}
|
||||
else if (!rippleCalc.mActiveLedger.dirNext (
|
||||
uDirectTip, sleDirectDir, uEntry, uOfferIndex))
|
||||
{
|
||||
// Failed to find an entry in directory.
|
||||
// Do another cur directory iff bMultiQuality
|
||||
if (bMultiQuality)
|
||||
{
|
||||
// We are allowed to process multiple qualities if this is the
|
||||
// only path.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: next quality";
|
||||
bDirectAdvance = true; // Process next quality.
|
||||
}
|
||||
else if (!bReverse)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeAdvance: unreachable: ran out of offers";
|
||||
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING :
|
||||
tecFAILED_PROCESSING;
|
||||
// TEMPORARY
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ran off end of offers.
|
||||
bEntryAdvance = false; // Done.
|
||||
uOfferIndex = 0; // Report nore more entries.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Got a new offer.
|
||||
sleOffer = rippleCalc.mActiveLedger.entryCache (ltOFFER, uOfferIndex);
|
||||
|
||||
if (!sleOffer)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) <<
|
||||
"Missing offer in directory";
|
||||
bEntryAdvance = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
uOfrOwnerID = sleOffer->getFieldAccount160 (sfAccount);
|
||||
saTakerPays = sleOffer->getFieldAmount (sfTakerPays);
|
||||
saTakerGets = sleOffer->getFieldAmount (sfTakerGets);
|
||||
|
||||
const AccountCurrencyIssuer asLine (
|
||||
uOfrOwnerID, node.uCurrencyID, node.uIssuerID);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: uOfrOwnerID="
|
||||
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
|
||||
<< " saTakerPays=" << saTakerPays
|
||||
<< " saTakerGets=" << saTakerGets
|
||||
<< " uOfferIndex=" << uOfferIndex;
|
||||
|
||||
if (sleOffer->isFieldPresent (sfExpiration) &&
|
||||
(sleOffer->getFieldU32 (sfExpiration) <=
|
||||
rippleCalc.mActiveLedger.getLedger ()->getParentCloseTimeNC ()))
|
||||
{
|
||||
// Offer is expired.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: expired offer";
|
||||
rippleCalc.mUnfundedOffers.insert(uOfferIndex);
|
||||
continue;
|
||||
}
|
||||
else if (saTakerPays <= zero || saTakerGets <= zero)
|
||||
{
|
||||
// Offer has bad amounts. Offers should never have a bad
|
||||
// amounts.
|
||||
|
||||
if (bReverse)
|
||||
{
|
||||
// Past internal error, offer had bad amounts.
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeAdvance: PAST INTERNAL ERROR:"
|
||||
<< " OFFER NON-POSITIVE:"
|
||||
<< " saTakerPays=" << saTakerPays
|
||||
<< " saTakerGets=%s" << saTakerGets;
|
||||
|
||||
// Mark offer for always deletion.
|
||||
rippleCalc.mUnfundedOffers.insert (uOfferIndex);
|
||||
}
|
||||
else if (rippleCalc.mUnfundedOffers.find (uOfferIndex) !=
|
||||
rippleCalc.mUnfundedOffers.end ())
|
||||
{
|
||||
// Past internal error, offer was found failed to place
|
||||
// this in mUnfundedOffers.
|
||||
// Just skip it. It will be deleted.
|
||||
WriteLog (lsDEBUG, RippleCalc)
|
||||
<< "calcNodeAdvance: PAST INTERNAL ERROR:"
|
||||
<< " OFFER NON-POSITIVE:"
|
||||
<< " saTakerPays=" << saTakerPays
|
||||
<< " saTakerGets=" << saTakerGets;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reverse should have previously put bad offer in list.
|
||||
// An internal error previously left a bad offer.
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeAdvance: INTERNAL ERROR:"
|
||||
<<" OFFER NON-POSITIVE:"
|
||||
<< " saTakerPays=" << saTakerPays
|
||||
<< " saTakerGets=" << saTakerGets;
|
||||
|
||||
// Don't process at all, things are in an unexpected
|
||||
// state for this transactions.
|
||||
errorCode = tefEXCEPTION;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allowed to access source from this node?
|
||||
//
|
||||
// XXX This can get called multiple times for same source in a
|
||||
// row, caching result would be nice.
|
||||
//
|
||||
// XXX Going forward could we fund something with a worse
|
||||
// quality which was previously skipped? Might need to check
|
||||
// quality.
|
||||
auto itForward = pathState.umForward.find (asLine);
|
||||
const bool bFoundForward = itForward != pathState.umForward.end ();
|
||||
|
||||
// Only allow a source to be used once, in the first node
|
||||
// encountered from initial path scan. This prevents
|
||||
// conflicting uses of the same balance when going reverse vs
|
||||
// forward.
|
||||
if (bFoundForward &&
|
||||
itForward->second != nodeIndex &&
|
||||
uOfrOwnerID != node.uIssuerID)
|
||||
{
|
||||
// Temporarily unfunded. Another node uses this source,
|
||||
// ignore in this offer.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: temporarily unfunded offer"
|
||||
<< " (forward)";
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is overly strict. For contributions to past. We should
|
||||
// only count source if actually used.
|
||||
auto itReverse = pathState.umReverse.find (asLine);
|
||||
bool bFoundReverse = itReverse != pathState.umReverse.end ();
|
||||
|
||||
// For this quality increment, only allow a source to be used
|
||||
// from a single node, in the first node encountered from
|
||||
// applying offers in reverse.
|
||||
if (bFoundReverse &&
|
||||
itReverse->second != nodeIndex &&
|
||||
uOfrOwnerID != node.uIssuerID)
|
||||
{
|
||||
// Temporarily unfunded. Another node uses this source,
|
||||
// ignore in this offer.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: temporarily unfunded offer"
|
||||
<<" (reverse)";
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine if used in past.
|
||||
// We only need to know if it might need to be marked unfunded.
|
||||
auto itPast = rippleCalc.mumSource.find (asLine);
|
||||
bool bFoundPast = (itPast != rippleCalc.mumSource.end ());
|
||||
|
||||
// Only the current node is allowed to use the source.
|
||||
|
||||
saOfferFunds = rippleCalc.mActiveLedger.accountFunds
|
||||
(uOfrOwnerID, saTakerGets); // Funds held.
|
||||
|
||||
if (saOfferFunds <= zero)
|
||||
{
|
||||
// Offer is unfunded.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: unfunded offer";
|
||||
|
||||
if (bReverse && !bFoundReverse && !bFoundPast)
|
||||
{
|
||||
// Never mentioned before, clearly just: found unfunded.
|
||||
// That is, even if this offer fails due to fill or kill
|
||||
// still do deletions.
|
||||
// Mark offer for always deletion.
|
||||
rippleCalc.mUnfundedOffers.insert (uOfferIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Moving forward, don't need to insert again
|
||||
// Or, already found it.
|
||||
}
|
||||
|
||||
// YYY Could verify offer is correct place for unfundeds.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bReverse // Need to remember reverse mention.
|
||||
&& !bFoundPast // Not mentioned in previous passes.
|
||||
&& !bFoundReverse) // New to pass.
|
||||
{
|
||||
// Consider source mentioned by current path state.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: remember="
|
||||
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
|
||||
<< "/"
|
||||
<< STAmount::createHumanCurrency (node.uCurrencyID)
|
||||
<< "/"
|
||||
<< RippleAddress::createHumanAccountID (node.uIssuerID);
|
||||
|
||||
pathState.umReverse.insert (std::make_pair (asLine, nodeIndex));
|
||||
}
|
||||
|
||||
bFundsDirty = false;
|
||||
bEntryAdvance = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (errorCode == tesSUCCESS && (bEntryAdvance || bDirectAdvance));
|
||||
|
||||
if (errorCode == tesSUCCESS)
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeAdvance: uOfferIndex=" << uOfferIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc)
|
||||
<< "calcNodeAdvance: errorCode=" << transToken (errorCode);
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
385
src/ripple_app/paths/CalcNodeDeliverFwd.cpp
Normal file
385
src/ripple_app/paths/CalcNodeDeliverFwd.cpp
Normal file
@@ -0,0 +1,385 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// For current offer, get input from deliver/limbo and output to next account or
|
||||
// deliver for next offers.
|
||||
//
|
||||
// <-- node.saFwdDeliver: For calcNodeAccountFwd to know how much went through
|
||||
// --> node.saRevDeliver: Do not exceed.
|
||||
|
||||
TER calcNodeDeliverFwd (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex, // 0 < nodeIndex < lastNodeIndex
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID, // --> Input owner's account.
|
||||
const STAmount& saInReq, // --> Amount to deliver.
|
||||
STAmount& saInAct, // <-- Amount delivered, this invokation.
|
||||
STAmount& saInFees) // <-- Fees charged, this invokation.
|
||||
{
|
||||
TER errorCode = tesSUCCESS;
|
||||
|
||||
PathState::Node& previousNode = pathState.vpnNodes[nodeIndex - 1];
|
||||
PathState::Node& node = pathState.vpnNodes[nodeIndex];
|
||||
PathState::Node& nextNode = pathState.vpnNodes[nodeIndex + 1];
|
||||
|
||||
const uint160& nextAccountID = nextNode.uAccountID;
|
||||
const uint160& uCurCurrencyID = node.uCurrencyID;
|
||||
const uint160& uCurIssuerID = node.uIssuerID;
|
||||
uint256 const& uOfferIndex = node.uOfferIndex;
|
||||
const uint160& uPrvCurrencyID = previousNode.uCurrencyID;
|
||||
const uint160& uPrvIssuerID = previousNode.uIssuerID;
|
||||
const STAmount& saInTransRate = previousNode.saTransferRate;
|
||||
const STAmount& saCurDeliverMax = node.saRevDeliver;
|
||||
// Don't deliver more than wanted.
|
||||
|
||||
STAmount& saCurDeliverAct = node.saFwdDeliver;
|
||||
// Zeroed in reverse pass.
|
||||
|
||||
uint256& uDirectTip = node.uDirectTip;
|
||||
bool& bDirectRestart = node.bDirectRestart;
|
||||
|
||||
if (bMultiQuality)
|
||||
uDirectTip = 0; // Restart book searching.
|
||||
else
|
||||
bDirectRestart = true; // Restart at same quality.
|
||||
|
||||
saInAct.clear (saInReq);
|
||||
saInFees.clear (saInReq);
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
// XXX Perhaps make sure do not exceed saCurDeliverMax as another way to
|
||||
// stop?
|
||||
while (errorCode == tesSUCCESS && saInAct + saInFees < saInReq)
|
||||
{
|
||||
// Did not spend all inbound deliver funds.
|
||||
if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: max loops cndf";
|
||||
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
}
|
||||
|
||||
// Determine values for pass to adjust saInAct, saInFees, and
|
||||
// saCurDeliverAct.
|
||||
errorCode = calcNodeAdvance (
|
||||
rippleCalc,
|
||||
nodeIndex, pathState, bMultiQuality || saInAct == zero, false);
|
||||
// If needed, advance to next funded offer.
|
||||
|
||||
if (errorCode != tesSUCCESS)
|
||||
{
|
||||
nothing ();
|
||||
}
|
||||
else if (!uOfferIndex)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: INTERNAL ERROR: Ran out of offers.";
|
||||
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
}
|
||||
else if (errorCode == tesSUCCESS)
|
||||
{
|
||||
// Doesn't charge input. Input funds are in limbo.
|
||||
bool& bEntryAdvance = node.bEntryAdvance;
|
||||
STAmount& saOfrRate = node.saOfrRate;
|
||||
uint256& uOfferIndex = node.uOfferIndex;
|
||||
SLE::pointer& sleOffer = node.sleOffer;
|
||||
const uint160& uOfrOwnerID = node.uOfrOwnerID;
|
||||
bool& bFundsDirty = node.bFundsDirty;
|
||||
STAmount& saOfferFunds = node.saOfferFunds;
|
||||
STAmount& saTakerPays = node.saTakerPays;
|
||||
STAmount& saTakerGets = node.saTakerGets;
|
||||
|
||||
const STAmount saInFeeRate
|
||||
= !uPrvCurrencyID // XRP.
|
||||
|| uInAccountID == uPrvIssuerID // Sender is issuer.
|
||||
|| uOfrOwnerID == uPrvIssuerID // Reciever is issuer.
|
||||
? saOne // No fee.
|
||||
: saInTransRate; // Transfer rate of issuer.
|
||||
|
||||
// First calculate assuming no output fees: saInPassAct,
|
||||
// saInPassFees, saOutPassAct.
|
||||
|
||||
// Offer maximum out - limited by funds with out fees.
|
||||
STAmount saOutFunded = std::min (saOfferFunds, saTakerGets);
|
||||
|
||||
// Offer maximum out - limit by most to deliver.
|
||||
STAmount saOutPassFunded = std::min (
|
||||
saOutFunded, saCurDeliverMax - saCurDeliverAct);
|
||||
|
||||
// Offer maximum in - Limited by by payout.
|
||||
STAmount saInFunded = STAmount::mulRound (
|
||||
saOutPassFunded, saOfrRate, saTakerPays, true);
|
||||
|
||||
// Offer maximum in with fees.
|
||||
STAmount saInTotal = STAmount::mulRound (
|
||||
saInFunded, saInFeeRate, true);
|
||||
STAmount saInRemaining = saInReq - saInAct - saInFees;
|
||||
|
||||
if (saInRemaining < zero)
|
||||
saInRemaining.clear();
|
||||
|
||||
// In limited by remaining.
|
||||
STAmount saInSum = std::min (saInTotal, saInRemaining);
|
||||
|
||||
// In without fees.
|
||||
STAmount saInPassAct = std::min (
|
||||
saTakerPays, STAmount::divRound (saInSum, saInFeeRate, true));
|
||||
|
||||
// Out limited by in remaining.
|
||||
auto outPass = STAmount::divRound (
|
||||
saInPassAct, saOfrRate, saTakerGets, true);
|
||||
STAmount saOutPassMax = std::min (saOutPassFunded, outPass);
|
||||
|
||||
STAmount saInPassFeesMax = saInSum - saInPassAct;
|
||||
|
||||
// Will be determined by next node.
|
||||
STAmount saOutPassAct;
|
||||
|
||||
// Will be determined by adjusted saInPassAct.
|
||||
STAmount saInPassFees;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverFwd:"
|
||||
<< " nodeIndex=" << nodeIndex
|
||||
<< " saOutFunded=" << saOutFunded
|
||||
<< " saOutPassFunded=" << saOutPassFunded
|
||||
<< " saOfferFunds=" << saOfferFunds
|
||||
<< " saTakerGets=" << saTakerGets
|
||||
<< " saInReq=" << saInReq
|
||||
<< " saInAct=" << saInAct
|
||||
<< " saInFees=" << saInFees
|
||||
<< " saInFunded=" << saInFunded
|
||||
<< " saInTotal=" << saInTotal
|
||||
<< " saInSum=" << saInSum
|
||||
<< " saInPassAct=" << saInPassAct
|
||||
<< " saOutPassMax=" << saOutPassMax;
|
||||
|
||||
// FIXME: We remove an offer if WE didn't want anything out of it?
|
||||
if (!saTakerPays || saInSum <= zero)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: Microscopic offer unfunded.";
|
||||
|
||||
// After math offer is effectively unfunded.
|
||||
pathState.vUnfundedBecame.push_back (uOfferIndex);
|
||||
bEntryAdvance = true;
|
||||
continue;
|
||||
}
|
||||
else if (!saInFunded)
|
||||
{
|
||||
// Previous check should catch this.
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: UNREACHABLE REACHED";
|
||||
|
||||
// After math offer is effectively unfunded.
|
||||
pathState.vUnfundedBecame.push_back (uOfferIndex);
|
||||
bEntryAdvance = true;
|
||||
continue;
|
||||
}
|
||||
else if (!!nextAccountID)
|
||||
{
|
||||
// ? --> OFFER --> account
|
||||
// Input fees: vary based upon the consumed offer's owner.
|
||||
// Output fees: none as XRP or the destination account is the
|
||||
// issuer.
|
||||
|
||||
saOutPassAct = saOutPassMax;
|
||||
saInPassFees = saInPassFeesMax;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: ? --> OFFER --> account:"
|
||||
<< " uOfrOwnerID="
|
||||
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
|
||||
<< " nextAccountID="
|
||||
<< RippleAddress::createHumanAccountID (nextAccountID)
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutFunded=%s" << saOutFunded;
|
||||
|
||||
// Output: Debit offer owner, send XRP or non-XPR to next
|
||||
// account.
|
||||
errorCode = rippleCalc.mActiveLedger.accountSend (
|
||||
uOfrOwnerID, nextAccountID, saOutPassAct);
|
||||
|
||||
if (errorCode != tesSUCCESS)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// ? --> OFFER --> offer
|
||||
//
|
||||
// Offer to offer means current order book's output currency and
|
||||
// issuer match next order book's input current and issuer.
|
||||
//
|
||||
// Output fees: possible if issuer has fees and is not on either
|
||||
// side.
|
||||
STAmount saOutPassFees;
|
||||
|
||||
// Output fees vary as the next nodes offer owners may vary.
|
||||
// Therefore, immediately push through output for current offer.
|
||||
errorCode = calcNodeDeliverFwd (
|
||||
rippleCalc,
|
||||
nodeIndex + 1,
|
||||
pathState,
|
||||
bMultiQuality,
|
||||
uOfrOwnerID, // --> Current holder.
|
||||
saOutPassMax, // --> Amount available.
|
||||
saOutPassAct, // <-- Amount delivered.
|
||||
saOutPassFees); // <-- Fees charged.
|
||||
|
||||
if (errorCode != tesSUCCESS)
|
||||
break;
|
||||
|
||||
if (saOutPassAct == saOutPassMax)
|
||||
{
|
||||
// No fees and entire output amount.
|
||||
|
||||
saInPassFees = saInPassFeesMax;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fraction of output amount.
|
||||
// Output fees are paid by offer owner and not passed to
|
||||
// previous.
|
||||
|
||||
assert (saOutPassAct < saOutPassMax);
|
||||
auto inPassAct = STAmount::mulRound (
|
||||
saOutPassAct, saOfrRate, saInReq, true);
|
||||
saInPassAct = std::min (saTakerPays, inPassAct);
|
||||
auto inPassFees = STAmount::mulRound (
|
||||
saInPassAct, saInFeeRate, true);
|
||||
saInPassFees = std::min (saInPassFeesMax, inPassFees);
|
||||
}
|
||||
|
||||
// Do outbound debiting.
|
||||
// Send to issuer/limbo total amount including fees (issuer gets
|
||||
// fees).
|
||||
auto id = !!uCurCurrencyID ? uCurIssuerID : ACCOUNT_XRP;
|
||||
auto outPassTotal = saOutPassAct + saOutPassFees;
|
||||
rippleCalc.mActiveLedger.accountSend (uOfrOwnerID, id, outPassTotal);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: ? --> OFFER --> offer:"
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutPassFees=" << saOutPassFees;
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: "
|
||||
<< " nodeIndex=" << nodeIndex
|
||||
<< " saTakerGets=" << saTakerGets
|
||||
<< " saTakerPays=" << saTakerPays
|
||||
<< " saInPassAct=" << saInPassAct
|
||||
<< " saInPassFees=" << saInPassFees
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutFunded=" << saOutFunded;
|
||||
|
||||
// Funds were spent.
|
||||
bFundsDirty = true;
|
||||
|
||||
// Do inbound crediting.
|
||||
//
|
||||
// Credit offer owner from in issuer/limbo (input transfer fees left
|
||||
// with owner). Don't attempt to have someone credit themselves, it
|
||||
// is redundant.
|
||||
if (!uPrvCurrencyID // Always credit XRP from limbo.
|
||||
|| uInAccountID != uOfrOwnerID) // Never send non-XRP to the
|
||||
// same account.
|
||||
{
|
||||
auto id = !!uPrvCurrencyID ? uInAccountID : ACCOUNT_XRP;
|
||||
errorCode = rippleCalc.mActiveLedger.accountSend (
|
||||
id, uOfrOwnerID, saInPassAct);
|
||||
|
||||
if (errorCode != tesSUCCESS)
|
||||
break;
|
||||
}
|
||||
|
||||
// Adjust offer.
|
||||
//
|
||||
// Fees are considered paid from a seperate budget and are not named
|
||||
// in the offer.
|
||||
STAmount saTakerGetsNew = saTakerGets - saOutPassAct;
|
||||
STAmount saTakerPaysNew = saTakerPays - saInPassAct;
|
||||
|
||||
if (saTakerPaysNew < zero || saTakerGetsNew < zero)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: NEGATIVE:"
|
||||
<< " saTakerPaysNew=" << saTakerPaysNew
|
||||
<< " saTakerGetsNew=" << saTakerGetsNew;
|
||||
|
||||
// If mOpenLedger, then ledger is not final, can vote no.
|
||||
errorCode = rippleCalc.mOpenLedger
|
||||
? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
break;
|
||||
}
|
||||
|
||||
sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
|
||||
sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);
|
||||
|
||||
rippleCalc.mActiveLedger.entryModify (sleOffer);
|
||||
|
||||
if (saOutPassAct == saOutFunded || saTakerGetsNew == zero)
|
||||
{
|
||||
// Offer became unfunded.
|
||||
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: unfunded:"
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutFunded=" << saOutFunded;
|
||||
|
||||
pathState.vUnfundedBecame.push_back (uOfferIndex);
|
||||
bEntryAdvance = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CondLog (saOutPassAct >= saOutFunded, lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverFwd: TOO MUCH:"
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutFunded=" << saOutFunded;
|
||||
|
||||
assert (saOutPassAct < saOutFunded);
|
||||
}
|
||||
|
||||
saInAct += saInPassAct;
|
||||
saInFees += saInPassFees;
|
||||
|
||||
// Adjust amount available to next node.
|
||||
saCurDeliverAct = std::min (saCurDeliverMax,
|
||||
saCurDeliverAct + saOutPassAct);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverFwd<"
|
||||
<< " nodeIndex=" << nodeIndex
|
||||
<< " saInAct=" << saInAct
|
||||
<< " saInFees=" << saInFees;
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
382
src/ripple_app/paths/CalcNodeDeliverRev.cpp
Normal file
382
src/ripple_app/paths/CalcNodeDeliverRev.cpp
Normal file
@@ -0,0 +1,382 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// At the right most node of a list of consecutive offer nodes, given the amount
|
||||
// requested to be delivered, push toward node 0 the amount requested for
|
||||
// previous nodes to know how much to deliver.
|
||||
//
|
||||
// Between offer nodes, the fee charged may vary. Therefore, process one
|
||||
// inbound offer at a time. Propagate the inbound offer's requirements to the
|
||||
// previous node. The previous node adjusts the amount output and the amount
|
||||
// spent on fees. Continue processing until the request is satisified as long
|
||||
// as the rate does not increase past the initial rate.
|
||||
|
||||
TER calcNodeDeliverRev (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex,
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality, // True, if not constrained to the same
|
||||
// or better quality.
|
||||
const uint160& uOutAccountID, // --> Output owner's account.
|
||||
const STAmount& saOutReq, // --> Funds requested to be
|
||||
// delivered for an increment.
|
||||
STAmount& saOutAct) // <-- Funds actually delivered for an
|
||||
// increment.
|
||||
{
|
||||
TER errorCode = tesSUCCESS;
|
||||
|
||||
PathState::Node& previousNode = pathState.vpnNodes[nodeIndex - 1];
|
||||
PathState::Node& node = pathState.vpnNodes[nodeIndex];
|
||||
|
||||
const uint160& uCurIssuerID = node.uIssuerID;
|
||||
const uint160& uPrvAccountID = previousNode.uAccountID;
|
||||
const STAmount& saTransferRate = node.saTransferRate;
|
||||
// Transfer rate of the TakerGets issuer.
|
||||
|
||||
STAmount& saPrvDlvReq = previousNode.saRevDeliver;
|
||||
// Accumulation of what the previous node must deliver.
|
||||
|
||||
uint256& uDirectTip = node.uDirectTip;
|
||||
bool& bDirectRestart = node.bDirectRestart;
|
||||
|
||||
if (bMultiQuality)
|
||||
uDirectTip = 0; // Restart book searching.
|
||||
else
|
||||
bDirectRestart = true; // Restart at same quality.
|
||||
|
||||
// YYY Note this gets zeroed on each increment, ideally only on first
|
||||
// increment, then it could be a limit on the forward pass.
|
||||
saOutAct.clear (saOutReq);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev>"
|
||||
<< " saOutAct=" << saOutAct
|
||||
<< " saOutReq=" << saOutReq
|
||||
<< " saPrvDlvReq=" << saPrvDlvReq;
|
||||
|
||||
assert (!!saOutReq);
|
||||
|
||||
int loopCount = 0;
|
||||
|
||||
// While we did not deliver as much as requested:
|
||||
while (saOutAct < saOutReq)
|
||||
{
|
||||
if (++loopCount > CALC_NODE_DELIVER_MAX_LOOPS)
|
||||
{
|
||||
WriteLog (lsFATAL, RippleCalc) << "loop count exceeded";
|
||||
return rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
}
|
||||
|
||||
bool& bEntryAdvance = node.bEntryAdvance;
|
||||
STAmount& saOfrRate = node.saOfrRate;
|
||||
uint256& uOfferIndex = node.uOfferIndex;
|
||||
SLE::pointer& sleOffer = node.sleOffer;
|
||||
const uint160& uOfrOwnerID = node.uOfrOwnerID;
|
||||
bool& bFundsDirty = node.bFundsDirty;
|
||||
STAmount& saOfferFunds = node.saOfferFunds;
|
||||
STAmount& saTakerPays = node.saTakerPays;
|
||||
STAmount& saTakerGets = node.saTakerGets;
|
||||
STAmount& saRateMax = node.saRateMax;
|
||||
|
||||
errorCode = calcNodeAdvance (
|
||||
rippleCalc,
|
||||
nodeIndex, pathState, bMultiQuality || saOutAct == zero, true);
|
||||
// If needed, advance to next funded offer.
|
||||
|
||||
if (errorCode != tesSUCCESS || !uOfferIndex)
|
||||
{
|
||||
// Error or out of offers.
|
||||
break;
|
||||
}
|
||||
|
||||
auto const hasFee = uOfrOwnerID == uCurIssuerID
|
||||
|| uOutAccountID == uCurIssuerID; // Issuer sending or receiving.
|
||||
const STAmount saOutFeeRate = hasFee
|
||||
? saOne // No fee.
|
||||
: saTransferRate; // Transfer rate of issuer.
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev:"
|
||||
<< " uOfrOwnerID="
|
||||
<< RippleAddress::createHumanAccountID (uOfrOwnerID)
|
||||
<< " uOutAccountID="
|
||||
<< RippleAddress::createHumanAccountID (uOutAccountID)
|
||||
<< " uCurIssuerID="
|
||||
<< RippleAddress::createHumanAccountID (uCurIssuerID)
|
||||
<< " saTransferRate=" << saTransferRate
|
||||
<< " saOutFeeRate=" << saOutFeeRate;
|
||||
|
||||
if (bMultiQuality)
|
||||
{
|
||||
// In multi-quality mode, ignore rate.
|
||||
}
|
||||
else if (!saRateMax)
|
||||
{
|
||||
// Set initial rate.
|
||||
saRateMax = saOutFeeRate;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: Set initial rate:"
|
||||
<< " saRateMax=" << saRateMax
|
||||
<< " saOutFeeRate=" << saOutFeeRate;
|
||||
}
|
||||
else if (saOutFeeRate > saRateMax)
|
||||
{
|
||||
// Offer exceeds initial rate.
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: Offer exceeds initial rate:"
|
||||
<< " saRateMax=" << saRateMax
|
||||
<< " saOutFeeRate=" << saOutFeeRate;
|
||||
|
||||
break; // Done. Don't bother looking for smaller saTransferRates.
|
||||
}
|
||||
else if (saOutFeeRate < saRateMax)
|
||||
{
|
||||
// Reducing rate. Additional offers will only considered for this
|
||||
// increment if they are at least this good.
|
||||
//
|
||||
// At this point, the overall rate is reducing, while the overall
|
||||
// rate is not saOutFeeRate, it would be wrong to add anything with
|
||||
// a rate above saOutFeeRate.
|
||||
//
|
||||
// The rate would be reduced if the current offer was from the
|
||||
// issuer and the previous offer wasn't.
|
||||
|
||||
saRateMax = saOutFeeRate;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: Reducing rate:"
|
||||
<< " saRateMax=" << saRateMax;
|
||||
}
|
||||
|
||||
// Amount that goes to the taker.
|
||||
STAmount saOutPassReq = std::min (
|
||||
std::min (saOfferFunds, saTakerGets),
|
||||
saOutReq - saOutAct);
|
||||
|
||||
// Maximum out - assuming no out fees.
|
||||
STAmount saOutPassAct = saOutPassReq;
|
||||
|
||||
// Amount charged to the offer owner.
|
||||
//
|
||||
// The fee goes to issuer. The fee is paid by offer owner and not passed
|
||||
// as a cost to taker.
|
||||
//
|
||||
// Round down: prefer liquidity rather than microscopic fees.
|
||||
STAmount saOutPlusFees = STAmount::mulRound (
|
||||
saOutPassAct, saOutFeeRate, false);
|
||||
// Offer out with fees.
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev:"
|
||||
<< " saOutReq=" << saOutReq
|
||||
<< " saOutAct=" << saOutAct
|
||||
<< " saTakerGets=" << saTakerGets
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutPlusFees=" << saOutPlusFees
|
||||
<< " saOfferFunds=" << saOfferFunds;
|
||||
|
||||
if (saOutPlusFees > saOfferFunds)
|
||||
{
|
||||
// Offer owner can not cover all fees, compute saOutPassAct based on
|
||||
// saOfferFunds.
|
||||
saOutPlusFees = saOfferFunds;
|
||||
|
||||
// Round up: prefer liquidity rather than microscopic fees. But,
|
||||
// limit by requested.
|
||||
auto fee = STAmount::divRound (saOutPlusFees, saOutFeeRate, true);
|
||||
saOutPassAct = std::min (saOutPassReq, fee);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: Total exceeds fees:"
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutPlusFees=" << saOutPlusFees
|
||||
<< " saOfferFunds=" << saOfferFunds;
|
||||
}
|
||||
|
||||
// Compute portion of input needed to cover actual output.
|
||||
auto outputFee = STAmount::mulRound (
|
||||
saOutPassAct, saOfrRate, saTakerPays, true);
|
||||
STAmount saInPassReq = std::min (saTakerPays, outputFee);
|
||||
STAmount saInPassAct;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev:"
|
||||
<< " outputFee=" << outputFee
|
||||
<< " saInPassReq=" << saInPassReq
|
||||
<< " saOfrRate=" << saOfrRate
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutPlusFees=" << saOutPlusFees;
|
||||
|
||||
if (!saInPassReq) // FIXME: This is bogus
|
||||
{
|
||||
// After rounding did not want anything.
|
||||
WriteLog (lsDEBUG, RippleCalc)
|
||||
<< "calcNodeDeliverRev: micro offer is unfunded.";
|
||||
|
||||
bEntryAdvance = true;
|
||||
continue;
|
||||
}
|
||||
// Find out input amount actually available at current rate.
|
||||
else if (!!uPrvAccountID)
|
||||
{
|
||||
// account --> OFFER --> ?
|
||||
// Due to node expansion, previous is guaranteed to be the issuer.
|
||||
//
|
||||
// Previous is the issuer and receiver is an offer, so no fee or
|
||||
// quality.
|
||||
//
|
||||
// Previous is the issuer and has unlimited funds.
|
||||
//
|
||||
// Offer owner is obtaining IOUs via an offer, so credit line limits
|
||||
// are ignored. As limits are ignored, don't need to adjust
|
||||
// previous account's balance.
|
||||
|
||||
saInPassAct = saInPassReq;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: account --> OFFER --> ? :"
|
||||
<< " saInPassAct=" << saInPassAct;
|
||||
}
|
||||
else
|
||||
{
|
||||
// offer --> OFFER --> ?
|
||||
// Compute in previous offer node how much could come in.
|
||||
|
||||
errorCode = calcNodeDeliverRev (
|
||||
rippleCalc,
|
||||
nodeIndex - 1,
|
||||
pathState,
|
||||
bMultiQuality,
|
||||
uOfrOwnerID,
|
||||
saInPassReq,
|
||||
saInPassAct);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: offer --> OFFER --> ? :"
|
||||
<< " saInPassAct=" << saInPassAct;
|
||||
}
|
||||
|
||||
if (errorCode != tesSUCCESS)
|
||||
break;
|
||||
|
||||
if (saInPassAct < saInPassReq)
|
||||
{
|
||||
// Adjust output to conform to limited input.
|
||||
auto outputRequirements = STAmount::divRound (
|
||||
saInPassAct, saOfrRate, saTakerGets, true);
|
||||
saOutPassAct = std::min (saOutPassReq, outputRequirements);
|
||||
auto outputFees = STAmount::mulRound (
|
||||
saOutPassAct, saOutFeeRate, true);
|
||||
saOutPlusFees = std::min (saOfferFunds, outputFees);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev: adjusted:"
|
||||
<< " saOutPassAct=" << saOutPassAct
|
||||
<< " saOutPlusFees=" << saOutPlusFees;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(tom): more logging here.
|
||||
assert (saInPassAct == saInPassReq);
|
||||
}
|
||||
|
||||
// Funds were spent.
|
||||
bFundsDirty = true;
|
||||
|
||||
// Want to deduct output to limit calculations while computing reverse.
|
||||
// Don't actually need to send.
|
||||
//
|
||||
// Sending could be complicated: could fund a previous offer not yet
|
||||
// visited. However, these deductions and adjustments are tenative.
|
||||
//
|
||||
// Must reset balances when going forward to perform actual transfers.
|
||||
errorCode = rippleCalc.mActiveLedger.accountSend (
|
||||
uOfrOwnerID, uCurIssuerID, saOutPassAct);
|
||||
|
||||
if (errorCode != tesSUCCESS)
|
||||
break;
|
||||
|
||||
// Adjust offer
|
||||
STAmount saTakerGetsNew = saTakerGets - saOutPassAct;
|
||||
STAmount saTakerPaysNew = saTakerPays - saInPassAct;
|
||||
|
||||
if (saTakerPaysNew < zero || saTakerGetsNew < zero)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverRev: NEGATIVE:"
|
||||
<< " saTakerPaysNew=" << saTakerPaysNew
|
||||
<< " saTakerGetsNew=%s" << saTakerGetsNew;
|
||||
|
||||
// If mOpenLedger then ledger is not final, can vote no.
|
||||
errorCode = rippleCalc.mOpenLedger ? telFAILED_PROCESSING : tecFAILED_PROCESSING;
|
||||
break;
|
||||
}
|
||||
|
||||
sleOffer->setFieldAmount (sfTakerGets, saTakerGetsNew);
|
||||
sleOffer->setFieldAmount (sfTakerPays, saTakerPaysNew);
|
||||
|
||||
rippleCalc.mActiveLedger.entryModify (sleOffer);
|
||||
|
||||
if (saOutPassAct == saTakerGets)
|
||||
{
|
||||
// Offer became unfunded.
|
||||
WriteLog (lsDEBUG, RippleCalc)
|
||||
<< "calcNodeDeliverRev: offer became unfunded.";
|
||||
|
||||
bEntryAdvance = true; // XXX When don't we want to set advance?
|
||||
}
|
||||
else
|
||||
{
|
||||
assert (saOutPassAct < saTakerGets);
|
||||
}
|
||||
|
||||
saOutAct += saOutPassAct;
|
||||
// Accumulate what is to be delivered from previous node.
|
||||
saPrvDlvReq += saInPassAct;
|
||||
}
|
||||
|
||||
CondLog (saOutAct > saOutReq, lsWARNING, RippleCalc)
|
||||
<< "calcNodeDeliverRev: TOO MUCH:"
|
||||
<< " saOutAct=" << saOutAct
|
||||
<< " saOutReq=" << saOutReq;
|
||||
|
||||
assert (saOutAct <= saOutReq);
|
||||
|
||||
// XXX Perhaps need to check if partial is okay to relax this?
|
||||
if (errorCode == tesSUCCESS && !saOutAct)
|
||||
errorCode = tecPATH_DRY;
|
||||
// Unable to meet request, consider path dry.
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeDeliverRev<"
|
||||
<< " saOutAct=" << saOutAct
|
||||
<< " saOutReq=" << saOutReq
|
||||
<< " saPrvDlvReq=" << saPrvDlvReq;
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
118
src/ripple_app/paths/CalcNodeOffer.cpp
Normal file
118
src/ripple_app/paths/CalcNodeOffer.cpp
Normal file
@@ -0,0 +1,118 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Called to drive the from the first offer node in a chain.
|
||||
//
|
||||
// - Offer input is in issuer/limbo.
|
||||
// - Current offers consumed.
|
||||
// - Current offer owners debited.
|
||||
// - Transfer fees credited to issuer.
|
||||
// - Payout to issuer or limbo.
|
||||
// - Deliver is set without transfer fees.
|
||||
TER calcNodeOfferFwd (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex,
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality
|
||||
)
|
||||
{
|
||||
TER errorCode;
|
||||
PathState::Node& previousNode = pathState.vpnNodes [nodeIndex - 1];
|
||||
|
||||
if (!!previousNode.uAccountID)
|
||||
{
|
||||
// Previous is an account node, resolve its deliver.
|
||||
STAmount saInAct;
|
||||
STAmount saInFees;
|
||||
|
||||
errorCode = calcNodeDeliverFwd (
|
||||
rippleCalc,
|
||||
nodeIndex,
|
||||
pathState,
|
||||
bMultiQuality,
|
||||
previousNode.uAccountID,
|
||||
previousNode.saFwdDeliver, // Previous is sending this much.
|
||||
saInAct,
|
||||
saInFees);
|
||||
|
||||
assert (errorCode != tesSUCCESS ||
|
||||
previousNode.saFwdDeliver == saInAct + saInFees);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previous is an offer. Deliver has already been resolved.
|
||||
errorCode = tesSUCCESS;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
|
||||
}
|
||||
|
||||
// Called to drive from the last offer node in a chain.
|
||||
TER calcNodeOfferRev (
|
||||
RippleCalc& rippleCalc,
|
||||
const unsigned int nodeIndex,
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality)
|
||||
{
|
||||
TER errorCode;
|
||||
|
||||
PathState::Node& node = pathState.vpnNodes [nodeIndex];
|
||||
PathState::Node& nextNode = pathState.vpnNodes [nodeIndex + 1];
|
||||
|
||||
if (!!nextNode.uAccountID)
|
||||
{
|
||||
// Next is an account node, resolve current offer node's deliver.
|
||||
STAmount saDeliverAct;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeOfferRev: OFFER --> account:"
|
||||
<< " nodeIndex=" << nodeIndex
|
||||
<< " saRevDeliver=" << node.saRevDeliver;
|
||||
|
||||
errorCode = calcNodeDeliverRev (
|
||||
rippleCalc,
|
||||
nodeIndex,
|
||||
pathState,
|
||||
bMultiQuality,
|
||||
nextNode.uAccountID,
|
||||
|
||||
// The next node wants the current node to deliver this much:
|
||||
node.saRevDeliver,
|
||||
saDeliverAct);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeOfferRev: OFFER --> offer: nodeIndex=" << nodeIndex;
|
||||
|
||||
// Next is an offer. Deliver has already been resolved.
|
||||
errorCode = tesSUCCESS;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
175
src/ripple_app/paths/CalcNodeRipple.cpp
Normal file
175
src/ripple_app/paths/CalcNodeRipple.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Compute how much might flow for the node for the pass. Does not actually
|
||||
// adjust balances.
|
||||
//
|
||||
// uQualityIn -> uQualityOut
|
||||
// saPrvReq -> saCurReq
|
||||
// sqPrvAct -> saCurAct
|
||||
//
|
||||
// This is a minimizing routine: moving in reverse it propagates the send limit
|
||||
// to the sender, moving forward it propagates the actual send toward the
|
||||
// receiver.
|
||||
//
|
||||
// This routine works backwards:
|
||||
// - cur is the driver: it calculates previous wants based on previous credit
|
||||
// limits and current wants.
|
||||
//
|
||||
// This routine works forwards:
|
||||
// - prv is the driver: it calculates current deliver based on previous delivery
|
||||
// limits and current wants.
|
||||
//
|
||||
// This routine is called one or two times for a node in a pass. If called once,
|
||||
// it will work and set a rate. If called again, the new work must not worsen
|
||||
// the previous rate.
|
||||
void calcNodeRipple (
|
||||
RippleCalc& rippleCalc,
|
||||
const std::uint32_t uQualityIn,
|
||||
const std::uint32_t uQualityOut,
|
||||
const STAmount& saPrvReq, // --> in limit including fees, <0 = unlimited
|
||||
const STAmount& saCurReq, // --> out limit (driver)
|
||||
STAmount& saPrvAct, // <-> in limit including achieved so far: <-- <= -->
|
||||
STAmount& saCurAct, // <-> out limit including achieved : <-- <= -->
|
||||
std::uint64_t& uRateMax)
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRipple>"
|
||||
<< " uQualityIn=" << uQualityIn
|
||||
<< " uQualityOut=" << uQualityOut
|
||||
<< " saPrvReq=" << saPrvReq
|
||||
<< " saCurReq=" << saCurReq
|
||||
<< " saPrvAct=" << saPrvAct
|
||||
<< " saCurAct=" << saCurAct;
|
||||
|
||||
assert (saCurReq > zero); // FIXME: saCurReq was zero
|
||||
assert (saPrvReq.getCurrency () == saCurReq.getCurrency ());
|
||||
assert (saPrvReq.getCurrency () == saPrvAct.getCurrency ());
|
||||
assert (saPrvReq.getIssuer () == saPrvAct.getIssuer ());
|
||||
|
||||
const bool bPrvUnlimited = saPrvReq < zero;
|
||||
const STAmount saPrv = bPrvUnlimited ? STAmount (saPrvReq)
|
||||
: saPrvReq - saPrvAct;
|
||||
const STAmount saCur = saCurReq - saCurAct;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRipple: "
|
||||
<< " bPrvUnlimited=" << bPrvUnlimited
|
||||
<< " saPrv=" << saPrv
|
||||
<< " saCur=" << saCur;
|
||||
|
||||
if (uQualityIn >= uQualityOut)
|
||||
{
|
||||
// No fee.
|
||||
WriteLog (lsTRACE, RippleCalc) << "calcNodeRipple: No fees";
|
||||
|
||||
// Only process if we are not worsing previously processed.
|
||||
if (!uRateMax || STAmount::uRateOne <= uRateMax)
|
||||
{
|
||||
// Limit amount to transfer if need.
|
||||
STAmount saTransfer = bPrvUnlimited ? saCur
|
||||
: std::min (saPrv, saCur);
|
||||
|
||||
// In reverse, we want to propagate the limited cur to prv and set
|
||||
// actual cur.
|
||||
//
|
||||
// In forward, we want to propagate the limited prv to cur and set
|
||||
// actual prv.
|
||||
saPrvAct += saTransfer;
|
||||
saCurAct += saTransfer;
|
||||
|
||||
// If no rate limit, set rate limit to avoid combining with
|
||||
// something with a worse rate.
|
||||
if (!uRateMax)
|
||||
uRateMax = STAmount::uRateOne;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fee.
|
||||
WriteLog (lsTRACE, RippleCalc) << "calcNodeRipple: Fee";
|
||||
|
||||
std::uint64_t uRate = STAmount::getRate (
|
||||
STAmount (uQualityOut), STAmount (uQualityIn));
|
||||
|
||||
if (!uRateMax || uRate <= uRateMax)
|
||||
{
|
||||
const uint160 uCurrencyID = saCur.getCurrency ();
|
||||
const uint160 uCurIssuerID = saCur.getIssuer ();
|
||||
|
||||
// TODO(tom): what's this?
|
||||
auto someFee = STAmount::mulRound (
|
||||
saCur, uQualityOut, uCurrencyID, uCurIssuerID, true);
|
||||
|
||||
STAmount saCurIn = STAmount::divRound (
|
||||
someFee, uQualityIn, uCurrencyID, uCurIssuerID, true);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRipple:"
|
||||
<< " bPrvUnlimited=" << bPrvUnlimited
|
||||
<< " saPrv=" << saPrv
|
||||
<< " saCurIn=" << saCurIn;
|
||||
|
||||
if (bPrvUnlimited || saCurIn <= saPrv)
|
||||
{
|
||||
// All of cur. Some amount of prv.
|
||||
saCurAct += saCur;
|
||||
saPrvAct += saCurIn;
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRipple:3c:"
|
||||
<< " saCurReq=" << saCurReq
|
||||
<< " saPrvAct=" << saPrvAct;
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO(tom): what's this?
|
||||
auto someFee = STAmount::mulRound (
|
||||
saPrv, uQualityIn, uCurrencyID, uCurIssuerID, true);
|
||||
// A part of cur. All of prv. (cur as driver)
|
||||
STAmount saCurOut = STAmount::divRound (
|
||||
someFee, uQualityOut, uCurrencyID, uCurIssuerID, true);
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRipple:4: saCurReq=" << saCurReq;
|
||||
|
||||
saCurAct += saCurOut;
|
||||
saPrvAct = saPrvReq;
|
||||
|
||||
}
|
||||
if (!uRateMax)
|
||||
uRateMax = uRate;
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "calcNodeRipple<"
|
||||
<< " uQualityIn=" << uQualityIn
|
||||
<< " uQualityOut=" << uQualityOut
|
||||
<< " saPrvReq=" << saPrvReq
|
||||
<< " saCurReq=" << saCurReq
|
||||
<< " saPrvAct=" << saPrvAct
|
||||
<< " saCurAct=" << saCurAct;
|
||||
}
|
||||
|
||||
} // ripple
|
||||
111
src/ripple_app/paths/CalcState.h
Normal file
111
src/ripple_app/paths/CalcState.h
Normal file
@@ -0,0 +1,111 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_CALCSTATE_H
|
||||
#define RIPPLE_CALCSTATE_H
|
||||
|
||||
namespace ripple {
|
||||
|
||||
typedef TER ErrorCode;
|
||||
|
||||
class CalcState {
|
||||
public:
|
||||
CalcState(
|
||||
unsigned int nodeIndex, PathState& state, LedgerEntrySet& ledger, bool quality)
|
||||
: nodeIndex_(nodeIndex),
|
||||
pathState_(state),
|
||||
ledger_(ledger),
|
||||
quality_(quality)
|
||||
{}
|
||||
|
||||
enum Direction { BACKWARD, FORWARD };
|
||||
TER calc(Direction);
|
||||
TER calcAccount(Direction);
|
||||
TER calcOffer(Direction);
|
||||
TER calcDeliver(Direction);
|
||||
TER calcAdvance(Direction);
|
||||
void nextPath(LedgerEntrySet const& checkpoint) const;
|
||||
|
||||
private:
|
||||
enum NodeCursor { FIRST, PREVIOUS, CURRENT, NEXT, LAST };
|
||||
|
||||
unsigned int index(NodeCursor cursor = CURRENT) const
|
||||
{
|
||||
switch (cursor) {
|
||||
case FIRST:
|
||||
return 0;
|
||||
case PREVIOUS:
|
||||
return nodeIndex_ ? nodeIndex_ - 1 : 0;
|
||||
case CURRENT:
|
||||
return nodeIndex_;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
unsigned int size = pathState_.vpnNodes.size ();
|
||||
auto last = size ? size - 1 : 0;
|
||||
switch (cursor) {
|
||||
case NEXT:
|
||||
return std::min(nodeIndex_ + 1, last);
|
||||
case LAST:
|
||||
return last;
|
||||
default:
|
||||
bassert(false);
|
||||
}
|
||||
}
|
||||
|
||||
PathState::Node& node(NodeCursor cursor = CURRENT)
|
||||
{
|
||||
return pathState_.vpnNodes[index(cursor)];
|
||||
}
|
||||
|
||||
CalcState state(NodeCursor cursor = CURRENT)
|
||||
{
|
||||
return CalcState(index(cursor), pathState_, ledger_, quality_);
|
||||
}
|
||||
|
||||
LedgerEntrySet& ledger()
|
||||
{
|
||||
return ledger_;
|
||||
}
|
||||
|
||||
bool quality() const
|
||||
{
|
||||
return quality_;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int const nodeIndex_;
|
||||
PathState& pathState_;
|
||||
LedgerEntrySet& ledger_;
|
||||
bool const quality_;
|
||||
};
|
||||
|
||||
inline bool isAccount(PathState::Node const& node)
|
||||
{
|
||||
return is_bit_set (node.uFlags, STPathElement::typeAccount);
|
||||
}
|
||||
|
||||
inline STAmount copyCurrencyAndIssuer(const STAmount& a)
|
||||
{
|
||||
return STAmount(a.getCurrency(), a.getIssuer());
|
||||
}
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
109
src/ripple_app/paths/Calculators.h
Normal file
109
src/ripple_app/paths/Calculators.h
Normal file
@@ -0,0 +1,109 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_PATHS_CALCULATORS_H
|
||||
#define RIPPLE_PATHS_CALCULATORS_H
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// TODO(vfalco) What's the difference between a RippleState versus PathState?
|
||||
//
|
||||
struct RippleCalc
|
||||
{
|
||||
LedgerEntrySet& mActiveLedger;
|
||||
bool mOpenLedger;
|
||||
// First time working in reverse a funding source was mentioned. Source may
|
||||
// only be used there.
|
||||
//
|
||||
// Map of currency, issuer to node index.
|
||||
AccountCurrencyIssuerToNodeIndex mumSource;
|
||||
|
||||
// If the transaction fails to meet some constraint, still need to delete
|
||||
// unfunded offers.
|
||||
//
|
||||
// Offers that were found unfunded.
|
||||
unordered_set<uint256> mUnfundedOffers;
|
||||
|
||||
RippleCalc (LedgerEntrySet& activeLedger, const bool bOpenLedger)
|
||||
: mActiveLedger (activeLedger), mOpenLedger (bOpenLedger)
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void pathNext (
|
||||
RippleCalc&,
|
||||
PathState& pathState, const bool bMultiQuality,
|
||||
const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||
|
||||
TER calcNodeRev (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
|
||||
TER calcNodeFwd (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
|
||||
TER calcNodeOfferRev (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
|
||||
TER calcNodeOfferFwd (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
|
||||
TER calcNodeAccountRev (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
|
||||
TER calcNodeAccountFwd (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality);
|
||||
TER calcNodeAdvance (
|
||||
RippleCalc&,
|
||||
unsigned int nodeIndex, PathState& pathState, bool bMultiQuality,
|
||||
bool bReverse);
|
||||
|
||||
TER calcNodeDeliverRev (
|
||||
RippleCalc&,
|
||||
const unsigned int nodeIndex,
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uOutAccountID,
|
||||
const STAmount& saOutReq,
|
||||
STAmount& saOutAct);
|
||||
|
||||
TER calcNodeDeliverFwd (
|
||||
RippleCalc&,
|
||||
const unsigned int nodeIndex,
|
||||
PathState& pathState,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID,
|
||||
const STAmount& saInReq,
|
||||
STAmount& saInAct,
|
||||
STAmount& saInFees);
|
||||
|
||||
void calcNodeRipple (
|
||||
RippleCalc&,
|
||||
const std::uint32_t uQualityIn, const std::uint32_t uQualityOut,
|
||||
const STAmount& saPrvReq, const STAmount& saCurReq,
|
||||
STAmount& saPrvAct, STAmount& saCurAct, std::uint64_t& uRateMax);
|
||||
|
||||
void setCanonical (
|
||||
STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded,
|
||||
bool bKeepDefault);
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
107
src/ripple_app/paths/PathNext.cpp
Normal file
107
src/ripple_app/paths/PathNext.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "Calculators.h"
|
||||
#include "CalcState.h"
|
||||
#include "RippleCalc.h"
|
||||
#include "Tuning.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// Calculate the next increment of a path.
|
||||
//
|
||||
// The increment is what can satisfy a portion or all of the requested output at
|
||||
// the best quality.
|
||||
//
|
||||
// <-- pathState.uQuality
|
||||
|
||||
void pathNext (
|
||||
RippleCalc& rippleCalc,
|
||||
PathState& pathState, const bool bMultiQuality,
|
||||
const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent)
|
||||
{
|
||||
// The next state is what is available in preference order.
|
||||
// This is calculated when referenced accounts changed.
|
||||
const unsigned int lastNodeIndex = pathState.vpnNodes.size () - 1;
|
||||
|
||||
pathState.bConsumed = false;
|
||||
|
||||
// YYY This clearing should only be needed for nice logging.
|
||||
pathState.saInPass = STAmount (
|
||||
pathState.saInReq.getCurrency (), pathState.saInReq.getIssuer ());
|
||||
pathState.saOutPass = STAmount (
|
||||
pathState.saOutReq.getCurrency (), pathState.saOutReq.getIssuer ());
|
||||
|
||||
pathState.vUnfundedBecame.clear ();
|
||||
pathState.umReverse.clear ();
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "pathNext: Path In: " << pathState.getJson ();
|
||||
|
||||
assert (pathState.vpnNodes.size () >= 2);
|
||||
|
||||
lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint.
|
||||
|
||||
for (unsigned int uIndex = pathState.vpnNodes.size (); uIndex--;)
|
||||
{
|
||||
auto& node = pathState.vpnNodes[uIndex];
|
||||
|
||||
node.saRevRedeem.clear ();
|
||||
node.saRevIssue.clear ();
|
||||
node.saRevDeliver.clear ();
|
||||
node.saFwdDeliver.clear ();
|
||||
}
|
||||
|
||||
pathState.terStatus = calcNodeRev (rippleCalc, lastNodeIndex, pathState, bMultiQuality);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "pathNext: Path after reverse: " << pathState.getJson ();
|
||||
|
||||
if (tesSUCCESS == pathState.terStatus)
|
||||
{
|
||||
// Do forward.
|
||||
lesCurrent = lesCheckpoint.duplicate (); // Restore from checkpoint.
|
||||
|
||||
pathState.terStatus = calcNodeFwd (rippleCalc, 0, pathState, bMultiQuality);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == pathState.terStatus)
|
||||
{
|
||||
CondLog (!pathState.saInPass || !pathState.saOutPass, lsDEBUG, RippleCalc)
|
||||
<< "pathNext: Error calcNodeFwd reported success for nothing:"
|
||||
<< " saOutPass=" << pathState.saOutPass
|
||||
<< " saInPass=" << pathState.saInPass;
|
||||
|
||||
if (!pathState.saOutPass || !pathState.saInPass)
|
||||
throw std::runtime_error ("Made no progress.");
|
||||
|
||||
// Calculate relative quality.
|
||||
pathState.uQuality = STAmount::getRate (
|
||||
pathState.saOutPass, pathState.saInPass);
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc)
|
||||
<< "pathNext: Path after forward: " << pathState.getJson ();
|
||||
}
|
||||
else
|
||||
{
|
||||
pathState.uQuality = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // ripple
|
||||
@@ -17,6 +17,10 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "Calculators.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (PathRequest)
|
||||
@@ -422,25 +426,25 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
|
||||
(currIssuer.first.isZero () ? ACCOUNT_XRP : raSrcAccount.getAccountID ()), 1);
|
||||
saMaxAmount.negate ();
|
||||
m_journal.debug << iIdentifier << " Paths found, calling rippleCalc";
|
||||
TER terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
TER errorCode = rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
vpsExpanded, saMaxAmount, saDstAmount,
|
||||
raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
|
||||
spsPaths, false, false, false, true);
|
||||
|
||||
|
||||
if ((extraPath.size() > 0) && ((terResult == terNO_LINE) || (terResult == tecPATH_PARTIAL)))
|
||||
if ((extraPath.size() > 0) && ((errorCode == terNO_LINE) || (errorCode == tecPATH_PARTIAL)))
|
||||
{
|
||||
m_journal.debug << iIdentifier << " Trying with an extra path element";
|
||||
spsPaths.addPath(extraPath);
|
||||
vpsExpanded.clear ();
|
||||
terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
errorCode = rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
vpsExpanded, saMaxAmount, saDstAmount,
|
||||
raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
|
||||
spsPaths, false, false, false, true);
|
||||
m_journal.debug << iIdentifier << " Extra path element gives " << transHuman (terResult);
|
||||
m_journal.debug << iIdentifier << " Extra path element gives " << transHuman (errorCode);
|
||||
}
|
||||
|
||||
if (terResult == tesSUCCESS)
|
||||
if (errorCode == tesSUCCESS)
|
||||
{
|
||||
Json::Value jvEntry (Json::objectValue);
|
||||
jvEntry["source_amount"] = saMaxAmountAct.getJson (0);
|
||||
@@ -450,7 +454,7 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
|
||||
}
|
||||
else
|
||||
{
|
||||
m_journal.debug << iIdentifier << " rippleCalc returns " << transHuman (terResult);
|
||||
m_journal.debug << iIdentifier << " rippleCalc returns " << transHuman (errorCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -26,12 +26,12 @@ namespace ripple {
|
||||
// all liquidity is used.
|
||||
//
|
||||
|
||||
class RippleCalc; // for logging
|
||||
struct RippleCalc; // for logging
|
||||
|
||||
std::size_t hash_value (const aciSource& asValue)
|
||||
std::size_t hash_value (const AccountCurrencyIssuer& asValue)
|
||||
{
|
||||
std::size_t const seed = 0;
|
||||
return beast::hardened_hash<aciSource>{seed}(asValue);
|
||||
return beast::hardened_hash<AccountCurrencyIssuer>{seed}(asValue);
|
||||
}
|
||||
|
||||
// Compare the non-calculated fields.
|
||||
@@ -126,19 +126,19 @@ TER PathState::pushImply (
|
||||
const uint160& uCurrencyID, // --> Delivering this currency.
|
||||
const uint160& uIssuerID) // --> Delivering this issuer.
|
||||
{
|
||||
const Node& pnPrv = vpnNodes.back ();
|
||||
TER terResult = tesSUCCESS;
|
||||
const Node& previousNode = vpnNodes.back ();
|
||||
TER errorCode = tesSUCCESS;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushImply>" <<
|
||||
" " << RippleAddress::createHumanAccountID (uAccountID) <<
|
||||
" " << STAmount::createHumanCurrency (uCurrencyID) <<
|
||||
" " << RippleAddress::createHumanAccountID (uIssuerID);
|
||||
|
||||
if (pnPrv.uCurrencyID != uCurrencyID)
|
||||
if (previousNode.uCurrencyID != uCurrencyID)
|
||||
{
|
||||
// Currency is different, need to convert via an offer.
|
||||
|
||||
terResult = pushNode ( // Offer.
|
||||
errorCode = pushNode ( // Offer.
|
||||
!!uCurrencyID
|
||||
? STPathElement::typeCurrency | STPathElement::typeIssuer
|
||||
: STPathElement::typeCurrency,
|
||||
@@ -150,37 +150,37 @@ TER PathState::pushImply (
|
||||
const Node& pnBck = vpnNodes.back ();
|
||||
|
||||
// For ripple, non-XRP, ensure the issuer is on at least one side of the transaction.
|
||||
if (tesSUCCESS == terResult
|
||||
&& !!uCurrencyID // Not XRP.
|
||||
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
|
||||
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
|
||||
if (errorCode == tesSUCCESS
|
||||
&& !!uCurrencyID // Not XRP.
|
||||
&& (pnBck.uAccountID != uIssuerID // Previous is not issuing own IOUs.
|
||||
&& uAccountID != uIssuerID)) // Current is not receiving own IOUs.
|
||||
{
|
||||
// Need to ripple through uIssuerID's account.
|
||||
|
||||
terResult = pushNode (
|
||||
errorCode = pushNode (
|
||||
STPathElement::typeAccount | STPathElement::typeCurrency | STPathElement::typeIssuer,
|
||||
uIssuerID, // Intermediate account is the needed issuer.
|
||||
uCurrencyID,
|
||||
uIssuerID);
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushImply< : " << transToken (terResult);
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushImply< : " << transToken (errorCode);
|
||||
|
||||
return terResult;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
// Append a node and insert before it any implied nodes.
|
||||
// Offers may go back to back.
|
||||
// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
|
||||
// <-- errorCode: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
|
||||
TER PathState::pushNode (
|
||||
const int iType,
|
||||
const uint160& uAccountID,
|
||||
const uint160& uCurrencyID,
|
||||
const uint160& uIssuerID)
|
||||
{
|
||||
Node pnCur;
|
||||
Node node;
|
||||
const bool bFirst = vpnNodes.empty ();
|
||||
const Node& pnPrv = bFirst ? Node () : vpnNodes.back ();
|
||||
const Node& previousNode = bFirst ? Node () : vpnNodes.back ();
|
||||
// true, iff node is a ripple account. false, iff node is an offer node.
|
||||
const bool bAccount = is_bit_set (iType, STPathElement::typeAccount);
|
||||
// true, iff currency supplied.
|
||||
@@ -188,7 +188,7 @@ TER PathState::pushNode (
|
||||
const bool bCurrency = is_bit_set (iType, STPathElement::typeCurrency);
|
||||
// Issuer is specified for the output of the current node.
|
||||
const bool bIssuer = is_bit_set (iType, STPathElement::typeIssuer);
|
||||
TER terResult = tesSUCCESS;
|
||||
TER errorCode = tesSUCCESS;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode> " <<
|
||||
iType <<
|
||||
@@ -196,47 +196,47 @@ TER PathState::pushNode (
|
||||
" " << (bCurrency ? STAmount::createHumanCurrency (uCurrencyID) : "-") <<
|
||||
"/" << (bIssuer ? RippleAddress::createHumanAccountID (uIssuerID) : "-");
|
||||
|
||||
pnCur.uFlags = iType;
|
||||
pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID;
|
||||
node.uFlags = iType;
|
||||
node.uCurrencyID = bCurrency ? uCurrencyID : previousNode.uCurrencyID;
|
||||
|
||||
if (iType & ~STPathElement::typeValidBits)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad bits.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
errorCode = temBAD_PATH;
|
||||
}
|
||||
else if (bIssuer && !pnCur.uCurrencyID)
|
||||
else if (bIssuer && !node.uCurrencyID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: issuer specified for XRP.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
errorCode = temBAD_PATH;
|
||||
}
|
||||
else if (bIssuer && !uIssuerID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
errorCode = temBAD_PATH;
|
||||
}
|
||||
else if (!bAccount && !bCurrency && !bIssuer)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: offer must specify at least currency or issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
errorCode = temBAD_PATH;
|
||||
}
|
||||
else if (bAccount)
|
||||
{
|
||||
// Account link
|
||||
|
||||
pnCur.uAccountID = uAccountID;
|
||||
pnCur.uIssuerID = bIssuer
|
||||
node.uAccountID = uAccountID;
|
||||
node.uIssuerID = bIssuer
|
||||
? uIssuerID
|
||||
: !!pnCur.uCurrencyID
|
||||
: !!node.uCurrencyID
|
||||
? uAccountID
|
||||
: ACCOUNT_XRP;
|
||||
pnCur.saRevRedeem = STAmount (pnCur.uCurrencyID, uAccountID);
|
||||
pnCur.saRevIssue = STAmount (pnCur.uCurrencyID, uAccountID);
|
||||
pnCur.saRevDeliver = STAmount (pnCur.uCurrencyID, pnCur.uIssuerID);
|
||||
pnCur.saFwdDeliver = pnCur.saRevDeliver;
|
||||
node.saRevRedeem = STAmount (node.uCurrencyID, uAccountID);
|
||||
node.saRevIssue = STAmount (node.uCurrencyID, uAccountID);
|
||||
node.saRevDeliver = STAmount (node.uCurrencyID, node.uIssuerID);
|
||||
node.saFwdDeliver = node.saRevDeliver;
|
||||
|
||||
if (bFirst)
|
||||
{
|
||||
@@ -248,62 +248,62 @@ TER PathState::pushNode (
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad account.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
errorCode = temBAD_PATH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add required intermediate nodes to deliver to current account.
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for account.";
|
||||
|
||||
terResult = pushImply (
|
||||
pnCur.uAccountID, // Current account.
|
||||
pnCur.uCurrencyID, // Wanted currency.
|
||||
!!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XRP); // Account as wanted issuer.
|
||||
errorCode = pushImply (
|
||||
node.uAccountID, // Current account.
|
||||
node.uCurrencyID, // Wanted currency.
|
||||
!!node.uCurrencyID ? uAccountID : ACCOUNT_XRP); // Account as wanted issuer.
|
||||
|
||||
// Note: pnPrv may no longer be the immediately previous node.
|
||||
// Note: previousNode may no longer be the immediately previous node.
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult && !vpnNodes.empty ())
|
||||
if (errorCode == tesSUCCESS && !vpnNodes.empty ())
|
||||
{
|
||||
const Node& pnBck = vpnNodes.back ();
|
||||
bool bBckAccount = is_bit_set (pnBck.uFlags, STPathElement::typeAccount);
|
||||
|
||||
if (bBckAccount)
|
||||
{
|
||||
SLE::pointer sleRippleState = lesEntries.entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));
|
||||
SLE::pointer sleRippleState = lesEntries.entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (pnBck.uAccountID, node.uAccountID, previousNode.uCurrencyID));
|
||||
|
||||
if (!sleRippleState)
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: No credit line between "
|
||||
<< RippleAddress::createHumanAccountID (pnBck.uAccountID)
|
||||
<< " and "
|
||||
<< RippleAddress::createHumanAccountID (pnCur.uAccountID)
|
||||
<< RippleAddress::createHumanAccountID (node.uAccountID)
|
||||
<< " for "
|
||||
<< STAmount::createHumanCurrency (pnCur.uCurrencyID)
|
||||
<< STAmount::createHumanCurrency (node.uCurrencyID)
|
||||
<< "." ;
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << getJson ();
|
||||
|
||||
terResult = terNO_LINE;
|
||||
errorCode = terNO_LINE;
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: Credit line found between "
|
||||
<< RippleAddress::createHumanAccountID (pnBck.uAccountID)
|
||||
<< " and "
|
||||
<< RippleAddress::createHumanAccountID (pnCur.uAccountID)
|
||||
<< RippleAddress::createHumanAccountID (node.uAccountID)
|
||||
<< " for "
|
||||
<< STAmount::createHumanCurrency (pnCur.uCurrencyID)
|
||||
<< STAmount::createHumanCurrency (node.uCurrencyID)
|
||||
<< "." ;
|
||||
|
||||
SLE::pointer sleBck = lesEntries.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (pnBck.uAccountID));
|
||||
bool bHigh = pnBck.uAccountID > pnCur.uAccountID;
|
||||
bool bHigh = pnBck.uAccountID > node.uAccountID;
|
||||
|
||||
if (!sleBck)
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID (pnBck.uAccountID);
|
||||
|
||||
terResult = terNO_ACCOUNT;
|
||||
errorCode = terNO_ACCOUNT;
|
||||
}
|
||||
else if ((is_bit_set (sleBck->getFieldU32 (sfFlags), lsfRequireAuth)
|
||||
&& !is_bit_set (sleRippleState->getFieldU32 (sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth)))
|
||||
@@ -311,76 +311,76 @@ TER PathState::pushNode (
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from issuer without auth.";
|
||||
|
||||
terResult = terNO_AUTH;
|
||||
errorCode = terNO_AUTH;
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
if (errorCode == tesSUCCESS)
|
||||
{
|
||||
STAmount saOwed = lesEntries.rippleOwed (pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
|
||||
STAmount saOwed = lesEntries.rippleOwed (node.uAccountID, pnBck.uAccountID, node.uCurrencyID);
|
||||
STAmount saLimit;
|
||||
|
||||
if (saOwed <= zero
|
||||
&& -saOwed >= (saLimit = lesEntries.rippleLimit (pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID)))
|
||||
&& -saOwed >= (saLimit = lesEntries.rippleLimit (node.uAccountID, pnBck.uAccountID, node.uCurrencyID)))
|
||||
{
|
||||
WriteLog (lsWARNING, RippleCalc) <<
|
||||
"pushNode: dry:" <<
|
||||
" saOwed=" << saOwed <<
|
||||
" saLimit=" << saLimit;
|
||||
|
||||
terResult = tecPATH_DRY;
|
||||
errorCode = tecPATH_DRY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
if (errorCode == tesSUCCESS)
|
||||
{
|
||||
vpnNodes.push_back (pnCur);
|
||||
vpnNodes.push_back (node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Offer link
|
||||
// Offers bridge a change in currency & issuer or just a change in issuer.
|
||||
pnCur.uIssuerID = bIssuer
|
||||
node.uIssuerID = bIssuer
|
||||
? uIssuerID
|
||||
: !!pnCur.uCurrencyID
|
||||
? !!pnPrv.uIssuerID
|
||||
? pnPrv.uIssuerID // Default to previous issuer
|
||||
: pnPrv.uAccountID // Or previous account if no previous issuer.
|
||||
: !!node.uCurrencyID
|
||||
? !!previousNode.uIssuerID
|
||||
? previousNode.uIssuerID // Default to previous issuer
|
||||
: previousNode.uAccountID // Or previous account if no previous issuer.
|
||||
: ACCOUNT_XRP;
|
||||
pnCur.saRateMax = saZero;
|
||||
pnCur.saRevDeliver = STAmount (pnCur.uCurrencyID, pnCur.uIssuerID);
|
||||
pnCur.saFwdDeliver = pnCur.saRevDeliver;
|
||||
node.saRateMax = saZero;
|
||||
node.saRevDeliver = STAmount (node.uCurrencyID, node.uIssuerID);
|
||||
node.saFwdDeliver = node.saRevDeliver;
|
||||
|
||||
if (!!pnCur.uCurrencyID != !!pnCur.uIssuerID)
|
||||
if (!!node.uCurrencyID != !!node.uIssuerID)
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "pushNode: currency is inconsistent with issuer.";
|
||||
|
||||
terResult = temBAD_PATH;
|
||||
errorCode = temBAD_PATH;
|
||||
}
|
||||
else if (!!pnPrv.uAccountID)
|
||||
else if (!!previousNode.uAccountID)
|
||||
{
|
||||
// Previous is an account.
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for offer.";
|
||||
|
||||
// Insert intermediary issuer account if needed.
|
||||
terResult = pushImply (
|
||||
errorCode = pushImply (
|
||||
ACCOUNT_XRP, // Rippling, but offers don't have an account.
|
||||
pnPrv.uCurrencyID,
|
||||
pnPrv.uIssuerID);
|
||||
previousNode.uCurrencyID,
|
||||
previousNode.uIssuerID);
|
||||
}
|
||||
|
||||
if (tesSUCCESS == terResult)
|
||||
if (errorCode == tesSUCCESS)
|
||||
{
|
||||
vpnNodes.push_back (pnCur);
|
||||
vpnNodes.push_back (node);
|
||||
}
|
||||
}
|
||||
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode< : " << transToken (terResult);
|
||||
WriteLog (lsTRACE, RippleCalc) << "pushNode< : " << transToken (errorCode);
|
||||
|
||||
return terResult;
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
// Set to an expanded path.
|
||||
@@ -440,7 +440,7 @@ void PathState::setExpanded (
|
||||
const uint160 uNxtCurrencyID = spSourcePath.size ()
|
||||
? spSourcePath.getElement (0).getCurrency () // Use next node.
|
||||
: uOutCurrencyID; // Use send.
|
||||
const uint160 uNxtAccountID = spSourcePath.size ()
|
||||
const uint160 nextAccountID = spSourcePath.size ()
|
||||
? spSourcePath.getElement (0).getAccountID ()
|
||||
: !!uOutCurrencyID
|
||||
? uOutIssuerID == uReceiverID
|
||||
@@ -452,12 +452,12 @@ void PathState::setExpanded (
|
||||
" uMaxIssuerID=" << RippleAddress::createHumanAccountID (uMaxIssuerID) <<
|
||||
" uSenderIssuerID=" << RippleAddress::createHumanAccountID (uSenderIssuerID) <<
|
||||
" uNxtCurrencyID=" << STAmount::createHumanCurrency (uNxtCurrencyID) <<
|
||||
" uNxtAccountID=" << RippleAddress::createHumanAccountID (uNxtAccountID);
|
||||
" nextAccountID=" << RippleAddress::createHumanAccountID (nextAccountID);
|
||||
|
||||
// Can't just use push implied, because it can't compensate for next account.
|
||||
if (!uNxtCurrencyID // Next is XRP, offer next. Must go through issuer.
|
||||
|| uMaxCurrencyID != uNxtCurrencyID // Next is different currency, offer next...
|
||||
|| uMaxIssuerID != uNxtAccountID) // Next is not implied issuer
|
||||
|| uMaxIssuerID != nextAccountID) // Next is not implied issuer
|
||||
{
|
||||
WriteLog (lsDEBUG, RippleCalc) << "setExpanded: sender implied:" <<
|
||||
" account=" << RippleAddress::createHumanAccountID (uMaxIssuerID) <<
|
||||
@@ -481,18 +481,18 @@ void PathState::setExpanded (
|
||||
{
|
||||
WriteLog (lsTRACE, RippleCalc) << "setExpanded: element in path";
|
||||
terStatus = pushNode (
|
||||
speElement.getNodeType (), speElement.getAccountID (),
|
||||
speElement.getNodeType (), speElement.getAccountID (),
|
||||
speElement.getCurrency (), speElement.getIssuerID ());
|
||||
}
|
||||
}
|
||||
|
||||
const Node& pnPrv = vpnNodes.back ();
|
||||
const Node& previousNode = vpnNodes.back ();
|
||||
|
||||
if (tesSUCCESS == terStatus
|
||||
&& !!uOutCurrencyID // Next is not XRP
|
||||
&& uOutIssuerID != uReceiverID // Out issuer is not receiver
|
||||
&& (pnPrv.uCurrencyID != uOutCurrencyID // Previous will be an offer.
|
||||
|| pnPrv.uAccountID != uOutIssuerID)) // Need the implied issuer.
|
||||
&& (previousNode.uCurrencyID != uOutCurrencyID // Previous will be an offer.
|
||||
|| previousNode.uAccountID != uOutIssuerID)) // Need the implied issuer.
|
||||
{
|
||||
// Add implied account.
|
||||
WriteLog (lsDEBUG, RippleCalc) << "setExpanded: receiver implied:" <<
|
||||
@@ -530,11 +530,13 @@ void PathState::setExpanded (
|
||||
|
||||
const unsigned int uNodes = vpnNodes.size ();
|
||||
|
||||
for (unsigned int uNode = 0; tesSUCCESS == terStatus && uNode != uNodes; ++uNode)
|
||||
for (unsigned int nodeIndex = 0; tesSUCCESS == terStatus && nodeIndex != uNodes; ++nodeIndex)
|
||||
{
|
||||
const Node& pnCur = vpnNodes[uNode];
|
||||
const Node& node = vpnNodes[nodeIndex];
|
||||
|
||||
if (!umForward.insert (std::make_pair (std::make_tuple (pnCur.uAccountID, pnCur.uCurrencyID, pnCur.uIssuerID), uNode)).second)
|
||||
AccountCurrencyIssuer aci(
|
||||
node.uAccountID, node.uCurrencyID, node.uIssuerID);
|
||||
if (!umForward.insert (std::make_pair (aci, nodeIndex)).second)
|
||||
{
|
||||
// Failed to insert. Have a loop.
|
||||
WriteLog (lsDEBUG, RippleCalc) <<
|
||||
@@ -589,7 +591,7 @@ void PathState::setCanonical (
|
||||
const uint160 uOutCurrencyID = saOutAct.getCurrency ();
|
||||
const uint160 uOutIssuerID = saOutAct.getIssuer ();
|
||||
|
||||
unsigned int uNode = 0;
|
||||
unsigned int nodeIndex = 0;
|
||||
|
||||
unsigned int uEnd = psExpanded.vpnNodes.size (); // The node, indexed by 0, not to include.
|
||||
|
||||
@@ -600,7 +602,7 @@ void PathState::setCanonical (
|
||||
uint160 uIssuerID = uMaxIssuerID;
|
||||
|
||||
// Node 0 is a composite of the sending account and saInAct.
|
||||
++uNode; // skip node 0
|
||||
++nodeIndex; // skip node 0
|
||||
|
||||
// Last node is implied: Always skip last node
|
||||
--uEnd; // skip last node
|
||||
@@ -609,21 +611,21 @@ void PathState::setCanonical (
|
||||
// - currency is always the same as vpnNodes[0].
|
||||
#if 1
|
||||
|
||||
if (uNode != uEnd && uMaxIssuerID != uAccountID)
|
||||
if (nodeIndex != uEnd && uMaxIssuerID != uAccountID)
|
||||
{
|
||||
// saInAct issuer is not the sender. This forces an implied node.
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: in diff: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: in diff: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
|
||||
|
||||
// skip node 1
|
||||
|
||||
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
|
||||
uIssuerID = psExpanded.vpnNodes[nodeIndex].uIssuerID;
|
||||
|
||||
++uNode;
|
||||
++nodeIndex;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (uNode != uEnd)
|
||||
if (nodeIndex != uEnd)
|
||||
{
|
||||
// Have another node
|
||||
bool bKeep = false;
|
||||
@@ -656,17 +658,17 @@ void PathState::setCanonical (
|
||||
|
||||
if (bKeep)
|
||||
{
|
||||
uCurrencyID = psExpanded.vpnNodes[uNode].uCurrencyID;
|
||||
uIssuerID = psExpanded.vpnNodes[uNode].uIssuerID;
|
||||
++uNode; // Keep it.
|
||||
uCurrencyID = psExpanded.vpnNodes[nodeIndex].uCurrencyID;
|
||||
uIssuerID = psExpanded.vpnNodes[nodeIndex].uIssuerID;
|
||||
++nodeIndex; // Keep it.
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (uNode != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
|
||||
if (nodeIndex != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
|
||||
{
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out diff: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out diff: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
|
||||
// The next to last node is saOutAct if an issuer different from receiver is supplied.
|
||||
// The next to last node can be implied.
|
||||
|
||||
@@ -675,33 +677,33 @@ void PathState::setCanonical (
|
||||
|
||||
const Node& pnEnd = psExpanded.vpnNodes[uEnd];
|
||||
|
||||
if (uNode != uEnd
|
||||
if (nodeIndex != uEnd
|
||||
&& !pnEnd.uAccountID && pnEnd.uCurrencyID == uOutCurrencyID && pnEnd.uIssuerID == uOutIssuerID)
|
||||
{
|
||||
// The current end node is an offer converting to saOutAct's currency and issuer and can be implied.
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out offer: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out offer: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
|
||||
|
||||
--uEnd;
|
||||
}
|
||||
|
||||
// Do not include uEnd.
|
||||
for (; uNode != uEnd; ++uNode)
|
||||
for (; nodeIndex != uEnd; ++nodeIndex)
|
||||
{
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: loop: uNode=%d uEnd=%d") % uNode % uEnd);
|
||||
const Node& pnPrv = psExpanded.vpnNodes[uNode - 1];
|
||||
const Node& pnCur = psExpanded.vpnNodes[uNode];
|
||||
const Node& pnNxt = psExpanded.vpnNodes[uNode + 1];
|
||||
// WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: loop: nodeIndex=%d uEnd=%d") % nodeIndex % uEnd);
|
||||
const Node& previousNode = psExpanded.vpnNodes[nodeIndex - 1];
|
||||
const Node& node = psExpanded.vpnNodes[nodeIndex];
|
||||
const Node& nextNode = psExpanded.vpnNodes[nodeIndex + 1];
|
||||
|
||||
const bool bCurAccount = is_bit_set (pnCur.uFlags, STPathElement::typeAccount);
|
||||
const bool nodeIsAccount = is_bit_set (node.uFlags, STPathElement::typeAccount);
|
||||
|
||||
bool bSkip = false;
|
||||
|
||||
if (bCurAccount)
|
||||
if (nodeIsAccount)
|
||||
{
|
||||
// Currently at an account.
|
||||
|
||||
// Output is non-XRP and issuer is account.
|
||||
if (!!pnCur.uCurrencyID && pnCur.uIssuerID == pnCur.uAccountID)
|
||||
if (!!node.uCurrencyID && node.uIssuerID == node.uAccountID)
|
||||
{
|
||||
// Account issues itself.
|
||||
// XXX Not good enough. Previous account must mention it.
|
||||
@@ -712,11 +714,11 @@ void PathState::setCanonical (
|
||||
else
|
||||
{
|
||||
// Currently at an offer.
|
||||
const bool bPrvAccount = is_bit_set (pnPrv.uFlags, STPathElement::typeAccount);
|
||||
const bool bNxtAccount = is_bit_set (pnNxt.uFlags, STPathElement::typeAccount);
|
||||
const bool bPrvAccount = is_bit_set (previousNode.uFlags, STPathElement::typeAccount);
|
||||
const bool bNxtAccount = is_bit_set (nextNode.uFlags, STPathElement::typeAccount);
|
||||
|
||||
if (bPrvAccount && bNxtAccount // Offer surrounded by accounts.
|
||||
&& pnPrv.uCurrencyID != pnNxt.uCurrencyID)
|
||||
&& previousNode.uCurrencyID != nextNode.uCurrencyID)
|
||||
{
|
||||
// Offer can be implied by currency change.
|
||||
// XXX What about issuer?
|
||||
@@ -730,26 +732,25 @@ void PathState::setCanonical (
|
||||
// Copy node
|
||||
Node pnNew;
|
||||
|
||||
bool bSetAccount = bCurAccount;
|
||||
bool bSetCurrency = uCurrencyID != pnCur.uCurrencyID;
|
||||
bool bSetCurrency = (uCurrencyID != node.uCurrencyID);
|
||||
// XXX What if we need the next account because we want to skip it?
|
||||
bool bSetIssuer = !uCurrencyID && uIssuerID != pnCur.uIssuerID;
|
||||
bool bSetIssuer = !uCurrencyID && (uIssuerID != node.uIssuerID);
|
||||
|
||||
pnNew.uFlags = (bSetAccount ? STPathElement::typeAccount : 0)
|
||||
pnNew.uFlags = (nodeIsAccount ? STPathElement::typeAccount : 0)
|
||||
| (bSetCurrency ? STPathElement::typeCurrency : 0)
|
||||
| (bSetIssuer ? STPathElement::typeIssuer : 0);
|
||||
|
||||
if (bSetAccount)
|
||||
pnNew.uAccountID = pnCur.uAccountID;
|
||||
if (nodeIsAccount)
|
||||
pnNew.uAccountID = node.uAccountID;
|
||||
|
||||
if (bSetCurrency)
|
||||
{
|
||||
pnNew.uCurrencyID = pnCur.uCurrencyID;
|
||||
pnNew.uCurrencyID = node.uCurrencyID;
|
||||
uCurrencyID = pnNew.uCurrencyID;
|
||||
}
|
||||
|
||||
if (bSetIssuer)
|
||||
pnNew.uIssuerID = pnCur.uIssuerID;
|
||||
pnNew.uIssuerID = node.uIssuerID;
|
||||
|
||||
// XXX ^^^ What about setting uIssuerID?
|
||||
|
||||
|
||||
@@ -23,11 +23,13 @@
|
||||
namespace ripple {
|
||||
|
||||
// account id, currency id, issuer id :: node
|
||||
typedef std::tuple <uint160, uint160, uint160> aciSource;
|
||||
typedef ripple::unordered_map <aciSource, unsigned int> curIssuerNode; // Map of currency, issuer to node index.
|
||||
typedef ripple::unordered_map <aciSource, unsigned int>::const_iterator curIssuerNodeConstIterator;
|
||||
typedef std::tuple <uint160, uint160, uint160> AccountCurrencyIssuer;
|
||||
|
||||
extern std::size_t hash_value (const aciSource& asValue);
|
||||
// Map of currency, issuer to node index.
|
||||
typedef ripple::unordered_map <AccountCurrencyIssuer, unsigned int>
|
||||
AccountCurrencyIssuerToNodeIndex;
|
||||
|
||||
extern std::size_t hash_value (const AccountCurrencyIssuer& asValue);
|
||||
|
||||
// Holds a path state under incremental application.
|
||||
class PathState : public CountedObject <PathState>
|
||||
@@ -89,11 +91,10 @@ public:
|
||||
STAmount saTakerGets;
|
||||
|
||||
};
|
||||
public:
|
||||
typedef boost::shared_ptr<PathState> pointer;
|
||||
typedef const boost::shared_ptr<PathState>& ref;
|
||||
|
||||
public:
|
||||
typedef std::shared_ptr<PathState> pointer;
|
||||
typedef const std::shared_ptr<PathState>& ref;
|
||||
|
||||
PathState* setIndex (const int iIndex)
|
||||
{
|
||||
mIndex = iIndex;
|
||||
@@ -131,27 +132,12 @@ public:
|
||||
void checkNoRipple (uint160 const& destinationAccountID, uint160 const& sourceAccountID);
|
||||
void checkNoRipple (uint160 const&, uint160 const&, uint160 const&, uint160 const&);
|
||||
|
||||
void setCanonical (
|
||||
const PathState& psExpanded
|
||||
);
|
||||
void setCanonical (const PathState& psExpanded);
|
||||
|
||||
Json::Value getJson () const;
|
||||
|
||||
#if 0
|
||||
static PathState::pointer createCanonical (
|
||||
PathState& ref pspExpanded
|
||||
)
|
||||
{
|
||||
PathState::pointer pspNew = boost::make_shared<PathState> (pspExpanded->saOutAct, pspExpanded->saInAct);
|
||||
|
||||
pspNew->setCanonical (pspExpanded);
|
||||
|
||||
return pspNew;
|
||||
}
|
||||
#endif
|
||||
static bool lessPriority (PathState& lhs, PathState& rhs);
|
||||
|
||||
public:
|
||||
TER terStatus;
|
||||
std::vector<Node> vpnNodes;
|
||||
|
||||
@@ -160,11 +146,11 @@ public:
|
||||
|
||||
// First time scanning foward, as part of path contruction, a funding source was mentioned for accounts. Source may only be
|
||||
// used there.
|
||||
curIssuerNode umForward; // Map of currency, issuer to node index.
|
||||
AccountCurrencyIssuerToNodeIndex umForward;
|
||||
|
||||
// First time working in reverse a funding source was used.
|
||||
// Source may only be used there if not mentioned by an account.
|
||||
curIssuerNode umReverse; // Map of currency, issuer to node index.
|
||||
AccountCurrencyIssuerToNodeIndex umReverse;
|
||||
|
||||
LedgerEntrySet lesEntries;
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "Calculators.h"
|
||||
|
||||
namespace ripple {
|
||||
|
||||
SETUP_LOG (Pathfinder)
|
||||
@@ -244,7 +246,7 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath)
|
||||
std::vector<PathState::pointer> vpsExpanded;
|
||||
LedgerEntrySet lesSandbox (mLedger, tapNONE);
|
||||
|
||||
TER result = RippleCalc::rippleCalc (
|
||||
TER result = rippleCalculate (
|
||||
lesSandbox,
|
||||
saMaxAmountAct,
|
||||
saDstAmountAct,
|
||||
@@ -290,13 +292,13 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath)
|
||||
|
||||
spsPaths.addPath (spCurrent); // Just checking the current path.
|
||||
|
||||
TER terResult;
|
||||
TER errorCode;
|
||||
|
||||
try
|
||||
{
|
||||
LedgerEntrySet lesSandbox (mLedger, tapNONE);
|
||||
|
||||
terResult = RippleCalc::rippleCalc (
|
||||
errorCode = rippleCalculate (
|
||||
lesSandbox,
|
||||
saMaxAmountAct, // --> computed input
|
||||
saDstAmountAct, // --> computed output
|
||||
@@ -315,13 +317,13 @@ STPathSet Pathfinder::filterPaths(int iMaxPaths, STPath& extraPath)
|
||||
{
|
||||
WriteLog (lsINFO, Pathfinder) << "findPaths: Caught throw: " << e.what ();
|
||||
|
||||
terResult = tefEXCEPTION;
|
||||
errorCode = tefEXCEPTION;
|
||||
}
|
||||
|
||||
if (tesSUCCESS != terResult)
|
||||
if (errorCode != tesSUCCESS)
|
||||
{
|
||||
WriteLog (lsDEBUG, Pathfinder) <<
|
||||
"findPaths: dropping: " << transToken (terResult) <<
|
||||
"findPaths: dropping: " << transToken (errorCode) <<
|
||||
": " << spCurrent.getJson (0);
|
||||
}
|
||||
else if (saDstAmountAct < saMinDstAmount)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -28,100 +28,24 @@ namespace ripple {
|
||||
input required to produce a given output along a specified path.
|
||||
*/
|
||||
|
||||
// TODO(vfalco) What's the difference between a RippleState versus PathState?
|
||||
//
|
||||
class RippleCalc
|
||||
{
|
||||
private:
|
||||
// First time working in reverse a funding source was mentioned. Source may
|
||||
// only be used there.
|
||||
//
|
||||
// Map of currency, issuer to node index.
|
||||
curIssuerNode mumSource;
|
||||
|
||||
// If the transaction fails to meet some constraint, still need to delete
|
||||
// unfunded offers.
|
||||
//
|
||||
// Offers that were found unfunded.
|
||||
unordered_set<uint256> mUnfundedOffers;
|
||||
|
||||
void pathNext (
|
||||
PathState::ref psrCur, const bool bMultiQuality,
|
||||
const LedgerEntrySet& lesCheckpoint, LedgerEntrySet& lesCurrent);
|
||||
|
||||
TER calcNode (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeRev (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeFwd (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferRev (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeOfferFwd (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountRev (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAccountFwd (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality);
|
||||
TER calcNodeAdvance (
|
||||
const unsigned int uNode, PathState& psCur, const bool bMultiQuality,
|
||||
const bool bReverse);
|
||||
|
||||
TER calcNodeDeliverRev (
|
||||
const unsigned int uNode,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uOutAccountID,
|
||||
const STAmount& saOutReq,
|
||||
STAmount& saOutAct);
|
||||
|
||||
TER calcNodeDeliverFwd (
|
||||
const unsigned int uNode,
|
||||
PathState& psCur,
|
||||
const bool bMultiQuality,
|
||||
const uint160& uInAccountID,
|
||||
const STAmount& saInReq,
|
||||
STAmount& saInAct,
|
||||
STAmount& saInFees);
|
||||
|
||||
void calcNodeRipple (
|
||||
const std::uint32_t uQualityIn, const std::uint32_t uQualityOut,
|
||||
const STAmount& saPrvReq, const STAmount& saCurReq,
|
||||
STAmount& saPrvAct, STAmount& saCurAct, std::uint64_t& uRateMax);
|
||||
|
||||
RippleCalc (LedgerEntrySet& activeLedger, const bool bOpenLedger)
|
||||
: mActiveLedger (activeLedger), mOpenLedger (bOpenLedger)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
static TER rippleCalc (
|
||||
LedgerEntrySet& lesActive,
|
||||
STAmount& saMaxAmountAct,
|
||||
STAmount& saDstAmountAct,
|
||||
std::vector<PathState::pointer>& vpsExpanded,
|
||||
const STAmount& saDstAmountReq,
|
||||
const STAmount& saMaxAmountReq,
|
||||
const uint160& uDstAccountID,
|
||||
const uint160& uSrcAccountID,
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect,
|
||||
// --> True, not to affect accounts.
|
||||
const bool bStandAlone,
|
||||
// --> What kind of errors to return.
|
||||
const bool bOpenLedger = true
|
||||
);
|
||||
|
||||
static void setCanonical (
|
||||
STPathSet& spsDst, const std::vector<PathState::pointer>& vpsExpanded,
|
||||
bool bKeepDefault);
|
||||
|
||||
private:
|
||||
LedgerEntrySet& mActiveLedger;
|
||||
bool mOpenLedger;
|
||||
};
|
||||
TER rippleCalculate (
|
||||
LedgerEntrySet& lesActive,
|
||||
STAmount& saMaxAmountAct,
|
||||
STAmount& saDstAmountAct,
|
||||
std::vector<PathState::pointer>& vpsExpanded,
|
||||
const STAmount& saDstAmountReq,
|
||||
const STAmount& saMaxAmountReq,
|
||||
const uint160& uDstAccountID,
|
||||
const uint160& uSrcAccountID,
|
||||
const STPathSet& spsPaths,
|
||||
const bool bPartialPayment,
|
||||
const bool bLimitQuality,
|
||||
const bool bNoRippleDirect,
|
||||
// --> True, not to affect accounts.
|
||||
const bool bStandAlone,
|
||||
// --> What kind of errors to return.
|
||||
const bool bOpenLedger = true
|
||||
);
|
||||
|
||||
} // ripple
|
||||
|
||||
|
||||
31
src/ripple_app/paths/Tuning.h
Normal file
31
src/ripple_app/paths/Tuning.h
Normal file
@@ -0,0 +1,31 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_APP_PATHS_TUNING_H
|
||||
#define RIPPLE_APP_PATHS_TUNING_H
|
||||
|
||||
namespace ripple {
|
||||
|
||||
// VFALCO TODO Why 40? is the number 40 part of protocol?
|
||||
const int CALC_NODE_DELIVER_MAX_LOOPS = 40;
|
||||
const int NODE_ADVANCE_MAX_LOOPS = 100;
|
||||
|
||||
} // ripple
|
||||
|
||||
#endif
|
||||
@@ -243,7 +243,7 @@ TER PaymentTransactor::doApply ()
|
||||
|
||||
terResult = openLedger && tooManyPaths
|
||||
? telBAD_PATH_COUNT // Too many paths for proposed ledger.
|
||||
: RippleCalc::rippleCalc (
|
||||
: rippleCalculate (
|
||||
mEngine->view (),
|
||||
maxSourceAmountAct,
|
||||
saDstAmountAct,
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
namespace ripple {
|
||||
|
||||
// This interface is deprecated.
|
||||
Json::Value RPCHandler::doRipplePathFind (Json::Value params, Resource::Charge& loadType, Application::ScopedLockType& masterLockHolder)
|
||||
Json::Value RPCHandler::doRipplePathFind (
|
||||
Json::Value params, Resource::Charge& loadType,
|
||||
Application::ScopedLockType& masterLockHolder)
|
||||
{
|
||||
masterLockHolder.unlock ();
|
||||
|
||||
@@ -217,7 +219,7 @@ Json::Value RPCHandler::doRipplePathFind (Json::Value params, Resource::Charge&
|
||||
LedgerEntrySet lesSandbox (lpLedger, tapNONE);
|
||||
|
||||
TER terResult =
|
||||
RippleCalc::rippleCalc (
|
||||
rippleCalculate (
|
||||
lesSandbox,
|
||||
saMaxAmountAct, // <--
|
||||
saDstAmountAct, // <--
|
||||
@@ -249,7 +251,7 @@ Json::Value RPCHandler::doRipplePathFind (Json::Value params, Resource::Charge&
|
||||
spsComputed.addPath(extraPath);
|
||||
vpsExpanded.clear ();
|
||||
lesSandbox.clear ();
|
||||
terResult = RippleCalc::rippleCalc (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
terResult = rippleCalculate (lesSandbox, saMaxAmountAct, saDstAmountAct,
|
||||
vpsExpanded, saMaxAmount, saDstAmount,
|
||||
raDst.getAccountID (), raSrc.getAccountID (),
|
||||
spsComputed, false, false, false, true);
|
||||
|
||||
Reference in New Issue
Block a user