From 22a1cb6eedd049f2aedbb745989b18b6a4d3badb Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 30 Aug 2012 15:19:28 -0700 Subject: [PATCH 1/4] Work towards ripple. --- src/TransactionEngine.cpp | 104 +++++++++++++++++++++++--------------- src/TransactionEngine.h | 48 ++++++++++++++---- 2 files changed, 101 insertions(+), 51 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 1fbb473c26..9809364661 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -2204,6 +2204,7 @@ TER TransactionEngine::calcNodeOfferRev( return terResult; } +// Offer input and output is issuer or limbo. TER TransactionEngine::calcNodeOfferFwd( const unsigned int uIndex, // 0 < uIndex < uLast const PathState::pointer& pspCur, @@ -2223,7 +2224,7 @@ TER TransactionEngine::calcNodeOfferFwd( const uint160& uNxtCurrencyID = pnNxt.uCurrencyID; const uint160& uNxtIssuerID = pnNxt.uIssuerID; -// const uint160& uPrvAccountID = pnPrv.uAccountID; + const uint160& uPrvAccountID = pnPrv.uAccountID; const uint160& uNxtAccountID = pnNxt.uAccountID; const STAmount saTransferRate = STAmount::saFromRate(rippleTransferRate(uCurIssuerID)); @@ -2234,15 +2235,28 @@ TER TransactionEngine::calcNodeOfferFwd( const STAmount& saPrvDlvReq = pnPrv.saFwdDeliver; // Forward driver. STAmount saPrvDlvAct; - STAmount& saCurDlvReq = pnCur.saFwdDeliver; - STAmount saCurDlvAct; + STAmount& saCurDlvAct = pnCur.saFwdDeliver; // How much current node will deliver. + saCurDlvAct; = 0; - while (!!uDirectTip // Have a quality. - && saPrvDlvAct != saPrvDlvReq) + bool bNxtOffer = !uNxtAccountID; + uint256 uNxtTip; + uint256 uNxtEnd; + bool bNxtDirAdvance; + bool bNxtSubAdvance; + + if (bNxtOffer) + { + uNxtTip = Ledger::getBookBase(uCurCurrencyID, uCurIssuerID, uNxtCurrencyID, uNxtIssuerID); + uNxtEnd = Ledger::getQualityNext(uNxtTip); + bNxtDirAdvance = !entryCache(ltDIR_NODE, uNxtTip); + bNxtSubAdvance = true; + } + + while (!!uDirectTip && saPrvDlvAct != saPrvDlvReq) // Have a quality and not done. { - // Get next quality. if (bAdvance) { + // Get next quality. uDirectTip = mLedger->getNextLedgerIndex(uDirectTip, uDirectEnd); } else @@ -2291,10 +2305,7 @@ TER TransactionEngine::calcNodeOfferFwd( } const STAmount& saCurOfrOutReq = sleOffer->getIValueFieldAmount(sfTakerGets); - const STAmount& saCurOfrInReq = sleOffer->getIValueFieldAmount(sfTakerPays); - STAmount saCurOfrInAct; STAmount saCurOfrFunds = accountFunds(uCurOfrAccountID, saCurOfrOutReq); // Funds left. - STAmount saCurOfrInMax = MIN(saCurOfrInReq, saPrvDlvReq-saPrvDlvAct); if (!saCurOfrFunds) { @@ -2306,6 +2317,12 @@ TER TransactionEngine::calcNodeOfferFwd( continue; } + const STAmount& saCurOfrInReq = sleOffer->getIValueFieldAmount(sfTakerPays); + STAmount saCurOfrInMax = MIN(saCurOfrInReq, saPrvDlvReq-saPrvDlvAct); + STAmount saCurOfrInAct; + + STAmount saInDlvAct; + if (!!uNxtAccountID) { // Next is an account node. @@ -2328,8 +2345,9 @@ TER TransactionEngine::calcNodeOfferFwd( const STAmount saOutDlvAct = bFee ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutCost; // Out amount after fees. + // Compute input w/o fees required. - const STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uPrvIssuerID); + saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uPrvIssuerID); // Deliver to output. accountSend(uCurOfrAccountID, uNxtAccountID, saOutDlvAct); @@ -2341,20 +2359,15 @@ TER TransactionEngine::calcNodeOfferFwd( // Next is an offer node. // Need to step through next offer's nodes to figure out fees. - uint256 uNxtTip = Ledger::getBookBase(uCurCurrencyID, uCurIssuerID, uNxtCurrencyID, uNxtIssuerID); - const uint256 uNxtEnd = Ledger::getQualityNext(uNxtTip); - bool bNxtAdvance = !entryCache(ltDIR_NODE, uNxtTip); + STAmount saOutDlvAct; - while (!!uNxtTip // Have a quality. - && saPrvDlvAct != saPrvDlvReq) // Have more to do. + while (!!uNxtTip && saCurOfrInAct != saCurOfrInMax) // An offer may be available and have more to do. { - if (bNxtAdvance) + if (bNxtDirAdvance) { - uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); - } - else - { - bNxtAdvance = true; + uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); + bNxtSubAdvance = true; + bNxtDirAdvance = false; } if (!!uNxtTip) @@ -2366,10 +2379,25 @@ TER TransactionEngine::calcNodeOfferFwd( unsigned int uEntry = 0; uint256 uNxtIndex; - while (saPrvDlvReq != saPrvDlvAct // Have not met request. - && dirNext(uNxtTip, sleNxtDir, uEntry, uNxtIndex)) + while (saCurOfrInAct != saCurOfrInMax) // Have not met request. { - // YYY This could combine offers with the same fee before doing math. + if (!bNxtSubAdvance) + { + // Continue with current uNxtIndex. + nothing(); + } + else if (dirNext(uNxtTip, sleNxtDir, uEntry, uNxtIndex)) + { + // Found a next uNxtIndex. + bNxtSubAdvance = false; + } + else + { + // No more offers in directory. + bNxtDirAdvance = true; + break; + } + SLE::pointer sleNxtOfr = entryCache(ltOFFER, uNxtIndex); const uint160 uNxtOfrAccountID = sleNxtOfr->getIValueFieldAccount(sfAccount).getAccountID(); const STAmount& saNxtOfrIn = sleNxtOfr->getIValueFieldAmount(sfTakerPays); @@ -2389,14 +2417,19 @@ TER TransactionEngine::calcNodeOfferFwd( ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutBase, saCurOfrFunds); // Limit cost by fees & funds. - const STAmount saOutDlvAct = bFee + const STAmount saOutDlvPass= bFee ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutCost; // Out amount after fees. - const STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. + const STAmount saInDlvPass = STAmount::multiply(saOutDlvPass, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. - saCurOfrInAct += saOutDlvAct; // Portion of driver served. - saPrvDlvAct += saOutDlvAct; // Portion needed in previous. - saCurDlvAct += saInDlvAct; // Portion of driver served. + saCurOfrInAct += saOutDlvPass; // Portion of driver served. + saCurDlvAct += saInDlvPass; // Portion of driver served. + + // Deliver input + saInDlvAct += saInDlvPass; + + // Deliver output. + saOutDlvAct += saOutDlvPass; } } @@ -2404,7 +2437,7 @@ TER TransactionEngine::calcNodeOfferFwd( if (!bMultiQuality) uNxtTip = 0; } -#if 0 + // Deliver output to limbo or currency issuer. accountSend( uCurOfrAccountID, // Offer owner pays. @@ -2412,11 +2445,9 @@ TER TransactionEngine::calcNodeOfferFwd( ? uCurIssuerID // Output is non-XNS send to issuer. : ACCOUNT_XNS, // Output is XNS send to limbo (ACCOUNT_XNS). saOutDlvAct); -#endif } // Deliver input to offer owner. -#if 0 accountSend( !!uPrvAccountID ? uPrvAccountID // Previous is an account. Source is previous account. @@ -2427,7 +2458,6 @@ TER TransactionEngine::calcNodeOfferFwd( saInDlvAct); saPrvDlvAct += saInDlvAct; // Portion needed in previous. -#endif } } @@ -2436,13 +2466,7 @@ TER TransactionEngine::calcNodeOfferFwd( uDirectTip = 0; } - if (saCurDlvAct) - { - saCurDlvReq = saCurDlvAct; // Adjust request. - terResult = tesSUCCESS; - } - - return terResult; + return !!saCurDlvAct ? tesSUCCESS : terResult; } #if 0 diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 115e4e68a2..67f8077819 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -19,14 +19,22 @@ enum TER // aka TransactionEngineResult // Note: Range is stable. Exact numbers are currently unstable. Use tokens. // -399 .. -300: L Local error (transaction fee inadequate, exceeds local limit) - // Not forwarded, no fee. Only valid during non-consensus processing + // Only valid during non-consensus processing. + // Implications: + // - Not forwarded + // - No fee check telLOCAL_ERROR = -399, telBAD_PATH_COUNT, telINSUF_FEE_P, // -299 .. -200: M Malformed (bad signature) - // Transaction corrupt, not forwarded, cannot charge fee, reject - // Never can succeed in any ledger + // Causes: + // - Transaction corrupt. + // Implications: + // - Not applied + // - Not forwarded + // - Reject + // - Can not succeed in any imagined ledger. temMALFORMED = -299, temBAD_AMOUNT, temBAD_AUTH_MASTER, @@ -48,8 +56,14 @@ enum TER // aka TransactionEngineResult temUNKNOWN, // -199 .. -100: F Failure (sequence number previously used) - // Transaction cannot succeed because of ledger state, unexpected ledger state, C++ exception, not forwarded, cannot be - // applied, Could succeed in an imaginary ledger. + // Causes: + // - Transaction cannot succeed because of ledger state. + // - Unexpected ledger state. + // - C++ exception. + // Implications: + // - Not applied + // - Not forwarded + // - Could succeed in an imaginared ledger. tefFAILURE = -199, tefALREADY, tefBAD_ADD_AUTH, @@ -64,7 +78,13 @@ enum TER // aka TransactionEngineResult tefPAST_SEQ, // -99 .. -1: R Retry (sequence too high, no funds for txn fee, originating account non-existent) - // Transaction cannot be applied, not forwarded, might succeed later, hold + // Causes: + // - Priror application of another, possibly non-existant, transaction could allow this transaction to succeed. + // Implications: + // - Not applied + // - Not forwarded + // - Might succeed later + // - Hold terRETRY = -99, terDIR_FULL, terFUNDS_SPENT, @@ -79,14 +99,20 @@ enum TER // aka TransactionEngineResult terUNFUNDED, // 0: S Success (success) - // Transaction succeeds, can be applied, can charge fee, forwarded - // applyTransaction: addTransaction and destroyCoins + // Causes: + // - Success. + // Implications: + // - Applied + // - Forwarded tesSUCCESS = 0, // 100 .. P Partial success (SR) (ripple transaction with no good paths, pay to non-existent account) - // Transaction can be applied, forwarded, but does not achieve optimal result. - // Only allowed as a return code of appliedTransaction when !tapRetry. - // applyTransaction: addTransaction and destroyCoins + // Causes: + // - Success, but does not achieve optimal result. + // Implications: + // - Applied + // - Forwarded + // Only allowed as a return code of appliedTransaction when !tapRetry. Otherwise, treated as terRETRY. tepPARTIAL = 100, tepPATH_DRY, tepPATH_PARTIAL, From e8a74c7679dade6b15c0e17a5bc1ac9a74db92f5 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 30 Aug 2012 21:15:46 -0700 Subject: [PATCH 2/4] Work on ripple. --- src/TransactionEngine.cpp | 170 +++++++++++++++++++++----------------- src/TransactionEngine.h | 20 +++-- 2 files changed, 107 insertions(+), 83 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 9809364661..dfecf4489c 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -266,7 +266,7 @@ STAmount TransactionEngine::accountHolds(const uint160& uAccountID, const uint16 // Use when you need a default for rippling uAccountID's currency. // --> saDefault/currency/issuer // <-- saFunds: Funds available. May be negative. -// If the issuer is the same as uAccountID, result is Default. +// If the issuer is the same as uAccountID, funds are unlimited, use result is saDefault. STAmount TransactionEngine::accountFunds(const uint160& uAccountID, const STAmount& saDefault) { STAmount saFunds; @@ -1321,13 +1321,13 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, Log(lsINFO) << "applyTransaction: terResult=" << strToken << " : " << terResult << " : " << strHuman; - if (terResult >= tepPATH_PARTIAL && isSetBit(params, tapRETRY)) + if (isTepPartial(terResult) && isSetBit(params, tapRETRY)) { // Partial result and allowed to retry, reclassify as a retry. terResult = terRETRY; } - if (tesSUCCESS == terResult || terResult >= tepPATH_PARTIAL) + if (tesSUCCESS == terResult || isTepPartial(terResult)) { // Transaction succeeded fully or (retries are not allowed and the transaction succeeded partially). txnWrite(); @@ -1348,8 +1348,7 @@ TER TransactionEngine::applyTransaction(const SerializedTransaction& txn, musUnfundedFound.clear(); if (!isSetBit(params, tapOPEN_LEDGER) - && ((terResult >= temMALFORMED && terResult <= tefFAILURE) - || ((terResult >= tefFAILURE && terResult <= terRETRY)))) + && (isTemMalformed(terResult) || isTefFailure(terResult))) { // XXX Malformed or failed transaction in closed ledger must bow out. } @@ -1968,8 +1967,7 @@ TER TransactionEngine::calcNodeOfferRev( Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev: uDirectTip=%s") % uDirectTip.ToString()); - while (!!uDirectTip // Have a quality. - && saCurDlvAct != saCurDlvReq) + while (!!uDirectTip && saCurDlvAct != saCurDlvReq) // Have a quality and not done. { // Get next quality. if (bAdvance) @@ -2033,7 +2031,7 @@ TER TransactionEngine::calcNodeOfferRev( curIssuerNodeConstIterator itSourcePast = mumSource.find(asLine); bool bFoundPast = itSourcePast != mumSource.end(); - if (!saCurOfrFunds) + if (!saCurOfrFunds.isPositive()) { // Offer is unfunded. Log(lsINFO) << "calcNodeOfferRev: encountered unfunded offer"; @@ -2048,11 +2046,6 @@ TER TransactionEngine::calcNodeOfferRev( // Never mentioned before: found unfunded. musUnfundedFound.insert(uOfferIndex); // Mark offer for always deletion. } - else - { - // Mentioned before: source became unfunded. - pspCur->vUnfundedBecame.push_back(uOfferIndex); // Mark offer for deletion on use of current path state. - } continue; } @@ -2069,8 +2062,8 @@ TER TransactionEngine::calcNodeOfferRev( : saTransferRate; bool bFee = saFeeRate != saOne; - STAmount saOutBase = MIN(saCurOfrOutReq, saCurDlvReq-saCurDlvAct); // Limit offer out by needed. - STAmount saOutCost = MIN( + STAmount saOutBase = std::min(saCurOfrOutReq, saCurDlvReq-saCurDlvAct); // Limit offer out by needed. + STAmount saOutCost = std::min( bFee ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutBase, @@ -2149,9 +2142,9 @@ TER TransactionEngine::calcNodeOfferRev( : saTransferRate; bool bFee = saFeeRate != saOne; - STAmount saOutBase = MIN(saCurOfrOutReq, saCurDlvReq-saCurDlvAct); // Limit offer out by needed. - saOutBase = MIN(saOutBase, saNxtOfrIn); // Limit offer out by supplying offer. - STAmount saOutCost = MIN( + 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, @@ -2236,19 +2229,23 @@ TER TransactionEngine::calcNodeOfferFwd( STAmount saPrvDlvAct; STAmount& saCurDlvAct = pnCur.saFwdDeliver; // How much current node will deliver. - saCurDlvAct; = 0; + saCurDlvAct = 0; bool bNxtOffer = !uNxtAccountID; uint256 uNxtTip; uint256 uNxtEnd; bool bNxtDirAdvance; bool bNxtSubAdvance; + unsigned int uNxtEntry = 0; + uint256 uNxtIndex; + SLE::pointer sleNxtDir; if (bNxtOffer) { uNxtTip = Ledger::getBookBase(uCurCurrencyID, uCurIssuerID, uNxtCurrencyID, uNxtIssuerID); uNxtEnd = Ledger::getQualityNext(uNxtTip); - bNxtDirAdvance = !entryCache(ltDIR_NODE, uNxtTip); + sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); + bNxtDirAdvance = !sleNxtDir; bNxtSubAdvance = true; } @@ -2306,8 +2303,9 @@ TER TransactionEngine::calcNodeOfferFwd( const STAmount& saCurOfrOutReq = sleOffer->getIValueFieldAmount(sfTakerGets); STAmount saCurOfrFunds = accountFunds(uCurOfrAccountID, saCurOfrOutReq); // Funds left. + STAmount saCurOfrSpent; - if (!saCurOfrFunds) + if (!saCurOfrFunds.isPositive()) { // Offer is unfunded. Log(lsINFO) << "calcNodeOfferFwd: unfunded offer"; @@ -2318,10 +2316,9 @@ TER TransactionEngine::calcNodeOfferFwd( } const STAmount& saCurOfrInReq = sleOffer->getIValueFieldAmount(sfTakerPays); - STAmount saCurOfrInMax = MIN(saCurOfrInReq, saPrvDlvReq-saPrvDlvAct); + STAmount saCurOfrInMax = std::min(saCurOfrInReq, saPrvDlvReq-saPrvDlvAct); STAmount saCurOfrInAct; - - STAmount saInDlvAct; + STAmount saCurOfrOutAct; if (!!uNxtAccountID) { @@ -2337,49 +2334,47 @@ TER TransactionEngine::calcNodeOfferFwd( const bool bFee = saFeeRate != saOne; const STAmount saOutPass = STAmount::divide(saCurOfrInMax, saOfrRate, uCurCurrencyID, uCurIssuerID); - const STAmount saOutBase = MIN(saCurOfrOutReq, saOutPass); // Limit offer out by needed. + 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 = MIN(saOutCostRaw, saCurOfrFunds); // Limit cost by fees & funds. - const STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; // Out amount after fees. + 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. - saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uPrvIssuerID); + saCurOfrInAct = STAmount::multiply(saCurOfrOutAct, saOfrRate, uPrvCurrencyID, uPrvIssuerID); // Deliver to output. - accountSend(uCurOfrAccountID, uNxtAccountID, saOutDlvAct); - - saCurDlvAct += saOutDlvAct; // Portion of driver served. + accountSend(uCurOfrAccountID, uNxtAccountID, saCurOfrOutAct); } else { // Next is an offer node. // Need to step through next offer's nodes to figure out fees. - STAmount saOutDlvAct; - - while (!!uNxtTip && saCurOfrInAct != saCurOfrInMax) // An offer may be available and have more to do. + while (!!uNxtTip && saCurOfrInAct != saCurOfrInMax) // A next offer may be available and have more to do. { if (bNxtDirAdvance) { uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); bNxtSubAdvance = true; bNxtDirAdvance = false; + uNxtEntry = 0; + if (!!uNxtTip) + sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); } if (!!uNxtTip) { // Do a directory. // - Drive on computing saCurDlvAct to derive saPrvDlvAct. - SLE::pointer sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); // ??? STAmount saOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip)); // For correct ratio - unsigned int uEntry = 0; - uint256 uNxtIndex; - while (saCurOfrInAct != saCurOfrInMax) // Have not met request. + while (saCurOfrInAct != saCurOfrInMax) // Have more to do. { if (!bNxtSubAdvance) { @@ -2401,35 +2396,53 @@ TER TransactionEngine::calcNodeOfferFwd( SLE::pointer sleNxtOfr = entryCache(ltOFFER, uNxtIndex); const uint160 uNxtOfrAccountID = sleNxtOfr->getIValueFieldAccount(sfAccount).getAccountID(); const STAmount& saNxtOfrIn = sleNxtOfr->getIValueFieldAmount(sfTakerPays); + const STAmount& saNxtOfrOut = sleNxtOfr->getIValueFieldAmount(sfTakerGets); - const STAmount saFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtOfrAccountID == uCurIssuerID - ? saOne - : saTransferRate; - const bool bFee = saFeeRate != saOne; + const STAmount saFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtOfrAccountID == uCurIssuerID + ? saOne + : saTransferRate; + const bool bFee = saFeeRate != saOne; -// XXX Skip expireds and unfundeds. - const STAmount saInBase = saCurOfrInMax-saCurOfrInAct; - const STAmount saOutPass = STAmount::divide(saInBase, saOfrRate, uCurCurrencyID, uCurIssuerID); - STAmount saOutBase = MIN(saCurOfrOutReq, saOutPass); // Limit offer out by needed. - saOutBase = MIN(saOutBase, saNxtOfrIn); // Limit offer out by supplying offer. - const STAmount saOutCost = MIN( - bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutBase, - saCurOfrFunds); // Limit cost by fees & funds. - const STAmount saOutDlvPass= bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; // Out amount after fees. - const STAmount saInDlvPass = STAmount::multiply(saOutDlvPass, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. + bool bNxtExpired = sleNxtOfr->getIFieldPresent(sfExpiration) + && sleNxtOfr->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC(); + STAmount saNxtOfrFunds = accountFunds(uNxtOfrAccountID, saNxtOfrOut); // Funds left. - saCurOfrInAct += saOutDlvPass; // Portion of driver served. - saCurDlvAct += saInDlvPass; // Portion of driver served. + if (!bNxtExpired && saNxtOfrFunds.isPositive()) + { + // Offer is not expired and is funded. + const STAmount saInBase = saCurOfrInMax-saCurOfrInAct; + const STAmount saOutPass = STAmount::divide(saInBase, saOfrRate, uCurCurrencyID, uCurIssuerID); + STAmount saOutBase = std::min(saCurOfrOutReq, saOutPass); // Limit offer out by needed. + saOutBase = std::min(saOutBase, saNxtOfrIn); // Limit offer out by supplying offer. + const STAmount saOutCost = std::min( + bFee + ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) + : saOutBase, + saCurOfrFunds); // Limit cost by fees & funds. + const STAmount saOutDlvPass= bFee + ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) + : saOutCost; // Out amount after fees. + const STAmount saInDlvPass = STAmount::multiply(saOutDlvPass, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. - // Deliver input - saInDlvAct += saInDlvPass; + // XXX Check fees. + sleNxtOfr->setIFieldAmount(sfTakerGets, saNxtOfrIn - saInDlvPass); + sleNxtOfr->setIFieldAmount(sfTakerPays, saNxtOfrOut - saOutDlvPass); - // Deliver output. - saOutDlvAct += saOutDlvPass; + if (saNxtOfrOut == saOutDlvPass) { + // Consumed all of offer. + Log(lsINFO) << "calcNodeOfferFwd: offer consumed"; + + pspCur->vUnfundedBecame.push_back(uNxtIndex); // Mark offer for deletion on use of current path state. + + bNxtSubAdvance = true; + } + + saCurOfrSpent += saOutDlvPass; // XXX Check. + + saCurOfrInAct += saInDlvPass; // Add to input handled. + + saCurOfrOutAct += saOutDlvPass; // Add to output handled. + } } } @@ -2440,11 +2453,11 @@ TER TransactionEngine::calcNodeOfferFwd( // Deliver output to limbo or currency issuer. accountSend( - uCurOfrAccountID, // Offer owner pays. + uCurOfrAccountID, // Offer owner pays. !!uCurIssuerID ? uCurIssuerID // Output is non-XNS send to issuer. : ACCOUNT_XNS, // Output is XNS send to limbo (ACCOUNT_XNS). - saOutDlvAct); + saCurOfrOutAct); } // Deliver input to offer owner. @@ -2455,9 +2468,16 @@ TER TransactionEngine::calcNodeOfferFwd( ? 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. - saInDlvAct); + saCurOfrInAct); - saPrvDlvAct += saInDlvAct; // Portion needed in previous. + 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. } } @@ -2740,7 +2760,7 @@ void TransactionEngine::calcNodeRipple( // No fee. Log(lsINFO) << boost::str(boost::format("calcNodeRipple: No fees")); - STAmount saTransfer = bPrvUnlimited ? saCur : MIN(saPrv, saCur); + STAmount saTransfer = bPrvUnlimited ? saCur : std::min(saPrv, saCur); saPrvAct += saTransfer; saCurAct += saTransfer; @@ -2874,8 +2894,8 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS // account --> ACCOUNT --> $ // Overall deliverable. const STAmount& saCurWantedReq = bPrvAccount - ? MIN(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. - : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. + ? std::min(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. + : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> $ : saCurWantedReq=%s") @@ -2888,7 +2908,7 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS // Redeem at 1:1 Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Redeem at 1:1")); - saCurWantedAct = MIN(saPrvRedeemReq, saCurWantedReq); + saCurWantedAct = std::min(saPrvRedeemReq, saCurWantedReq); saPrvRedeemAct = saCurWantedAct; } @@ -3029,7 +3049,7 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS { // offer --> ACCOUNT --> $ const STAmount& saCurWantedReq = bPrvAccount - ? MIN(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. + ? std::min(pspCur->saOutReq, saPrvLimit+saPrvOwed) // If previous is an account, limit. : pspCur->saOutReq; // Previous is an offer, no limit: redeem own IOUs. STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); @@ -3196,7 +3216,7 @@ TER TransactionEngine::calcNodeAccountFwd( // Redeem requested. saCurRedeemAct = saCurRedeemReq.isNegative() ? saCurRedeemReq - : MIN(saCurRedeemReq, saCurSendMaxReq); + : std::min(saCurRedeemReq, saCurSendMaxReq); } else { @@ -3209,7 +3229,7 @@ TER TransactionEngine::calcNodeAccountFwd( // Issue requested and not over budget. saCurIssueAct = saCurSendMaxReq.isNegative() ? saCurIssueReq - : MIN(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); + : std::min(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); } else { diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 67f8077819..149c6289b7 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -118,6 +118,10 @@ enum TER // aka TransactionEngineResult tepPATH_PARTIAL, }; +#define isTemMalformed(x) ((x) >= temMALFORMED && (x) < tefFAILURE) +#define isTefFailure(x) ((x) >= tefFAILURE && (x) < terRETRY) +#define isTepPartial(x) ((x) >= tepPATH_PARTIAL) + bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman); enum TransactionEngineParams @@ -179,23 +183,23 @@ public: std::vector vpnNodes; // When processing, don't want to complicate directory walking with deletion. - std::vector vUnfundedBecame; // Offers that became unfunded. + 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. - curIssuerNode umForward; // Map of currency, issuer to node index. + curIssuerNode umForward; // Map of currency, issuer to node index. // First time working in reverse a funding source was used. // Source may only be used there if not mentioned by an account. - curIssuerNode umReverse; // Map of currency, issuer to node index. + curIssuerNode umReverse; // Map of currency, issuer to node index. LedgerEntrySet lesEntries; int mIndex; - uint64 uQuality; // 0 = none. - STAmount saInReq; // Max amount to spend by sender - STAmount saInAct; // Amount spent by sender (calc output) - STAmount saOutReq; // Amount to send (calc input) - STAmount saOutAct; // Amount actually sent (calc output). + uint64 uQuality; // 0 = none. + STAmount saInReq; // Max amount to spend by sender + STAmount saInAct; // Amount spent by sender (calc output) + STAmount saOutReq; // Amount to send (calc input) + STAmount saOutAct; // Amount actually sent (calc output). PathState( const Ledger::pointer& lpLedger, From 73e6e70f13a17d9229fdb285d88c75d634ca0c0e Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 30 Aug 2012 21:16:07 -0700 Subject: [PATCH 3/4] Use stl for min and max. --- src/Config.cpp | 11 ++++++----- src/ConnectionPool.cpp | 3 ++- src/utils.h | 8 -------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index 4e36f39a1b..a419ae0aae 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #define SECTION_ACCOUNT_PROBE_MAX "account_probe_max" #define SECTION_DEBUG_LOGFILE "debug_logfile" @@ -232,19 +233,19 @@ void Config::load() if (sectionSingleB(secConfig, SECTION_PEER_SCAN_INTERVAL_MIN, strTemp)) // Minimum for min is 60 seconds. - PEER_SCAN_INTERVAL_MIN = MAX(60, boost::lexical_cast(strTemp)); + PEER_SCAN_INTERVAL_MIN = std::max(60, boost::lexical_cast(strTemp)); if (sectionSingleB(secConfig, SECTION_PEER_START_MAX, strTemp)) - PEER_START_MAX = MAX(1, boost::lexical_cast(strTemp)); + PEER_START_MAX = std::max(1, boost::lexical_cast(strTemp)); if (sectionSingleB(secConfig, SECTION_PEER_CONNECT_LOW_WATER, strTemp)) - PEER_CONNECT_LOW_WATER = MAX(1, boost::lexical_cast(strTemp)); + PEER_CONNECT_LOW_WATER = std::max(1, boost::lexical_cast(strTemp)); if (sectionSingleB(secConfig, SECTION_NETWORK_QUORUM, strTemp)) - NETWORK_QUORUM = MAX(0, boost::lexical_cast(strTemp)); + NETWORK_QUORUM = std::max(0, boost::lexical_cast(strTemp)); if (sectionSingleB(secConfig, SECTION_VALIDATION_QUORUM, strTemp)) - VALIDATION_QUORUM = MAX(0, boost::lexical_cast(strTemp)); + VALIDATION_QUORUM = std::max(0, boost::lexical_cast(strTemp)); if (sectionSingleB(secConfig, SECTION_FEE_ACCOUNT_CREATE, strTemp)) FEE_ACCOUNT_CREATE = boost::lexical_cast(strTemp); diff --git a/src/ConnectionPool.cpp b/src/ConnectionPool.cpp index 14e5604841..acceef4887 100644 --- a/src/ConnectionPool.cpp +++ b/src/ConnectionPool.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "Config.h" #include "Peer.h" @@ -645,7 +646,7 @@ void ConnectionPool::scanRefresh() (void) mScanTimer.cancel(); - iInterval = MAX(iInterval, theConfig.PEER_SCAN_INTERVAL_MIN); + iInterval = std::max(iInterval, theConfig.PEER_SCAN_INTERVAL_MIN); tpNext = tpNow + boost::posix_time::seconds(iInterval); diff --git a/src/utils.h b/src/utils.h index f11b82a1b7..91c5ff9b36 100644 --- a/src/utils.h +++ b/src/utils.h @@ -16,14 +16,6 @@ #define ADDRESS(p) strHex(uint64( ((char*) p) - ((char*) 0))) #define ADDRESS_SHARED(p) strHex(uint64( ((char*) (p).get()) - ((char*) 0))) -#ifndef MAX -#define MAX(x,y) ((x) < (y) ? (y) : (x)) -#endif - -#ifndef MIN -#define MIN(x,y) ((x) > (y) ? (y) : (x)) -#endif - #define isSetBit(x,y) (!!((x) & (y))) #ifdef WIN32 From 69de9f9ce2829f3609191b242540051b80b15c2e Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Fri, 31 Aug 2012 14:05:37 -0700 Subject: [PATCH 4/4] Progress toward ripple pre restructuring forward. --- src/TransactionEngine.cpp | 323 +++++++++++++++++++++++--------------- 1 file changed, 199 insertions(+), 124 deletions(-) diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index dfecf4489c..a99b4088fa 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -861,7 +861,9 @@ SLE::pointer TransactionEngine::entryCache(LedgerEntryType letType, const uint25 mNodes.entryCache(sleEntry); } else if (action == taaDELETE) + { assert(false); + } } return sleEntry; @@ -2106,7 +2108,7 @@ TER TransactionEngine::calcNodeOfferRev( // 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); + SLE::pointer sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); // ??? STAmount saOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip), uCurCurrencyID); // For correct ratio unsigned int uEntry = 0; uint256 uNxtIndex; @@ -2197,7 +2199,12 @@ TER TransactionEngine::calcNodeOfferRev( return terResult; } -// Offer input and output is issuer or limbo. +// - Offer input is limbo. +// - Current offers consumed. +// - Current offer owners debited. +// - Transfer fees credited to issuer. +// - Payout to issuer or limbo. +// - Deliver is set without transfer fees. TER TransactionEngine::calcNodeOfferFwd( const unsigned int uIndex, // 0 < uIndex < uLast const PathState::pointer& pspCur, @@ -2205,7 +2212,7 @@ TER TransactionEngine::calcNodeOfferFwd( ) { TER terResult = tepPATH_DRY; - +#if 0 paymentNode& pnPrv = pspCur->vpnNodes[uIndex-1]; paymentNode& pnCur = pspCur->vpnNodes[uIndex]; paymentNode& pnNxt = pspCur->vpnNodes[uIndex+1]; @@ -2234,19 +2241,29 @@ TER TransactionEngine::calcNodeOfferFwd( bool bNxtOffer = !uNxtAccountID; uint256 uNxtTip; uint256 uNxtEnd; - bool bNxtDirAdvance; - bool bNxtSubAdvance; - unsigned int uNxtEntry = 0; - uint256 uNxtIndex; 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) { - uNxtTip = Ledger::getBookBase(uCurCurrencyID, uCurIssuerID, uNxtCurrencyID, uNxtIssuerID); - uNxtEnd = Ledger::getQualityNext(uNxtTip); - sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); - bNxtDirAdvance = !sleNxtDir; - bNxtSubAdvance = true; + 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. @@ -2289,8 +2306,8 @@ TER TransactionEngine::calcNodeOfferFwd( } // Allowed to access source from this node? - curIssuerNodeConstIterator itAllow = pspCur->umForward.find(asLine); - bool bFoundForward = itAllow != pspCur->umForward.end(); + curIssuerNodeConstIterator itAllow = pspCur->umForward.find(asLine); + const bool bFoundForward = itAllow != pspCur->umForward.end(); if (bFoundForward || itAllow->second != uIndex) { @@ -2301,7 +2318,7 @@ TER TransactionEngine::calcNodeOfferFwd( continue; } - const STAmount& saCurOfrOutReq = sleOffer->getIValueFieldAmount(sfTakerGets); + const STAmount saCurOfrOutReq = sleOffer->getIValueFieldAmount(sfTakerGets); STAmount saCurOfrFunds = accountFunds(uCurOfrAccountID, saCurOfrOutReq); // Funds left. STAmount saCurOfrSpent; @@ -2315,7 +2332,7 @@ TER TransactionEngine::calcNodeOfferFwd( continue; } - const STAmount& saCurOfrInReq = sleOffer->getIValueFieldAmount(sfTakerPays); + const STAmount saCurOfrInReq = sleOffer->getIValueFieldAmount(sfTakerPays); STAmount saCurOfrInMax = std::min(saCurOfrInReq, saPrvDlvReq-saPrvDlvAct); STAmount saCurOfrInAct; STAmount saCurOfrOutAct; @@ -2355,100 +2372,155 @@ TER TransactionEngine::calcNodeOfferFwd( { // 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) + if (!bNxtDirAdvance) { - uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); - bNxtSubAdvance = true; - bNxtDirAdvance = false; - uNxtEntry = 0; - if (!!uNxtTip) - sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); + // Current directory is fine. + nothing(); } - - if (!!uNxtTip) + else if (bDirectoryFirst || bMultiQuality) { - // Do a directory. - // - Drive on computing saCurDlvAct to derive saPrvDlvAct. -// ??? STAmount saOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip)); // For correct ratio + if (bDirectoryFirst) + bDirectoryFirst = false; - while (saCurOfrInAct != saCurOfrInMax) // Have more to do. + uNxtTip = mLedger->getNextLedgerIndex(uNxtTip, uNxtEnd); + if (!!uNxtTip) { - if (!bNxtSubAdvance) - { - // Continue with current uNxtIndex. - nothing(); - } - else if (dirNext(uNxtTip, sleNxtDir, uEntry, uNxtIndex)) - { - // Found a next uNxtIndex. - bNxtSubAdvance = false; - } - else - { - // No more offers in directory. - bNxtDirAdvance = true; - break; - } + saNxtOfrRate = STAmount::setRate(STAmount::getQuality(uNxtTip)); // For correct ratio + sleNxtDir = entryCache(ltDIR_NODE, uNxtTip); - SLE::pointer sleNxtOfr = entryCache(ltOFFER, uNxtIndex); - const uint160 uNxtOfrAccountID = sleNxtOfr->getIValueFieldAccount(sfAccount).getAccountID(); - const STAmount& saNxtOfrIn = sleNxtOfr->getIValueFieldAmount(sfTakerPays); - const STAmount& saNxtOfrOut = sleNxtOfr->getIValueFieldAmount(sfTakerGets); + assert(!!sleNxtDir); - const STAmount saFeeRate = uCurOfrAccountID == uCurIssuerID || uNxtOfrAccountID == uCurIssuerID - ? saOne - : saTransferRate; - const bool bFee = saFeeRate != saOne; - - bool bNxtExpired = sleNxtOfr->getIFieldPresent(sfExpiration) - && sleNxtOfr->getIFieldU32(sfExpiration) <= mLedger->getParentCloseTimeNC(); - STAmount saNxtOfrFunds = accountFunds(uNxtOfrAccountID, saNxtOfrOut); // Funds left. - - if (!bNxtExpired && saNxtOfrFunds.isPositive()) - { - // Offer is not expired and is funded. - const STAmount saInBase = saCurOfrInMax-saCurOfrInAct; - const STAmount saOutPass = STAmount::divide(saInBase, saOfrRate, uCurCurrencyID, uCurIssuerID); - STAmount saOutBase = std::min(saCurOfrOutReq, saOutPass); // Limit offer out by needed. - saOutBase = std::min(saOutBase, saNxtOfrIn); // Limit offer out by supplying offer. - const STAmount saOutCost = std::min( - bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutBase, - saCurOfrFunds); // Limit cost by fees & funds. - const STAmount saOutDlvPass= bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) - : saOutCost; // Out amount after fees. - const STAmount saInDlvPass = STAmount::multiply(saOutDlvPass, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. - - // XXX Check fees. - sleNxtOfr->setIFieldAmount(sfTakerGets, saNxtOfrIn - saInDlvPass); - sleNxtOfr->setIFieldAmount(sfTakerPays, saNxtOfrOut - saOutDlvPass); - - if (saNxtOfrOut == saOutDlvPass) { - // Consumed all of offer. - Log(lsINFO) << "calcNodeOfferFwd: offer consumed"; - - pspCur->vUnfundedBecame.push_back(uNxtIndex); // Mark offer for deletion on use of current path state. - - bNxtSubAdvance = true; - } - - saCurOfrSpent += saOutDlvPass; // XXX Check. - - saCurOfrInAct += saInDlvPass; // Add to input handled. - - saCurOfrOutAct += saOutDlvPass; // Add to output handled. - } + 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; + } - // Do another nxt directory iff bMultiQuality - if (!bMultiQuality) - uNxtTip = 0; + 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. @@ -2487,6 +2559,9 @@ TER TransactionEngine::calcNodeOfferFwd( } return !!saCurDlvAct ? tesSUCCESS : terResult; +#else + return terResult; +#endif } #if 0 @@ -2815,12 +2890,12 @@ TER TransactionEngine::calcNodeAccountRev(const unsigned int uIndex, const PathS paymentNode& pnCur = pspCur->vpnNodes[uIndex]; paymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; - const bool bRedeem = !!(pnCur.uFlags & STPathElement::typeRedeem); - const bool bPrvRedeem = !!(pnPrv.uFlags & STPathElement::typeRedeem); - const bool bIssue = !!(pnCur.uFlags & STPathElement::typeIssue); - const bool bPrvIssue = !!(pnPrv.uFlags & STPathElement::typeIssue); - const bool bPrvAccount = !uIndex || !!(pnPrv.uFlags & STPathElement::typeAccount); - const bool bNxtAccount = uIndex == uLast || !!(pnNxt.uFlags & STPathElement::typeAccount); + 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); + 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; @@ -3143,10 +3218,10 @@ TER TransactionEngine::calcNodeAccountFwd( paymentNode& pnCur = pspCur->vpnNodes[uIndex]; paymentNode& pnNxt = pspCur->vpnNodes[uIndex == uLast ? uLast : uIndex+1]; - const bool bRedeem = !!(pnCur.uFlags & STPathElement::typeRedeem); - const bool bIssue = !!(pnCur.uFlags & STPathElement::typeIssue); - const bool bPrvAccount = !!(pnPrv.uFlags & STPathElement::typeAccount); - const bool bNxtAccount = !!(pnNxt.uFlags & STPathElement::typeAccount); + 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); const uint160& uCurAccountID = pnCur.uAccountID; const uint160& uPrvAccountID = bPrvAccount ? pnPrv.uAccountID : uCurAccountID; @@ -3477,15 +3552,15 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint const bool bFirst = vpnNodes.empty(); const paymentNode& pnPrv = bFirst ? paymentNode() : vpnNodes.back(); // true, iff node is a ripple account. false, iff node is an offer node. - const bool bAccount = !!(iType & STPathElement::typeAccount); + const bool bAccount = isSetBit(iType, STPathElement::typeAccount); // true, iff currency supplied. // Currency is specified for the output of the current node. - const bool bCurrency = !!(iType & STPathElement::typeCurrency); + const bool bCurrency = isSetBit(iType, STPathElement::typeCurrency); // Issuer is specified for the output of the current node. - const bool bIssuer = !!(iType & STPathElement::typeIssuer); + const bool bIssuer = isSetBit(iType, STPathElement::typeIssuer); // true, iff account is allowed to redeem it's IOUs to next node. - const bool bRedeem = !!(iType & STPathElement::typeRedeem); - const bool bIssue = !!(iType & STPathElement::typeIssue); + const bool bRedeem = isSetBit(iType, STPathElement::typeRedeem); + const bool bIssue = isSetBit(iType, STPathElement::typeIssue); TER terResult = tesSUCCESS; pnCur.uFlags = iType; @@ -3520,7 +3595,7 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint if (tesSUCCESS == terResult && !vpnNodes.empty()) { const paymentNode& pnBck = vpnNodes.back(); - bool bBckAccount = !!(pnBck.uFlags & STPathElement::typeAccount); + bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); if (bBckAccount) { @@ -3595,8 +3670,8 @@ TER PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint { // Verify that previous account is allowed to issue. const paymentNode& pnBck = vpnNodes.back(); - bool bBckAccount = !!(pnBck.uFlags & STPathElement::typeAccount); - bool bBckIssue = !!(pnBck.uFlags & STPathElement::typeIssue); + bool bBckAccount = isSetBit(pnBck.uFlags, STPathElement::typeAccount); + bool bBckIssue = isSetBit(pnBck.uFlags, STPathElement::typeIssue); if (bBckAccount && !bBckIssue) { @@ -3795,15 +3870,15 @@ Json::Value PathState::getJson() const TER TransactionEngine::calcNode(const unsigned int uIndex, const PathState::pointer& pspCur, const bool bMultiQuality) { const paymentNode& pnCur = pspCur->vpnNodes[uIndex]; - const bool bCurAccount = !!(pnCur.uFlags & STPathElement::typeAccount); + const bool bCurAccount = isSetBit(pnCur.uFlags, STPathElement::typeAccount); TER terResult; Log(lsINFO) << boost::str(boost::format("calcNode> uIndex=%d") % uIndex); // Do current node reverse. terResult = bCurAccount - ? calcNodeAccountRev(uIndex, pspCur, bMultiQuality) - : calcNodeOfferRev(uIndex, pspCur, bMultiQuality); + ? calcNodeAccountRev(uIndex, pspCur, bMultiQuality) + : calcNodeOfferRev(uIndex, pspCur, bMultiQuality); // Do previous. if (tesSUCCESS == terResult && uIndex) @@ -3815,8 +3890,8 @@ TER TransactionEngine::calcNode(const unsigned int uIndex, const PathState::poin if (tesSUCCESS == terResult) { terResult = bCurAccount - ? calcNodeAccountFwd(uIndex, pspCur, bMultiQuality) - : calcNodeOfferFwd(uIndex, pspCur, bMultiQuality); + ? calcNodeAccountFwd(uIndex, pspCur, bMultiQuality) + : calcNodeOfferFwd(uIndex, pspCur, bMultiQuality); } Log(lsINFO) << boost::str(boost::format("calcNode< uIndex=%d terResult=%d") % uIndex % terResult); @@ -3860,9 +3935,9 @@ 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 = !!(uTxFlags & tfCreateAccount); - const bool bNoRippleDirect = !!(uTxFlags & tfNoRippleDirect); - const bool bPartialPayment = !!(uTxFlags & tfPartialPayment); + const bool bCreate = isSetBit(uTxFlags, tfCreateAccount); + const bool bNoRippleDirect = isSetBit(uTxFlags, tfNoRippleDirect); + const bool bPartialPayment = isSetBit(uTxFlags, tfPartialPayment); const bool bPaths = txn.getITFieldPresent(sfPaths); const bool bMax = txn.getITFieldPresent(sfSendMax); const uint160 uDstAccountID = txn.getITFieldAccount(sfDestination); @@ -4458,7 +4533,7 @@ TER TransactionEngine::doOfferCreate(const SerializedTransaction& txn) { Log(lsWARNING) << "doOfferCreate> " << txn.getJson(0); const uint32 txFlags = txn.getFlags(); - const bool bPassive = !!(txFlags & tfPassive); + const bool bPassive = isSetBit(txFlags, tfPassive); STAmount saTakerPays = txn.getITFieldAmount(sfTakerPays); STAmount saTakerGets = txn.getITFieldAmount(sfTakerGets); Log(lsWARNING) << "doOfferCreate: saTakerPays=" << saTakerPays.getFullText();