Fixes for rippling through offers.

This commit is contained in:
Arthur Britto
2012-11-08 15:17:46 -08:00
parent d2eea88cd8
commit ca0ab6c01d
2 changed files with 45 additions and 25 deletions

View File

@@ -42,7 +42,7 @@ TER RippleCalc::calcNodeAdvance(
const uint160& uCurIssuerID = pnCur.uIssuerID; const uint160& uCurIssuerID = pnCur.uIssuerID;
uint256& uDirectTip = pnCur.uDirectTip; uint256& uDirectTip = pnCur.uDirectTip;
uint256 uDirectEnd = pnCur.uDirectEnd; uint256& uDirectEnd = pnCur.uDirectEnd;
bool& bDirectAdvance = pnCur.bDirectAdvance; bool& bDirectAdvance = pnCur.bDirectAdvance;
SLE::pointer& sleDirectDir = pnCur.sleDirectDir; SLE::pointer& sleDirectDir = pnCur.sleDirectDir;
STAmount& saOfrRate = pnCur.saOfrRate; STAmount& saOfrRate = pnCur.saOfrRate;
@@ -63,12 +63,13 @@ TER RippleCalc::calcNodeAdvance(
{ {
bool bDirectDirDirty = false; bool bDirectDirDirty = false;
if (!uDirectEnd) if (!uDirectTip)
{ {
// Need to initialize current node. // Need to initialize current node.
uDirectTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID); uDirectTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID);
uDirectEnd = Ledger::getQualityNext(uDirectTip); uDirectEnd = Ledger::getQualityNext(uDirectTip);
sleDirectDir = lesActive.entryCache(ltDIR_NODE, uDirectTip); sleDirectDir = lesActive.entryCache(ltDIR_NODE, uDirectTip);
bDirectAdvance = !sleDirectDir; bDirectAdvance = !sleDirectDir;
bDirectDirDirty = true; bDirectDirDirty = true;
@@ -100,7 +101,7 @@ TER RippleCalc::calcNodeAdvance(
else else
{ {
// No more offers. Should be done rather than fall off end of book. // No more offers. Should be done rather than fall off end of book.
cLog(lsINFO) << "calcNodeAdvance: Unreachable: Fell off end of order book."; cLog(lsWARNING) << "calcNodeAdvance: Unreachable: Fell off end of order book.";
assert(false); assert(false);
terResult = tefEXCEPTION; terResult = tefEXCEPTION;
@@ -148,7 +149,7 @@ TER RippleCalc::calcNodeAdvance(
} }
else if (!bReverse) else if (!bReverse)
{ {
cLog(lsINFO) << boost::str(boost::format("calcNodeAdvance: unreachable: ran out of offers")); cLog(lsWARNING) << boost::str(boost::format("calcNodeAdvance: unreachable: ran out of offers"));
assert(false); // Can't run out of offers in forward direction. assert(false); // Can't run out of offers in forward direction.
terResult = tefEXCEPTION; terResult = tefEXCEPTION;
} }
@@ -175,6 +176,8 @@ TER RippleCalc::calcNodeAdvance(
// Allowed to access source from this node? // 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 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.
curIssuerNodeConstIterator itForward = pspCur->umForward.find(asLine); curIssuerNodeConstIterator itForward = pspCur->umForward.find(asLine);
const bool bFoundForward = itForward != pspCur->umForward.end(); const bool bFoundForward = itForward != pspCur->umForward.end();
@@ -263,10 +266,9 @@ TER RippleCalc::calcNodeAdvance(
return terResult; return terResult;
} }
// Between offer nodes, the fee charged may vary. Therefore, process one inbound offer at a time. // Between offer nodes, the fee charged may vary. Therefore, process one inbound offer at a time. Propagate the inbound offer's
// Propagate the inbound offer's requirements to the previous node. The previous node adjusts the amount output and the // requirements to the previous node. The previous node adjusts the amount output and the amount spent on fees. Continue process
// amount spent on fees. // till request is satisified while we the rate does not increase past the initial rate.
// Continue process till request is satisified while we the rate does not increase past the initial rate.
TER RippleCalc::calcNodeDeliverRev( TER RippleCalc::calcNodeDeliverRev(
const unsigned int uIndex, // 0 < uIndex < uLast const unsigned int uIndex, // 0 < uIndex < uLast
PathState::ref pspCur, PathState::ref pspCur,
@@ -284,8 +286,13 @@ TER RippleCalc::calcNodeDeliverRev(
const uint160& uPrvAccountID = pnPrv.uAccountID; const uint160& uPrvAccountID = pnPrv.uAccountID;
const STAmount& saTransferRate = pnCur.saTransferRate; const STAmount& saTransferRate = pnCur.saTransferRate;
STAmount& saPrvDlvReq = pnPrv.saRevDeliver; // To be adjusted. STAmount& saPrvDlvReq = pnPrv.saRevDeliver; // To be set.
uint256& uDirectTip = pnCur.uDirectTip;
uDirectTip = 0; // Restart book searching.
saPrvDlvReq.zero(pnPrv.uCurrencyID, pnPrv.uIssuerID);
saOutAct.zero(saOutReq); saOutAct.zero(saOutReq);
while (saOutAct != saOutReq) // Did not deliver limit. while (saOutAct != saOutReq) // Did not deliver limit.
@@ -310,8 +317,8 @@ TER RippleCalc::calcNodeDeliverRev(
} }
const STAmount saOutFeeRate = uOfrOwnerID == uCurIssuerID || uOutAccountID == uCurIssuerID // Issuer receiving or sending. const STAmount saOutFeeRate = uOfrOwnerID == uCurIssuerID || uOutAccountID == uCurIssuerID // Issuer receiving or sending.
? saOne // No fee. ? saOne // No fee.
: saTransferRate; // Transfer rate of issuer. : saTransferRate; // Transfer rate of issuer.
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: uOfrOwnerID=%s uOutAccountID=%s uCurIssuerID=%s saTransferRate=%s saOutFeeRate=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: uOfrOwnerID=%s uOutAccountID=%s uCurIssuerID=%s saTransferRate=%s saOutFeeRate=%s")
% RippleAddress::createHumanAccountID(uOfrOwnerID) % RippleAddress::createHumanAccountID(uOfrOwnerID)
% RippleAddress::createHumanAccountID(uOutAccountID) % RippleAddress::createHumanAccountID(uOutAccountID)
@@ -328,19 +335,18 @@ TER RippleCalc::calcNodeDeliverRev(
% saRateMax % saRateMax
% saOutFeeRate); % saOutFeeRate);
} }
else if (saRateMax < saOutFeeRate) else if (saOutFeeRate > saRateMax)
{ {
// Offer exceeds initial rate. // Offer exceeds initial rate.
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: Offer exceeds initial rate: saRateMax=%s saOutFeeRate=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: Offer exceeds initial rate: saRateMax=%s saOutFeeRate=%s")
% saRateMax % saRateMax
% saOutFeeRate); % saOutFeeRate);
nothing(); break; // Done. Don't bother looking for smaller saTransferRates.
break;
} }
else if (saOutFeeRate < saRateMax) else if (saOutFeeRate < saRateMax)
{ {
// Reducing rate. // Reducing rate. Additional offers will only considered for this increment if they are at least this good.
saRateMax = saOutFeeRate; saRateMax = saOutFeeRate;
@@ -348,7 +354,10 @@ TER RippleCalc::calcNodeDeliverRev(
% saRateMax); % saRateMax);
} }
// Amount that goes to the taker.
STAmount saOutPass = std::min(std::min(saOfferFunds, saTakerGets), saOutReq-saOutAct); // Offer maximum out - assuming no out fees. STAmount saOutPass = std::min(std::min(saOfferFunds, saTakerGets), saOutReq-saOutAct); // Offer maximum out - assuming no out fees.
// 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.
STAmount saOutPlusFees = STAmount::multiply(saOutPass, saOutFeeRate); // Offer out with fees. STAmount saOutPlusFees = STAmount::multiply(saOutPass, saOutFeeRate); // Offer out with fees.
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saOutReq=%s saOutAct=%s saTakerGets=%s saOutPass=%s saOutPlusFees=%s saOfferFunds=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverRev: saOutReq=%s saOutAct=%s saTakerGets=%s saOutPass=%s saOutPlusFees=%s saOfferFunds=%s")
@@ -372,7 +381,7 @@ TER RippleCalc::calcNodeDeliverRev(
% saOfferFunds); % saOfferFunds);
} }
// Compute portion of input needed to cover output. // Compute portion of input needed to cover actual output.
STAmount saInPassReq = STAmount::multiply(saOutPass, saOfrRate, saTakerPays); STAmount saInPassReq = STAmount::multiply(saOutPass, saOfrRate, saTakerPays);
STAmount saInPassAct; STAmount saInPassAct;
@@ -387,6 +396,7 @@ TER RippleCalc::calcNodeDeliverRev(
if (!!uPrvAccountID) if (!!uPrvAccountID)
{ {
// account --> OFFER --> ? // 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 receiver is an offer, so no fee or quality.
// Previous is the issuer and has unlimited funds. // Previous is the issuer and has unlimited funds.
// Offer owner is obtaining IOUs via an offer, so credit line limits are ignored. // Offer owner is obtaining IOUs via an offer, so credit line limits are ignored.
@@ -400,6 +410,7 @@ TER RippleCalc::calcNodeDeliverRev(
else else
{ {
// offer --> OFFER --> ? // offer --> OFFER --> ?
// Chain and compute the previous offer now.
terResult = calcNodeDeliverRev( terResult = calcNodeDeliverRev(
uIndex-1, uIndex-1,
@@ -430,7 +441,10 @@ TER RippleCalc::calcNodeDeliverRev(
// Funds were spent. // Funds were spent.
bFundsDirty = true; bFundsDirty = true;
// Deduct output, don't actually need to send. // 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.
lesActive.accountSend(uOfrOwnerID, uCurIssuerID, saOutPass); lesActive.accountSend(uOfrOwnerID, uCurIssuerID, saOutPass);
// Adjust offer // Adjust offer
@@ -482,8 +496,12 @@ TER RippleCalc::calcNodeDeliverFwd(
STAmount& saCurDeliverAct = pnCur.saFwdDeliver; STAmount& saCurDeliverAct = pnCur.saFwdDeliver;
saInAct = 0; uint256& uDirectTip = pnCur.uDirectTip;
saInFees = 0;
uDirectTip = 0; // Restart book searching.
saInAct.zero(saInFunds);
saInFees.zero(saInFunds);
while (tesSUCCESS == terResult while (tesSUCCESS == terResult
&& saInAct != saInReq // Did not deliver limit. && saInAct != saInReq // Did not deliver limit.
@@ -503,10 +521,9 @@ TER RippleCalc::calcNodeDeliverFwd(
STAmount& saTakerPays = pnCur.saTakerPays; STAmount& saTakerPays = pnCur.saTakerPays;
STAmount& saTakerGets = pnCur.saTakerGets; STAmount& saTakerGets = pnCur.saTakerGets;
const STAmount saInFeeRate = uInAccountID == uPrvIssuerID || uOfrOwnerID == uPrvIssuerID // Issuer receiving or sending.
const STAmount saInFeeRate = uInAccountID == uPrvIssuerID || uOfrOwnerID == uPrvIssuerID // Issuer receiving or sending. ? saOne // No fee.
? saOne // No fee. : saTransferRate; // Transfer rate of issuer.
: saTransferRate; // Transfer rate of issuer.
// //
// First calculate assuming no output fees. // First calculate assuming no output fees.
@@ -519,8 +536,8 @@ TER RippleCalc::calcNodeDeliverFwd(
STAmount saInPassAct = STAmount::divide(saInSum, saInFeeRate); // In without fees. STAmount saInPassAct = STAmount::divide(saInSum, saInFeeRate); // In without fees.
STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate, saOutFunded); // Out. STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate, saOutFunded); // Out.
STAmount saInPassFees; STAmount saInPassFees(saInFunds.getCurrency(), saInFunds.getIssuer());
STAmount saOutPassAct; STAmount saOutPassAct(saOfferFunds.getCurrency(), saOfferFunds.getIssuer());
cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: saOutFunded=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s") cLog(lsINFO) << boost::str(boost::format("calcNodeDeliverFwd: saOutFunded=%s saInFunded=%s saInTotal=%s saInSum=%s saInPassAct=%s saOutPassMax=%s")
% saOutFunded % saOutFunded
@@ -1483,6 +1500,7 @@ TER PathState::pushImply(
} }
// Append a node and insert before it any implied nodes. // Append a node and insert before it any implied nodes.
// Offers may go back to back.
// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_LINE, tepPATH_DRY // <-- terResult: tesSUCCESS, temBAD_PATH, terNO_LINE, tepPATH_DRY
TER PathState::pushNode( TER PathState::pushNode(
const int iType, const int iType,

View File

@@ -296,6 +296,8 @@ public:
// Zero while copying currency and issuer. // Zero while copying currency and issuer.
STAmount* zero(const STAmount& saTmpl) STAmount* zero(const STAmount& saTmpl)
{ mCurrency = saTmpl.mCurrency; mIssuer = saTmpl.mIssuer; mIsNative = saTmpl.mIsNative; return zero(); } { mCurrency = saTmpl.mCurrency; mIssuer = saTmpl.mIssuer; mIsNative = saTmpl.mIsNative; return zero(); }
STAmount* zero(const uint160& uCurrencyID, const uint160& uIssuerID)
{ mCurrency = uCurrencyID; mIssuer = uIssuerID; mIsNative = !uCurrencyID; return zero(); }
int compare(const STAmount&) const; int compare(const STAmount&) const;