From 794fe66008d0e15d394e1457258b449ac1d3cd88 Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Wed, 1 Aug 2012 12:44:22 -0700 Subject: [PATCH] Work towards ripple. --- src/SerializedTypes.cpp | 8 +- src/SerializedTypes.h | 15 +- src/TransactionEngine.cpp | 287 ++++++++++++++++++++++++++++++-------- src/TransactionEngine.h | 46 +++--- 4 files changed, 261 insertions(+), 95 deletions(-) diff --git a/src/SerializedTypes.cpp b/src/SerializedTypes.cpp index a341225d19..311f004edb 100644 --- a/src/SerializedTypes.cpp +++ b/src/SerializedTypes.cpp @@ -318,16 +318,10 @@ STPathSet* STPathSet::construct(SerializerIterator& s, const char *name) } else { - bool bAccount = !!(iType & STPathElement::typeAccount); - bool bOffer = !!(iType & STPathElement::typeOffer); + bool bAccount = !!(iType & STPathElement::typeAccount); bool bCurrency = !!(iType & STPathElement::typeCurrency); bool bIssuer = !!(iType & STPathElement::typeIssuer); - if (!bAccount && !bOffer) - { - throw std::runtime_error("bad path element"); - } - uint160 uAccountID; uint160 uCurrency; uint160 uIssuerID; diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 89c3a89dd7..c3ba0f0471 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -520,12 +520,13 @@ class STPathElement public: enum { typeEnd = 0x00, - typeAccount = 0x01, // Rippling through an account - typeOffer = 0x02, // Claiming an offer - typeCurrency = 0x10, // Currency follows - typeIssuer = 0x20, // Issuer follows - typeBoundary = 0xFF, // boundary between alternate paths - typeStrayBits = 0xCC, // Bits that must be zero. + 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. + typeStrayBits = 0xC0, // Bits that must be zero. }; protected: @@ -539,7 +540,7 @@ public: : mAccountID(uAccountID), mCurrency(uCurrency), mIssuerID(uIssuerID) { mType = - (uAccountID.isZero() ? STPathElement::typeOffer : STPathElement::typeAccount) + (uAccountID.isZero() ? 0 : STPathElement::typeAccount) | (uCurrency.isZero() ? 0 : STPathElement::typeCurrency) | (uIssuerID.isZero() ? 0 : STPathElement::typeIssuer); } diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 5fb1fc94d4..dd3d3a8c8c 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -2017,6 +2017,7 @@ void TransactionEngine::calcNodeOfferReverse( } } } +#endif // From the destination work towards the source calculating how much must be asked for. // --> bAllowPartial: If false, fail if can't meet requirements. @@ -2028,64 +2029,31 @@ void TransactionEngine::calcNodeOfferReverse( // <-> [0]saWanted.mAmount : --> limit, <-- actual // XXX Disallow looping. // XXX With multiple path and due to offers, must consider consumed. -bool calcPaymentReverse(std::vector& pnNodes, bool bAllowPartial) +bool TransactionEngine::calcPathReverse(PathState::pointer pspCur) { + bool bSent = true; +#if 0 TransactionEngineResult terResult = tenUNKNOWN; - uIndex = pnNodes.size(); + unsigned int uIndex = pspCur->vpnNodes.size() - 1; - while (tenUNKNOWN == terResult && uIndex--) + while (bSent && tenUNKNOWN == terResult && uIndex--) { // 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 = pnNodes[uIndex-1]; paymentNode& nxtPN = pnNodes[uIndex+1]; + bool bRedeem = !!(curPN.uFlags & STPathElement::typeRedeem); + bool bIssue = !!(curPN.uFlags & STPathElement::typeIssue); + bool bOffer = !!(curPN.uFlags & STPathElement::typeOffer); - if (!(uFlags & (PF_REDEEM|PF_ISSUE))) - { - // Redeem IOUs - terResult = tenBAD_PATH; - } - else if (curPN.saWanted.isZero()) + if (curPN.saWanted.isZero()) { // Must want something. terResult = tenBAD_AMOUNT; } - else if (curPN->uFlags & PF_ACCOUNT) - { - // Account node. - // Rippling through this accounts balances. - // No currency change. - // Issuer change possible. - - SLE::pointer sleRippleCur = ; - SLE::pointer sleRippleNxt = ; - STAmount saBalanceCur = ; - - if ((uFlags & PF_REDEEM) && saBalanceCur.isPositive()) - { - // Redeem IOUs - - // XXX - curPN.saWanted += ___; - - bSent = true; - } - - if ((uFlags & PF_ISSUE) // Allowed to issue. - && !saWantedNxt.isZero() // Need to issue. - && !saBalanceCur.isPositive()) // Can issue. - { - // Issue IOUs - - // XXX - curPN.saWanted += ___; - bSent = true; - } - - } - else if (curPN->uFlags & PF_OFFER) + else if (bOffer) { // Offer node. // Ripple or transfering from previous node through this offer to next node. @@ -2125,13 +2093,91 @@ bool calcPaymentReverse(std::vector& pnNodes, bool bAllowPartial) // Save sent amount } } + // Account node. + else if (!bIssue && !bOffer) + { + terResult = tenBAD_PATH; + } else { - assert(false); - } + // Rippling through this accounts balances. + // No currency change. + // Issuer change possible. + // Figuring out want current account should redeem and send. - if (tenUNKNOWN == terResult == curPN.saWanted.isZero()) - terResult = terZERO; // Path contributes nothing. + SLE::pointer sleRippleCur = ; + SLE::pointer sleRippleNxt = ; + STAmount saBalance = peekBalance(curPN.uAccountID, nxtPN.uAccountID); + STAmount saLimit = peekLimit(curPN.uAccountID, nxtPN.uAccountID); + STAmount saWanted = nxtPN.saWanted; + + curPN.saWanted.zero(); + + if (bRedeem && saBalance.isPositive()) + { + // 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(); + } + + if (bIssue // Allowed to issue. + && !saWanted.isZero() // Need to issue. + && !saBalance.isPositive()) // Can issue. (Can't if must redeem). + { + curPN.saIOUIssue = min(saLimit+saBalance, saWanted); + curPN.saWanted += curPN.saIOUIssue; // XXX Assumes we want 1::1 + } + else + { + curPN.saIOUIssue.zero(); + } + + bSent = !curPN.saIOURedeem.isZero() || !curPN.saIOUIssue.isZero(); + } + } +#endif + return bSent; +} + +// 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) +{ + if (uQualityIn >= uQualityOut) + { + // No fee. + STAmount saTransfer = MIN(saPrvReq-saPrvAct, saCurReq-saCurAct); + + saPrvAct += saTransfer; + saCurAct += saTransfer; + } + else + { + // Fee. + STAmount saPrv = saPrvReq-saPrvAct; + STAmount saCur = saCurReq-saCurAct; + STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityIn, uint160(1)), uQualityOut, uint160(1)); + + if (saCurIn >= saPrv) + { + // All of cur. Some amount of prv. + saCurAct = saCurReq; + saPrvAct += saCurIn; + } + else + { + // A part of cur. All of prv. + STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityOut, uint160(1)), uQualityIn, uint160(1)); + + saCurAct += saCurOut; + saPrvAct = saPrvReq; + } } } @@ -2142,24 +2188,130 @@ bool calcPaymentReverse(std::vector& pnNodes, bool bAllowPartial) // --> [all]saWanted.IOURedeem // --> [all]saWanted.IOUIssue // --> [all]saAccount -bool calcPaymentForward(std::vector& pnNodes) +// XXX This path becomes dirty if saSendMaxFirst must be changed. +void TransactionEngine::calcPathForward(PathState::pointer pspCur) { - cur = src; +#if 0 + TransactionEngineResult terResult = tenUNKNOWN; + unsigned int uIndex = 0; - if (!cur->saSend.isZero()) - { - // Sending stamps - always final step. - assert(!cur->next); - nxt->saReceive = cur->saSend; - bDone = true; - } - else - { - // Rippling. + unsigned int uEnd = pspCur->vpnNodes.size(); + unsigned int uLast = uEnd - 1; + while (tenUNKNOWN == terResult && 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]; + + // XXX Assume rippling. + + if (!uIndex) + { + // First node, calculate amount to send. + STAmount& saCurRedeemReq = curPN.saRevRedeem; + STAmount& saCurRedeemAct = curPN.saFwdRedeem; + STAmount& saCurIssueReq = curPN.saRevIssue; + STAmount& saCurIssueAct = curPN.saFwdIssue; + + STAmount& saCurSendMaxReq = curPN.saSendMax; + STAmount saCurSendMaxAct; + + if (saCurRedeemReq) + { + // Redeem requested. + saCurRedeemAct = MIN(saCurRedeemAct, saCurSendMaxReq); + saCurSendMaxAct = saCurRedeemAct; + } + + if (saCurIssueReq && saCurSendMaxReq != saCurRedeemAct) + { + // Issue requested and not over budget. + saCurIssueAct = MIN(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); + // saCurSendMaxAct += saCurIssueReq; // Not needed. + } + } + else if (uIndex == uLast) + { + // Last node. Accept all funds. Calculate amount actually to credit. + uint32 uQualityIn = peekQualityIn(curPN.uAccountID, prvPN.uAccountID); + STAmount& saPrvRedeemReq = prvPN.saFwdRedeem; + STAmount& saPrvIssueReq = prvPN.saFwdIssue; + STAmount saPrvIssueAct = uQualityIn >= QUALITY_ONE + ? saPrvIssueReq // No fee. + : multiply(saPrvIssueReq, uQualityIn, _); // Fee. + STAmount& saCurReceive = curPN.saReceive; + + // Amount to credit. + saCurReceive = saPrvRedeemReq+saPrvIssueAct; + + // Actually receive. + rippleCredit(curPN.uAccountID, prvPN.uAccountID, saPrvRedeemReq + saPrvIssueReq); + } + 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. + // 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; + + // Have funds in previous node to transfer. + uint32 uQualityIn = peekQualityIn(curPN.uAccountID, prvPN.uAccountID); + uint32 uQualityOut = peekQualityOut(curPN.uAccountID, nxtPN.uAccountID); + + STAmount& saPrvRedeemReq = prvPN.saFwdRedeem; + STAmount saPrvRedeemAct; + STAmount& saCurRedeemReq = curPN.saRevRedeem; + STAmount& saCurRedeemAct = curPN.saFwdRedeem; + STAmount& saPrvIssueReq = prvPN.saFwdIssue; + STAmount saPrvIssueAct; + + // 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); + } + + // 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 (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); + } + + // 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 + calcNodeFwd(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); + } + + // Adjust prv --> cur balance : take all inbound + rippleCredit(cur, prv, saPrvRedeemReq + saPrvIssueReq); + } } -} #endif +} bool PathState::less(const PathState::pointer& lhs, const PathState::pointer& rhs) { @@ -2207,6 +2359,19 @@ PathState::PathState( // Calculate the next increment of a path. void TransactionEngine::pathNext(PathState::pointer pspCur) { + // The next state is what is available in preference order. + // This is calculated when referenced accounts changed. + + if (calcPathReverse(pspCur)) + { + calcPathForward(pspCur); + } + else + { + // Mark path as inactive. + pspCur->uQuality = 0; + pspCur->bDirty = false; + } } // Apply an increment of the path, then calculate the next increment. diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index f470e98ab4..144dc79883 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -99,32 +99,33 @@ enum TransactionEngineParams tepMETADATA = 5, // put metadata in tree, not transaction }; +typedef struct { + bool bPartialPayment; // --> + uint16 uFlags; // --> from path + + uint160 uAccount; // --> recieving/sending account + + STAmount saSendMax; // --> First node: most to send. + STAmount saRecieve; // <-- Last node: Value received (minus fees) from upstream. + + 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. +} paymentNode; + // Hold a path state under incremental application. class PathState { public: typedef boost::shared_ptr pointer; - typedef struct { - bool bPartialPayment; // --> - uint16 uFlags; // --> from path - - uint160 uAccount; // --> recieving/sending account - - STAmount saWanted; // --> What this node wants from upstream. - - // Maybe this should just be a bool: - // STAmount saIOURedeemMax; // --> Max amount of IOUs to redeem downstream. - // Maybe this should just be a bool: - // STAmount saIOUIssueMax; // --> Max Amount of IOUs to issue downstream. - - STAmount saIOURedeem; // <-- What this node will redeem downstream. - STAmount saIOUIssue; // <-- What this node will issue downstream. - STAmount saSend; // <-- Stamps this node will send downstream. - - STAmount saRecieve; // Amount stamps to receive. - - } paymentNode; std::vector vpnNodes; LedgerEntrySet lesEntries; @@ -229,6 +230,11 @@ protected: PathState::pointer pathCreate(const STPath& spPath); void pathApply(PathState::pointer pspCur); void pathNext(PathState::pointer pspCur); + void calcNodeFwd(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();