From 42bd29c212f6869992387ade887e0ea401aef8ee Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Thu, 2 Aug 2012 19:16:24 -0700 Subject: [PATCH] Work towards ripple. --- src/SerializedTypes.h | 1 + src/TransactionEngine.cpp | 491 ++++++++++++++++++++++++++------------ src/TransactionEngine.h | 71 +++--- 3 files changed, 375 insertions(+), 188 deletions(-) diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 21a3ca8f6b..392966fcbb 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -614,6 +614,7 @@ namespace boost typedef std::vector::const_iterator type; }; } + class STPathSet : public SerializedType { // A set of zero or more payment paths protected: diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 50d70ea75d..25a0cef547 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -21,6 +21,11 @@ #define DIR_NODE_MAX 2 #define RIPPLE_PATHS_MAX 3 +#define QUALITY_ONE 100000000 // 10e9 +#define CURRENCY_ONE uint160(1) + +// static STAmount saOne(CURRENCY_ONE, 1, 0); + bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std::string& strHuman) { static struct { @@ -35,6 +40,7 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std { tenBAD_GEN_AUTH, "tenBAD_GEN_AUTH", "Not authorized to claim generator." }, { tenBAD_ISSUER, "tenBAD_ISSUER", "Malformed." }, { tenBAD_OFFER, "tenBAD_OFFER", "Malformed." }, + { tenBAD_PATH, "tenBAD_PATH", "Malformed: path." }, { tenBAD_PATH_COUNT, "tenBAD_PATH_COUNT", "Malformed: too many paths." }, { tenBAD_PUBLISH, "tenBAD_PUBLISH", "Malformed: bad publish." }, { tenBAD_RIPPLE, "tenBAD_RIPPLE", "Ledger prevents ripple from succeeding." }, @@ -99,12 +105,92 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std return iIndex >= 0; } -// Return how much of uIssuerID's uCurrency IOUs that uAccountID holds. May be negative. +STAmount TransactionEngine::rippleBalance(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) +{ + STAmount saBalance; + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID)); + + assert(sleRippleState); + if (sleRippleState) + { + saBalance = sleRippleState->getIValueFieldAmount(sfBalance); + if (uToAccountID < uFromAccountID) + saBalance.negate(); + } + + return saBalance; + +} + +STAmount TransactionEngine::rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) +{ + STAmount saLimit; + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID)); + + assert(sleRippleState); + if (sleRippleState) + { + saLimit = sleRippleState->getIValueFieldAmount(uToAccountID < uFromAccountID ? sfLowLimit : sfHighLimit); + } + + return saLimit; + +} + +uint32 TransactionEngine::rippleTransfer(const uint160& uIssuerID) +{ + SLE::pointer sleAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uIssuerID)); + + return sleAccount->getIFieldPresent(sfTransferRate) + ? sleAccount->getIFieldU32(sfTransferRate) + : QUALITY_ONE; +} + +// XXX Might not need this, might store in nodes on calc reverse. +uint32 TransactionEngine::rippleQualityIn(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) +{ + uint32 uQualityIn; + + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID)); + + if (sleRippleState) + { + uQualityIn = sleRippleState->getIFieldU32(uToAccountID < uFromAccountID ? sfLowQualityIn : sfHighQualityIn); + } + else + { + assert(false); + uQualityIn = QUALITY_ONE; + } + + return uQualityIn; +} + +uint32 TransactionEngine::rippleQualityOut(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) +{ + uint32 uQualityOut; + + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID)); + + if (sleRippleState) + { + uQualityOut = sleRippleState->getIFieldU32(uToAccountID < uFromAccountID ? sfLowQualityOut : sfHighQualityOut); + } + else + { + assert(false); + uQualityOut = QUALITY_ONE; + } + + return uQualityOut; +} + +// Return how much of uIssuerID's uCurrencyID IOUs that uAccountID holds. May be negative. // <-- IOU's uAccountID has of uIssuerID -STAmount TransactionEngine::rippleHolds(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID) +STAmount TransactionEngine::rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID) { STAmount saBalance; - SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uAccountID, uIssuerID, uCurrency)); + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uAccountID, uIssuerID, uCurrencyID)); if (sleRippleState) { @@ -117,12 +203,12 @@ STAmount TransactionEngine::rippleHolds(const uint160& uAccountID, const uint160 return saBalance; } -// <-- saAmount: amount of uCurrency held by uAccountID. May be negative. -STAmount TransactionEngine::accountHolds(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID) +// <-- saAmount: amount of uCurrencyID held by uAccountID. May be negative. +STAmount TransactionEngine::accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID) { STAmount saAmount; - if (uCurrency.isZero()) + if (uCurrencyID.isZero()) { SLE::pointer sleAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uAccountID)); @@ -132,12 +218,12 @@ STAmount TransactionEngine::accountHolds(const uint160& uAccountID, const uint16 } else { - saAmount = rippleHolds(uAccountID, uCurrency, uIssuerID); + saAmount = rippleHolds(uAccountID, uCurrencyID, uIssuerID); Log(lsINFO) << "accountHolds: " << saAmount.getFullText() << " : " - << STAmount::createHumanCurrency(uCurrency) + << STAmount::createHumanCurrency(uCurrencyID) << "/" << NewcoinAddress::createHumanAccountID(uIssuerID); } @@ -190,16 +276,11 @@ STAmount TransactionEngine::rippleTransit(const uint160& uSenderID, const uint16 if (uSenderID != uIssuerID && uReceiverID != uIssuerID) { - SLE::pointer sleIssuerAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(uIssuerID)); - uint32 uTransitRate; + uint32 uTransitRate = rippleTransfer(uIssuerID); - if (sleIssuerAccount->getIFieldPresent(sfTransferRate)) - uTransitRate = sleIssuerAccount->getIFieldU32(sfTransferRate); - - if (uTransitRate) + if (QUALITY_ONE != uTransitRate) { - - STAmount saTransitRate(uint160(1), uTransitRate, -9); + STAmount saTransitRate(CURRENCY_ONE, uTransitRate, -9); saTransitFee = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency()); } @@ -208,6 +289,50 @@ STAmount TransactionEngine::rippleTransit(const uint160& uSenderID, const uint16 return saTransitFee; } +// Direct send w/o fees: redeeming IOUs and/or sending own IOUs. +void TransactionEngine::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount) +{ + uint160 uIssuerID = saAmount.getIssuer(); + + assert(uSenderID == uIssuerID || uReceiverID == uIssuerID); + + bool bFlipped = uSenderID > uReceiverID; + uint256 uIndex = Ledger::getRippleStateIndex(uSenderID, uReceiverID, saAmount.getCurrency()); + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, uIndex); + + if (!sleRippleState) + { + Log(lsINFO) << "rippleCredit: Creating ripple line: " << uIndex.ToString(); + + STAmount saBalance = saAmount; + + sleRippleState = entryCreate(ltRIPPLE_STATE, uIndex); + + if (!bFlipped) + saBalance.negate(); + + sleRippleState->setIFieldAmount(sfBalance, saBalance); + sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, uSenderID); + sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uReceiverID); + } + else + { + STAmount saBalance = sleRippleState->getIValueFieldAmount(sfBalance); + + if (!bFlipped) + saBalance.negate(); // Put balance in low terms. + + saBalance += saAmount; + + if (!bFlipped) + saBalance.negate(); + + sleRippleState->setIFieldAmount(sfBalance, saBalance); + + entryModify(sleRippleState); + } +} + // Send regardless of limits. // --> saAmount: Amount/currency/issuer for receiver to get. // <-- saActual: Amount actually sent. Sender pay's fees. @@ -219,42 +344,7 @@ STAmount TransactionEngine::rippleSend(const uint160& uSenderID, const uint160& if (uSenderID == uIssuerID || uReceiverID == uIssuerID) { // Direct send: redeeming IOUs and/or sending own IOUs. - - bool bFlipped = uSenderID > uReceiverID; - uint256 uIndex = Ledger::getRippleStateIndex(uSenderID, uReceiverID, saAmount.getCurrency()); - SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, uIndex); - - if (!sleRippleState) - { - Log(lsINFO) << "rippleSend: Creating ripple line: " << uIndex.ToString(); - - STAmount saBalance = saAmount; - - sleRippleState = entryCreate(ltRIPPLE_STATE, uIndex); - - if (!bFlipped) - saBalance.negate(); - - sleRippleState->setIFieldAmount(sfBalance, saBalance); - sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, uSenderID); - sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uReceiverID); - } - else - { - STAmount saBalance = sleRippleState->getIValueFieldAmount(sfBalance); - - if (!bFlipped) - saBalance.negate(); // Put balance in low terms. - - saBalance += saAmount; - - if (!bFlipped) - saBalance.negate(); - - sleRippleState->setIFieldAmount(sfBalance, saBalance); - - entryModify(sleRippleState); - } + rippleCredit(uSenderID, uReceiverID, saAmount); saActual = saAmount; } @@ -268,8 +358,8 @@ STAmount TransactionEngine::rippleSend(const uint160& uSenderID, const uint160& saActual.setIssuer(uIssuerID); // XXX Make sure this done in + above. - rippleSend(uIssuerID, uReceiverID, saAmount); - rippleSend(uSenderID, uIssuerID, saActual); + rippleCredit(uIssuerID, uReceiverID, saAmount); + rippleCredit(uSenderID, uIssuerID, saActual); } return saActual; @@ -1405,12 +1495,12 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti uint32 uQualityIn = bQualityIn ? txn.getITFieldU32(sfQualityIn) : 0; bool bQualityOut = txn.getITFieldPresent(sfQualityOut); uint32 uQualityOut = bQualityIn ? txn.getITFieldU32(sfQualityOut) : 0; - uint160 uCurrency = saLimitAmount.getCurrency(); - STAmount saBalance(uCurrency); + uint160 uCurrencyID = saLimitAmount.getCurrency(); + STAmount saBalance(uCurrencyID); bool bAddIndex = false; bool bDelIndex = false; - SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrency)); + SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); if (sleRippleState) { // A line exists in one or more directions. @@ -1489,10 +1579,10 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti else { // Create a new ripple line. - STAmount saZero(uCurrency); + STAmount saZero(uCurrencyID); bAddIndex = true; - sleRippleState = entryCreate(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrency)); + sleRippleState = entryCreate(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); Log(lsINFO) << "doCreditSet: Creating ripple line: " << sleRippleState->getIndex().ToString(); @@ -2024,7 +2114,7 @@ void TransactionEngine::calcNodeOfferReverse( // From the destination work towards the source calculating how much must be asked for. // --> bAllowPartial: If false, fail if can't meet requirements. -// <-- bSuccess: true=success, false=insufficient funds. +// <-- bSuccess: true=success, false=insufficient funds / liqudity. // <-> pnNodes: // --> [end]saWanted.mAmount // --> [all]saWanted.mCurrency @@ -2034,29 +2124,70 @@ void TransactionEngine::calcNodeOfferReverse( // XXX With multiple path and due to offers, must consider consumed. bool TransactionEngine::calcPathReverse(PathState::pointer pspCur) { - bool bSent = true; -#if 0 TransactionEngineResult terResult = tenUNKNOWN; - unsigned int uIndex = pspCur->vpnNodes.size() - 1; + unsigned int uLast = pspCur->vpnNodes.size() - 1; + unsigned int uIndex = pspCur->vpnNodes.size(); - while (bSent && tenUNKNOWN == terResult && uIndex--) + while (tenUNKNOWN == terResult && uIndex--) { - // Calculate (1) sending by fullfilling next wants and (2) setting current wants. + // Calculate: + // (1) saPrvRedeem & saPrvIssue from saCurRevRedeem & saCurRevIssue. + // (2) saCurWanted by summing saRevRedeem & saRevIssue. <-- XXX might not need this as it can be infered. + // XXX We might not need to do 0. - paymentNode& prvPN = uIndex ? pnNodes[uIndex-1] : pnNodes[0]; - paymentNode& curPN = pnNodes[uIndex]; - paymentNode& nxtPN = pnNodes[uIndex+1]; - bool bRedeem = !!(curPN.uFlags & STPathElement::typeRedeem); - bool bIssue = !!(curPN.uFlags & STPathElement::typeIssue); - bool bOffer = !!(curPN.uFlags & STPathElement::typeOffer); + paymentNode& prvPN = uIndex ? pspCur->vpnNodes[uIndex-1] : pspCur->vpnNodes[0]; + paymentNode& curPN = pspCur->vpnNodes[uIndex]; + paymentNode& nxtPN = uIndex == uLast ? pspCur->vpnNodes[uLast] : pspCur->vpnNodes[uIndex+1]; + bool bAccount = !!(curPN.uFlags & STPathElement::typeAccount); + bool bRedeem = !!(curPN.uFlags & STPathElement::typeRedeem); + bool bIssue = !!(curPN.uFlags & STPathElement::typeIssue); + uint160& uPrvAccountID = prvPN.uAccountID; + uint160& uCurAccountID = curPN.uAccountID; + uint160& uNxtAccountID = nxtPN.uAccountID; + uint160& uCurrencyID = curPN.uCurrencyID; - if (curPN.saWanted.isZero()) + if (uIndex == uLast) { - // Must want something. - terResult = tenBAD_AMOUNT; + // saCurWanted set by caller. + uint32 uQualityIn = rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID); + STAmount saPrvBalance = rippleBalance(uCurAccountID, uPrvAccountID, uCurrencyID); + STAmount saPrvLimit = rippleLimit(uCurAccountID, uPrvAccountID, uCurAccountID); + + STAmount saPrvRedeemReq = saPrvBalance.isNative() ? -saPrvBalance : STAmount(uCurrencyID, 0); + STAmount& saPrvRedeemAct = prvPN.saRevRedeem; // What previous should redeem with cur. + + STAmount& saPrvIssueAct = prvPN.saRevIssue; + STAmount saPrvIssueReq = saPrvBalance.isPositive() ? saPrvLimit - saPrvBalance : saPrvLimit; + + STAmount saCurWantedReq = pspCur->saOutReq; + STAmount saCurWantedAct; + + // Calculate redeem + if (bRedeem + && saPrvRedeemReq) // Previous has IOUs to redeem. + { + // Redeem at 1:1 + saCurWantedAct = MIN(saPrvRedeemReq, saCurWantedReq); + saPrvRedeemAct = saCurWantedAct; + } + + // Calculate issuing. + if (bIssue + && saCurWantedReq != saCurWantedAct // Need more. + && saPrvIssueReq) // Will accept IOUs. + { + // Rate: quality in : 1.0 + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq, saPrvIssueAct, saCurWantedAct); + } + + if (!saCurWantedAct) + { + // Must have processed something. + terResult = tenBAD_AMOUNT; + } } - else if (bOffer) + else if (!bAccount) { // Offer node. // Ripple or transfering from previous node through this offer to next node. @@ -2065,12 +2196,12 @@ bool TransactionEngine::calcPathReverse(PathState::pointer pspCur) // We limit what this node sends by this nodes redeem and issue max. // This allows path lists to be strictly redeem. // XXX Make sure offer book was not previously mentioned. - - uint160 uPrvCurrency = curPN->uFlags & PF_WANTED_CURRENCY +#if 0 + uint160 uPrvCurrency = curPN.uFlags & PF_WANTED_CURRENCY ? curPN->saWanted.getCurrency() : saSendMax.getCurrency(); - uint160 uPrvIssuer = curPN->uFlags & PF_WANTED_ISSUER + uint160 uPrvIssuer = curPN.uFlags & PF_WANTED_ISSUER ? curPN->saWanted.getIssuer() : saSendMax.getIssuer(); @@ -2095,67 +2226,114 @@ bool TransactionEngine::calcPathReverse(PathState::pointer pspCur) // Save sent amount } +#endif } - // Account node. - else if (!bIssue && !bOffer) + // Ripple through account. + else if (!bIssue && !bRedeem) { - terResult = tenBAD_PATH; + // XXX This might be checked sooner. + terResult = tenBAD_PATH; // Must be able to redeem and issue. + } + else if (!uIndex) + { + // Done. No previous. We know what we would like to redeem and issue. + nothing(); } else { // Rippling through this accounts balances. // No currency change. - // Issuer change possible. - // Figuring out want current account should redeem and send. + // Given saCurRedeem & saCurIssue figure out saPrvRedeem & saPrvIssue. - SLE::pointer sleRippleCur = ; - SLE::pointer sleRippleNxt = ; - STAmount saBalance = peekBalance(curPN.uAccountID, nxtPN.uAccountID); - STAmount saLimit = peekLimit(curPN.uAccountID, nxtPN.uAccountID); - STAmount saWanted = nxtPN.saWanted; + uint32 uQualityIn = rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID); + uint32 uQualityOut = rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID); + STAmount saPrvBalance = rippleBalance(uCurAccountID, uPrvAccountID, uCurrencyID); + STAmount saPrvLimit = rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID); - curPN.saWanted.zero(); + STAmount saPrvRedeemReq = saPrvBalance.isNegative() ? -saPrvBalance : STAmount(uCurrencyID, 0); + STAmount& saPrvRedeemAct = prvPN.saRevRedeem; - if (bRedeem && saBalance.isPositive()) + STAmount saPrvIssueReq = saPrvLimit - saPrvBalance; + STAmount& saPrvIssueAct = prvPN.saRevIssue; + + const STAmount& saCurRedeemReq = curPN.saRevRedeem; + STAmount saCurRedeemAct; + + const STAmount& saCurIssueReq = curPN.saRevIssue; + STAmount saCurIssueAct; // Track progess. + + + // Previous redeem part 1: redeem -> redeem + if (bRedeem + && saCurRedeemReq // Next wants us to redeem. + && saPrvBalance.isNegative()) // Previous has IOUs to redeem. { - // Allowed to redeem and have IOUs. - curPN.saIOURedeem = min(saBalance, saWanted); - curPN.saWanted = curPN.saIOURedeem; // XXX Assumes we want 1::1 - saWanted -= curPN.saIOURedeem; - } - else - { - curPN.saIOURedeem.zero(); + // Rate : 1.0 : quality out + + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct); } - if (bIssue // Allowed to issue. - && !saWanted.isZero() // Need to issue. - && !saBalance.isPositive()) // Can issue. (Can't if must redeem). + // Previous redeem part 2: redeem -> issue. + if (bIssue + && saCurRedeemReq != saCurRedeemAct // Can only if issue if more can not be redeemed. + && saPrvBalance.isNegative() // Previous still has IOUs. + && saCurIssueReq) // Need some issued. { - curPN.saIOUIssue = min(saLimit+saBalance, saWanted); - curPN.saWanted += curPN.saIOUIssue; // XXX Assumes we want 1::1 - } - else - { - curPN.saIOUIssue.zero(); + // Rate : 1.0 : transfer_rate + calcNodeRipple(QUALITY_ONE, rippleTransfer(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct); } - bSent = !curPN.saIOURedeem.isZero() || !curPN.saIOUIssue.isZero(); + // Previous issue part 1: issue -> redeem + if (bRedeem + && saCurRedeemReq != saCurRedeemAct // Can only redeem if more to be redeemed. + && !saPrvBalance.isNegative()) // Previous has no IOUs. + { + // Rate: quality in : quality out + calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct); + } + + // Previous issue part 2 : issue -> issue + if (bIssue + && saCurRedeemReq != saCurRedeemAct // Can only if issue if more can not be redeemed. + && !saPrvBalance.isNegative() // Previous has no IOUs. + && saCurIssueReq != saCurIssueAct) // Need some issued. + { + // Rate: quality in : 1.0 + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct); + } + + if (!saCurRedeemAct && !saCurIssueAct) + { + // Must want something. + terResult = tenBAD_AMOUNT; + } } } -#endif - return bSent; + + return tenUNKNOWN == terResult; } // Cur is the driver and will be filled exactly. -void TransactionEngine::calcNodeFwd(const uint32 uQualityIn, const uint32 uQualityOut, - const STAmount& saPrvReq, const STAmount& saCurReq, - STAmount& saPrvAct, STAmount& saCurAct) +// uQualityIn -> uQualityOut +// saPrvReq -> saCurReq +// sqPrvAct -> saCurAct +// This routine works backwards as it calculates previous wants based on previous credit limits and current wants. +// This routine works forwards as it calculates current deliver based on previous delivery limits and current wants. +void TransactionEngine::calcNodeRipple( + const uint32 uQualityIn, + const uint32 uQualityOut, + const STAmount& saPrvReq, // --> in limit including fees + const STAmount& saCurReq, // --> out limit (driver) + STAmount& saPrvAct, // <-> in limit including achieved + STAmount& saCurAct) // <-> out limit achieved. { + STAmount saPrv = saPrvReq-saPrvAct; + STAmount saCur = saCurReq-saCurAct; + if (uQualityIn >= uQualityOut) { // No fee. - STAmount saTransfer = MIN(saPrvReq-saPrvAct, saCurReq-saCurAct); + STAmount saTransfer = MIN(saPrv, saCur); saPrvAct += saTransfer; saCurAct += saTransfer; @@ -2163,9 +2341,7 @@ void TransactionEngine::calcNodeFwd(const uint32 uQualityIn, const uint32 uQuali else { // Fee. - STAmount saPrv = saPrvReq-saPrvAct; - STAmount saCur = saCurReq-saCurAct; - STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityIn, uint160(1)), uQualityOut, uint160(1)); + STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, CURRENCY_ONE), uQualityIn, CURRENCY_ONE); if (saCurIn >= saPrv) { @@ -2175,8 +2351,8 @@ void TransactionEngine::calcNodeFwd(const uint32 uQualityIn, const uint32 uQuali } else { - // A part of cur. All of prv. - STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityOut, uint160(1)), uQualityIn, uint160(1)); + // A part of cur. All of prv. (cur as driver) + STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, CURRENCY_ONE), uQualityOut, CURRENCY_ONE); saCurAct += saCurOut; saPrvAct = saPrvReq; @@ -2191,22 +2367,20 @@ void TransactionEngine::calcNodeFwd(const uint32 uQualityIn, const uint32 uQuali // --> [all]saWanted.IOURedeem // --> [all]saWanted.IOUIssue // --> [all]saAccount -// XXX This path becomes dirty if saSendMaxFirst must be changed. void TransactionEngine::calcPathForward(PathState::pointer pspCur) { -#if 0 - TransactionEngineResult terResult = tenUNKNOWN; unsigned int uIndex = 0; unsigned int uEnd = pspCur->vpnNodes.size(); unsigned int uLast = uEnd - 1; - while (tenUNKNOWN == terResult && uIndex != uEnd) + while (uIndex != uEnd) { // Calculate (1) sending by fullfilling next wants and (2) setting current wants. - paymentNode& prvPN = uIndex ? pnNodes[uIndex-1] : pnNodes[0]; - paymentNode& curPN = pnNodes[uIndex]; + paymentNode& prvPN = uIndex ? pspCur->vpnNodes[uIndex-1] : pspCur->vpnNodes[0]; + paymentNode& curPN = pspCur->vpnNodes[uIndex]; + paymentNode& nxtPN = uIndex == uLast ? pspCur->vpnNodes[uLast] : pspCur->vpnNodes[uIndex+1]; // XXX Assume rippling. @@ -2218,8 +2392,8 @@ void TransactionEngine::calcPathForward(PathState::pointer pspCur) STAmount& saCurIssueReq = curPN.saRevIssue; STAmount& saCurIssueAct = curPN.saFwdIssue; - STAmount& saCurSendMaxReq = curPN.saSendMax; - STAmount saCurSendMaxAct; + STAmount& saCurSendMaxReq = pspCur->saInReq; + STAmount& saCurSendMaxAct = pspCur->saInAct; if (saCurRedeemReq) { @@ -2238,50 +2412,55 @@ void TransactionEngine::calcPathForward(PathState::pointer pspCur) else if (uIndex == uLast) { // Last node. Accept all funds. Calculate amount actually to credit. - uint32 uQualityIn = peekQualityIn(curPN.uAccountID, prvPN.uAccountID); + uint32 uQualityIn = rippleQualityIn(curPN.uAccountID, prvPN.uAccountID, curPN.uCurrencyID); STAmount& saPrvRedeemReq = prvPN.saFwdRedeem; STAmount& saPrvIssueReq = prvPN.saFwdIssue; STAmount saPrvIssueAct = uQualityIn >= QUALITY_ONE - ? saPrvIssueReq // No fee. - : multiply(saPrvIssueReq, uQualityIn, _); // Fee. - STAmount& saCurReceive = curPN.saReceive; + ? saPrvIssueReq // No fee. + : STAmount::multiply(saPrvIssueReq, uQualityIn, curPN.uCurrencyID); // Fee. + STAmount& saCurReceive = pspCur->saOutAct; // Amount to credit. saCurReceive = saPrvRedeemReq+saPrvIssueAct; // Actually receive. - rippleCredit(curPN.uAccountID, prvPN.uAccountID, saPrvRedeemReq + saPrvIssueReq); + rippleCredit(curPN.uAccountID, prvPN.uAccountID, saCurReceive); } else { // Handle middle node. // The previous nodes tells want it wants to push through to current. - // The current node know what it woud push through next. + // The current node know what it would push through next. // Determine for the current node: // - Output to next node minus fees. // Perform balance adjustment with previous. // All of previous output is consumed. - STAmount saSrcRedeem; // To start, redeeming none. - STAmount saSrcIssue; // To start, issuing none. - STAmount saDstRedeem; + uint160& uCurrencyID = curPN.uCurrencyID; - // Have funds in previous node to transfer. - uint32 uQualityIn = peekQualityIn(curPN.uAccountID, prvPN.uAccountID); - uint32 uQualityOut = peekQualityOut(curPN.uAccountID, nxtPN.uAccountID); + uint160& uPrvAccountID = prvPN.uAccountID; + uint160& uCurAccountID = curPN.uAccountID; + uint160& uNxtAccountID = nxtPN.uAccountID; STAmount& saPrvRedeemReq = prvPN.saFwdRedeem; STAmount saPrvRedeemAct; - STAmount& saCurRedeemReq = curPN.saRevRedeem; - STAmount& saCurRedeemAct = curPN.saFwdRedeem; STAmount& saPrvIssueReq = prvPN.saFwdIssue; STAmount saPrvIssueAct; + STAmount& saCurRedeemReq = curPN.saRevRedeem; + STAmount& saCurRedeemAct = curPN.saFwdRedeem; + STAmount& saCurIssueReq = curPN.saRevIssue; + STAmount& saCurIssueAct = curPN.saFwdIssue; + + // Have funds in previous node to transfer. + uint32 uQualityIn = rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID); + uint32 uQualityOut = rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID); + // Previous redeem part 1: redeem -> redeem if (saPrvRedeemReq != saPrvRedeemAct) // Previous wants to redeem. To next must be ok. { // Rate : 1.0 : quality out - calcNodeFwd(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct); + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct); } // Previous redeem part 2: redeem -> issue. @@ -2290,8 +2469,8 @@ void TransactionEngine::calcPathForward(PathState::pointer pspCur) if (saPrvRedeemReq != saPrvRedeemAct // Previous still wants to redeem. && saCurRedeemReq == saCurRedeemAct) // Current has more to redeem to next. { - // Rate : 1.0 : 1.0 + transfer_rate - calcNodeFwd(QUALITY_ONE, QUALITY_ONE+peekTransfer(curPN.uAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct); + // Rate : 1.0 : transfer_rate + calcNodeRipple(QUALITY_ONE, rippleTransfer(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct); } // Previous issue part 1: issue -> redeem @@ -2299,21 +2478,21 @@ void TransactionEngine::calcPathForward(PathState::pointer pspCur) && saCurRedeemReq == saCurRedeemAct) // Current has more to redeem to next. { // Rate: quality in : quality out - calcNodeFwd(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct); + calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct); } // Previous issue part 2 : issue -> issue if (saPrvIssueReq != saPrvIssueAct) // Previous wants to issue. To next must be ok. { // Rate: quality in : 1.0 - calcNodeFwd(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct); } // Adjust prv --> cur balance : take all inbound - rippleCredit(cur, prv, saPrvRedeemReq + saPrvIssueReq); + // XXX Currency must be in amount. + rippleCredit(uCurAccountID, uPrvAccountID, saPrvRedeemReq + saPrvIssueReq); } } -#endif } bool PathState::less(const PathState::pointer& lhs, const PathState::pointer& rhs) @@ -2324,8 +2503,8 @@ bool PathState::less(const PathState::pointer& lhs, const PathState::pointer& rh return lhs->uQuality > rhs->uQuality; // Bigger is worse. // Best quanity is second rank. - if (lhs->saOut != rhs->saOut) - return lhs->saOut < rhs->saOut; // Smaller is worse. + if (lhs->saOutAct != rhs->saOutAct) + return lhs->saOutAct < rhs->saOutAct; // Smaller is worse. // Path index is third rank. return lhs->mIndex > rhs->mIndex; // Bigger is worse. @@ -2345,15 +2524,17 @@ PathState::PathState( { lesEntries = lesSource.duplicate(); + saOutReq = saSend; + saInReq = saSendMax; + paymentNode pnFirst; paymentNode pnLast; - pnLast.bPartialPayment = bPartialPayment; - pnLast.uAccount = uReceiverID; - pnLast.saWanted = saSend; + pnLast.uAccountID = uReceiverID; + pnLast.uCurrencyID = saOutReq.getCurrency(); - pnFirst.uAccount = uSenderID; - pnFirst.saWanted = saSendMax; + pnFirst.uAccountID = uSenderID; + pnFirst.uCurrencyID = saSendMax.getCurrency(); vpnNodes.push_back(pnFirst); vpnNodes.push_back(pnLast); diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 6143468d10..e246895cc0 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -27,6 +27,7 @@ enum TransactionEngineResult tenBAD_GEN_AUTH, tenBAD_ISSUER, tenBAD_OFFER, + tenBAD_PATH, tenBAD_PATH_COUNT, tenBAD_PUBLISH, tenBAD_SET_ID, @@ -100,24 +101,19 @@ enum TransactionEngineParams }; typedef struct { - bool bPartialPayment; // --> uint16 uFlags; // --> from path - uint160 uAccount; // --> recieving/sending account + uint160 uAccountID; // --> recieving/sending account + uint160 uCurrencyID; // --> currency to recieve - STAmount saSendMax; // --> First node: most to send. - STAmount saRecieve; // <-- Last node: Value received (minus fees) from upstream. + // Computed by Reverse. + STAmount saRevRedeem; // <-- Amount to redeem to next. + STAmount saRevIssue; // <-- Amount to issue to next limited by credit and outstanding IOUs. + // ? STAmount saSend; // <-- Stamps this node will send downstream. - STAmount saRevRedeem; // <-- Computed amount node needs at most redeem. - STAmount saRevIssue; // <-- Computed amount node ____ - STAmount saCurRedeem; // <-- Amount node will redeem to next. - STAmount saCurIssue; // <-- Amount node will issue to next. - - STAmount saWanted; // <-- What this node wants from upstream. - - STAmount saSend; // <-- Stamps this node will send downstream. - - STAmount saXNSRecieve; // Amount stamps to receive. + // Computed by forward. + STAmount saFwdRedeem; // <-- Amount node will redeem to next. + STAmount saFwdIssue; // <-- Amount node will issue to next. } paymentNode; // Hold a path state under incremental application. @@ -132,8 +128,10 @@ public: int mIndex; uint64 uQuality; // 0 = none. - STAmount saIn; - STAmount saOut; + 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). bool bDirty; // Path not computed. PathState( @@ -204,39 +202,46 @@ private: STAmount& saTakerGot); protected: - Ledger::pointer mLedger; - uint64 mLedgerParentCloseTime; + Ledger::pointer mLedger; + uint64 mLedgerParentCloseTime; - uint160 mTxnAccountID; - SLE::pointer mTxnAccount; + uint160 mTxnAccountID; + SLE::pointer mTxnAccount; boost::unordered_set mUnfunded; // Indexes that were found unfunded. - SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex); - SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex); - void entryDelete(SLE::pointer sleEntry, bool unfunded = false); - void entryModify(SLE::pointer sleEntry); + SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex); + SLE::pointer entryCache(LedgerEntryType letType, const uint256& uIndex); + void entryDelete(SLE::pointer sleEntry, bool unfunded = false); + void entryModify(SLE::pointer sleEntry); - void entryReset(); + void entryReset(); - STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID); - STAmount rippleTransit(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount); - STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); + uint32 rippleTransfer(const uint160& uIssuerID); + STAmount rippleBalance(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID); + STAmount rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID); + uint32 rippleQualityIn(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID); + uint32 rippleQualityOut(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID); - STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID); - STAmount accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); - STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault); + STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID); + STAmount rippleTransit(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount); + void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); + STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); + + STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID); + STAmount accountSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); + STAmount accountFunds(const uint160& uAccountID, const STAmount& saDefault); PathState::pointer pathCreate(const STPath& spPath); void pathApply(PathState::pointer pspCur); void pathNext(PathState::pointer pspCur); - void calcNodeFwd(const uint32 uQualityIn, const uint32 uQualityOut, + void calcNodeRipple(const uint32 uQualityIn, const uint32 uQualityOut, const STAmount& saPrvReq, const STAmount& saCurReq, STAmount& saPrvAct, STAmount& saCurAct); bool calcPathReverse(PathState::pointer pspCur); void calcPathForward(PathState::pointer pspCur); - void txnWrite(); + void txnWrite(); TransactionEngineResult offerDelete(const SLE::pointer& sleOffer, const uint256& uOfferIndex, const uint160& uOwnerID);