From 8d7773dcd63af57f181a9a8d2945448f702f8e60 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 1 Sep 2012 17:23:40 -0700 Subject: [PATCH 01/17] Remove unused variable. --- src/RPCServer.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 58f74143fc..5c47ed30a3 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1341,7 +1341,6 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) STAmount saDstAmount; uint160 uDstCurrencyID; - std::vector vpnPath; STPathSet spsPaths; naSrcIssuerID.setAccountID(params[4u].asString()); // From 60653a5108531e1d2fefa291cb1920552174f21d Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 1 Sep 2012 17:24:12 -0700 Subject: [PATCH 02/17] Add STAmount divide and multiply shortcuts. --- src/SerializedTypes.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 1017f758a3..065c6895d7 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -353,7 +353,16 @@ public: friend STAmount operator-(const STAmount& v1, const STAmount& v2); static STAmount divide(const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID); + static STAmount divide(const STAmount& v1, const STAmount& v2, const STAmount& saUnit) + { return divide(v1, v2, saUnit.getCurrency(), saUnit.getIssuer()); } + static STAmount divide(const STAmount& v1, const STAmount& v2) + { return divide(v1, v2, v1); } + static STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID); + static STAmount multiply(const STAmount& v1, const STAmount& v2, const STAmount& saUnit) + { return multiply(v1, v2, saUnit.getCurrency(), saUnit.getIssuer()); } + static STAmount multiply(const STAmount& v1, const STAmount& v2) + { return multiply(v1, v2, v1); } // Someone is offering X for Y, what is the rate? // Rate: smaller is better, the taker wants the most out: in/out From 93296048367cfef2b6ef873a6478c2782d632be3 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sat, 1 Sep 2012 17:24:14 -0700 Subject: [PATCH 03/17] Restructure forward ripple through offers. --- src/TransactionEngine.cpp | 1555 +++++++++++++++++++------------------ src/TransactionEngine.h | 51 +- 2 files changed, 823 insertions(+), 783 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index a99b4088fa..b7cc2200d2 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -1755,177 +1755,7 @@ TER TransactionEngine::doPasswordSet(const SerializedTransaction& txn) return terResult; } -#if 0 -// XXX Need to adjust for fees. -// Find offers to satisfy pnDst. -// - Does not adjust any balances as there is at least a forward pass to come. -// --> pnDst.saWanted: currency and amount wanted -// --> pnSrc.saIOURedeem.mCurrency: use this before saIOUIssue, limit to use. -// --> pnSrc.saIOUIssue.mCurrency: use this after saIOURedeem, limit to use. -// <-- pnDst.saReceive -// <-- pnDst.saIOUForgive -// <-- pnDst.saIOUAccept -// <-- terResult : tesSUCCESS = no error and if !bAllowPartial complelely satisfied wanted. -// <-> usOffersDeleteAlways: -// <-> usOffersDeleteOnSuccess: -TER calcOfferFill(paymentNode& pnSrc, paymentNode& pnDst, bool bAllowPartial) -{ - TER terResult; - - if (pnDst.saWanted.isNative()) - { - // Transfer stamps. - - STAmount saSrcFunds = pnSrc.saAccount->accountHolds(pnSrc.saAccount, uint160(0), uint160(0)); - - if (saSrcFunds && (bAllowPartial || saSrcFunds > pnDst.saWanted)) - { - pnSrc.saSend = min(saSrcFunds, pnDst.saWanted); - pnDst.saReceive = pnSrc.saSend; - } - else - { - terResult = terINSUF_PATH; - } - } - else - { - // Ripple funds. - - // Redeem to limit. - terResult = calcOfferFill( - accountHolds(pnSrc.saAccount, pnDst.saWanted.getCurrency(), pnDst.saWanted.getIssuer()), - pnSrc.saIOURedeem, - pnDst.saIOUForgive, - bAllowPartial); - - if (tesSUCCESS == terResult) - { - // Issue to wanted. - terResult = calcOfferFill( - pnDst.saWanted, // As much as wanted is available, limited by credit limit. - pnSrc.saIOUIssue, - pnDst.saIOUAccept, - bAllowPartial); - } - - if (tesSUCCESS == terResult && !bAllowPartial) - { - STAmount saTotal = pnDst.saIOUForgive + pnSrc.saIOUAccept; - - if (saTotal != saWanted) - terResult = terINSUF_PATH; - } - } - - return terResult; -} -#endif - -#if 0 -// Get the next offer limited by funding. -// - Stop when becomes unfunded. -void TransactionEngine::calcOfferBridgeNext( - const uint256& uBookRoot, // --> Which order book to look in. - const uint256& uBookEnd, // --> Limit of how far to look. - uint256& uBookDirIndex, // <-> Current directory. <-- 0 = no offer available. - uint64& uBookDirNode, // <-> Which node. 0 = first. - unsigned int& uBookDirEntry, // <-> Entry in node. 0 = first. - STAmount& saOfferIn, // <-- How much to pay in, fee inclusive, to get saOfferOut out. - STAmount& saOfferOut // <-- How much offer pays out. - ) -{ - saOfferIn = 0; // XXX currency & issuer - saOfferOut = 0; // XXX currency & issuer - - bool bDone = false; - - while (!bDone) - { - uint256 uOfferIndex; - - // Get uOfferIndex. - dirNext(uBookRoot, uBookEnd, uBookDirIndex, uBookDirNode, uBookDirEntry, uOfferIndex); - - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - - uint160 uOfferOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); - STAmount saOfferPays = sleOffer->getIValueFieldAmount(sfTakerGets); - STAmount saOfferGets = sleOffer->getIValueFieldAmount(sfTakerPays); - - if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) - { - // Offer is expired. - Log(lsINFO) << "calcOfferFirst: encountered expired offer"; - } - else - { - STAmount saOfferFunds = accountFunds(uOfferOwnerID, saOfferPays); - // Outbound fees are paid by offer owner. - // XXX Calculate outbound fee rate. - - if (saOfferPays.isNative()) - { - // No additional fees for stamps. - - nothing(); - } - else if (saOfferPays.getIssuer() == uOfferOwnerID) - { - // Offerer is issue own IOUs. - // No fees at this exact point, XXX receiving node may charge a fee. - // XXX Make sure has a credit line with receiver, limit by credit line. - - nothing(); - // XXX Broken - could be issuing or redeeming or both. - } - else - { - // Offer must be redeeming IOUs. - - // No additional - // XXX Broken - } - - if (!saOfferFunds.isPositive()) - { - // Offer is unfunded. - Log(lsINFO) << "calcOfferFirst: offer unfunded: delete"; - } - else if (saOfferFunds >= saOfferPays) - { - // Offer fully funded. - - // Account transfering funds in to offer always pays inbound fees. - - saOfferIn = saOfferGets; // XXX Add in fees? - - saOfferOut = saOfferPays; - - bDone = true; - } - else - { - // Offer partially funded. - - // saOfferIn/saOfferFunds = saOfferGets/saOfferPays - // XXX Round such that all saOffer funds are exhausted. - saOfferIn = (saOfferFunds*saOfferGets)/saOfferPays; // XXX Add in fees? - saOfferOut = saOfferFunds; - - bDone = true; - } - } - - if (!bDone) - { - // musUnfundedFound.insert(uOfferIndex); - } - } - while (bNext); -} -#endif - +// XXX Need to track balances for offer funding. TER TransactionEngine::calcNodeOfferRev( const unsigned int uIndex, // 0 < uIndex < uLast const PathState::pointer& pspCur, @@ -1933,9 +1763,9 @@ TER TransactionEngine::calcNodeOfferRev( { TER terResult = tepPATH_DRY; - paymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; - paymentNode& pnCur = pspCur->vpnNodes[uIndex]; - paymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; const uint160& uPrvIssuerID = pnPrv.uIssuerID; @@ -1945,12 +1775,12 @@ TER TransactionEngine::calcNodeOfferRev( const uint160& uNxtIssuerID = pnNxt.uIssuerID; const uint160& uNxtAccountID = pnNxt.uAccountID; - const STAmount saTransferRate = STAmount::saFromRate(rippleTransferRate(uCurIssuerID)); - uint256 uDirectTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID); const uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip); bool bAdvance = !entryCache(ltDIR_NODE, uDirectTip); + const STAmount& saTransferRate = pnCur.saTransferRate; + Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev> uIndex=%d prv=%s/%s cur=%s/%s nxt=%s/%s saTransferRate=%s") % uIndex % STAmount::createHumanCurrency(uPrvCurrencyID) @@ -2199,6 +2029,295 @@ TER TransactionEngine::calcNodeOfferRev( return terResult; } +// If needed, advance to next funded offer. +TER TransactionEngine::calcNodeAdvance( + const unsigned int uIndex, // 0 < uIndex < uLast + const PathState::pointer& pspCur, + const bool bMultiQuality) +{ + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; +// PaymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; + + const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; + const uint160& uPrvIssuerID = pnPrv.uIssuerID; + const uint160& uCurCurrencyID = pnCur.uCurrencyID; + const uint160& uCurIssuerID = pnCur.uIssuerID; +// const uint160& uNxtCurrencyID = pnNxt.uCurrencyID; +// const uint160& uNxtIssuerID = pnNxt.uIssuerID; + +// const uint160& uPrvAccountID = pnPrv.uAccountID; +// const uint160& uNxtAccountID = pnNxt.uAccountID; + + uint256& uDirectTip = pnCur.uDirectTip; + uint256 uDirectEnd = pnCur.uDirectEnd; + bool& bDirectAdvance = pnCur.bDirectAdvance; + SLE::pointer& sleDirectDir = pnCur.sleDirectDir; + STAmount& saOfrRate = pnCur.saOfrRate; + + bool& bEntryAdvance = pnCur.bEntryAdvance; + unsigned int& uEntry = pnCur.uEntry; + uint256& uOfferIndex = pnCur.uOfferIndex; + SLE::pointer& sleOffer = pnCur.sleOffer; + uint160& uOfrOwnerID = pnCur.uOfrOwnerID; + STAmount& saOfferFunds = pnCur.saOfferFunds; + STAmount& saTakerPays = pnCur.saTakerPays; + STAmount& saTakerGets = pnCur.saTakerGets; + bool& bFundsDirty = pnCur.bFundsDirty; + + TER terResult = tesSUCCESS; + + do + { + bool bDirectDirDirty = false; + + if (!pnCur.uDirectEnd) + { + // Need to initialize current node. + + uDirectTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID); + uDirectEnd = Ledger::getQualityNext(uDirectTip); + sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); + bDirectAdvance = !sleDirectDir; + bDirectDirDirty = true; + } + + if (bDirectAdvance) + { + // Get next quality. + uDirectTip = mLedger->getNextLedgerIndex(uDirectTip, uDirectEnd); + bDirectDirDirty = true; + bDirectAdvance = false; + + if (!uDirectTip) + { + // No more offers. Should be done rather than fall off end of book. + Log(lsINFO) << "calcNodeAdvance: Unreachable: Fell off end of order book."; + assert(false); + } + } + + if (bDirectDirDirty) + { + saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio + uEntry = 0; + bEntryAdvance = true; + } + + if (!bEntryAdvance) + { + if (bFundsDirty) + { + saTakerPays = sleOffer->getIValueFieldAmount(sfTakerPays); + saTakerGets = sleOffer->getIValueFieldAmount(sfTakerGets); + + saOfferFunds = accountFunds(uOfrOwnerID, saTakerGets); // Funds left. + bFundsDirty = false; + } + } + else if (!dirNext(uDirectTip, sleDirectDir, uEntry, uOfferIndex)) + { + // Failed to find an entry in directory. + + uOfferIndex = 0; + + // Do another cur directory iff bMultiQuality + if (bMultiQuality) + { + bDirectAdvance = true; + } + else + { + assert(false); // Can't run out of offers in forward direction. + terResult = tefEXCEPTION; + } + } + else + { + // Got a new offer. + sleOffer = entryCache(ltOFFER, uOfferIndex); + uOfrOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); + + const aciSource asLine = boost::make_tuple(uOfrOwnerID, uCurCurrencyID, uCurIssuerID); + + if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) + { + // Offer is expired. + Log(lsINFO) << "calcNodeAdvance: expired offer"; + + assert(musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()); // Verify reverse found it too. + bEntryAdvance = true; + continue; + } + + // Allowed to access source from this node? + curIssuerNodeConstIterator itAllow = pspCur->umForward.find(asLine); + const bool bFoundForward = itAllow != pspCur->umForward.end(); + + if (bFoundForward || itAllow->second != uIndex) + { + // Temporarily unfunded. Another node uses this source, ignore in this offer. + Log(lsINFO) << "calcNodeAdvance: temporarily unfunded offer"; + + bEntryAdvance = true; + continue; + } + + saTakerPays = sleOffer->getIValueFieldAmount(sfTakerPays); + saTakerGets = sleOffer->getIValueFieldAmount(sfTakerGets); + + saOfferFunds = accountFunds(uOfrOwnerID, saTakerGets); // Funds left. + + if (!saOfferFunds.isPositive()) + { + // Offer is unfunded. + Log(lsINFO) << "calcNodeAdvance: unfunded offer"; + + // YYY Could verify offer is correct place for unfundeds. + bEntryAdvance = true; + continue; + } + + bFundsDirty = false; + bEntryAdvance = false; + } + } + while (tesSUCCESS == terResult && (bEntryAdvance || bDirectAdvance)); + + return terResult; +} + +// Deliver maximum amount of funds from previous node. +// Goal: Make progress consuming the offer. +TER TransactionEngine::calcNodeDeliver( + const unsigned int uIndex, // 0 < uIndex < uLast + const PathState::pointer& pspCur, + const bool bMultiQuality, + const uint160& uInAccountID, // --> Input owner's account. + const STAmount& saInFunds, // --> Funds available for delivery and fees. + const STAmount& saInReq, // --> Limit to deliver. + STAmount& saInAct, // <-- Amount delivered. + STAmount& saInFees) // <-- Fees charged. +{ + TER terResult = tesSUCCESS; + + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; + + const uint160& uNxtAccountID = pnNxt.uAccountID; + const uint160& uCurIssuerID = pnCur.uIssuerID; + const uint160& uPrvIssuerID = pnPrv.uIssuerID; + const STAmount& saTransferRate = pnPrv.saTransferRate; + + saInAct = 0; + saInFees = 0; + + while (tesSUCCESS == terResult + && saInAct != saInReq // Did not deliver limit. + && saInAct + saInFees != saInFunds) // Did not deliver all funds. + { + terResult = calcNodeAdvance(uIndex, pspCur, bMultiQuality); // If needed, advance to next funded offer. + + if (tesSUCCESS == terResult) + { + bool& bEntryAdvance = pnCur.bEntryAdvance; + STAmount& saOfrRate = pnCur.saOfrRate; + uint256& uOfferIndex = pnCur.uOfferIndex; + SLE::pointer& sleOffer = pnCur.sleOffer; + const uint160& uOfrOwnerID = pnCur.uOfrOwnerID; + bool& bFundsDirty = pnCur.bFundsDirty; + STAmount& saOfferFunds = pnCur.saOfferFunds; + STAmount& saTakerPays = pnCur.saTakerPays; + STAmount& saTakerGets = pnCur.saTakerGets; + + + const STAmount saInFeeRate = uInAccountID == uPrvIssuerID || uOfrOwnerID == uPrvIssuerID // Issuer receiving or sending. + ? saOne // No fee. + : saTransferRate; // Transfer rate of issuer. + + // + // First calculate assuming no output fees. + // XXX Make sure derived in does not exceed actual saTakerPays + + STAmount saOutFunded = std::max(saOfferFunds, saTakerGets); // Offer maximum out - There are no out fees. + STAmount saInFunded = STAmount::multiply(saOutFunded, saOfrRate, saInReq); // Offer maximum in - Limited by by payout. + STAmount saInTotal = STAmount::multiply(saInFunded, saTransferRate); // Offer maximum in with fees. + STAmount saInSum = std::min(saInTotal, saInFunds-saInAct-saInFees); // In limited by saInFunds. + STAmount saInPassAct = STAmount::divide(saInSum, saInFeeRate); // In without fees. + STAmount saOutPassMax = STAmount::divide(saInPassAct, saOfrRate); // Out. + + STAmount saInPassFees; + STAmount saOutPassAct; + + if (!!uNxtAccountID) + { + // ? --> OFFER --> account + // Input fees: vary based upon the consumed offer's owner. + // Output fees: none as the destination account is the issuer. + + // XXX This doesn't claim input. + // XXX Assumes input is in limbo. XXX Check. + + // Debit offer owner. + accountSend(uOfrOwnerID, uCurIssuerID, saOutPassMax); + + saOutPassAct = saOutPassMax; + } + else + { + // ? --> OFFER --> offer + STAmount saOutPassFees; + + terResult = TransactionEngine::calcNodeDeliver( + uIndex+1, + pspCur, + bMultiQuality, + uOfrOwnerID, + saOutPassMax, + saOutPassMax, + saOutPassAct, // <-- Amount delivered. + saOutPassFees); // <-- Fees charged. + + if (tesSUCCESS != terResult) + break; + + // Offer maximum in limited by next payout. + saInPassAct = STAmount::multiply(saOutPassAct, saOfrRate); + saInPassFees = STAmount::multiply(saInFunded, saInFeeRate)-saInPassAct; + } + + // Funds were spent. + bFundsDirty = true; + + // Credit issuer transfer fees. + accountSend(uInAccountID, uOfrOwnerID, saInPassFees); + + // Credit offer owner from offer. + accountSend(uInAccountID, uOfrOwnerID, saInPassAct); + + // Adjust offer + sleOffer->setIFieldAmount(sfTakerGets, saTakerGets - saOutPassAct); + sleOffer->setIFieldAmount(sfTakerPays, saTakerPays - saInPassAct); + + entryModify(sleOffer); + + if (saOutPassAct == saTakerGets) + { + // Offer became unfunded. + pspCur->vUnfundedBecame.push_back(uOfferIndex); + bEntryAdvance = true; + } + + saInAct += saInPassAct; + saInFees += saInPassFees; + } + } + + return terResult; +} + +// Called to drive the from the first offer node in a chain. // - Offer input is limbo. // - Current offers consumed. // - Current offer owners debited. @@ -2211,586 +2330,36 @@ TER TransactionEngine::calcNodeOfferFwd( const bool bMultiQuality ) { - TER terResult = tepPATH_DRY; -#if 0 - paymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; - paymentNode& pnCur = pspCur->vpnNodes[uIndex]; - paymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; + TER terResult; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; - const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; - const uint160& uPrvIssuerID = pnPrv.uIssuerID; - const uint160& uCurCurrencyID = pnCur.uCurrencyID; - const uint160& uCurIssuerID = pnCur.uIssuerID; - const uint160& uNxtCurrencyID = pnNxt.uCurrencyID; - const uint160& uNxtIssuerID = pnNxt.uIssuerID; - - const uint160& uPrvAccountID = pnPrv.uAccountID; - const uint160& uNxtAccountID = pnNxt.uAccountID; - const STAmount saTransferRate = STAmount::saFromRate(rippleTransferRate(uCurIssuerID)); - - uint256 uDirectTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID); - const uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip); - bool bAdvance = !entryCache(ltDIR_NODE, uDirectTip); - - const STAmount& saPrvDlvReq = pnPrv.saFwdDeliver; // Forward driver. - STAmount saPrvDlvAct; - - STAmount& saCurDlvAct = pnCur.saFwdDeliver; // How much current node will deliver. - saCurDlvAct = 0; - - bool bNxtOffer = !uNxtAccountID; - uint256 uNxtTip; - uint256 uNxtEnd; - SLE::pointer sleNxtDir; - bool bNxtDirAdvance; - bool bNxtEntryDirty; - bool bNxtEntryAdvance; - unsigned int uNxtEntry; // Next nodes index. - uint256 uNxtIndex; // Next offer. - boost::unordered_map umNxtBalance; // Account valances. - STAmount saNxtOfrFunds; - STAmount saNxtOfrIn; - STAmount saNxtOfrOut; - SLE::pointer sleNxtOfr; - uint160 uNxtOfrAccountID; - STAmount saNxtFeeRate; - bool bNxtFee; - - if (bNxtOffer) + if (!!pnPrv.uAccountID) { - uNxtTip = Ledger::getBookBase(uCurCurrencyID, uCurIssuerID, uNxtCurrencyID, uNxtIssuerID); - uNxtEnd = Ledger::getQualityNext(uNxtTip); - sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); - bNxtDirAdvance = !sleNxtDir; - uNxtEntry = 0; - bNxtEntryAdvance = true; - } - - while (!!uDirectTip && saPrvDlvAct != saPrvDlvReq) // Have a quality and not done. - { - if (bAdvance) - { - // Get next quality. - uDirectTip = mLedger->getNextLedgerIndex(uDirectTip, uDirectEnd); - } - else - { - bAdvance = true; - } - - if (!!uDirectTip) - { - // Do a directory. - // - Drive on computing saPrvDlvAct to derive saCurDlvAct. - SLE::pointer sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); - STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio - unsigned int uEntry = 0; - uint256 uOfferIndex; - - while (saPrvDlvReq != saPrvDlvAct // Have not met request. - && dirNext(uDirectTip, sleDirectDir, uEntry, uOfferIndex)) - { - // Have an offer from the directory. - // Handle offers such that there is no need to look back to the previous nodes offers. - - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - const uint160 uCurOfrAccountID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); - const aciSource asLine = boost::make_tuple(uCurOfrAccountID, uCurCurrencyID, uCurIssuerID); - if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) - { - // Offer is expired. - Log(lsINFO) << "calcNodeOfferFwd: expired offer"; - - assert(musUnfundedFound.find(uOfferIndex) != musUnfundedFound.end()); - continue; - } - - // Allowed to access source from this node? - curIssuerNodeConstIterator itAllow = pspCur->umForward.find(asLine); - const bool bFoundForward = itAllow != pspCur->umForward.end(); - - if (bFoundForward || itAllow->second != uIndex) - { - // Temporarily unfunded. Another node uses this source, ignore in this node. - Log(lsINFO) << "calcNodeOfferFwd: temporarily unfunded offer"; - - nothing(); - continue; - } - - const STAmount saCurOfrOutReq = sleOffer->getIValueFieldAmount(sfTakerGets); - STAmount saCurOfrFunds = accountFunds(uCurOfrAccountID, saCurOfrOutReq); // Funds left. - STAmount saCurOfrSpent; - - if (!saCurOfrFunds.isPositive()) - { - // Offer is unfunded. - Log(lsINFO) << "calcNodeOfferFwd: unfunded offer"; - - // YYY Could verify offer is correct place for unfundeds. - nothing(); - continue; - } - - const STAmount saCurOfrInReq = sleOffer->getIValueFieldAmount(sfTakerPays); - STAmount saCurOfrInMax = std::min(saCurOfrInReq, saPrvDlvReq-saPrvDlvAct); - STAmount saCurOfrInAct; - STAmount saCurOfrOutAct; - - if (!!uNxtAccountID) - { - // Next is an account node. - // If previous is an account, then inbound funds can be credited offer with no fees or quality. - // Transfer fees were previously handled. XXX Verify. - // If previous is an offer, then inbound funds are with issuer or in limbo. - - // The only fee ever charged is an output transfer fee. - const STAmount saFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtAccountID == uCurIssuerID - ? saOne - : saTransferRate; - const bool bFee = saFeeRate != saOne; - - const STAmount saOutPass = STAmount::divide(saCurOfrInMax, saOfrRate, uCurCurrencyID, uCurIssuerID); - const STAmount saOutBase = std::min(saCurOfrOutReq, saOutPass); // Limit offer out by needed. - const STAmount saOutCostRaw= bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutBase; - const STAmount saOutCost = std::min(saOutCostRaw, saCurOfrFunds); // Limit cost by fees & funds. - - saCurOfrSpent = saOutCost; // XXX Check. - saCurOfrOutAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; // Out amount after fees. - - // Compute input w/o fees required. - saCurOfrInAct = STAmount::multiply(saCurOfrOutAct, saOfrRate, uPrvCurrencyID, uPrvIssuerID); - - // Deliver to output. - accountSend(uCurOfrAccountID, uNxtAccountID, saCurOfrOutAct); - } - else - { - // Next is an offer node. - // Need to step through next offer's nodes to figure out fees. - STAmount saNxtOfrRate; - bool bDirectoryFirst = true; - - while (!!uNxtTip && saCurOfrInAct != saCurOfrInMax) // A next offer may be available and have more to do. - { - if (!bNxtDirAdvance) - { - // Current directory is fine. - nothing(); - } - else if (bDirectoryFirst || bMultiQuality) - { - if (bDirectoryFirst) - bDirectoryFirst = false; - - uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); - if (!!uNxtTip) - { - saNxtOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip)); // For correct ratio - sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); - - assert(!!sleNxtDir); - - bNxtDirAdvance = false; - uNxtEntry = 0; - bNxtEntryAdvance = true; - } - else - { - // No more next offers. Should be done rather than fall off end of book. - Log(lsINFO) << "Unreachable."; - assert(false); - } - } - else - { - // Don't do another directory. - break; - } - - if (!bNxtEntryAdvance) - { - // Current next directory is fine. - nothing(); - } - else if (dirNext(uNxtTip, sleNxtDir, uEntry, uNxtIndex)) - { - // Found a next uNxtIndex. - bNxtEntryAdvance = false; - bNxtEntryDirty = true; - } - else - { - // No more offers in nxt directory. - bNxtDirAdvance = true; - continue; - } - - if (bNxtEntryDirty) - { - sleNxtOfr = entryCache(ltOFFER, uNxtIndex); - - if (sleNxtOfr->getIFieldPresent(sfExpiration) - && sleNxtOfr->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) - { - // Offer is expired. - bNxtEntryAdvance = true; - continue; - } - - uNxtOfrAccountID = sleNxtOfr->getIValueFieldAccount(sfAccount).getAccountID(); - - saNxtFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtOfrAccountID == uCurIssuerID - ? saOne - : saTransferRate; - - bNxtFee = saNxtFeeRate != saOne; - - boost::unordered_map::const_iterator itNxtBalance = umNxtBalance.find(uNxtOfrAccountID); - saNxtOfrFunds = itNxtBalance == umNxtBalance.end() - ? accountFunds(uNxtOfrAccountID, saNxtOfrOut) - : it->second; - - saNxtOfrIn = sleNxtOfr->getIValueFieldAmount(sfTakerPays); - saNxtOfrOut = sleNxtOfr->getIValueFieldAmount(sfTakerGets); - - // Cost (payout + fees) to next offer owner if offer is fully redeem. - STAmount saNxtOutCost = STAmount::multiply(saNxtOfrOut, saNxtFeeRate, saNxtOfrOut.getCurrency(), saNxtOfrOut.getIssuer()); - - if (saNxtOfrOut > saNxtOfrFunds) - { - // Limit offer by funds available. - STAmount saNxtOutMax = STAmount::divide(saNxtOfrOut, saNxtFeeRate, uCurCurrencyID, uCurIssuerID); - - } - - bNxtEntryDirty = false; - } - - if (!saNxtOfrFunds.isPositive()) - { - // Offer is unfunded. - bNxtEntryAdvance = true; - continue; - } - - STAmount saNxtOutAvail = - // Driving amount of input. - const STAmount saInBase = saCurOfrInMax-saCurOfrInAct; - - // Desired amount of output not including fees. - const STAmount saOutReq = STAmount::divide(saInBase, saOfrRate, uCurCurrencyID, uCurIssuerID); - STAmount saOutBase = std::min( - std::min(saCurOfrOutReq, saOutReq), // Limit offer out by needed. - saNxtOfrIn); // Limit offer out by supplying offer. - // Limit cost by fees & funds. - const STAmount saOutCost = std::min( - bNxtFee - ? STAmount::multiply(saOutBase, saNxtFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutBase, - saCurOfrFunds); - // Compute output minus fees. Fees are offer's obligation and not passed to input. - const STAmount saOutDlvPass= bNxtFee - ? STAmount::divide(saOutCost, saNxtFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; - // Compute input based on output minus fees. - const STAmount saInDlvPass = STAmount::multiply(saOutDlvPass, saOfrRate, uPrvCurrencyID, uCurIssuerID); - - // XXX Check fees. - sleNxtOfr->setIFieldAmount(sfTakerGets, saNxtOfrIn - saInDlvPass); - sleNxtOfr->setIFieldAmount(sfTakerPays, saNxtOfrOut - saOutDlvPass); - - if (saNxtOfrOut == saOutDlvPass) { - // Consumed all of offer. - // XXX Move to outside. - Log(lsINFO) << "calcNodeOfferFwd: offer consumed"; - - pspCur->vUnfundedBecame.push_back(uNxtIndex); // Mark offer for deletion on use of current path state. - - bNxtEntryAdvance = true; - } - - umNxtBalance[uNxtOfrAccountID] = saNxtOfrFunds; - - saCurOfrSpent += saOutDlvPass; // XXX Check. - - saCurOfrInAct += saInDlvPass; // Add to input handled. - - saCurOfrOutAct += saOutDlvPass; // Add to output handled. - } - - // Deliver output to limbo or currency issuer. - accountSend( - uCurOfrAccountID, // Offer owner pays. - !!uCurIssuerID - ? uCurIssuerID // Output is non-XNS send to issuer. - : ACCOUNT_XNS, // Output is XNS send to limbo (ACCOUNT_XNS). - saCurOfrOutAct); - } - - // Deliver input to offer owner. - accountSend( - !!uPrvAccountID - ? uPrvAccountID // Previous is an account. Source is previous account. - : !!uPrvIssuerID - ? ACCOUNT_XNS // Previous is offer outputing XNS, source is limbo (ACCOUNT_XNS). - : uPrvIssuerID, // Previous is offer outputing non-XNS, source is input issuer. - uCurOfrAccountID, // Offer owner receives. - saCurOfrInAct); - - if (saCurOfrFunds == saCurOfrSpent) - { - // Offer became unfunded. - pspCur->vUnfundedBecame.push_back(uOfferIndex); // Mark offer for deletion on use of current path state. - } - - saPrvDlvAct += saCurOfrInAct; // Portion needed in previous. - saCurDlvAct += saCurOfrOutAct; // Portion of driver served. - } - } - - // Do another cur directory iff bMultiQuality - if (!bMultiQuality) - uDirectTip = 0; - } - - return !!saCurDlvAct ? tesSUCCESS : terResult; -#else - return terResult; -#endif -} - -#if 0 -// If either currency is not stamps, then also calculates vs stamp bridge. -// --> saWanted: Limit of how much is wanted out. -// <-- saPay: How much to pay into the offer. -// <-- saGot: How much to the offer pays out. Never more than saWanted. -// Given two value's enforce a minimum: -// - reverse: prv is maximum to pay in (including fee) - cur is what is wanted: generally, minimizing prv -// - forward: prv is actual amount to pay in (including fee) - cur is what is wanted: generally, minimizing cur -// Value in is may be rippled or credited from limbo. Value out is put in limbo. -// If next is an offer, the amount needed is in cur reedem. -// XXX What about account mentioned multiple times via offers? -void TransactionEngine::calcNodeOffer( - bool bForward, - bool bMultiQuality, // True, if this is the only active path: we can do multiple qualities in this pass. - const uint160& uPrvAccountID, // If 0, then funds from previous offer's limbo - const uint160& uPrvCurrencyID, - const uint160& uPrvIssuerID, - const uint160& uCurCurrencyID, - const uint160& uCurIssuerID, - - const STAmount& uPrvRedeemReq, // --> In limit. - STAmount& uPrvRedeemAct, // <-> In limit achived. - const STAmount& uCurRedeemReq, // --> Out limit. Driver when uCurIssuerID == uNxtIssuerID (offer would redeem to next) - STAmount& uCurRedeemAct, // <-> Out limit achived. - - const STAmount& uCurIssueReq, // --> In limit. - STAmount& uCurIssueAct, // <-> In limit achived. - const STAmount& uCurIssueReq, // --> Out limit. Driver when uCurIssueReq != uNxtIssuerID (offer would effectively issue or transfer to next) - STAmount& uCurIssueAct, // <-> Out limit achived. - - STAmount& saPay, - STAmount& saGot - ) const -{ - TER terResult = temUNKNOWN; - - // Direct: not bridging via XNS - bool bDirectNext = true; // True, if need to load. - uint256 uDirectQuality; - uint256 uDirectTip = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID); - uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip); - - // Bridging: bridging via XNS - bool bBridge = true; // True, if bridging active. False, missing an offer. - uint256 uBridgeQuality; - STAmount saBridgeIn; // Amount available. - STAmount saBridgeOut; - - bool bInNext = true; // True, if need to load. - STAmount saInIn; // Amount available. Consumed in loop. Limited by offer funding. - STAmount saInOut; - uint256 uInTip; // Current entry. - uint256 uInEnd; - unsigned int uInEntry; - - bool bOutNext = true; - STAmount saOutIn; - STAmount saOutOut; - uint256 uOutTip; - uint256 uOutEnd; - unsigned int uOutEntry; - - saPay.zero(); - saPay.setCurrency(uPrvCurrencyID); - saPay.setIssuer(uPrvIssuerID); - - saNeed = saWanted; - - if (!uCurCurrencyID && !uPrvCurrencyID) - { - // Bridging: Neither currency is XNS. - uInTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, CURRENCY_XNS, ACCOUNT_XNS); - uInEnd = Ledger::getQualityNext(uInTip); - uOutTip = Ledger::getBookBase(CURRENCY_XNS, ACCOUNT_XNS, uCurCurrencyID, uCurIssuerID); - uOutEnd = Ledger::getQualityNext(uInTip); - } - - // Find our head offer. - - bool bRedeeming = false; - bool bIssuing = false; - - // The price varies as we change between issuing and transfering, so unless bMultiQuality, we must stick with a mode once it - // is determined. - - if (bBridge && (bInNext || bOutNext)) - { - // Bridging and need to calculate next bridge rate. - // A bridge can consist of multiple offers. As offer's are consumed, the effective rate changes. - - if (bInNext) - { -// sleInDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uInIndex, uInEnd)); - // Get the next funded offer. - offerBridgeNext(uInIndex, uInEnd, uInEntry, saInIn, saInOut); // Get offer limited by funding. - bInNext = false; - } - - if (bOutNext) - { -// sleOutDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uOutIndex, uOutEnd)); - offerNext(uOutIndex, uOutEnd, uOutEntry, saOutIn, saOutOut); - bOutNext = false; - } - - if (!uInIndex || !uOutIndex) - { - bBridge = false; // No more offers to bridge. - } - else - { - // Have bridge in and out entries. - // Calculate bridge rate. Out offer pay ripple fee. In offer fee is added to in cost. - - saBridgeOut.zero(); - - if (saInOut < saOutIn) - { - // Limit by in. - - // XXX Need to include fees in saBridgeIn. - saBridgeIn = saInIn; // All of in - // Limit bridge out: saInOut/saBridgeOut = saOutIn/saOutOut - // Round such that we would take all of in offer, otherwise would have leftovers. - saBridgeOut = (saInOut * saOutOut) / saOutIn; - } - else if (saInOut > saOutIn) - { - // Limit by out, if at all. - - // XXX Need to include fees in saBridgeIn. - // Limit bridge in:saInIn/saInOuts = aBridgeIn/saOutIn - // Round such that would take all of out offer. - saBridgeIn = (saInIn * saOutIn) / saInOuts; - saBridgeOut = saOutOut; // All of out. - } - else - { - // Entries match, - - // XXX Need to include fees in saBridgeIn. - saBridgeIn = saInIn; // All of in - saBridgeOut = saOutOut; // All of out. - } - - uBridgeQuality = STAmount::getRate(saBridgeIn, saBridgeOut); // Inclusive of fees. - } - } - - if (bBridge) - { - bUseBridge = !uDirectTip || (uBridgeQuality < uDirectQuality) - } - else if (!!uDirectTip) - { - bUseBridge = false + // Previous is an account node, resolve its deliver. + STAmount saInAct; + STAmount saInFees; + + terResult = calcNodeDeliver( + uIndex, + pspCur, + bMultiQuality, + pnPrv.uAccountID, + pnPrv.saFwdDeliver, + pnPrv.saFwdDeliver, + saInAct, + saInFees); + + assert(tesSUCCESS != terResult || pnPrv.saFwdDeliver == saInAct+saInFees); } else { - // No more offers. Declare success, even if none returned. - saGot = saWanted-saNeed; + // Previous is an offer. Deliver has already been resolved. terResult = tesSUCCESS; } - if (tesSUCCESS != terResult) - { - STAmount& saAvailIn = bUseBridge ? saBridgeIn : saDirectIn; - STAmount& saAvailOut = bUseBridge ? saBridgeOut : saDirectOut; + return terResult; - if (saAvailOut > saNeed) - { - // Consume part of offer. Done. - - saNeed = 0; - saPay += (saNeed*saAvailIn)/saAvailOut; // Round up, prefer to pay more. - } - else - { - // Consume entire offer. - - saNeed -= saAvailOut; - saPay += saAvailIn; - - if (bUseBridge) - { - // Consume bridge out. - if (saOutOut == saAvailOut) - { - // Consume all. - saOutOut = 0; - saOutIn = 0; - bOutNext = true; - } - else - { - // Consume portion of bridge out, must be consuming all of bridge in. - // saOutIn/saOutOut = saSpent/saAvailOut - // Round? - saOutIn -= (saOutIn*saAvailOut)/saOutOut; - saOutOut -= saAvailOut; - } - - // Consume bridge in. - if (saOutIn == saAvailIn) - { - // Consume all. - saInOut = 0; - saInIn = 0; - bInNext = true; - } - else - { - // Consume portion of bridge in, must be consuming all of bridge out. - // saInIn/saInOut = saAvailIn/saPay - // Round? - saInOut -= (saInOut*saAvailIn)/saInIn; - saInIn -= saAvailIn; - } - } - else - { - bDirectNext = true; - } - } - } } -#endif // Cur is the driver and will be filled exactly. // uQualityIn -> uQualityOut @@ -2886,9 +2455,9 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS TER terResult = tesSUCCESS; const unsigned int uLast = pspCur->vpnNodes.size() - 1; - paymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; - paymentNode& pnCur = pspCur->vpnNodes[uIndex]; - paymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; const bool bRedeem = isSetBit(pnCur.uFlags, STPathElement::typeRedeem); const bool bPrvRedeem = isSetBit(pnPrv.uFlags, STPathElement::typeRedeem); @@ -3214,9 +2783,9 @@ TER TransactionEngine::calcNodeAccountFwd( TER terResult = tesSUCCESS; const unsigned int uLast = pspCur->vpnNodes.size() - 1; - paymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; - paymentNode& pnCur = pspCur->vpnNodes[uIndex]; - paymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; const bool bRedeem = isSetBit(pnCur.uFlags, STPathElement::typeRedeem); const bool bIssue = isSetBit(pnCur.uFlags, STPathElement::typeIssue); @@ -3276,7 +2845,7 @@ TER TransactionEngine::calcNodeAccountFwd( // First node, calculate amount to send. // XXX Use stamp/ripple balance - paymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; const STAmount& saCurRedeemReq = pnCur.saRevRedeem; STAmount& saCurRedeemAct = pnCur.saFwdRedeem; @@ -3498,7 +3067,7 @@ bool PathState::lessPriority(const PathState::pointer& lhs, const PathState::poi // - Offers can only go directly to another offer if the currency and issuer are an exact match. TER PathState::pushImply(uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID) { - const paymentNode& pnPrv = vpnNodes.back(); + const PaymentNode& pnPrv = vpnNodes.back(); TER terResult = tesSUCCESS; Log(lsINFO) << "pushImply> " @@ -3548,9 +3117,9 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint << NewcoinAddress::createHumanAccountID(uAccountID) << " " << STAmount::createHumanCurrency(uCurrencyID) << " " << NewcoinAddress::createHumanAccountID(uIssuerID); - paymentNode pnCur; + PaymentNode pnCur; const bool bFirst = vpnNodes.empty(); - const paymentNode& pnPrv = bFirst ? paymentNode() : vpnNodes.back(); + const PaymentNode& pnPrv = bFirst ? PaymentNode() : vpnNodes.back(); // true, iff node is a ripple account. false, iff node is an offer node. const bool bAccount = isSetBit(iType, STPathElement::typeAccount); // true, iff currency supplied. @@ -3594,7 +3163,7 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint if (tesSUCCESS == terResult && !vpnNodes.empty()) { - const paymentNode& pnBck = vpnNodes.back(); + const PaymentNode& pnBck = vpnNodes.back(); bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); if (bBckAccount) @@ -3669,7 +3238,7 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint if (tesSUCCESS == terResult) { // Verify that previous account is allowed to issue. - const paymentNode& pnBck = vpnNodes.back(); + const PaymentNode& pnBck = vpnNodes.back(); bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); bool bBckIssue = isSetBit(pnBck.uFlags, STPathElement::typeIssue); @@ -3682,7 +3251,9 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint } if (tesSUCCESS == terResult) + { vpnNodes.push_back(pnCur); + } } } Log(lsINFO) << "pushNode< " << terResult; @@ -3758,7 +3329,7 @@ PathState::PathState( for (unsigned int uIndex = 0; tesSUCCESS == terStatus && uIndex != uNodes; ++uIndex) { - const paymentNode& pnCur = vpnNodes[uIndex]; + const PaymentNode& pnCur = vpnNodes[uIndex]; if (!!pnCur.uAccountID) { @@ -3789,7 +3360,7 @@ Json::Value PathState::getJson() const Json::Value jvPathState(Json::objectValue); Json::Value jvNodes(Json::arrayValue); - BOOST_FOREACH(const paymentNode& pnNode, vpnNodes) + BOOST_FOREACH(const PaymentNode& pnNode, vpnNodes) { Json::Value jvNode(Json::objectValue); @@ -3858,6 +3429,27 @@ Json::Value PathState::getJson() const return jvPathState; } +TER TransactionEngine::calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) +{ + const PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); + + Log(lsINFO) << boost::str(boost::format("calcNodeFwd> uIndex=%d") % uIndex); + + TER terResult = bCurAccount + ? calcNodeAccountFwd(uIndex, pspCur, bMultiQuality) + : calcNodeOfferFwd(uIndex, pspCur, bMultiQuality); + + if (tesSUCCESS == terResult && uIndex != pspCur->vpnNodes.size()) + { + terResult = calcNodeFwd(uIndex+1, pspCur, bMultiQuality); + } + + Log(lsINFO) << boost::str(boost::format("calcNodeFwd< uIndex=%d terResult=%d") % uIndex % terResult); + + return terResult; +} + // 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. @@ -3867,34 +3459,44 @@ Json::Value PathState::getJson() const // --> [all]saWanted.mCurrency // --> [all]saAccount // <-> [0]saWanted.mAmount : --> limit, <-- actual -TER TransactionEngine::calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) +TER TransactionEngine::calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) { - const paymentNode& pnCur = pspCur->vpnNodes[uIndex]; - const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); - TER terResult; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); + TER terResult; - Log(lsINFO) << boost::str(boost::format("calcNode> uIndex=%d") % uIndex); + Log(lsINFO) << boost::str(boost::format("calcNodeRev> uIndex=%d") % uIndex); // Do current node reverse. + const uint160& uCurIssuerID = pnCur.uIssuerID; + STAmount& saTransferRate = pnCur.saTransferRate; + + saTransferRate = STAmount::saFromRate(rippleTransferRate(uCurIssuerID)); + terResult = bCurAccount ? calcNodeAccountRev(uIndex, pspCur, bMultiQuality) : calcNodeOfferRev(uIndex, pspCur, bMultiQuality); // Do previous. - if (tesSUCCESS == terResult && uIndex) + if (tesSUCCESS != terResult) { - terResult = calcNode(uIndex-1, pspCur, bMultiQuality); + // Error, don't continue. + nothing(); + } + else if (uIndex) + { + // Continue in reverse. + + terResult = calcNodeRev(uIndex-1, pspCur, bMultiQuality); + } + else + { + // Do forward. + + terResult = calcNodeFwd(0, pspCur, bMultiQuality); } - // Do current node forward. - if (tesSUCCESS == terResult) - { - terResult = bCurAccount - ? calcNodeAccountFwd(uIndex, pspCur, bMultiQuality) - : calcNodeOfferFwd(uIndex, pspCur, bMultiQuality); - } - - Log(lsINFO) << boost::str(boost::format("calcNode< uIndex=%d terResult=%d") % uIndex % terResult); + Log(lsINFO) << boost::str(boost::format("calcNodeRev< uIndex=%d terResult=%d") % uIndex % terResult); return terResult; } @@ -3916,7 +3518,7 @@ void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPa pspCur->vUnfundedBecame.clear(); pspCur->umReverse.clear(); - pspCur->terStatus = calcNode(uLast, pspCur, iPaths == 1); + pspCur->terStatus = calcNodeRev(uLast, pspCur, iPaths == 1); Log(lsINFO) << "pathNext: terStatus=" << pspCur->terStatus @@ -4757,4 +4359,403 @@ TER TransactionEngine::doDelete(const SerializedTransaction& txn) return temUNKNOWN; } +#if 0 +// XXX Need to adjust for fees. +// Find offers to satisfy pnDst. +// - Does not adjust any balances as there is at least a forward pass to come. +// --> pnDst.saWanted: currency and amount wanted +// --> pnSrc.saIOURedeem.mCurrency: use this before saIOUIssue, limit to use. +// --> pnSrc.saIOUIssue.mCurrency: use this after saIOURedeem, limit to use. +// <-- pnDst.saReceive +// <-- pnDst.saIOUForgive +// <-- pnDst.saIOUAccept +// <-- terResult : tesSUCCESS = no error and if !bAllowPartial complelely satisfied wanted. +// <-> usOffersDeleteAlways: +// <-> usOffersDeleteOnSuccess: +TER calcOfferFill(PaymentNode& pnSrc, PaymentNode& pnDst, bool bAllowPartial) +{ + TER terResult; + + if (pnDst.saWanted.isNative()) + { + // Transfer stamps. + + STAmount saSrcFunds = pnSrc.saAccount->accountHolds(pnSrc.saAccount, uint160(0), uint160(0)); + + if (saSrcFunds && (bAllowPartial || saSrcFunds > pnDst.saWanted)) + { + pnSrc.saSend = min(saSrcFunds, pnDst.saWanted); + pnDst.saReceive = pnSrc.saSend; + } + else + { + terResult = terINSUF_PATH; + } + } + else + { + // Ripple funds. + + // Redeem to limit. + terResult = calcOfferFill( + accountHolds(pnSrc.saAccount, pnDst.saWanted.getCurrency(), pnDst.saWanted.getIssuer()), + pnSrc.saIOURedeem, + pnDst.saIOUForgive, + bAllowPartial); + + if (tesSUCCESS == terResult) + { + // Issue to wanted. + terResult = calcOfferFill( + pnDst.saWanted, // As much as wanted is available, limited by credit limit. + pnSrc.saIOUIssue, + pnDst.saIOUAccept, + bAllowPartial); + } + + if (tesSUCCESS == terResult && !bAllowPartial) + { + STAmount saTotal = pnDst.saIOUForgive + pnSrc.saIOUAccept; + + if (saTotal != saWanted) + terResult = terINSUF_PATH; + } + } + + return terResult; +} +#endif + +#if 0 +// Get the next offer limited by funding. +// - Stop when becomes unfunded. +void TransactionEngine::calcOfferBridgeNext( + const uint256& uBookRoot, // --> Which order book to look in. + const uint256& uBookEnd, // --> Limit of how far to look. + uint256& uBookDirIndex, // <-> Current directory. <-- 0 = no offer available. + uint64& uBookDirNode, // <-> Which node. 0 = first. + unsigned int& uBookDirEntry, // <-> Entry in node. 0 = first. + STAmount& saOfferIn, // <-- How much to pay in, fee inclusive, to get saOfferOut out. + STAmount& saOfferOut // <-- How much offer pays out. + ) +{ + saOfferIn = 0; // XXX currency & issuer + saOfferOut = 0; // XXX currency & issuer + + bool bDone = false; + + while (!bDone) + { + uint256 uOfferIndex; + + // Get uOfferIndex. + dirNext(uBookRoot, uBookEnd, uBookDirIndex, uBookDirNode, uBookDirEntry, uOfferIndex); + + SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); + + uint160 uOfferOwnerID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); + STAmount saOfferPays = sleOffer->getIValueFieldAmount(sfTakerGets); + STAmount saOfferGets = sleOffer->getIValueFieldAmount(sfTakerPays); + + if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) + { + // Offer is expired. + Log(lsINFO) << "calcOfferFirst: encountered expired offer"; + } + else + { + STAmount saOfferFunds = accountFunds(uOfferOwnerID, saOfferPays); + // Outbound fees are paid by offer owner. + // XXX Calculate outbound fee rate. + + if (saOfferPays.isNative()) + { + // No additional fees for stamps. + + nothing(); + } + else if (saOfferPays.getIssuer() == uOfferOwnerID) + { + // Offerer is issue own IOUs. + // No fees at this exact point, XXX receiving node may charge a fee. + // XXX Make sure has a credit line with receiver, limit by credit line. + + nothing(); + // XXX Broken - could be issuing or redeeming or both. + } + else + { + // Offer must be redeeming IOUs. + + // No additional + // XXX Broken + } + + if (!saOfferFunds.isPositive()) + { + // Offer is unfunded. + Log(lsINFO) << "calcOfferFirst: offer unfunded: delete"; + } + else if (saOfferFunds >= saOfferPays) + { + // Offer fully funded. + + // Account transfering funds in to offer always pays inbound fees. + + saOfferIn = saOfferGets; // XXX Add in fees? + + saOfferOut = saOfferPays; + + bDone = true; + } + else + { + // Offer partially funded. + + // saOfferIn/saOfferFunds = saOfferGets/saOfferPays + // XXX Round such that all saOffer funds are exhausted. + saOfferIn = (saOfferFunds*saOfferGets)/saOfferPays; // XXX Add in fees? + saOfferOut = saOfferFunds; + + bDone = true; + } + } + + if (!bDone) + { + // musUnfundedFound.insert(uOfferIndex); + } + } + while (bNext); +} +#endif + +#if 0 +// If either currency is not stamps, then also calculates vs stamp bridge. +// --> saWanted: Limit of how much is wanted out. +// <-- saPay: How much to pay into the offer. +// <-- saGot: How much to the offer pays out. Never more than saWanted. +// Given two value's enforce a minimum: +// - reverse: prv is maximum to pay in (including fee) - cur is what is wanted: generally, minimizing prv +// - forward: prv is actual amount to pay in (including fee) - cur is what is wanted: generally, minimizing cur +// Value in is may be rippled or credited from limbo. Value out is put in limbo. +// If next is an offer, the amount needed is in cur reedem. +// XXX What about account mentioned multiple times via offers? +void TransactionEngine::calcNodeOffer( + bool bForward, + bool bMultiQuality, // True, if this is the only active path: we can do multiple qualities in this pass. + const uint160& uPrvAccountID, // If 0, then funds from previous offer's limbo + const uint160& uPrvCurrencyID, + const uint160& uPrvIssuerID, + const uint160& uCurCurrencyID, + const uint160& uCurIssuerID, + + const STAmount& uPrvRedeemReq, // --> In limit. + STAmount& uPrvRedeemAct, // <-> In limit achived. + const STAmount& uCurRedeemReq, // --> Out limit. Driver when uCurIssuerID == uNxtIssuerID (offer would redeem to next) + STAmount& uCurRedeemAct, // <-> Out limit achived. + + const STAmount& uCurIssueReq, // --> In limit. + STAmount& uCurIssueAct, // <-> In limit achived. + const STAmount& uCurIssueReq, // --> Out limit. Driver when uCurIssueReq != uNxtIssuerID (offer would effectively issue or transfer to next) + STAmount& uCurIssueAct, // <-> Out limit achived. + + STAmount& saPay, + STAmount& saGot + ) const +{ + TER terResult = temUNKNOWN; + + // Direct: not bridging via XNS + bool bDirectNext = true; // True, if need to load. + uint256 uDirectQuality; + uint256 uDirectTip = Ledger::getBookBase(uGetsCurrency, uGetsIssuerID, uPaysCurrency, uPaysIssuerID); + uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip); + + // Bridging: bridging via XNS + bool bBridge = true; // True, if bridging active. False, missing an offer. + uint256 uBridgeQuality; + STAmount saBridgeIn; // Amount available. + STAmount saBridgeOut; + + bool bInNext = true; // True, if need to load. + STAmount saInIn; // Amount available. Consumed in loop. Limited by offer funding. + STAmount saInOut; + uint256 uInTip; // Current entry. + uint256 uInEnd; + unsigned int uInEntry; + + bool bOutNext = true; + STAmount saOutIn; + STAmount saOutOut; + uint256 uOutTip; + uint256 uOutEnd; + unsigned int uOutEntry; + + saPay.zero(); + saPay.setCurrency(uPrvCurrencyID); + saPay.setIssuer(uPrvIssuerID); + + saNeed = saWanted; + + if (!uCurCurrencyID && !uPrvCurrencyID) + { + // Bridging: Neither currency is XNS. + uInTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, CURRENCY_XNS, ACCOUNT_XNS); + uInEnd = Ledger::getQualityNext(uInTip); + uOutTip = Ledger::getBookBase(CURRENCY_XNS, ACCOUNT_XNS, uCurCurrencyID, uCurIssuerID); + uOutEnd = Ledger::getQualityNext(uInTip); + } + + // Find our head offer. + + bool bRedeeming = false; + bool bIssuing = false; + + // The price varies as we change between issuing and transfering, so unless bMultiQuality, we must stick with a mode once it + // is determined. + + if (bBridge && (bInNext || bOutNext)) + { + // Bridging and need to calculate next bridge rate. + // A bridge can consist of multiple offers. As offer's are consumed, the effective rate changes. + + if (bInNext) + { +// sleInDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uInIndex, uInEnd)); + // Get the next funded offer. + offerBridgeNext(uInIndex, uInEnd, uInEntry, saInIn, saInOut); // Get offer limited by funding. + bInNext = false; + } + + if (bOutNext) + { +// sleOutDir = entryCache(ltDIR_NODE, mLedger->getNextLedgerIndex(uOutIndex, uOutEnd)); + offerNext(uOutIndex, uOutEnd, uOutEntry, saOutIn, saOutOut); + bOutNext = false; + } + + if (!uInIndex || !uOutIndex) + { + bBridge = false; // No more offers to bridge. + } + else + { + // Have bridge in and out entries. + // Calculate bridge rate. Out offer pay ripple fee. In offer fee is added to in cost. + + saBridgeOut.zero(); + + if (saInOut < saOutIn) + { + // Limit by in. + + // XXX Need to include fees in saBridgeIn. + saBridgeIn = saInIn; // All of in + // Limit bridge out: saInOut/saBridgeOut = saOutIn/saOutOut + // Round such that we would take all of in offer, otherwise would have leftovers. + saBridgeOut = (saInOut * saOutOut) / saOutIn; + } + else if (saInOut > saOutIn) + { + // Limit by out, if at all. + + // XXX Need to include fees in saBridgeIn. + // Limit bridge in:saInIn/saInOuts = aBridgeIn/saOutIn + // Round such that would take all of out offer. + saBridgeIn = (saInIn * saOutIn) / saInOuts; + saBridgeOut = saOutOut; // All of out. + } + else + { + // Entries match, + + // XXX Need to include fees in saBridgeIn. + saBridgeIn = saInIn; // All of in + saBridgeOut = saOutOut; // All of out. + } + + uBridgeQuality = STAmount::getRate(saBridgeIn, saBridgeOut); // Inclusive of fees. + } + } + + if (bBridge) + { + bUseBridge = !uDirectTip || (uBridgeQuality < uDirectQuality) + } + else if (!!uDirectTip) + { + bUseBridge = false + } + else + { + // No more offers. Declare success, even if none returned. + saGot = saWanted-saNeed; + terResult = tesSUCCESS; + } + + if (tesSUCCESS != terResult) + { + STAmount& saAvailIn = bUseBridge ? saBridgeIn : saDirectIn; + STAmount& saAvailOut = bUseBridge ? saBridgeOut : saDirectOut; + + if (saAvailOut > saNeed) + { + // Consume part of offer. Done. + + saNeed = 0; + saPay += (saNeed*saAvailIn)/saAvailOut; // Round up, prefer to pay more. + } + else + { + // Consume entire offer. + + saNeed -= saAvailOut; + saPay += saAvailIn; + + if (bUseBridge) + { + // Consume bridge out. + if (saOutOut == saAvailOut) + { + // Consume all. + saOutOut = 0; + saOutIn = 0; + bOutNext = true; + } + else + { + // Consume portion of bridge out, must be consuming all of bridge in. + // saOutIn/saOutOut = saSpent/saAvailOut + // Round? + saOutIn -= (saOutIn*saAvailOut)/saOutOut; + saOutOut -= saAvailOut; + } + + // Consume bridge in. + if (saOutIn == saAvailIn) + { + // Consume all. + saInOut = 0; + saInIn = 0; + bInNext = true; + } + else + { + // Consume portion of bridge in, must be consuming all of bridge out. + // saInIn/saInOut = saAvailIn/saPay + // Round? + saInOut -= (saInOut*saAvailIn)/saInIn; + saInIn -= saAvailIn; + } + } + else + { + bDirectNext = true; + } + } + } +} +#endif + // vim:ts=4 diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 149c6289b7..2310551db6 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -138,7 +138,11 @@ enum TransactionEngineParams // Transaction can be retried, soft failures allowed }; -typedef struct { +class PaymentNode { +protected: + friend class TransactionEngine; + friend class PathState; + uint16 uFlags; // --> From path. uint160 uAccountID; // --> Accounts: Recieving/sending account. @@ -146,6 +150,8 @@ typedef struct { // --- For offer's next has currency out. uint160 uIssuerID; // --> Currency's issuer + STAmount saTransferRate; // Transfer rate for uIssuerID. + // Computed by Reverse. STAmount saRevRedeem; // <-- Amount to redeem to next. STAmount saRevIssue; // <-- Amount to issue to next limited by credit and outstanding IOUs. @@ -157,7 +163,27 @@ typedef struct { STAmount saFwdIssue; // <-- Amount node will issue to next. // Issue isn't used by offers. STAmount saFwdDeliver; // <-- Amount to deliver to next regardless of fee. -} paymentNode; + + // For offers: + + // Directory + uint256 uDirectTip; // Current directory. + uint256 uDirectEnd; // Next order book. + bool bDirectAdvance; // Need to advance directory. + SLE::pointer sleDirectDir; + STAmount saOfrRate; // For correct ratio. + + // Node + bool bEntryAdvance; // Need to advance entry. + unsigned int uEntry; + uint256 uOfferIndex; + SLE::pointer sleOffer; + uint160 uOfrOwnerID; + bool bFundsDirty; // Need to refresh saOfferFunds, saTakerPays, & saTakerGets. + STAmount saOfferFunds; + STAmount saTakerPays; + STAmount saTakerGets; +}; // account id, currency id, issuer id :: node typedef boost::tuple aciSource; @@ -165,9 +191,8 @@ typedef boost::unordered_map curIssuerNode; // Map typedef boost::unordered_map::const_iterator curIssuerNodeConstIterator; extern std::size_t hash_value(const aciSource& asValue); -// extern std::size_t hash_value(const boost::tuple& bt); -// Hold a path state under incremental application. +// Holds a path state under incremental application. class PathState { protected: @@ -180,12 +205,13 @@ public: typedef boost::shared_ptr pointer; TER terStatus; - std::vector vpnNodes; + std::vector vpnNodes; // When processing, don't want to complicate directory walking with deletion. std::vector vUnfundedBecame; // Offers that became unfunded or were completely consumed. - // First time working foward a funding source was mentioned for accounts. Source may only be used there. + // 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. // First time working in reverse a funding source was used. @@ -310,10 +336,23 @@ protected: PathState::pointer pathCreate(const STPath& spPath); void pathNext(const PathState::pointer& pspCur, const int iPaths); TER calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); + TER calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); + TER calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeOfferRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeOfferFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeAccountFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); + TER calcNodeAdvance(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); + TER calcNodeDeliver( + const unsigned int uIndex, + const PathState::pointer& pspCur, + const bool bMultiQuality, + const uint160& uInAccountID, + const STAmount& saInFunds, + const STAmount& saInReq, + STAmount& saInAct, + STAmount& saInFees); + void calcNodeRipple(const uint32 uQualityIn, const uint32 uQualityOut, const STAmount& saPrvReq, const STAmount& saCurReq, STAmount& saPrvAct, STAmount& saCurAct); From 1024af54b6455e35ff7b67d42227477a10050ac7 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Sun, 2 Sep 2012 21:32:52 -0700 Subject: [PATCH 04/17] Modify RPC and transactions to use quality flags for ripple. --- src/RPCServer.cpp | 59 +++++++++++++++------------------------ src/SerializedTypes.cpp | 4 +-- src/SerializedTypes.h | 6 ++-- src/Transaction.cpp | 10 +++++-- src/Transaction.h | 8 ++++-- src/TransactionEngine.cpp | 9 +++--- src/TransactionFormats.h | 3 +- 7 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/RPCServer.cpp b/src/RPCServer.cpp index 5c47ed30a3..cd79010c4a 100644 --- a/src/RPCServer.cpp +++ b/src/RPCServer.cpp @@ -1316,15 +1316,15 @@ Json::Value RPCServer::doPeers(const Json::Value& params) } // ripple -// [] // XXX [noredeem] [noissue] +// [] // + -// full|partial [] +// full|partial limit|average [] // // path: // path + // // path_element: -// account [] [] [noredeem] [noissue] +// account [] [] // offer [] Json::Value RPCServer::doRipple(const Json::Value ¶ms) { @@ -1333,10 +1333,10 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) uint160 uSrcCurrencyID; NewcoinAddress naSrcAccountID; NewcoinAddress naSrcIssuerID; - bool bSrcRedeem = true; - bool bSrcIssue = true; bool bPartial; bool bFull; + bool bLimit; + bool bAverage; NewcoinAddress naDstAccountID; STAmount saDstAmount; uint160 uDstCurrencyID; @@ -1366,18 +1366,6 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) int iArg = 4 + naSrcIssuerID.isValid(); - if (params[iArg].asString() == "noredeem") // [noredeem] - { - ++iArg; - bSrcRedeem = false; - } - - if (params[iArg].asString() == "noissue") // [noissue] - { - ++iArg; - bSrcIssue = false; - } - // XXX bSrcRedeem & bSrcIssue not used. STPath spPath; @@ -1417,8 +1405,6 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) NewcoinAddress naAccountID; uint160 uCurrencyID; NewcoinAddress naIssuerID; - bool bRedeem = true; - bool bIssue = true; ++iArg; @@ -1437,24 +1423,10 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) ++iArg; } - if (params.size() != iArg && params[iArg].asString() == "noredeem") // [noredeem] - { - ++iArg; - bRedeem = false; - } - - if (params.size() != iArg && params[iArg].asString() == "noissue") // [noissue] - { - ++iArg; - bIssue = false; - } - spPath.addElement(STPathElement( naAccountID.getAccountID(), uCurrencyID, - naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0), - bRedeem, - bIssue)); + naIssuerID.isValid() ? naIssuerID.getAccountID() : uint160(0))); } else { @@ -1486,6 +1458,19 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) ++iArg; } + // limit|average + bLimit = params.size() != iArg ? params[iArg].asString() == "limit" : false; + bAverage = params.size() != iArg ? params[iArg].asString() == "average" : false; + + if (!bPartial && !bFull) + { + return RPCError(rpcINVALID_PARAMS); + } + else + { + ++iArg; + } + if (params.size() != iArg && !naDstAccountID.setAccountID(params[iArg++].asString())) // { return RPCError(rpcDST_ACT_MALFORMED); @@ -1543,7 +1528,9 @@ Json::Value RPCServer::doRipple(const Json::Value ¶ms) naDstAccountID, saDstAmount, saSrcAmountMax, - spsPaths); + spsPaths, + bPartial, + bLimit); trans = mNetOps->submitTransaction(trans); @@ -2564,7 +2551,7 @@ Json::Value RPCServer::doCommand(const std::string& command, Json::Value& params { "password_fund", &RPCServer::doPasswordFund, 2, 3, false, optCurrent }, { "password_set", &RPCServer::doPasswordSet, 2, 3, false, optNetwork }, { "peers", &RPCServer::doPeers, 0, 0, true }, - { "ripple", &RPCServer::doRipple, 8, -1, false, optCurrent|optClosed }, + { "ripple", &RPCServer::doRipple, 9, -1, false, optCurrent|optClosed }, { "ripple_lines_get", &RPCServer::doRippleLinesGet, 1, 2, false, optCurrent }, { "ripple_line_set", &RPCServer::doRippleLineSet, 4, 7, false, optCurrent }, { "send", &RPCServer::doSend, 3, 9, false, optCurrent }, diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index 16572e5d78..4684345496 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -332,8 +332,6 @@ STPathSet* STPathSet::construct(SerializerIterator& s, const char *name) else { const bool bAccount = !!(iType & STPathElement::typeAccount); - const bool bRedeem = !!(iType & STPathElement::typeRedeem); - const bool bIssue = !!(iType & STPathElement::typeIssue); const bool bCurrency = !!(iType & STPathElement::typeCurrency); const bool bIssuer = !!(iType & STPathElement::typeIssuer); @@ -350,7 +348,7 @@ STPathSet* STPathSet::construct(SerializerIterator& s, const char *name) if (bIssuer) uIssuerID = s.get160(); - path.push_back(STPathElement(uAccountID, uCurrency, uIssuerID, bRedeem, bIssue)); + path.push_back(STPathElement(uAccountID, uCurrency, uIssuerID)); } } while(1); } diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 065c6895d7..4baa4e9b40 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -564,15 +564,13 @@ protected: uint160 mIssuerID; public: - STPathElement(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID, bool bRedeem=false, bool bIssue=false) + STPathElement(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID) : mAccountID(uAccountID), mCurrencyID(uCurrencyID), mIssuerID(uIssuerID) { mType = (uAccountID.isZero() ? 0 : STPathElement::typeAccount) | (uCurrencyID.isZero() ? 0 : STPathElement::typeCurrency) - | (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer) - | (bRedeem ? STPathElement::typeRedeem : 0) - | (bIssue ? STPathElement::typeIssue : 0); + | (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer); } int getNodeType() const { return mType; } diff --git a/src/Transaction.cpp b/src/Transaction.cpp index 38aec72865..00988712d9 100644 --- a/src/Transaction.cpp +++ b/src/Transaction.cpp @@ -489,7 +489,9 @@ Transaction::pointer Transaction::setPayment( const NewcoinAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, - const STPathSet& spsPaths) + const STPathSet& spsPaths, + const bool bPartial, + const bool bLimit) { mTransaction->setITFieldAccount(sfDestination, naDstAccountID); mTransaction->setITFieldAmount(sfAmount, saAmount); @@ -518,11 +520,13 @@ Transaction::pointer Transaction::sharedPayment( const NewcoinAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, - const STPathSet& spsPaths) + const STPathSet& spsPaths, + const bool bPartial, + const bool bLimit) { pointer tResult = boost::make_shared(ttPAYMENT, naPublicKey, naSourceAccount, uSeq, saFee, uSourceTag); - return tResult->setPayment(naPrivateKey, naDstAccountID, saAmount, saSendMax, spsPaths); + return tResult->setPayment(naPrivateKey, naDstAccountID, saAmount, saSendMax, spsPaths, bPartial, bLimit); } // diff --git a/src/Transaction.h b/src/Transaction.h index 7198ef04ea..1ae8c0aa43 100644 --- a/src/Transaction.h +++ b/src/Transaction.h @@ -116,7 +116,9 @@ private: const NewcoinAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, - const STPathSet& spsPaths); + const STPathSet& spsPaths, + const bool bPartial, + const bool bLimit); Transaction::pointer setWalletAdd( const NewcoinAddress& naPrivateKey, @@ -231,7 +233,9 @@ public: const NewcoinAddress& naDstAccountID, const STAmount& saAmount, const STAmount& saSendMax, - const STPathSet& spsPaths); + const STPathSet& spsPaths, + const bool bPartial = false, + const bool bLimit = false); // Place an offer. static Transaction::pointer sharedOfferCreate( diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index b7cc2200d2..947b53f50d 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -2770,11 +2770,12 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS return terResult; } -// The previous node: specifies what to push through to current. +// Perfrom balance adjustments between previous and current node. +// - The previous node: specifies what to push through to current. // - All of previous output is consumed. -// The current node: specify what to push through to next. -// - Output to next node minus fees. -// Perform balance adjustment with previous. +// Then, compute 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. TER TransactionEngine::calcNodeAccountFwd( const unsigned int uIndex, // 0 <= uIndex <= uLast const PathState::pointer& pspCur, diff --git a/src/TransactionFormats.h b/src/TransactionFormats.h index a49a1513e1..6aadec3a68 100644 --- a/src/TransactionFormats.h +++ b/src/TransactionFormats.h @@ -45,7 +45,8 @@ const uint32 tfPassive = 0x00010000; // Payment flags: const uint32 tfCreateAccount = 0x00010000; const uint32 tfPartialPayment = 0x00020000; -const uint32 tfNoRippleDirect = 0x00040000; +const uint32 tfLimitQuality = 0x00040000; +const uint32 tfNoRippleDirect = 0x00080000; extern TransactionFormat InnerTxnFormats[]; extern TransactionFormat* getTxnFormat(TransactionType t); From 61831eaa3c76e340a9b8e9d49b68bef5de9196ff Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 3 Sep 2012 13:57:01 -0700 Subject: [PATCH 05/17] Obsolete redeem and issue flag support from ripple paths. --- src/Amount.cpp | 2 + src/SerializedTypes.h | 2 + src/TransactionEngine.cpp | 251 ++++++++++++++++++-------------------- src/TransactionEngine.h | 11 +- 4 files changed, 129 insertions(+), 137 deletions(-) diff --git a/src/Amount.cpp b/src/Amount.cpp index b1588dc05a..3391f470ff 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -10,6 +10,8 @@ #include "SerializedTypes.h" #include "utils.h" +uint64 STAmount::uRateOne = STAmount::getRate(STAmount(1), STAmount(1)); + bool STAmount::currencyFromString(uint160& uDstCurrency, const std::string& sCurrency) { bool bSuccess = true; diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 4baa4e9b40..8f8718fa0c 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -254,6 +254,8 @@ protected: static uint64 muldiv(uint64, uint64, uint64); public: + static uint64 uRateOne; + STAmount(uint64 v = 0, bool isNeg = false) : mValue(v), mOffset(0), mIsNative(true), mIsNegative(isNeg) { if (v==0) mIsNegative = false; } diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 947b53f50d..7ff93aaca9 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -1048,7 +1048,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, if (tesSUCCESS == terResult) { - if (!!saCost) + if (saCost) { // Only check fee is sufficient when the ledger is open. if (isSetBit(params, tapOPEN_LEDGER) && saPaid < saCost) @@ -1060,7 +1060,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, } else { - if (!!saPaid) + if (saPaid) { // Transaction is malformed. Log(lsWARNING) << "applyTransaction: fee not allowed"; @@ -1210,7 +1210,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, { nothing(); } - else if (!!saCost) + else if (saCost) { uint32 a_seq = mTxnAccount->getIFieldU32(sfSequence); @@ -1670,7 +1670,7 @@ TER TransactionEngine::doNicknameSet(const SerializedTransaction& txn) // Edit old entry. sleNickname->setIFieldAccount(sfAccount, mTxnAccountID); - if (bMinOffer && !!saMinOffer) + if (bMinOffer && saMinOffer) { sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); } @@ -1692,7 +1692,7 @@ TER TransactionEngine::doNicknameSet(const SerializedTransaction& txn) sleNickname->setIFieldAccount(sfAccount, mTxnAccountID); - if (bMinOffer && !!saMinOffer) + if (bMinOffer && saMinOffer) sleNickname->setIFieldAmount(sfMinimumOffer, saMinOffer); } @@ -2376,7 +2376,8 @@ void TransactionEngine::calcNodeRipple( const STAmount& saPrvReq, // --> in limit including fees, <0 = unlimited const STAmount& saCurReq, // --> out limit (driver) STAmount& saPrvAct, // <-> in limit including achieved - STAmount& saCurAct) // <-> out limit achieved. + STAmount& saCurAct, // <-> out limit achieved. + uint64& uRateMax) { Log(lsINFO) << boost::str(boost::format("calcNodeRipple> uQualityIn=%d uQualityOut=%d saPrvReq=%s saCurReq=%s saPrvAct=%s saCurAct=%s") % uQualityIn @@ -2404,38 +2405,52 @@ void TransactionEngine::calcNodeRipple( // No fee. Log(lsINFO) << boost::str(boost::format("calcNodeRipple: No fees")); - STAmount saTransfer = bPrvUnlimited ? saCur : std::min(saPrv, saCur); + if (!uRateMax || STAmount::uRateOne <= uRateMax) + { + STAmount saTransfer = bPrvUnlimited ? saCur : std::min(saPrv, saCur); - saPrvAct += saTransfer; - saCurAct += saTransfer; + saPrvAct += saTransfer; + saCurAct += saTransfer; + + if (!uRateMax) + uRateMax = STAmount::uRateOne; + } } else { // Fee. Log(lsINFO) << boost::str(boost::format("calcNodeRipple: Fee")); - const uint160 uCurrencyID = saCur.getCurrency(); - const uint160 uCurIssuerID = saCur.getIssuer(); - const uint160 uPrvIssuerID = saPrv.getIssuer(); + uint64 uRate = STAmount::getRate(STAmount(uQualityIn), STAmount(uQualityOut)); - STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID, uCurIssuerID), uQualityIn, uCurrencyID, uCurIssuerID); - -Log(lsINFO) << boost::str(boost::format("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCurIn=%s") % bPrvUnlimited % saPrv.getFullText() % saCurIn.getFullText()); - if (bPrvUnlimited || saCurIn <= saPrv) + if (!uRateMax || uRate <= uRateMax) { - // All of cur. Some amount of prv. - saCurAct += saCur; - saPrvAct += saCurIn; -Log(lsINFO) << boost::str(boost::format("calcNodeRipple:3c: saCurReq=%s saPrvAct=%s") % saCurReq.getFullText() % saPrvAct.getFullText()); - } - else - { - // A part of cur. All of prv. (cur as driver) - STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, uCurrencyID, uCurIssuerID), uQualityOut, uCurrencyID, uCurIssuerID); -Log(lsINFO) << boost::str(boost::format("calcNodeRipple:4: saCurReq=%s") % saCurReq.getFullText()); + const uint160 uCurrencyID = saCur.getCurrency(); + const uint160 uCurIssuerID = saCur.getIssuer(); + const uint160 uPrvIssuerID = saPrv.getIssuer(); - saCurAct += saCurOut; - saPrvAct = saPrvReq; + STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID, uCurIssuerID), uQualityIn, uCurrencyID, uCurIssuerID); + + Log(lsINFO) << boost::str(boost::format("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCurIn=%s") % bPrvUnlimited % saPrv.getFullText() % saCurIn.getFullText()); + if (bPrvUnlimited || saCurIn <= saPrv) + { + // All of cur. Some amount of prv. + saCurAct += saCur; + saPrvAct += saCurIn; + Log(lsINFO) << boost::str(boost::format("calcNodeRipple:3c: saCurReq=%s saPrvAct=%s") % saCurReq.getFullText() % saPrvAct.getFullText()); + } + else + { + // A part of cur. All of prv. (cur as driver) + STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, uCurrencyID, uCurIssuerID), uQualityOut, uCurrencyID, uCurIssuerID); + Log(lsINFO) << boost::str(boost::format("calcNodeRipple:4: saCurReq=%s") % saCurReq.getFullText()); + + saCurAct += saCurOut; + saPrvAct = saPrvReq; + + if (!uRateMax) + uRateMax = uRate; + } } } @@ -2455,14 +2470,13 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS TER terResult = tesSUCCESS; const unsigned int uLast = pspCur->vpnNodes.size() - 1; + uint64 uRateMax = 0; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; - const bool bRedeem = isSetBit(pnCur.uFlags, STPathElement::typeRedeem); - const bool bPrvRedeem = isSetBit(pnPrv.uFlags, STPathElement::typeRedeem); - const bool bIssue = isSetBit(pnCur.uFlags, STPathElement::typeIssue); - const bool bPrvIssue = isSetBit(pnPrv.uFlags, STPathElement::typeIssue); + // Current is allowed to redeem to next. const bool bPrvAccount = !uIndex || isSetBit(pnPrv.uFlags, STPathElement::typeAccount); const bool bNxtAccount = uIndex == uLast || isSetBit(pnNxt.uFlags, STPathElement::typeAccount); @@ -2483,10 +2497,13 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID, uCurAccountID); - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev> uIndex=%d/%d bPrvIssue=%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvOwed=%s saPrvLimit=%s") + const STAmount saNxtOwed = uIndex != uLast && bNxtAccount // Next account is owed. + ? rippleOwed(uCurAccountID, uNxtAccountID, uCurrencyID) + : STAmount(uCurrencyID, uCurAccountID); + + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev> uIndex=%d/%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvOwed=%s saPrvLimit=%s") % uIndex % uLast - % bPrvIssue % NewcoinAddress::createHumanAccountID(uPrvAccountID) % NewcoinAddress::createHumanAccountID(uCurAccountID) % NewcoinAddress::createHumanAccountID(uNxtAccountID) @@ -2497,11 +2514,11 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS % saPrvLimit.getFullText()); // Previous can redeem the owed IOUs it holds. - const STAmount saPrvRedeemReq = bPrvRedeem && saPrvOwed.isPositive() ? saPrvOwed : STAmount(uCurrencyID, 0); + const STAmount saPrvRedeemReq = saPrvOwed.isPositive() ? saPrvOwed : STAmount(uCurrencyID, 0); STAmount& saPrvRedeemAct = pnPrv.saRevRedeem; // Previous can issue up to limit minus whatever portion of limit already used (not including redeemable amount). - const STAmount saPrvIssueReq = bPrvIssue && saPrvOwed.isNegative() ? saPrvLimit+saPrvOwed : saPrvLimit; + const STAmount saPrvIssueReq = saPrvOwed.isNegative() ? saPrvLimit+saPrvOwed : saPrvLimit; STAmount& saPrvIssueAct = pnPrv.saRevIssue; // For !bPrvAccount @@ -2546,25 +2563,26 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS % saCurWantedReq.getFullText()); // Calculate redeem - if (bRedeem - && saPrvRedeemReq) // Previous has IOUs to redeem. + if (saPrvRedeemReq) // Previous has IOUs to redeem. { // Redeem at 1:1 Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Redeem at 1:1")); saCurWantedAct = std::min(saPrvRedeemReq, saCurWantedReq); saPrvRedeemAct = saCurWantedAct; + + uRateMax = STAmount::uRateOne; } // Calculate issuing. - if (bIssue - && saCurWantedReq != saCurWantedAct // Need more. - && saPrvIssueReq) // Will accept IOUs. + if (saCurWantedReq != saCurWantedAct // Need more. + && saPrvIssueReq) // Will accept IOUs from prevous. { // Rate: quality in : 1.0 Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0")); - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq, saPrvIssueAct, saCurWantedAct); + // If we previously redeemed and this has a poorer rate, this won't be included the current increment. + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq, saPrvIssueAct, saCurWantedAct, uRateMax); } if (!saCurWantedAct) @@ -2578,53 +2596,46 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS // ^|account --> ACCOUNT --> account // redeem (part 1) -> redeem - if (bPrvRedeem - && bRedeem // Allowed to redeem. - && saCurRedeemReq // Next wants us to redeem. - && saPrvOwed) // Previous has IOUs to redeem. + if (saCurRedeemReq // Next wants us to redeem. + && saPrvRedeemReq) // Previous has IOUs to redeem. { // Rate : 1.0 : quality out Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : quality out")); - calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct); - } - - // redeem (part 2) -> issue. - if (bPrvRedeem - && bIssue // Allowed to issue. - && saCurRedeemReq != saCurRedeemAct // Can only if issue if more can not be redeemed. - && saPrvOwed // Previous still has IOUs. - && saCurIssueReq) // Need some issued. - { - // Rate : 1.0 : transfer_rate - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : transfer_rate")); - - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct); + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax); } // issue (part 1)-> redeem - if (bPrvIssue - && bRedeem // Allowed to redeem. - && saCurRedeemReq != saCurRedeemAct // Can only redeem if more to be redeemed. - && !saPrvOwed.isPositive()) // Previous has no IOUs. + if (saCurRedeemReq != saCurRedeemAct // More to redeem. + && saPrvRedeemReq == saPrvRedeemAct) // Previously redeemed all owed IOUs. { // Rate: quality in : quality out Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : quality out")); - calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct); + calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax); + } + + // redeem (part 2) -> issue. + if (saCurIssueReq // Need some issued. + && saCurRedeemAct == saNxtOwed // Can only issue if previously redeemed all owed. + && saPrvOwed.isPositive() // Previous has IOUs to redeem. + && saPrvRedeemAct != saPrvOwed) // Did not previously redeem all IOUs. + { + // Rate : 1.0 : transfer_rate + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : transfer_rate")); + + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax); } // issue (part 2) -> issue - if (bPrvIssue - && bIssue // Allowed to issue. - && saCurRedeemReq == saCurRedeemAct // Can only if issue if more can not be redeemed. - && !saPrvOwed.isPositive() // Previous has no IOUs. - && saCurIssueReq != saCurIssueAct) // Need some issued. + if (saCurIssueReq != saCurIssueAct // Need some issued. + && saCurRedeemAct == saNxtOwed // Can only issue if previously redeemed all owed. + && saPrvRedeemReq == saPrvRedeemAct) // Previously redeemed all owed IOUs. { // Rate: quality in : 1.0 Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0")); - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax); } if (!saCurRedeemAct && !saCurIssueAct) @@ -2633,11 +2644,7 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS terResult = tepPATH_DRY; } - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: ^|account --> ACCOUNT --> account : bPrvRedeem=%d bPrvIssue=%d bRedeem=%d bIssue=%d saCurRedeemReq=%s saCurIssueReq=%s saPrvOwed=%s saCurRedeemAct=%s saCurIssueAct=%s") - % bPrvRedeem - % bPrvIssue - % bRedeem - % bIssue + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: ^|account --> ACCOUNT --> account : saCurRedeemReq=%s saCurIssueReq=%s saPrvOwed=%s saCurRedeemAct=%s saCurIssueAct=%s") % saCurRedeemReq.getFullText() % saCurIssueReq.getFullText() % saPrvOwed.getFullText() @@ -2652,24 +2659,19 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> offer")); // redeem -> deliver/issue. - if (bPrvRedeem - && bIssue // Allowed to issue. - && saPrvOwed.isPositive() // Previous redeeming: Previous still has IOUs. + if (saPrvOwed.isPositive() // Previous has IOUs to redeem. && saCurDeliverReq) // Need some issued. { // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct); + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct, uRateMax); } // issue -> deliver/issue - if (bPrvIssue - && bIssue // Allowed to issue. - && (!saPrvOwed.isPositive() // Previous issuing: Never had IOUs. - || saPrvOwed == saPrvRedeemAct) // Previous issuing: Previous has no IOUs left after redeeming. - && saCurDeliverReq != saCurDeliverAct) // Need some issued. + if (saPrvRedeemReq == saPrvRedeemAct // Previously redeemed all owed. + && saCurDeliverReq != saCurDeliverAct) // Still need some issued. { // Rate: quality in : 1.0 - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct, uRateMax); } if (!saCurDeliverAct) @@ -2678,11 +2680,7 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS terResult = tepPATH_DRY; } - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: bPrvRedeem=%d bPrvIssue=%d bRedeem=%d bIssue=%d saCurDeliverReq=%s saCurDeliverAct=%s saPrvOwed=%s") - % bPrvRedeem - % bPrvIssue - % bRedeem - % bIssue + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: saCurDeliverReq=%s saCurDeliverAct=%s saPrvOwed=%s") % saCurDeliverReq.getFullText() % saCurDeliverAct.getFullText() % saPrvOwed.getFullText()); @@ -2694,14 +2692,14 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS // offer --> ACCOUNT --> $ const STAmount& saCurWantedReq = bPrvAccount ? std::min(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. - : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. + : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> $ : saCurWantedReq=%s") % saCurWantedReq.getFullText()); // Rate: quality in : 1.0 - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq, saPrvDeliverAct, saCurWantedAct); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq, saPrvDeliverAct, saCurWantedAct, uRateMax); if (!saCurWantedAct) { @@ -2716,27 +2714,23 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> account")); // deliver -> redeem - if (bRedeem // Allowed to redeem. - && saCurRedeemReq) // Next wants us to redeem. + if (saCurRedeemReq) // Next wants us to redeem. { // Rate : 1.0 : quality out - calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct); + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct, uRateMax); } // deliver -> issue. - if (bIssue // Allowed to issue. - && saCurRedeemReq == saCurRedeemAct // Can only if issue if more can not be redeemed. + if (saCurRedeemReq == saCurRedeemAct // Can only issue if previously redeemed all. && saCurIssueReq) // Need some issued. { // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, saCurIssueAct); + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, saCurIssueAct, uRateMax); } - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: bRedeem=%d saCurRedeemReq=%s saCurIssueAct=%s bIssue=%d saCurIssueReq=%s saPrvDeliverAct=%s") - % bRedeem + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: saCurRedeemReq=%s saCurIssueAct=%s saCurIssueReq=%s saPrvDeliverAct=%s") % saCurRedeemReq.getFullText() % saCurRedeemAct.getFullText() - % bIssue % saCurIssueReq.getFullText() % saPrvDeliverAct.getFullText()); @@ -2753,12 +2747,8 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS // deliver/redeem -> deliver/issue. Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> offer")); - if (bIssue // Allowed to issue. - && saCurDeliverReq != saCurDeliverAct) // Can only if issue if more can not be redeemed. - { - // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct); - } + // Rate : 1.0 : transfer_rate + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct, uRateMax); if (!saCurDeliverAct) { @@ -2784,6 +2774,8 @@ TER TransactionEngine::calcNodeAccountFwd( TER terResult = tesSUCCESS; const unsigned int uLast = pspCur->vpnNodes.size() - 1; + uint64 uRateMax = 0; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; @@ -2921,7 +2913,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saPrvRedeemReq != saPrvRedeemAct) // Previous wants to redeem. To next must be ok. { // Rate : 1.0 : quality out - calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct); + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax); } // Previous redeem part 2: redeem -> issue. @@ -2933,7 +2925,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saCurIssueReq) { // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct); + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax); } // Previous issue part 1: issue -> redeem @@ -2942,7 +2934,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saCurRedeemReq != saCurRedeemAct) // Current has more to redeem to next. { // Rate: quality in : quality out - calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct); + calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax); } // Previous issue part 2 : issue -> issue @@ -2950,7 +2942,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saPrvIssueReq != saPrvIssueAct) // Previous wants to issue. To next must be ok. { // Rate: quality in : 1.0 - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax); } // Adjust prv --> cur balance : take all inbound @@ -2969,7 +2961,7 @@ TER TransactionEngine::calcNodeAccountFwd( if (saPrvRedeemReq) // Previous wants to redeem. { // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct); + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurDeliverReq, saPrvRedeemAct, saCurDeliverAct, uRateMax); } // issue -> issue @@ -2977,7 +2969,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saPrvIssueReq) // Previous wants to issue. To next must be ok. { // Rate: quality in : 1.0 - calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurDeliverReq, saPrvIssueAct, saCurDeliverAct, uRateMax); } // Adjust prv --> cur balance : take all inbound @@ -3008,7 +3000,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saPrvDeliverReq) // Previous wants to deliver. { // Rate : 1.0 : quality out - calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct); + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct, uRateMax); } // deliver -> issue @@ -3019,7 +3011,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saCurIssueReq) // Current wants issue. { // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, saCurIssueAct); + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurIssueReq, saPrvDeliverAct, saCurIssueAct, uRateMax); } // No income balance adjustments necessary. The paying side inside the offer paid and the next link will receive. @@ -3036,7 +3028,7 @@ TER TransactionEngine::calcNodeAccountFwd( && saCurIssueReq) // Current wants issue. { // Rate : 1.0 : transfer_rate - calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct); + calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvDeliverReq, saCurDeliverReq, saPrvDeliverAct, saCurDeliverAct, uRateMax); } // No income balance adjustments necessary. The paying side inside the offer paid and the next link will receive. @@ -3270,8 +3262,7 @@ PathState::PathState( const uint160& uReceiverID, const uint160& uSenderID, const STAmount& saSend, - const STAmount& saSendMax, - const bool bPartialPayment + const STAmount& saSendMax ) : mLedger(lpLedger), mIndex(iIndex), uQuality(0) { @@ -3412,16 +3403,16 @@ Json::Value PathState::getJson() const jvPathState["index"] = mIndex; jvPathState["nodes"] = jvNodes; - if (!!saInReq) + if (saInReq) jvPathState["in_req"] = saInReq.getJson(0); - if (!!saInAct) + if (saInAct) jvPathState["in_act"] = saInAct.getJson(0); - if (!!saOutReq) + if (saOutReq) jvPathState["out_req"] = saOutReq.getJson(0); - if (!!saOutAct) + if (saOutAct) jvPathState["out_act"] = saOutAct.getJson(0); if (uQuality) @@ -3539,8 +3530,8 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) // Ripple if source or destination is non-native or if there are paths. const uint32 uTxFlags = txn.getFlags(); const bool bCreate = isSetBit(uTxFlags, tfCreateAccount); - const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); const bool bPartialPayment = isSetBit(uTxFlags, tfPartialPayment); + const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); const bool bPaths = txn.getITFieldPresent(sfPaths); const bool bMax = txn.getITFieldPresent(sfSendMax); const uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); @@ -3673,8 +3664,7 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) uDstAccountID, mTxnAccountID, saDstAmount, - saMaxAmount, - bPartialPayment); + saMaxAmount); if (pspDirect) { @@ -3706,8 +3696,7 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) uDstAccountID, mTxnAccountID, saDstAmount, - saMaxAmount, - bPartialPayment); + saMaxAmount); if (pspExpanded) { @@ -3917,7 +3906,7 @@ TER TransactionEngine::takeOffers( STAmount& saTakerPaid, STAmount& saTakerGot) { - assert(!!saTakerPays && !!saTakerGets); + assert(saTakerPays && saTakerGets); Log(lsINFO) << "takeOffers: against book: " << uBookBase.ToString(); @@ -4265,8 +4254,8 @@ Log(lsWARNING) << "doOfferCreate: saTakerGets=" << saTakerGets.getFullText(); // Log(lsWARNING) << "doOfferCreate: takeOffers: uGetsIssuerID=" << NewcoinAddress::createHumanAccountID(uGetsIssuerID); if (tesSUCCESS == terResult - && !!saTakerPays // Still wanting something. - && !!saTakerGets // Still offering something. + && saTakerPays // Still wanting something. + && saTakerGets // Still offering something. && accountFunds(mTxnAccountID, saTakerGets).isPositive()) // Still funded. { // We need to place the remainder of the offer into its order book. diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 2310551db6..4d8aae4148 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -235,8 +235,7 @@ public: const uint160& uReceiverID, const uint160& uSenderID, const STAmount& saSend, - const STAmount& saSendMax, - const bool bPartialPayment + const STAmount& saSendMax ); Json::Value getJson() const; @@ -249,11 +248,10 @@ public: const uint160& uReceiverID, const uint160& uSenderID, const STAmount& saSend, - const STAmount& saSendMax, - const bool bPartialPayment + const STAmount& saSendMax ) { - return boost::make_shared(lpLedger, iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax, bPartialPayment); + return boost::make_shared(lpLedger, iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax); } static bool lessPriority(const PathState::pointer& lhs, const PathState::pointer& rhs); @@ -355,7 +353,8 @@ protected: void calcNodeRipple(const uint32 uQualityIn, const uint32 uQualityOut, const STAmount& saPrvReq, const STAmount& saCurReq, - STAmount& saPrvAct, STAmount& saCurAct); + STAmount& saPrvAct, STAmount& saCurAct, + uint64& uRateMax); void txnWrite(); From 8910a3e14b7659bb8eb5fde09646c9157b06416a Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 3 Sep 2012 14:15:31 -0700 Subject: [PATCH 06/17] Implement quality limit for ripple. --- src/TransactionEngine.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 7ff93aaca9..92f8c31514 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -3531,6 +3531,7 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) const uint32 uTxFlags = txn.getFlags(); const bool bCreate = isSetBit(uTxFlags, tfCreateAccount); const bool bPartialPayment = isSetBit(uTxFlags, tfPartialPayment); + const bool bLimitQuality = isSetBit(uTxFlags, tfLimitQuality); const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); const bool bPaths = txn.getITFieldPresent(sfPaths); const bool bMax = txn.getITFieldPresent(sfSendMax); @@ -3731,7 +3732,8 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) STAmount saPaid; STAmount saWanted; - LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid. + LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid. + uint64 uQualityLimit = STAmount::getRate(saDstAmount, saMaxAmount); while (temUNCERTAIN == terResult) { @@ -3748,8 +3750,12 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) mNodes.swapWith(pspCur->lesEntries); // For the path, save ledger state. - if (!pspBest || (pspCur->uQuality && PathState::lessPriority(pspBest, pspCur))) + if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. + || !pspBest // Best is not yet set. + || (pspCur->uQuality && PathState::lessPriority(pspBest, pspCur))) // Current is better than set. + { pspBest = pspCur; + } } if (pspBest) From 74392e59291736a0a9386437f3ff839384c7eaeb Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 3 Sep 2012 14:28:45 -0700 Subject: [PATCH 07/17] Provide seperate ledger entry to state to ripple reverse. --- src/TransactionEngine.cpp | 42 ++++++++++++++++++++------------------- src/TransactionEngine.h | 2 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 92f8c31514..e81ffee320 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -3481,12 +3481,6 @@ TER TransactionEngine::calcNodeRev(const unsigned int uIndex, const PathState::p terResult = calcNodeRev(uIndex-1, pspCur, bMultiQuality); } - else - { - // Do forward. - - terResult = calcNodeFwd(0, pspCur, bMultiQuality); - } Log(lsINFO) << boost::str(boost::format("calcNodeRev< uIndex=%d terResult=%d") % uIndex % terResult); @@ -3496,12 +3490,12 @@ TER TransactionEngine::calcNodeRev(const unsigned int uIndex, const PathState::p // 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. // <-- pspCur->uQuality -void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPaths) +void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint) { // The next state is what is available in preference order. // This is calculated when referenced accounts changed. - - unsigned int uLast = pspCur->vpnNodes.size() - 1; + const bool bMultiQuality = iPaths == 1; + const unsigned int uLast = pspCur->vpnNodes.size() - 1; Log(lsINFO) << "Path In: " << pspCur->getJson(); @@ -3510,7 +3504,19 @@ void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPa pspCur->vUnfundedBecame.clear(); pspCur->umReverse.clear(); - pspCur->terStatus = calcNodeRev(uLast, pspCur, iPaths == 1); + mNodes = lesCheckpoint; // Restore from checkpoint. + mNodes.bumpSeq(); // Begin ledger varance. + + pspCur->terStatus = calcNodeRev(uLast, pspCur, bMultiQuality); + + if (tesSUCCESS == pspCur->terStatus) + { + // Do forward. + mNodes = lesCheckpoint; // Restore from checkpoint. + mNodes.bumpSeq(); // Begin ledger varance. + + pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality); + } Log(lsINFO) << "pathNext: terStatus=" << pspCur->terStatus @@ -3518,8 +3524,8 @@ void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPa << " saInAct=" << pspCur->saInAct.getText(); pspCur->uQuality = tesSUCCESS == pspCur->terStatus - ? STAmount::getRate(pspCur->saOutAct, pspCur->saInAct) // Calculate relative quality. - : 0; // Mark path as inactive. + ? STAmount::getRate(pspCur->saOutAct, pspCur->saInAct) // Calculate relative quality. + : 0; // Mark path as inactive. Log(lsINFO) << "Path Out: " << pspCur->getJson(); } @@ -3732,8 +3738,8 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) STAmount saPaid; STAmount saWanted; - LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid. - uint64 uQualityLimit = STAmount::getRate(saDstAmount, saMaxAmount); + LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid. + uint64 uQualityLimit = bLimitQuality ? STAmount::getRate(saDstAmount, saMaxAmount) : 0; while (temUNCERTAIN == terResult) { @@ -3743,17 +3749,13 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) // Find the best path. BOOST_FOREACH(PathState::pointer pspCur, vpsPaths) { - mNodes = lesCheckpoint; // Restore from checkpoint. - mNodes.bumpSeq(); // Begin ledger varance. - - pathNext(pspCur, vpsPaths.size()); // Compute increment - - mNodes.swapWith(pspCur->lesEntries); // For the path, save ledger state. + pathNext(pspCur, vpsPaths.size(), lesCheckpoint); // Compute increment. if ((!bLimitQuality || pspCur->uQuality <= uQualityLimit) // Quality is not limted or increment has allowed quality. || !pspBest // Best is not yet set. || (pspCur->uQuality && PathState::lessPriority(pspBest, pspCur))) // Current is better than set. { + mNodes.swapWith(pspCur->lesEntries); // For the path, save ledger state. pspBest = pspCur; } } diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 4d8aae4148..2dcd04e2fa 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -332,7 +332,7 @@ protected: STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault); PathState::pointer pathCreate(const STPath& spPath); - void pathNext(const PathState::pointer& pspCur, const int iPaths); + void pathNext(const PathState::pointer& pspCur, const int iPaths, const LedgerEntrySet& lesCheckpoint); TER calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); From dda279e5a62c4c0df76f19807aaeec13abb52dfd Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 3 Sep 2012 14:37:00 -0700 Subject: [PATCH 08/17] Remove remaining support for redeem and issue flags. --- src/SerializedTypes.h | 4 - src/TransactionEngine.cpp | 217 +++++++++++++++----------------------- 2 files changed, 87 insertions(+), 134 deletions(-) diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 8f8718fa0c..b875acffb1 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -545,15 +545,11 @@ public: enum { typeEnd = 0x00, typeAccount = 0x01, // Rippling through an account (vs taking an offer). - typeRedeem = 0x04, // Redeem IOUs. - typeIssue = 0x08, // Issue IOUs. typeCurrency = 0x10, // Currency follows. typeIssuer = 0x20, // Issuer follows. typeBoundary = 0xFF, // Boundary between alternate paths. typeValidBits = ( typeAccount - | typeRedeem - | typeIssue | typeCurrency | typeIssuer ), // Bits that may be non-zero. diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index e81ffee320..d9d94fefcf 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -2780,8 +2780,6 @@ TER TransactionEngine::calcNodeAccountFwd( PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; - const bool bRedeem = isSetBit(pnCur.uFlags, STPathElement::typeRedeem); - const bool bIssue = isSetBit(pnCur.uFlags, STPathElement::typeIssue); const bool bPrvAccount = isSetBit(pnPrv.uFlags, STPathElement::typeAccount); const bool bNxtAccount = isSetBit(pnNxt.uFlags, STPathElement::typeAccount); @@ -2816,11 +2814,9 @@ TER TransactionEngine::calcNodeAccountFwd( const STAmount& saCurDeliverReq = pnCur.saRevDeliver; STAmount& saCurDeliverAct = pnCur.saFwdDeliver; - Log(lsINFO) << boost::str(boost::format("calcNodeAccountFwd> uIndex=%d/%d bRedeem=%d bIssue=%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s") + Log(lsINFO) << boost::str(boost::format("calcNodeAccountFwd> uIndex=%d/%d saPrvRedeemReq=%s saPrvIssueReq=%s saPrvDeliverReq=%s saCurRedeemReq=%s saCurIssueReq=%s saCurDeliverReq=%s") % uIndex % uLast - % bRedeem - % bIssue % saPrvRedeemReq.getFullText() % saPrvIssueReq.getFullText() % saPrvDeliverReq.getFullText() @@ -2909,18 +2905,24 @@ TER TransactionEngine::calcNodeAccountFwd( Log(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> account")); // Previous redeem part 1: redeem -> redeem - if (bRedeem // Can redeem. - && saPrvRedeemReq != saPrvRedeemAct) // Previous wants to redeem. To next must be ok. + if (saPrvRedeemReq != saPrvRedeemAct) // Previous wants to redeem. To next must be ok. { // Rate : 1.0 : quality out calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax); } + // Previous issue part 1: issue -> redeem + if (saPrvIssueReq != saPrvIssueAct // Previous wants to issue. + && saCurRedeemReq != saCurRedeemAct) // Current has more to redeem to next. + { + // Rate: quality in : quality out + calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax); + } + // Previous redeem part 2: redeem -> issue. // wants to redeem and current would and can issue. // If redeeming cur to next is done, this implies can issue. - if (bIssue // Can issue. - && saPrvRedeemReq != saPrvRedeemAct // Previous still wants to redeem. + if (saPrvRedeemReq != saPrvRedeemAct // Previous still wants to redeem. && saCurRedeemReq == saCurRedeemAct // Current has no more to redeem to next. && saCurIssueReq) { @@ -2928,18 +2930,8 @@ TER TransactionEngine::calcNodeAccountFwd( calcNodeRipple(QUALITY_ONE, rippleTransferRate(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct, uRateMax); } - // Previous issue part 1: issue -> redeem - if (bRedeem // Can redeem. - && saPrvIssueReq != saPrvIssueAct // Previous wants to issue. - && saCurRedeemReq != saCurRedeemAct) // Current has more to redeem to next. - { - // Rate: quality in : quality out - calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct, uRateMax); - } - // Previous issue part 2 : issue -> issue - if (bIssue // Can issue. - && saPrvIssueReq != saPrvIssueAct) // Previous wants to issue. To next must be ok. + if (saPrvIssueReq != saPrvIssueAct) // Previous wants to issue. To next must be ok. { // Rate: quality in : 1.0 calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct, uRateMax); @@ -2996,8 +2988,7 @@ TER TransactionEngine::calcNodeAccountFwd( Log(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> account")); // deliver -> redeem - if (bRedeem // Allowed to redeem. - && saPrvDeliverReq) // Previous wants to deliver. + if (saPrvDeliverReq) // Previous wants to deliver. { // Rate : 1.0 : quality out calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvDeliverReq, saCurRedeemReq, saPrvDeliverAct, saCurRedeemAct, uRateMax); @@ -3005,8 +2996,7 @@ TER TransactionEngine::calcNodeAccountFwd( // deliver -> issue // Wants to redeem and current would and can issue. - if (bIssue // Allowed to issue. - && saPrvDeliverReq != saPrvDeliverAct // Previous still wants to deliver. + if (saPrvDeliverReq != saPrvDeliverAct // Previous still wants to deliver. && saCurRedeemReq == saCurRedeemAct // Current has more to redeem to next. && saCurIssueReq) // Current wants issue. { @@ -3023,8 +3013,7 @@ TER TransactionEngine::calcNodeAccountFwd( // deliver/redeem -> deliver/issue. Log(lsINFO) << boost::str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> offer")); - if (bIssue // Allowed to issue. - && saPrvDeliverReq // Previous wants to deliver + if (saPrvDeliverReq // Previous wants to deliver && saCurIssueReq) // Current wants issue. { // Rate : 1.0 : transfer_rate @@ -3089,9 +3078,7 @@ TER PathState::pushImply(uint160 uAccountID, uint160 uCurrencyID, uint160 uIssue // Need to ripple through uIssuerID's account. terResult = pushNode( - STPathElement::typeAccount - | STPathElement::typeRedeem - | STPathElement::typeIssue, + STPathElement::typeAccount, uIssuerID, // Intermediate account is the needed issuer. uCurrencyID, uIssuerID); @@ -3120,9 +3107,6 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint const bool bCurrency = isSetBit(iType, STPathElement::typeCurrency); // Issuer is specified for the output of the current node. const bool bIssuer = isSetBit(iType, STPathElement::typeIssuer); - // true, iff account is allowed to redeem it's IOUs to next node. - const bool bRedeem = isSetBit(iType, STPathElement::typeRedeem); - const bool bIssue = isSetBit(iType, STPathElement::typeIssue); TER terResult = tesSUCCESS; pnCur.uFlags = iType; @@ -3135,118 +3119,101 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint } else if (bAccount) { - if (bRedeem || bIssue) + // Account link + + pnCur.uAccountID = uAccountID; + pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; + pnCur.uIssuerID = bIssuer ? uIssuerID : uAccountID; + pnCur.saRevRedeem = STAmount(uCurrencyID, uAccountID); + pnCur.saRevIssue = STAmount(uCurrencyID, uAccountID); + + if (!bFirst) { - // Account link + // Add required intermediate nodes to deliver to current account. + terResult = pushImply( + pnCur.uAccountID, // Current account. + pnCur.uCurrencyID, // Wanted currency. + !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer. + } - pnCur.uAccountID = uAccountID; - pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; - pnCur.uIssuerID = bIssuer ? uIssuerID : uAccountID; - pnCur.saRevRedeem = STAmount(uCurrencyID, uAccountID); - pnCur.saRevIssue = STAmount(uCurrencyID, uAccountID); + if (tesSUCCESS == terResult && !vpnNodes.empty()) + { + const PaymentNode& pnBck = vpnNodes.back(); + bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); - if (!bFirst) + if (bBckAccount) { - // Add required intermediate nodes to deliver to current account. - terResult = pushImply( - pnCur.uAccountID, // Current account. - pnCur.uCurrencyID, // Wanted currency. - !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer. - } + SLE::pointer sleRippleState = mLedger->getSLE(Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID)); - if (tesSUCCESS == terResult && !vpnNodes.empty()) - { - const PaymentNode& pnBck = vpnNodes.back(); - bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); - - if (bBckAccount) + if (!sleRippleState) { - SLE::pointer sleRippleState = mLedger->getSLE(Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID)); + Log(lsINFO) << "pushNode: No credit line between " + << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) + << " and " + << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) + << " for " + << STAmount::createHumanCurrency(pnPrv.uCurrencyID) + << "." ; - if (!sleRippleState) - { - Log(lsINFO) << "pushNode: No credit line between " - << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) - << " and " - << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) - << " for " - << STAmount::createHumanCurrency(pnPrv.uCurrencyID) - << "." ; - - terResult = terNO_LINE; - } - else - { - Log(lsINFO) << "pushNode: Credit line found between " - << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) - << " and " - << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) - << " for " - << STAmount::createHumanCurrency(pnPrv.uCurrencyID) - << "." ; - } + terResult = terNO_LINE; + } + else + { + Log(lsINFO) << "pushNode: Credit line found between " + << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) + << " and " + << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) + << " for " + << STAmount::createHumanCurrency(pnPrv.uCurrencyID) + << "." ; } } - - if (tesSUCCESS == terResult) - vpnNodes.push_back(pnCur); } - else - { - Log(lsINFO) << "pushNode: Account must redeem and/or issue."; - terResult = temBAD_PATH; - } + if (tesSUCCESS == terResult) + vpnNodes.push_back(pnCur); } else { // Offer link - if (bRedeem || bIssue) + // Offers bridge a change in currency & issuer or just a change in issuer. + pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; + pnCur.uIssuerID = bIssuer ? uIssuerID : pnCur.uAccountID; + + if (!!pnPrv.uAccountID) { - terResult = temBAD_PATH; + // Previous is an account. + + // Insert intermediary account if needed. + terResult = pushImply( + !!pnPrv.uCurrencyID ? ACCOUNT_ONE : ACCOUNT_XNS, + pnPrv.uCurrencyID, + pnPrv.uIssuerID); } else { - // Offers bridge a change in currency & issuer or just a change in issuer. - pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; - pnCur.uIssuerID = bIssuer ? uIssuerID : pnCur.uAccountID; + // Previous is an offer. + // XXX Need code if we don't do offer to offer. + nothing(); + } - if (!!pnPrv.uAccountID) + if (tesSUCCESS == terResult) + { + // Verify that previous is an account. + const PaymentNode& pnBck = vpnNodes.back(); + bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); + + if (bBckAccount) { - // Previous is an account. + Log(lsINFO) << "pushNode: previous must be account."; - // Insert intermediary account if needed. - terResult = pushImply( - !!pnPrv.uCurrencyID ? ACCOUNT_ONE : ACCOUNT_XNS, - pnPrv.uCurrencyID, - pnPrv.uIssuerID); - } - else - { - // Previous is an offer. - // XXX Need code if we don't do offer to offer. - nothing(); + terResult = temBAD_PATH; } + } - if (tesSUCCESS == terResult) - { - // Verify that previous account is allowed to issue. - const PaymentNode& pnBck = vpnNodes.back(); - bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); - bool bBckIssue = isSetBit(pnBck.uFlags, STPathElement::typeIssue); - - if (bBckAccount && !bBckIssue) - { - Log(lsINFO) << "pushNode: previous account must be allowed to issue."; - - terResult = temBAD_PATH; - } - } - - if (tesSUCCESS == terResult) - { - vpnNodes.push_back(pnCur); - } + if (tesSUCCESS == terResult) + { + vpnNodes.push_back(pnCur); } } Log(lsINFO) << "pushNode< " << terResult; @@ -3279,8 +3246,6 @@ PathState::PathState( // Push sending node. terStatus = pushNode( STPathElement::typeAccount - | STPathElement::typeRedeem - | STPathElement::typeIssue | STPathElement::typeCurrency | STPathElement::typeIssuer, uSenderID, @@ -3302,8 +3267,6 @@ PathState::PathState( { terStatus = pushNode( STPathElement::typeAccount // Last node is always an account. - | STPathElement::typeRedeem // Does not matter just pass error check. - | STPathElement::typeIssue // Does not matter just pass error check. | STPathElement::typeCurrency | STPathElement::typeIssuer, uReceiverID, // Receive to output @@ -3361,12 +3324,6 @@ Json::Value PathState::getJson() const if (pnNode.uFlags & STPathElement::typeAccount) jvFlags.append("account"); - if (pnNode.uFlags & STPathElement::typeRedeem) - jvFlags.append("redeem"); - - if (pnNode.uFlags & STPathElement::typeIssue) - jvFlags.append("issue"); - jvNode["flags"] = jvFlags; if (pnNode.uFlags & STPathElement::typeAccount) From 9a5d23d5d48957c888e36c21c379acf83c78cc8c Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 3 Sep 2012 14:43:16 -0700 Subject: [PATCH 09/17] Cosmetic. --- src/TransactionEngine.cpp | 87 +++++++++++++++------------------------ src/TransactionEngine.h | 3 -- 2 files changed, 33 insertions(+), 57 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index d9d94fefcf..d15eadb200 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -2037,17 +2037,11 @@ TER TransactionEngine::calcNodeAdvance( { PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; -// PaymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; const uint160& uPrvIssuerID = pnPrv.uIssuerID; const uint160& uCurCurrencyID = pnCur.uCurrencyID; const uint160& uCurIssuerID = pnCur.uIssuerID; -// const uint160& uNxtCurrencyID = pnNxt.uCurrencyID; -// const uint160& uNxtIssuerID = pnNxt.uIssuerID; - -// const uint160& uPrvAccountID = pnPrv.uAccountID; -// const uint160& uNxtAccountID = pnNxt.uAccountID; uint256& uDirectTip = pnCur.uDirectTip; uint256 uDirectEnd = pnCur.uDirectEnd; @@ -2425,9 +2419,9 @@ void TransactionEngine::calcNodeRipple( if (!uRateMax || uRate <= uRateMax) { - const uint160 uCurrencyID = saCur.getCurrency(); - const uint160 uCurIssuerID = saCur.getIssuer(); - const uint160 uPrvIssuerID = saPrv.getIssuer(); + const uint160 uCurrencyID = saCur.getCurrency(); + const uint160 uCurIssuerID = saCur.getIssuer(); + const uint160 uPrvIssuerID = saPrv.getIssuer(); STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID, uCurIssuerID), uQualityIn, uCurrencyID, uCurIssuerID); @@ -2472,32 +2466,32 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS uint64 uRateMax = 0; - PaymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; - PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; - PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex ? uIndex-1 : 0]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; // Current is allowed to redeem to next. - const bool bPrvAccount = !uIndex || isSetBit(pnPrv.uFlags, STPathElement::typeAccount); - const bool bNxtAccount = uIndex == uLast || isSetBit(pnNxt.uFlags, STPathElement::typeAccount); + const bool bPrvAccount = !uIndex || isSetBit(pnPrv.uFlags, STPathElement::typeAccount); + const bool bNxtAccount = uIndex == uLast || isSetBit(pnNxt.uFlags, STPathElement::typeAccount); - const uint160& uCurAccountID = pnCur.uAccountID; - const uint160& uPrvAccountID = bPrvAccount ? pnPrv.uAccountID : uCurAccountID; - const uint160& uNxtAccountID = bNxtAccount ? pnNxt.uAccountID : uCurAccountID; // Offers are always issue. + const uint160& uCurAccountID = pnCur.uAccountID; + const uint160& uPrvAccountID = bPrvAccount ? pnPrv.uAccountID : uCurAccountID; + const uint160& uNxtAccountID = bNxtAccount ? pnNxt.uAccountID : uCurAccountID; // Offers are always issue. - const uint160& uCurrencyID = pnCur.uCurrencyID; + const uint160& uCurrencyID = pnCur.uCurrencyID; - const uint32 uQualityIn = uIndex ? rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE; - const uint32 uQualityOut = uIndex != uLast ? rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE; + const uint32 uQualityIn = uIndex ? rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : QUALITY_ONE; + const uint32 uQualityOut = uIndex != uLast ? rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE; // For bPrvAccount - const STAmount saPrvOwed = uIndex && bPrvAccount // Previous account is owed. - ? rippleOwed(uCurAccountID, uPrvAccountID, uCurrencyID) - : STAmount(uCurrencyID, uCurAccountID); - const STAmount saPrvLimit = uIndex && bPrvAccount // Previous account may owe. - ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) - : STAmount(uCurrencyID, uCurAccountID); + const STAmount saPrvOwed = uIndex && bPrvAccount // Previous account is owed. + ? rippleOwed(uCurAccountID, uPrvAccountID, uCurrencyID) + : STAmount(uCurrencyID, uCurAccountID); + const STAmount saPrvLimit = uIndex && bPrvAccount // Previous account may owe. + ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) + : STAmount(uCurrencyID, uCurAccountID); - const STAmount saNxtOwed = uIndex != uLast && bNxtAccount // Next account is owed. + const STAmount saNxtOwed = uIndex != uLast && bNxtAccount // Next account is owed. ? rippleOwed(uCurAccountID, uNxtAccountID, uCurrencyID) : STAmount(uCurrencyID, uCurAccountID); @@ -3335,22 +3329,22 @@ Json::Value PathState::getJson() const if (!!pnNode.uIssuerID) jvNode["issuer"] = NewcoinAddress::createHumanAccountID(pnNode.uIssuerID); - // if (!!pnNode.saRevRedeem) + // if (pnNode.saRevRedeem) jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText(); - // if (!!pnNode.saRevIssue) + // if (pnNode.saRevIssue) jvNode["rev_issue"] = pnNode.saRevIssue.getFullText(); - // if (!!pnNode.saRevDeliver) + // if (pnNode.saRevDeliver) jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText(); - // if (!!pnNode.saFwdRedeem) + // if (pnNode.saFwdRedeem) jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText(); - // if (!!pnNode.saFwdIssue) + // if (pnNode.saFwdIssue) jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText(); - // if (!!pnNode.saFwdDeliver) + // if (pnNode.saFwdDeliver) jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText(); jvNodes.append(jvNode); @@ -3569,7 +3563,7 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) } // XXX Should bMax be sufficient to imply ripple? - bool bRipple = bPaths || bMax || !saDstAmount.isNative(); + const bool bRipple = bPaths || bMax || !saDstAmount.isNative(); if (!bRipple) { @@ -3693,15 +3687,15 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) terResult = temUNCERTAIN; } - STAmount saPaid; - STAmount saWanted; - LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid. - uint64 uQualityLimit = bLimitQuality ? STAmount::getRate(saDstAmount, saMaxAmount) : 0; + STAmount saPaid; + STAmount saWanted; + const LedgerEntrySet lesBase = mNodes; // Checkpoint with just fees paid. + const uint64 uQualityLimit = bLimitQuality ? STAmount::getRate(saDstAmount, saMaxAmount) : 0; while (temUNCERTAIN == terResult) { PathState::pointer pspBest; - LedgerEntrySet lesCheckpoint = mNodes; + const LedgerEntrySet lesCheckpoint = mNodes; // Find the best path. BOOST_FOREACH(PathState::pointer pspCur, vpsPaths) @@ -4299,21 +4293,6 @@ TER TransactionEngine::doOfferCancel(const SerializedTransaction& txn) return terResult; } -TER TransactionEngine::doTake(const SerializedTransaction& txn) -{ - return temUNKNOWN; -} - -TER TransactionEngine::doStore(const SerializedTransaction& txn) -{ - return temUNKNOWN; -} - -TER TransactionEngine::doDelete(const SerializedTransaction& txn) -{ - return temUNKNOWN; -} - #if 0 // XXX Need to adjust for fees. // Find offers to satisfy pnDst. diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 2dcd04e2fa..7c6ff47e5d 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -361,7 +361,6 @@ protected: TER doAccountSet(const SerializedTransaction& txn); TER doClaim(const SerializedTransaction& txn); TER doCreditSet(const SerializedTransaction& txn); - TER doDelete(const SerializedTransaction& txn); TER doInvoice(const SerializedTransaction& txn); TER doOfferCreate(const SerializedTransaction& txn); TER doOfferCancel(const SerializedTransaction& txn); @@ -369,8 +368,6 @@ protected: TER doPasswordFund(const SerializedTransaction& txn); TER doPasswordSet(const SerializedTransaction& txn); TER doPayment(const SerializedTransaction& txn); - TER doStore(const SerializedTransaction& txn); - TER doTake(const SerializedTransaction& txn); TER doWalletAdd(const SerializedTransaction& txn); public: From 829b57173f5fb1f8d731e0e5fa76b2a702dccdfc Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Tue, 4 Sep 2012 15:40:53 -0700 Subject: [PATCH 10/17] Restruct reverse ripple through offers. --- src/TransactionEngine.cpp | 509 +++++++++++++++++--------------------- src/TransactionEngine.h | 14 +- 2 files changed, 236 insertions(+), 287 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index d15eadb200..f1718518e0 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -1755,285 +1755,12 @@ TER TransactionEngine::doPasswordSet(const SerializedTransaction& txn) return terResult; } -// XXX Need to track balances for offer funding. -TER TransactionEngine::calcNodeOfferRev( - const unsigned int uIndex, // 0 < uIndex < uLast - const PathState::pointer& pspCur, - const bool bMultiQuality) -{ - TER terResult = tepPATH_DRY; - - PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; - PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; - PaymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; - - const uint160& uPrvCurrencyID = pnPrv.uCurrencyID; - const uint160& uPrvIssuerID = pnPrv.uIssuerID; - const uint160& uCurCurrencyID = pnCur.uCurrencyID; - const uint160& uCurIssuerID = pnCur.uIssuerID; - const uint160& uNxtCurrencyID = pnNxt.uCurrencyID; - const uint160& uNxtIssuerID = pnNxt.uIssuerID; - const uint160& uNxtAccountID = pnNxt.uAccountID; - - uint256 uDirectTip = Ledger::getBookBase(uPrvCurrencyID, uPrvIssuerID, uCurCurrencyID, uCurIssuerID); - const uint256 uDirectEnd = Ledger::getQualityNext(uDirectTip); - bool bAdvance = !entryCache(ltDIR_NODE, uDirectTip); - - const STAmount& saTransferRate = pnCur.saTransferRate; - - Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev> uIndex=%d prv=%s/%s cur=%s/%s nxt=%s/%s saTransferRate=%s") - % uIndex - % STAmount::createHumanCurrency(uPrvCurrencyID) - % NewcoinAddress::createHumanAccountID(uPrvIssuerID) - % STAmount::createHumanCurrency(uCurCurrencyID) - % NewcoinAddress::createHumanAccountID(uCurIssuerID) - % STAmount::createHumanCurrency(uNxtCurrencyID) - % NewcoinAddress::createHumanAccountID(uNxtIssuerID) - % saTransferRate.getText()); - - STAmount& saPrvDlvReq = pnPrv.saRevDeliver; // To be adjusted. - STAmount saPrvDlvAct; - - const STAmount& saCurDlvReq = pnCur.saRevDeliver; // Reverse driver. - STAmount saCurDlvAct; - - Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev: uDirectTip=%s") % uDirectTip.ToString()); - - while (!!uDirectTip && saCurDlvAct != saCurDlvReq) // Have a quality and not done. - { - // Get next quality. - if (bAdvance) - { - uDirectTip = mLedger->getNextLedgerIndex(uDirectTip, uDirectEnd); - - Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev: uDirectTip=%s") % uDirectTip.ToString()); - } - else - { - bAdvance = true; - } - - if (!!uDirectTip) - { - // Do a directory. - // - Drive on computing saCurDlvAct to derive saPrvDlvAct. - // XXX Behave well, if entry type is not ltDIR_NODE (someone beat us to using the hash) - SLE::pointer sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); - const STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio - unsigned int uEntry = 0; - uint256 uOfferIndex; - - while (saCurDlvReq != saCurDlvAct // Have not met request. - && dirNext(uDirectTip, sleDirectDir, uEntry, uOfferIndex)) - { - Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev: uOfferIndex=%s") % uOfferIndex.ToString()); - - SLE::pointer sleOffer = entryCache(ltOFFER, uOfferIndex); - - if (sleOffer->getIFieldPresent(sfExpiration) && sleOffer->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC()) - { - // Offer is expired. - Log(lsINFO) << "calcNodeOfferRev: encountered expired offer"; - - musUnfundedFound.insert(uOfferIndex); // Mark offer for always deletion. - continue; - } - - const uint160 uCurOfrAccountID = sleOffer->getIValueFieldAccount(sfAccount).getAccountID(); - const aciSource asLine = boost::make_tuple(uCurOfrAccountID, uCurCurrencyID, uCurIssuerID); - - // Allowed to access source from this node? - curIssuerNodeConstIterator itAllow = pspCur->umForward.find(asLine); - bool bFoundForward = itAllow != pspCur->umForward.end(); - - if (bFoundForward || itAllow->second != uIndex) - { - // Temporarily unfunded. Another node uses this source, ignore in this node. - Log(lsINFO) << "calcNodeOfferRev: temporarily unfunded offer"; - - nothing(); - continue; - } - - const STAmount& saCurOfrOutReq = sleOffer->getIValueFieldAmount(sfTakerGets); - // UNUSED? const STAmount& saCurOfrIn = sleOffer->getIValueFieldAmount(sfTakerPays); - - STAmount saCurOfrFunds = accountFunds(uCurOfrAccountID, saCurOfrOutReq); // Funds left. - - curIssuerNodeConstIterator itSourcePast = mumSource.find(asLine); - bool bFoundPast = itSourcePast != mumSource.end(); - - if (!saCurOfrFunds.isPositive()) - { - // Offer is unfunded. - Log(lsINFO) << "calcNodeOfferRev: encountered unfunded offer"; - - curIssuerNodeConstIterator itSourceCur = bFoundPast - ? pspCur->umReverse.end() - : pspCur->umReverse.find(asLine); - bool bFoundReverse = itSourceCur != pspCur->umReverse.end(); - - if (!bFoundReverse && !bFoundPast) - { - // Never mentioned before: found unfunded. - musUnfundedFound.insert(uOfferIndex); // Mark offer for always deletion. - } - continue; - } - - bool bMentioned = false; - - if (!!uNxtAccountID) - { - // Next is an account. - // Next is redeeming it's own IOUs - no quality. - // Offer is paying out IOUs via offer - no quality. - - STAmount saFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtAccountID == uCurIssuerID - ? saOne - : saTransferRate; - bool bFee = saFeeRate != saOne; - - STAmount saOutBase = std::min(saCurOfrOutReq, saCurDlvReq-saCurDlvAct); // Limit offer out by needed. - STAmount saOutCost = std::min( - bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutBase, - saCurOfrFunds); // Limit cost by fees & funds. - STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; // Out amount after fees. - STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. - - saCurDlvAct += saOutDlvAct; // Portion of driver served. - saPrvDlvAct += saInDlvAct; // Portion needed in previous. - - if (!bMentioned) - bMentioned = true; - } - else - { - // Next is an offer. - - uint256 uNxtTip = Ledger::getBookBase(uCurCurrencyID, uCurIssuerID, uNxtCurrencyID, uNxtIssuerID); - uint256 uNxtEnd = Ledger::getQualityNext(uNxtTip); - bool bNxtAdvance = !entryCache(ltDIR_NODE, uNxtTip); - - while (!!uNxtTip // Have a quality. - && saCurDlvAct != saCurDlvReq) // Have more to do. - { - if (bNxtAdvance) - { - uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); - } - else - { - bNxtAdvance = true; - } - - if (!!uNxtTip) - { - // Do a directory. - // - Drive on computing saCurDlvAct to derive saPrvDlvAct. - // Although the fee varies based upon the next offer it does not matter as the offer maker knows in - // advance that they are obligated to pay a transfer fee of necessary. The owner of next offer has no - // expectation of a quality in being applied. - SLE::pointer sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); -// ??? STAmount saOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip), uCurCurrencyID); // For correct ratio - unsigned int uEntry = 0; - uint256 uNxtIndex; - - while (saCurDlvReq != saCurDlvAct // Have not met request. - && dirNext(uNxtTip, sleNxtDir, uEntry, uNxtIndex)) - { - // YYY This could combine offers with the same fee before doing math. - SLE::pointer sleNxtOfr = entryCache(ltOFFER, uNxtIndex); - uint160 uNxtOfrAccountID = sleNxtOfr->getIValueFieldAccount(sfAccount).getAccountID(); - const STAmount& saNxtOfrIn = sleNxtOfr->getIValueFieldAmount(sfTakerPays); - - const aciSource asLineNxt = boost::make_tuple(uNxtOfrAccountID, uNxtCurrencyID, uNxtIssuerID); - - // Allowed to access source from this node? - curIssuerNodeConstIterator itAllowNxt = pspCur->umForward.find(asLineNxt); - curIssuerNodeConstIterator itNxt = itAllowNxt == pspCur->umForward.end() - ? mumSource.find(asLine) - : itAllowNxt; - - assert(itNxt != mumSource.end()); - - if (uIndex+1 != itNxt->second) - { - // Temporarily unfunded. Another node uses this source, ignore in this node. - - nothing(); - continue; - } - - STAmount saFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtOfrAccountID == uCurIssuerID - ? saOne - : saTransferRate; - bool bFee = saFeeRate != saOne; - - STAmount saOutBase = std::min(saCurOfrOutReq, saCurDlvReq-saCurDlvAct);// Limit offer out by needed. - saOutBase = std::min(saOutBase, saNxtOfrIn); // Limit offer out by supplying offer. - STAmount saOutCost = std::min( - bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutBase, - saCurOfrFunds); // Limit cost by fees & funds. - STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; // Out amount after fees. - // Compute input w/o fees required. - STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uCurIssuerID); - - saCurDlvAct += saOutDlvAct; // Portion of driver served. - saPrvDlvAct += saInDlvAct; // Portion needed in previous. - if (!bMentioned) - bMentioned = true; - } - } - - // Do another nxt directory iff bMultiQuality - if (!bMultiQuality) - uNxtTip = 0; - } - } - - if (bMentioned // Need to remember reverse mention. - && !bFoundPast // Not mentioned in previous passes. - && !bFoundForward) // Not mentioned for pass. - { - // Consider source mentioned by current path state. - pspCur->umReverse.insert(std::make_pair(asLine, uIndex)); - } - } - } - - // Do another cur directory iff bMultiQuality - if (!bMultiQuality) - uDirectTip = 0; - } - - if (saPrvDlvAct) - { - saPrvDlvReq = saPrvDlvAct; // Adjust request. - terResult = tesSUCCESS; - } - - Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev< uIndex=%d saPrvDlvReq=%s terResult=%d") - % uIndex - % saPrvDlvReq.getFullText() - % terResult); - - return terResult; -} - // If needed, advance to next funded offer. TER TransactionEngine::calcNodeAdvance( const unsigned int uIndex, // 0 < uIndex < uLast const PathState::pointer& pspCur, - const bool bMultiQuality) + const bool bMultiQuality, + const bool bReverse) { PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; @@ -2145,13 +1872,38 @@ TER TransactionEngine::calcNodeAdvance( } // Allowed to access source from this node? - curIssuerNodeConstIterator itAllow = pspCur->umForward.find(asLine); - const bool bFoundForward = itAllow != pspCur->umForward.end(); + // XXX This can get called multiple times for same source in a row, caching result would be nice. + curIssuerNodeConstIterator itForward = pspCur->umForward.find(asLine); + const bool bFoundForward = itForward != pspCur->umForward.end(); - if (bFoundForward || itAllow->second != uIndex) + if (bFoundForward || itForward->second != uIndex) { // Temporarily unfunded. Another node uses this source, ignore in this offer. - Log(lsINFO) << "calcNodeAdvance: temporarily unfunded offer"; + Log(lsINFO) << "calcNodeAdvance: temporarily unfunded offer (forward)"; + + bEntryAdvance = true; + continue; + } + + curIssuerNodeConstIterator itPast = mumSource.find(asLine); + bool bFoundPast = itPast != mumSource.end(); + + if (bFoundPast || itPast->second != uIndex) + { + // Temporarily unfunded. Another node uses this source, ignore in this offer. + Log(lsINFO) << "calcNodeAdvance: temporarily unfunded offer (past)"; + + bEntryAdvance = true; + continue; + } + + curIssuerNodeConstIterator itReverse = pspCur->umReverse.find(asLine); + bool bFoundReverse = itReverse != pspCur->umReverse.end(); + + if (bFoundReverse || itReverse->second != uIndex) + { + // Temporarily unfunded. Another node uses this source, ignore in this offer. + Log(lsINFO) << "calcNodeAdvance: temporarily unfunded offer (reverse)"; bEntryAdvance = true; continue; @@ -2167,11 +1919,25 @@ TER TransactionEngine::calcNodeAdvance( // Offer is unfunded. Log(lsINFO) << "calcNodeAdvance: unfunded offer"; + if (bReverse && !bFoundReverse && !bFoundPast) + { + // Never mentioned before: found unfunded. + musUnfundedFound.insert(uOfferIndex); // Mark offer for always deletion. + } + // YYY Could verify offer is correct place for unfundeds. bEntryAdvance = true; continue; } + if (bReverse // Need to remember reverse mention. + && !bFoundPast // Not mentioned in previous passes. + && !bFoundReverse) // Not mentioned for pass. + { + // Consider source mentioned by current path state. + pspCur->umReverse.insert(std::make_pair(asLine, uIndex)); + } + bFundsDirty = false; bEntryAdvance = false; } @@ -2181,9 +1947,147 @@ TER TransactionEngine::calcNodeAdvance( return terResult; } +// 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 process till request is satisified while we the rate does not increase past the initial rate. +TER TransactionEngine::calcNodeDeliverRev( + const unsigned int uIndex, // 0 < uIndex < uLast + const PathState::pointer& pspCur, + const bool bMultiQuality, + const uint160& uOutAccountID, // --> Output owner's account. + const STAmount& saOutReq, // --> Funds wanted. + STAmount& saOutAct) // <-- Funds delivered. +{ + TER terResult = tesSUCCESS; + + PaymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + + const uint160& uCurIssuerID = pnCur.uIssuerID; + const uint160& uPrvAccountID = pnPrv.uAccountID; + const STAmount& saTransferRate = pnCur.saTransferRate; + + STAmount& saPrvDlvReq = pnPrv.saRevDeliver; // To be adjusted. + + saOutAct = 0; + + while (saOutAct != saOutReq) // Did not deliver limit. + { + bool& bEntryAdvance = pnCur.bEntryAdvance; + STAmount& saOfrRate = pnCur.saOfrRate; + uint256& uOfferIndex = pnCur.uOfferIndex; + SLE::pointer& sleOffer = pnCur.sleOffer; + const uint160& uOfrOwnerID = pnCur.uOfrOwnerID; + bool& bFundsDirty = pnCur.bFundsDirty; + STAmount& saOfferFunds = pnCur.saOfferFunds; + STAmount& saTakerPays = pnCur.saTakerPays; + STAmount& saTakerGets = pnCur.saTakerGets; + STAmount& saRateMax = pnCur.saRateMax; + + terResult = calcNodeAdvance(uIndex, pspCur, bMultiQuality, true); // If needed, advance to next funded offer. + + if (tesSUCCESS != terResult || !uOfferIndex) + { + // Error or out of offers. + break; + } + + const STAmount saOutFeeRate = uOfrOwnerID == uCurIssuerID || uOutAccountID == uCurIssuerID // Issuer receiving or sending. + ? saOne // No fee. + : saTransferRate; // Transfer rate of issuer. + + if (!saRateMax) + { + // Set initial rate. + saRateMax = saOutFeeRate; + } + else if (saRateMax < saOutFeeRate) + { + // Offer exceeds initial rate. + break; + } + + STAmount saOutPass = std::max(std::max(saOfferFunds, saTakerGets), saOutReq-saOutAct); // Offer maximum out - assuming no out fees. + STAmount saOutPlusFees = STAmount::divide(saOutPass, saOutFeeRate); // Offer out with fees. + + if (saOutPlusFees > saOfferFunds) + { + // Offer owner can not cover all fees, compute saOutPass based on saOfferFunds. + + saOutPlusFees = saOfferFunds; + saOutPass = STAmount::divide(saOutPlusFees, saOutFeeRate); + } + + // Compute portion of input needed to cover output. + + STAmount saInPassReq = STAmount::multiply(saOutPass, saOfrRate, saTakerPays); + STAmount saInPassAct; + + // Find out input amount actually available at current rate. + if (!!uPrvAccountID) + { + // account --> OFFER --> ? + // 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; + + saPrvDlvReq = saInPassAct; + } + else + { + terResult = calcNodeDeliverRev( + uIndex-1, + pspCur, + bMultiQuality, + uOfrOwnerID, + saInPassReq, + saInPassAct); + } + + if (tesSUCCESS != terResult) + break; + + if (saInPassAct != saInPassReq) + { + // Adjust output to conform to limited input. + saOutPass = STAmount::divide(saInPassAct, saOfrRate, saTakerGets); + saOutPlusFees = STAmount::multiply(saOutPass, saOutFeeRate); + } + + // Funds were spent. + bFundsDirty = true; + + // Deduct output, don't actually need to send. + accountSend(uOfrOwnerID, uCurIssuerID, saOutPass); + + // Adjust offer + sleOffer->setIFieldAmount(sfTakerGets, saTakerGets - saOutPass); + sleOffer->setIFieldAmount(sfTakerPays, saTakerPays - saInPassAct); + + entryModify(sleOffer); + + if (saOutPass == saTakerGets) + { + // Offer became unfunded. + bEntryAdvance = true; + } + + saOutAct += saOutPass; + } + + if (!saOutAct) + terResult = tepPATH_DRY; + + return terResult; +} + // Deliver maximum amount of funds from previous node. // Goal: Make progress consuming the offer. -TER TransactionEngine::calcNodeDeliver( +TER TransactionEngine::calcNodeDeliverFwd( const unsigned int uIndex, // 0 < uIndex < uLast const PathState::pointer& pspCur, const bool bMultiQuality, @@ -2211,7 +2115,7 @@ TER TransactionEngine::calcNodeDeliver( && saInAct != saInReq // Did not deliver limit. && saInAct + saInFees != saInFunds) // Did not deliver all funds. { - terResult = calcNodeAdvance(uIndex, pspCur, bMultiQuality); // If needed, advance to next funded offer. + terResult = calcNodeAdvance(uIndex, pspCur, bMultiQuality, false); // If needed, advance to next funded offer. if (tesSUCCESS == terResult) { @@ -2232,7 +2136,7 @@ TER TransactionEngine::calcNodeDeliver( // // First calculate assuming no output fees. - // XXX Make sure derived in does not exceed actual saTakerPays + // XXX Make sure derived in does not exceed actual saTakerPays due to rounding. STAmount saOutFunded = std::max(saOfferFunds, saTakerGets); // Offer maximum out - There are no out fees. STAmount saInFunded = STAmount::multiply(saOutFunded, saOfrRate, saInReq); // Offer maximum in - Limited by by payout. @@ -2263,7 +2167,7 @@ TER TransactionEngine::calcNodeDeliver( // ? --> OFFER --> offer STAmount saOutPassFees; - terResult = TransactionEngine::calcNodeDeliver( + terResult = TransactionEngine::calcNodeDeliverFwd( uIndex+1, pspCur, bMultiQuality, @@ -2311,6 +2215,40 @@ TER TransactionEngine::calcNodeDeliver( return terResult; } +// Called to drive from the last offer node in a chain. +TER TransactionEngine::calcNodeOfferRev( + const unsigned int uIndex, // 0 < uIndex < uLast + const PathState::pointer& pspCur, + const bool bMultiQuality) +{ + TER terResult; + + PaymentNode& pnCur = pspCur->vpnNodes[uIndex]; + PaymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; + + if (!!pnNxt.uAccountID) + { + // Next is an account node, resolve current offer node's deliver. + STAmount saDeliverAct; + + terResult = calcNodeDeliverRev( + uIndex, + pspCur, + bMultiQuality, + + pnNxt.uAccountID, + pnCur.saRevDeliver, + saDeliverAct); + } + else + { + // Next is an offer. Deliver has already been resolved. + terResult = tesSUCCESS; + } + + return terResult; +} + // Called to drive the from the first offer node in a chain. // - Offer input is limbo. // - Current offers consumed. @@ -2333,7 +2271,7 @@ TER TransactionEngine::calcNodeOfferFwd( STAmount saInAct; STAmount saInFees; - terResult = calcNodeDeliver( + terResult = calcNodeDeliverFwd( uIndex, pspCur, bMultiQuality, @@ -2457,7 +2395,7 @@ void TransactionEngine::calcNodeRipple( % saCurAct.getFullText()); } -// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver; +// Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver from saCur... // <-- tesSUCCESS or tepPATH_DRY TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) { @@ -3173,6 +3111,7 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint // Offers bridge a change in currency & issuer or just a change in issuer. pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; pnCur.uIssuerID = bIssuer ? uIssuerID : pnCur.uAccountID; + pnCur.saRateMax = saZero; if (!!pnPrv.uAccountID) { diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 7c6ff47e5d..d2e7cd4016 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -166,6 +166,8 @@ protected: // For offers: + STAmount saRateMax; // XXX Should rate be sticky for forward too? + // Directory uint256 uDirectTip; // Current directory. uint256 uDirectEnd; // Next order book. @@ -340,8 +342,16 @@ protected: TER calcNodeOfferFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeAccountRev(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); TER calcNodeAccountFwd(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeAdvance(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality); - TER calcNodeDeliver( + TER calcNodeAdvance(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality, const bool bReverse); + TER calcNodeDeliverRev( + const unsigned int uIndex, + const PathState::pointer& pspCur, + const bool bMultiQuality, + const uint160& uOutAccountID, + const STAmount& saOutReq, + STAmount& saOutAct); + + TER calcNodeDeliverFwd( const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality, From 7785b89b5d6a8b2e8b70152a5eed51d532cfc948 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 5 Sep 2012 07:22:04 -0700 Subject: [PATCH 11/17] Some extra debugging. Don't jump back to the ledger we just left (yeah, there was another way that could happen.) --- src/LedgerConsensus.cpp | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index fa94f5630b..f2e73e647f 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -248,23 +248,39 @@ void LedgerConsensus::checkLCL() { uint256 netLgr = mPrevLedgerHash; int netLgrCount = 0; - { - boost::unordered_map vals = theApp->getValidations().getCurrentValidations(); - typedef std::pair u256_int_pair; - BOOST_FOREACH(u256_int_pair& it, vals) + boost::unordered_map vals = theApp->getValidations().getCurrentValidations(); + typedef std::pair u256_int_pair; + BOOST_FOREACH(u256_int_pair& it, vals) + { + if ((it.second > netLgrCount) && !theApp->getValidations().isDeadLedger(it.first)) { - if ((it.second > netLgrCount) && !theApp->getValidations().isDeadLedger(it.first)) - { - netLgr = it.first; - netLgrCount = it.second; - } + netLgr = it.first; + netLgrCount = it.second; } } + if (netLgr != mPrevLedgerHash) { // LCL change - Log(lsWARNING) << "View of consensus changed during consensus (" << netLgrCount << ")"; + const char *status; + switch (mState) + { + case lcsPRE_CLOSE: status="PreClose"; break; + case lcsESTABLISH: status="Establish"; break; + case lcsFINISHED: status="Finised"; break; + case lcsACCEPTED: status="Accepted"; break; + default: status="unknown"; + } + + Log(lsWARNING) << "View of consensus changed during consensus (" << netLgrCount << ") status=" + << status << ", " << (mHaveCorrectLCL ? "CorrectLCL" : "IncorrectLCL"); Log(lsWARNING) << mPrevLedgerHash << " to " << netLgr; + +#ifdef DEBUG + BOOST_FOREACH(u256_int_pair& it, vals) + Log(lsDEBUG) << "V: " << it.first << ", " << it.second; +#endif + if (mHaveCorrectLCL) theApp->getOPs().consensusViewChange(); handleLCL(netLgr); @@ -520,7 +536,7 @@ void LedgerConsensus::stateAccepted() void LedgerConsensus::timerEntry() { - if ((!mHaveCorrectLCL) || (mState == lcsPRE_CLOSE)) + if ((!mHaveCorrectLCL && (mState != lcsACCEPTED)) || (mState == lcsPRE_CLOSE)) checkLCL(); mCurrentMSeconds = From 1dfdf8e9d827634bf3d545854cd2db874e401257 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 5 Sep 2012 10:05:15 -0700 Subject: [PATCH 12/17] Count a validation for its previous ledger if it's during consensus time. --- src/LedgerConsensus.cpp | 2 +- src/SerializedValidation.h | 6 ++++++ src/ValidationCollection.cpp | 21 ++++++++++++++------- src/ValidationCollection.h | 2 +- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index f2e73e647f..12fd086f9c 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -249,7 +249,7 @@ void LedgerConsensus::checkLCL() uint256 netLgr = mPrevLedgerHash; int netLgrCount = 0; - boost::unordered_map vals = theApp->getValidations().getCurrentValidations(); + boost::unordered_map vals = theApp->getValidations().getCurrentValidations(mPrevLedgerHash); typedef std::pair u256_int_pair; BOOST_FOREACH(u256_int_pair& it, vals) { diff --git a/src/SerializedValidation.h b/src/SerializedValidation.h index 81e4c1d6e1..299c35a0ed 100644 --- a/src/SerializedValidation.h +++ b/src/SerializedValidation.h @@ -8,6 +8,7 @@ class SerializedValidation : public STObject { protected: STVariableLength mSignature; + uint256 mPreviousHash; bool mTrusted; void setNode(); @@ -40,6 +41,11 @@ public: void addSignature(Serializer&) const; std::vector getSigned() const; std::vector getSignature() const; + + // The validation this replaced + const uint256& getPreviousHash() { return mPreviousHash; } + bool isPreviousHash(const uint256& h) { return mPreviousHash == h; } + void setPreviousHash(const uint256& h) { mPreviousHash = h; } }; #endif diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index b6d109777c..28cb2a59d1 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -41,6 +41,7 @@ bool ValidationCollection::addValidation(const SerializedValidation::pointer& va it->second = val; else if (val->getSignTime() > it->second->getSignTime()) { + val->setPreviousHash(it->second->getPreviousHash()); mStaleValidations.push_back(it->second); it->second = val; condWrite(); @@ -128,9 +129,11 @@ int ValidationCollection::getCurrentValidationCount(uint32 afterTime) return count; } -boost::unordered_map ValidationCollection::getCurrentValidations() +boost::unordered_map ValidationCollection::getCurrentValidations(uint256 currentLedger) { uint32 cutoff = theApp->getOPs().getNetworkTimeNC() - LEDGER_VAL_INTERVAL; + bool valCurrentLedger = currentLedger.isNonZero(); + boost::unordered_map ret; { @@ -149,7 +152,10 @@ boost::unordered_map ValidationCollection::getCurrentValidations() } else { // contains a live record - ++ret[it->second->getLedgerHash()]; + if (valCurrentLedger && it->second->isPreviousHash(currentLedger)) + ++ret[currentLedger]; // count for the favored ledger + else + ++ret[it->second->getLedgerHash()]; ++it; } } @@ -160,6 +166,7 @@ boost::unordered_map ValidationCollection::getCurrentValidations() bool ValidationCollection::isDeadLedger(const uint256& ledger) { + boost::mutex::scoped_lock sl(mValidationLock); BOOST_FOREACH(const uint256& it, mDeadLedgers) if (it == ledger) return true; @@ -168,11 +175,13 @@ bool ValidationCollection::isDeadLedger(const uint256& ledger) void ValidationCollection::addDeadLedger(const uint256& ledger) { + boost::mutex::scoped_lock sl(mValidationLock); + if (isDeadLedger(ledger)) return; mDeadLedgers.push_back(ledger); - if (mDeadLedgers.size() >= 128) + if (mDeadLedgers.size() >= 32) mDeadLedgers.pop_front(); } @@ -220,13 +229,11 @@ void ValidationCollection::doWrite() std::vector vector; mStaleValidations.swap(vector); sl.unlock(); - { - ScopedLock dbl(theApp->getLedgerDB()->getDBLock()); Database *db = theApp->getLedgerDB()->getDB(); + ScopedLock dbl(theApp->getLedgerDB()->getDBLock()); + db->executeSQL("BEGIN TRANSACTION;"); - - BOOST_FOREACH(const SerializedValidation::pointer& it, vector) db->executeSQL(boost::str(insVal % it->getLedgerHash().GetHex() % it->getSignerPublic().humanNodePublic() % it->getFlags() % it->getSignTime() diff --git a/src/ValidationCollection.h b/src/ValidationCollection.h index 4a5912abfe..287ba6b68f 100644 --- a/src/ValidationCollection.h +++ b/src/ValidationCollection.h @@ -37,7 +37,7 @@ public: int getTrustedValidationCount(const uint256& ledger); int getCurrentValidationCount(uint32 afterTime); - boost::unordered_map getCurrentValidations(); + boost::unordered_map getCurrentValidations(uint256 currentLedger = uint256()); void addDeadLedger(const uint256&); bool isDeadLedger(const uint256&); From 7a300f6c7b8aad6e4e86a30c687435426f0bf25a Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 5 Sep 2012 10:18:23 -0700 Subject: [PATCH 13/17] Fix deadlock. --- src/ValidationCollection.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 28cb2a59d1..74039ad009 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -177,8 +177,9 @@ void ValidationCollection::addDeadLedger(const uint256& ledger) { boost::mutex::scoped_lock sl(mValidationLock); - if (isDeadLedger(ledger)) - return; + BOOST_FOREACH(const uint256& it, mDeadLedgers) + if (it == ledger) + return; mDeadLedgers.push_back(ledger); if (mDeadLedgers.size() >= 32) From 986fd52f1971872cc8c00d224445985219ec53a0 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 5 Sep 2012 14:18:58 -0700 Subject: [PATCH 14/17] Get indirect ripple working again. --- src/TransactionEngine.cpp | 80 ++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index c710775406..15534fc26a 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -109,6 +109,24 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) return iIndex >= 0; } +static std::string transToken(TER terCode) +{ + std::string strToken; + std::string strHuman; + + return transResultInfo(terCode, strToken, strHuman) ? strToken : "-"; +} + +#if 0 +static std::string transHuman(TER terCode) +{ + std::string strToken; + std::string strHuman; + + return transResultInfo(terCode, strToken, strHuman) ? strHuman : "-"; +} +#endif + // Returns amount owed by uToAccountID to uFromAccountID. // <-- $owed/uCurrencyID/uToAccountID: positive: uFromAccountID holds IOUs., negative: uFromAccountID owes IOUs. STAmount TransactionEngine::rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) @@ -1756,6 +1774,8 @@ TER TransactionEngine::doPasswordSet(const SerializedTransaction& txn) } // If needed, advance to next funded offer. +// - Automatically advances to first offer. +// - Set bEntryAdvance to advance to next entry. TER TransactionEngine::calcNodeAdvance( const unsigned int uIndex, // 0 < uIndex < uLast const PathState::pointer& pspCur, @@ -2425,13 +2445,14 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS const STAmount saPrvOwed = uIndex && bPrvAccount // Previous account is owed. ? rippleOwed(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID, uCurAccountID); + const STAmount saPrvLimit = uIndex && bPrvAccount // Previous account may owe. ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID, uCurAccountID); const STAmount saNxtOwed = uIndex != uLast && bNxtAccount // Next account is owed. - ? rippleOwed(uCurAccountID, uNxtAccountID, uCurrencyID) - : STAmount(uCurrencyID, uCurAccountID); + ? rippleOwed(uCurAccountID, uNxtAccountID, uCurrencyID) + : STAmount(uCurrencyID, uCurAccountID); Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev> uIndex=%d/%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvOwed=%s saPrvLimit=%s") % uIndex @@ -2474,6 +2495,9 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS Log(lsINFO) << pspCur->getJson(); + assert(!saCurRedeemReq || saNxtOwed >= saCurRedeemReq); // Current redeem req can't be more than IOUs on hand. + assert(!saCurIssueReq || !saNxtOwed.isPositive() || saNxtOwed == saCurRedeemReq); // If issue req, then redeem req must consume all owed. + if (bPrvAccount && bNxtAccount) { if (!uIndex) @@ -2528,7 +2552,7 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS // ^|account --> ACCOUNT --> account // redeem (part 1) -> redeem - if (saCurRedeemReq // Next wants us to redeem. + if (saCurRedeemReq // Next wants IOUs redeemed. && saPrvRedeemReq) // Previous has IOUs to redeem. { // Rate : 1.0 : quality out @@ -2537,9 +2561,9 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct, uRateMax); } - // issue (part 1)-> redeem - if (saCurRedeemReq != saCurRedeemAct // More to redeem. - && saPrvRedeemReq == saPrvRedeemAct) // Previously redeemed all owed IOUs. + // 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 Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : quality out")); @@ -2548,10 +2572,9 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS } // redeem (part 2) -> issue. - if (saCurIssueReq // Need some issued. - && saCurRedeemAct == saNxtOwed // Can only issue if previously redeemed all owed. - && saPrvOwed.isPositive() // Previous has IOUs to redeem. - && saPrvRedeemAct != saPrvOwed) // Did not previously redeem all IOUs. + 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 Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : transfer_rate")); @@ -2560,8 +2583,8 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS } // issue (part 2) -> issue - if (saCurIssueReq != saCurIssueAct // Need some issued. - && saCurRedeemAct == saNxtOwed // Can only issue if previously redeemed all owed. + if (saCurIssueReq != saCurIssueAct // Need wants more IOUs issued. + && saCurRedeemAct == saCurRedeemReq // Can only issue if completed redeeming. && saPrvRedeemReq == saPrvRedeemAct) // Previously redeemed all owed IOUs. { // Rate: quality in : 1.0 @@ -3372,7 +3395,7 @@ TER TransactionEngine::calcNodeRev(const unsigned int uIndex, const PathState::p terResult = calcNodeRev(uIndex-1, pspCur, bMultiQuality); } - Log(lsINFO) << boost::str(boost::format("calcNodeRev< uIndex=%d terResult=%d") % uIndex % terResult); + Log(lsINFO) << boost::str(boost::format("calcNodeRev< uIndex=%d terResult=%s/%d") % uIndex % transToken(terResult) % terResult); return terResult; } @@ -3399,6 +3422,8 @@ void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPa pspCur->terStatus = calcNodeRev(uLast, pspCur, bMultiQuality); + Log(lsINFO) << "Path after reverse: " << pspCur->getJson(); + if (tesSUCCESS == pspCur->terStatus) { // Do forward. @@ -3406,18 +3431,13 @@ void TransactionEngine::pathNext(const PathState::pointer& pspCur, const int iPa mNodes.bumpSeq(); // Begin ledger varance. pspCur->terStatus = calcNodeFwd(0, pspCur, bMultiQuality); + + pspCur->uQuality = tesSUCCESS == pspCur->terStatus + ? STAmount::getRate(pspCur->saOutAct, pspCur->saInAct) // Calculate relative quality. + : 0; // Mark path as inactive. + + Log(lsINFO) << "Path after forward: " << pspCur->getJson(); } - - Log(lsINFO) << "pathNext: terStatus=" - << pspCur->terStatus - << " saOutAct=" << pspCur->saOutAct.getText() - << " saInAct=" << pspCur->saInAct.getText(); - - pspCur->uQuality = tesSUCCESS == pspCur->terStatus - ? STAmount::getRate(pspCur->saOutAct, pspCur->saInAct) // Calculate relative quality. - : 0; // Mark path as inactive. - - Log(lsINFO) << "Path Out: " << pspCur->getJson(); } // XXX Need to audit for things like setting accountID not having memory. @@ -3573,13 +3593,13 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) { // Had a success. terResult = tesSUCCESS; - } - vpsPaths.push_back(pspDirect); + vpsPaths.push_back(pspDirect); + } } } - Log(lsINFO) << "doPayment: Paths: " << spsPaths.getPathCount(); + Log(lsINFO) << "doPayment: Paths in set: " << spsPaths.getPathCount(); BOOST_FOREACH(const STPath& spPath, spsPaths) { @@ -3633,11 +3653,11 @@ TER TransactionEngine::doPayment(const SerializedTransaction& txn) while (temUNCERTAIN == terResult) { - PathState::pointer pspBest; - const LedgerEntrySet lesCheckpoint = mNodes; + PathState::pointer pspBest; + const LedgerEntrySet lesCheckpoint = mNodes; // Find the best path. - BOOST_FOREACH(PathState::pointer pspCur, vpsPaths) + BOOST_FOREACH(PathState::pointer& pspCur, vpsPaths) { pathNext(pspCur, vpsPaths.size(), lesCheckpoint); // Compute increment. From 159fe2145a536467853b93094ebd601d5cee0a1d Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 5 Sep 2012 14:36:50 -0700 Subject: [PATCH 15/17] Copy the correct hash --- src/ValidationCollection.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ValidationCollection.cpp b/src/ValidationCollection.cpp index 74039ad009..e788b98e73 100644 --- a/src/ValidationCollection.cpp +++ b/src/ValidationCollection.cpp @@ -41,7 +41,7 @@ bool ValidationCollection::addValidation(const SerializedValidation::pointer& va it->second = val; else if (val->getSignTime() > it->second->getSignTime()) { - val->setPreviousHash(it->second->getPreviousHash()); + val->setPreviousHash(it->second->getLedgerHash()); mStaleValidations.push_back(it->second); it->second = val; condWrite(); From ec625a608f2a30706f207d21c38d0283dae7860d Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 5 Sep 2012 14:37:42 -0700 Subject: [PATCH 16/17] Small tweaks. --- src/LedgerConsensus.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 12fd086f9c..54c3caf61e 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -501,8 +501,6 @@ void LedgerConsensus::statePreClose() statusChange(newcoin::neCLOSING_LEDGER, *mPreviousLedger); takeInitialPosition(*theApp->getMasterLedger().closeLedger()); } - else if (mHaveCorrectLCL) - checkLCL(); // double check } void LedgerConsensus::stateEstablish() @@ -536,7 +534,7 @@ void LedgerConsensus::stateAccepted() void LedgerConsensus::timerEntry() { - if ((!mHaveCorrectLCL && (mState != lcsACCEPTED)) || (mState == lcsPRE_CLOSE)) + if ((mState != lcsFINISHED) && (mState != lcsACCEPTED)) checkLCL(); mCurrentMSeconds = @@ -639,7 +637,7 @@ void LedgerConsensus::updateOurPositions() Log(lsINFO) << "CCTime: " << it->first << " has " << it->second << " out of " << thresh; if (it->second > thresh) { - Log(lsINFO) << "Close time consensus reached: " << closeTime; + Log(lsINFO) << "Close time consensus reached: " << it->first; mHaveCloseTimeConsensus = true; closeTime = it->first; thresh = it->second; From 2512eac3700e792eded5d1d8c3bd5b72ae6adc00 Mon Sep 17 00:00:00 2001 From: JoelKatz Date: Wed, 5 Sep 2012 14:52:55 -0700 Subject: [PATCH 17/17] Rounding was messing up the close time consensus test. --- src/LedgerConsensus.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/LedgerConsensus.cpp b/src/LedgerConsensus.cpp index 54c3caf61e..e7b984fa8d 100644 --- a/src/LedgerConsensus.cpp +++ b/src/LedgerConsensus.cpp @@ -249,7 +249,11 @@ void LedgerConsensus::checkLCL() uint256 netLgr = mPrevLedgerHash; int netLgrCount = 0; - boost::unordered_map vals = theApp->getValidations().getCurrentValidations(mPrevLedgerHash); + uint256 favorLedger; + if (mState != lcsPRE_CLOSE) + favorLedger = mPrevLedgerHash; + boost::unordered_map vals = theApp->getValidations().getCurrentValidations(favorLedger); + typedef std::pair u256_int_pair; BOOST_FOREACH(u256_int_pair& it, vals) { @@ -628,7 +632,7 @@ void LedgerConsensus::updateOurPositions() ++closeTimes[roundCloseTime(mOurPosition->getCloseTime())]; ++thresh; } - thresh = thresh * neededWeight / 100; + thresh = ((thresh * neededWeight) + (neededWeight / 2)) / 100; if (thresh == 0) thresh = 1; @@ -643,6 +647,9 @@ void LedgerConsensus::updateOurPositions() thresh = it->second; } } + if (!mHaveCloseTimeConsensus) + Log(lsDEBUG) << "No CT consensus: Proposers:" << mPeerPositions.size() << " Proposing:" << + (mProposing ? "yes" : "no") << " Thresh:" << thresh << " Pos:" << closeTime; } if ((!changes) &&