diff --git a/src/Amount.cpp b/src/Amount.cpp index 1dfd549e4..b1588dc05 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -651,10 +651,10 @@ STAmount operator-(const STAmount& v1, const STAmount& v2) return STAmount(v1.name, v1.mCurrency, -fv, ov1, true); } -STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint160& currencyOut) +STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint160& uCurrencyID, const uint160& uIssuerID) { if (den.isZero()) throw std::runtime_error("division by zero"); - if (num.isZero()) return STAmount(currencyOut); + if (num.isZero()) return STAmount(uCurrencyID, uIssuerID); uint64 numVal = num.mValue, denVal = den.mValue; int numOffset = num.mOffset, denOffset = den.mOffset; @@ -690,14 +690,14 @@ STAmount STAmount::divide(const STAmount& num, const STAmount& den, const uint16 assert(BN_num_bytes(&v) <= 64); if (num.mIsNegative != den.mIsNegative) - return -STAmount(currencyOut, v.getulong(), finOffset); - else return STAmount(currencyOut, v.getulong(), finOffset); + return -STAmount(uCurrencyID, uIssuerID, v.getulong(), finOffset); + else return STAmount(uCurrencyID, uIssuerID, v.getulong(), finOffset); } -STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut) +STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID) { if (v1.isZero() || v2.isZero()) - return STAmount(currencyOut); + return STAmount(uCurrencyID, uIssuerID); if (v1.mIsNative && v2.mIsNative) // FIXME: overflow return STAmount(v1.name, v1.getSNValue() * v2.getSNValue()); @@ -753,8 +753,8 @@ STAmount STAmount::multiply(const STAmount& v1, const STAmount& v2, const uint16 assert(BN_num_bytes(&v) <= 64); if (v1.mIsNegative != v2.mIsNegative) - return -STAmount(currencyOut, v.getulong(), offset1 + offset2 + 14); - else return STAmount(currencyOut, v.getulong(), offset1 + offset2 + 14); + return -STAmount(uCurrencyID, uIssuerID, v.getulong(), offset1 + offset2 + 14); + else return STAmount(uCurrencyID, uIssuerID, v.getulong(), offset1 + offset2 + 14); } // Convert an offer into an index amount so they sort by rate. @@ -769,7 +769,7 @@ uint64 STAmount::getRate(const STAmount& offerOut, const STAmount& offerIn) { if (offerOut.isZero()) throw std::runtime_error("Worthless offer"); - STAmount r = divide(offerIn, offerOut, uint160(1)); + STAmount r = divide(offerIn, offerOut, CURRENCY_ONE, ACCOUNT_ONE); assert((r.getExponent() >= -100) && (r.getExponent() <= 155)); @@ -778,12 +778,12 @@ uint64 STAmount::getRate(const STAmount& offerOut, const STAmount& offerIn) return (ret << (64 - 8)) | r.getMantissa(); } -STAmount STAmount::setRate(uint64 rate, const uint160& currencyOut) +STAmount STAmount::setRate(uint64 rate) { uint64 mantissa = rate & ~(255ull << (64 - 8)); int exponent = static_cast(rate >> (64 - 8)) - 100; - return STAmount(currencyOut, mantissa, exponent); + return STAmount(CURRENCY_ONE, ACCOUNT_ONE, mantissa, exponent); } // Taker gets all taker can pay for with saTakerFunds, limited by saOfferPays and saOfferFunds. @@ -814,7 +814,7 @@ bool STAmount::applyOffer( STAmount saOfferGetsAvailable = saOfferFunds == saOfferPays ? saOfferGets // Offer was fully funded, avoid shenanigans. - : divide(multiply(saTakerPays, saOfferPaysAvailable, uint160(1)), saTakerGets, saOfferGets.getCurrency()); + : divide(multiply(saTakerPays, saOfferPaysAvailable, CURRENCY_ONE, ACCOUNT_ONE), saTakerGets, saOfferGets.getCurrency(), saOfferGets.getIssuer()); if (saOfferGets == saOfferGetsAvailable && saTakerFunds >= saOfferGets) { @@ -836,7 +836,7 @@ bool STAmount::applyOffer( { // Taker only get's a portion of offer. saTakerPaid = saTakerFunds; // Taker paid all he had. - saTakerGot = divide(multiply(saTakerFunds, saOfferPaysAvailable, uint160(1)), saOfferGetsAvailable, saOfferPays.getCurrency()); + saTakerGot = divide(multiply(saTakerFunds, saOfferPaysAvailable, CURRENCY_ONE, ACCOUNT_ONE), saOfferGetsAvailable, saOfferPays.getCurrency(), saOfferPays.getIssuer()); Log(lsINFO) << "applyOffer: saTakerGot=" << saTakerGot.getFullText(); Log(lsINFO) << "applyOffer: saOfferPaysAvailable=" << saOfferPaysAvailable.getFullText(); @@ -848,7 +848,7 @@ bool STAmount::applyOffer( STAmount STAmount::getPay(const STAmount& offerOut, const STAmount& offerIn, const STAmount& needed) { // Someone wants to get (needed) out of the offer, how much should they pay in? if (offerOut.isZero()) - return STAmount(offerIn.getCurrency()); + return STAmount(offerIn.getCurrency(), offerIn.getIssuer()); if (needed >= offerOut) { @@ -856,7 +856,7 @@ STAmount STAmount::getPay(const STAmount& offerOut, const STAmount& offerIn, con return needed; } - STAmount ret = divide(multiply(needed, offerIn, uint160(1)), offerOut, offerIn.getCurrency()); + STAmount ret = divide(multiply(needed, offerIn, CURRENCY_ONE, ACCOUNT_ONE), offerOut, offerIn.getCurrency(), offerIn.getIssuer()); return (ret > offerIn) ? offerIn : ret; } @@ -1063,8 +1063,7 @@ BOOST_AUTO_TEST_CASE( NativeCurrency_test ) BOOST_AUTO_TEST_CASE( CustomCurrency_test ) { - uint160 currency(1); - STAmount zero(currency), one(currency, 1), hundred(currency, 100); + STAmount zero(CURRENCY_ONE, ACCOUNT_ONE), one(CURRENCY_ONE, ACCOUNT_ONE, 1), hundred(CURRENCY_ONE, ACCOUNT_ONE, 100); serdes(one).getRaw(); @@ -1131,33 +1130,33 @@ BOOST_AUTO_TEST_CASE( CustomCurrency_test ) if (!(hundred != zero)) BOOST_FAIL("STAmount fail"); if (!(hundred != one)) BOOST_FAIL("STAmount fail"); if ((hundred != hundred)) BOOST_FAIL("STAmount fail"); - if (STAmount(currency).getText() != "0") BOOST_FAIL("STAmount fail"); - if (STAmount(currency,31).getText() != "31") BOOST_FAIL("STAmount fail"); - if (STAmount(currency,31,1).getText() != "310") BOOST_FAIL("STAmount fail"); - if (STAmount(currency,31,-1).getText() != "3.1") BOOST_FAIL("STAmount fail"); - if (STAmount(currency,31,-2).getText() != "0.31") BOOST_FAIL("STAmount fail"); + if (STAmount(CURRENCY_ONE, ACCOUNT_ONE).getText() != "0") BOOST_FAIL("STAmount fail"); + if (STAmount(CURRENCY_ONE, ACCOUNT_ONE, 31).getText() != "31") BOOST_FAIL("STAmount fail"); + if (STAmount(CURRENCY_ONE, ACCOUNT_ONE, 31,1).getText() != "310") BOOST_FAIL("STAmount fail"); + if (STAmount(CURRENCY_ONE, ACCOUNT_ONE, 31,-1).getText() != "3.1") BOOST_FAIL("STAmount fail"); + if (STAmount(CURRENCY_ONE, ACCOUNT_ONE, 31,-2).getText() != "0.31") BOOST_FAIL("STAmount fail"); - if (STAmount::multiply(STAmount(currency, 20), STAmount(3), currency).getText() != "60") + if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60") BOOST_FAIL("STAmount multiply fail"); - if (STAmount::multiply(STAmount(currency, 20), STAmount(3), uint160()).getText() != "60") + if (STAmount::multiply(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 20), STAmount(3), uint160(), ACCOUNT_XNS).getText() != "60") BOOST_FAIL("STAmount multiply fail"); - if (STAmount::multiply(STAmount(20), STAmount(3), currency).getText() != "60") + if (STAmount::multiply(STAmount(20), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "60") BOOST_FAIL("STAmount multiply fail"); - if (STAmount::multiply(STAmount(20), STAmount(3), uint160()).getText() != "60") + if (STAmount::multiply(STAmount(20), STAmount(3), uint160(), ACCOUNT_XNS).getText() != "60") BOOST_FAIL("STAmount multiply fail"); - if (STAmount::divide(STAmount(currency, 60) , STAmount(3), currency).getText() != "20") + if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "20") BOOST_FAIL("STAmount divide fail"); - if (STAmount::divide(STAmount(currency, 60) , STAmount(3), uint160()).getText() != "20") + if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(3), uint160(), ACCOUNT_XNS).getText() != "20") BOOST_FAIL("STAmount divide fail"); - if (STAmount::divide(STAmount(currency, 60) , STAmount(currency, 3), currency).getText() != "20") + if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 3), CURRENCY_ONE, ACCOUNT_ONE).getText() != "20") BOOST_FAIL("STAmount divide fail"); - if (STAmount::divide(STAmount(currency, 60) , STAmount(currency, 3), uint160()).getText() != "20") + if (STAmount::divide(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 60), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 3), uint160(), ACCOUNT_XNS).getText() != "20") BOOST_FAIL("STAmount divide fail"); - STAmount a1(currency, 60), a2 (currency, 10, -1); - if (STAmount::divide(a2, a1, currency) != STAmount::setRate(STAmount::getRate(a1, a2), currency)) + STAmount a1(CURRENCY_ONE, ACCOUNT_ONE, 60), a2 (CURRENCY_ONE, ACCOUNT_ONE, 10, -1); + if (STAmount::divide(a2, a1, CURRENCY_ONE, ACCOUNT_ONE) != STAmount::setRate(STAmount::getRate(a1, a2))) BOOST_FAIL("STAmount setRate(getRate) fail"); - if (STAmount::divide(a1, a2, currency) != STAmount::setRate(STAmount::getRate(a2, a1), currency)) + if (STAmount::divide(a1, a2, CURRENCY_ONE, ACCOUNT_ONE) != STAmount::setRate(STAmount::getRate(a2, a1))) BOOST_FAIL("STAmount setRate(getRate) fail"); BOOST_TEST_MESSAGE("Amount CC Complete"); @@ -1168,22 +1167,21 @@ BOOST_AUTO_TEST_CASE( CurrencyMulDivTests ) // Test currency multiplication and division operations such as // convertToDisplayAmount, convertToInternalAmount, getRate, getClaimed, and getNeeded - uint160 c(1); if (STAmount::getRate(STAmount(1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); if (STAmount::getRate(STAmount(10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(c, 1), STAmount(c, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(c, 10), STAmount(c, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(c, 1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1), STAmount(10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(c, 10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + if (STAmount::getRate(STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10), STAmount(1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(1), STAmount(c, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) + if (STAmount::getRate(STAmount(1), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 10)) != (((100ul-14)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); - if (STAmount::getRate(STAmount(10), STAmount(c, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) + if (STAmount::getRate(STAmount(10), STAmount(CURRENCY_ONE, ACCOUNT_ONE, 1)) != (((100ul-16)<<(64-8))|1000000000000000ul)) BOOST_FAIL("STAmount getRate fail"); } diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 46439587a..1017f758a 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -261,10 +261,11 @@ public: : SerializedType(n), mValue(v), mOffset(0), mIsNative(true), mIsNegative(false) { ; } - STAmount(const uint160& uCurrency, uint64 uV=0, int iOff=0, bool bNegative=false) - : mCurrency(uCurrency), mValue(uV), mOffset(iOff), mIsNegative(bNegative) + STAmount(const uint160& uCurrencyID, const uint160& uIssuerID, uint64 uV=0, int iOff=0, bool bNegative=false) + : mCurrency(uCurrencyID), mIssuer(uIssuerID), mValue(uV), mOffset(iOff), mIsNegative(bNegative) { canonicalize(); } + // YYY This should probably require issuer too. STAmount(const char* n, const uint160& currency, uint64 v = 0, int off = 0, bool isNeg = false) : SerializedType(n), mCurrency(currency), mValue(v), mOffset(off), mIsNegative(isNeg) { canonicalize(); } @@ -274,14 +275,14 @@ public: static std::auto_ptr deserialize(SerializerIterator& sit, const char* name) { return std::auto_ptr(construct(sit, name)); } - static STAmount saFromRate(uint64 uV = 0) + static STAmount saFromRate(uint64 uRate = 0) { - return STAmount(CURRENCY_ONE, uV, -9, false); + return STAmount(CURRENCY_ONE, ACCOUNT_ONE, uRate, -9, false); } - static STAmount saFromSigned(const uint160& uCurrency, int64 iV=0, int iOff=0) + static STAmount saFromSigned(const uint160& uCurrencyID, const uint160& uIssuerID, int64 iV=0, int iOff=0) { - return STAmount(uCurrency, iV < 0 ? -iV : iV, iOff, iV < 0); + return STAmount(uCurrencyID, uIssuerID, iV < 0 ? -iV : iV, iOff, iV < 0); } int getLength() const { return mIsNative ? 8 : 28; } @@ -351,13 +352,13 @@ public: friend STAmount operator+(const STAmount& v1, const STAmount& v2); friend STAmount operator-(const STAmount& v1, const STAmount& v2); - static STAmount divide(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); - static STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& currencyOut); + static STAmount divide(const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID); + static STAmount multiply(const STAmount& v1, const STAmount& v2, const uint160& uCurrencyID, const uint160& uIssuerID); // Someone is offering X for Y, what is the rate? // Rate: smaller is better, the taker wants the most out: in/out static uint64 getRate(const STAmount& offerOut, const STAmount& offerIn); - static STAmount setRate(uint64 rate, const uint160& currencyOut); + static STAmount setRate(uint64 rate); // Someone is offering X for Y, I try to pay Z, how much do I get? // And what's left of the offer? And how much do I actually pay? diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 88d7be642..477ee8284 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -102,7 +102,9 @@ bool transResultInfo(TransactionEngineResult terCode, std::string& strToken, std return iIndex >= 0; } -STAmount TransactionEngine::rippleBalance(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) +// Returns amount owed by uToAccountID to uFromAccountID. +// <-- $owed/uCurrencyID/uToAccountID: positive: uFromAccountID holds IOUs., negative: uFromAccountID owes IOUs. +STAmount TransactionEngine::rippleOwed(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) { STAmount saBalance; SLE::pointer sleRippleState = entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(uToAccountID, uFromAccountID, uCurrencyID)); @@ -112,10 +114,11 @@ STAmount TransactionEngine::rippleBalance(const uint160& uToAccountID, const uin saBalance = sleRippleState->getIValueFieldAmount(sfBalance); if (uToAccountID < uFromAccountID) saBalance.negate(); + saBalance.setIssuer(uToAccountID); } else { - Log(lsINFO) << "rippleBalance: No credit line between " + Log(lsINFO) << "rippleOwed: No credit line between " << NewcoinAddress::createHumanAccountID(uFromAccountID) << " and " << NewcoinAddress::createHumanAccountID(uToAccountID) @@ -130,6 +133,8 @@ STAmount TransactionEngine::rippleBalance(const uint160& uToAccountID, const uin } +// Maximum amount of IOUs uToAccountID will hold from uFromAccountID. +// <-- $amount/uCurrencyID/uToAccountID. STAmount TransactionEngine::rippleLimit(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID) { STAmount saLimit; @@ -139,6 +144,7 @@ STAmount TransactionEngine::rippleLimit(const uint160& uToAccountID, const uint1 if (sleRippleState) { saLimit = sleRippleState->getIValueFieldAmount(uToAccountID < uFromAccountID ? sfLowLimit : sfHighLimit); + saLimit.setIssuer(uToAccountID); } return saLimit; @@ -300,7 +306,7 @@ STAmount TransactionEngine::rippleTransferFee(const uint160& uSenderID, const ui { STAmount saTransitRate(CURRENCY_ONE, uTransitRate, -9); - saTransitFee = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency()); + saTransitFee = STAmount::multiply(saAmount, saTransitRate, saAmount.getCurrency(), saAmount.getIssuer()); } } @@ -1583,9 +1589,9 @@ TransactionEngineResult TransactionEngine::doCreditSet(const SerializedTransacti Log(lsINFO) << "doCreditSet: Creating ripple line: " << sleRippleState->getIndex().ToString(); - sleRippleState->setIFieldAmount(sfBalance, STAmount(uCurrencyID)); // Zero balance in currency. + sleRippleState->setIFieldAmount(sfBalance, STAmount(uCurrencyID, ACCOUNT_ONE)); // Zero balance in currency. sleRippleState->setIFieldAmount(bFlipped ? sfHighLimit : sfLowLimit, saLimitAmount); - sleRippleState->setIFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID)); + sleRippleState->setIFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID, ACCOUNT_ONE)); sleRippleState->setIFieldAccount(bFlipped ? sfHighID : sfLowID, mTxnAccountID); sleRippleState->setIFieldAccount(bFlipped ? sfLowID : sfHighID, uDstAccountID); if (uQualityIn) @@ -1944,7 +1950,7 @@ bool TransactionEngine::calcNodeOfferRev( // - Drive on computing saCurDlvAct to derive saPrvDlvAct. // XXX Behave well, if entry type is wrong (someone beat us to using the hash) SLE::pointer sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); - STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip), uCurCurrencyID); // For correct ratio + STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio unsigned int uEntry = 0; uint256 uCurIndex; @@ -1987,13 +1993,13 @@ bool TransactionEngine::calcNodeOfferRev( STAmount saOutBase = MIN(saCurOfrOutReq, saCurDlvReq-saCurDlvAct); // Limit offer out by needed. STAmount saOutCost = MIN( bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID) + ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutBase, saCurOfrFunds); // Limit cost by fees & funds. STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID) + ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutCost; // Out amount after fees. - STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID); // Compute input w/o fees required. + STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. saCurDlvAct += saOutDlvAct; // Portion of driver served. saPrvDlvAct += saInDlvAct; // Portion needed in previous. @@ -2044,13 +2050,14 @@ bool TransactionEngine::calcNodeOfferRev( saOutBase = MIN(saOutBase, saNxtOfrIn); // Limit offer out by supplying offer. STAmount saOutCost = MIN( bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID) + ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutBase, saCurOfrFunds); // Limit cost by fees & funds. STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID) + ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutCost; // Out amount after fees. - STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID); // Compute input w/o fees required. + // Compute input w/o fees required. + STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uCurIssuerID); saCurDlvAct += saOutDlvAct; // Portion of driver served. saPrvDlvAct += saInDlvAct; // Portion needed in previous. @@ -2078,7 +2085,7 @@ bool TransactionEngine::calcNodeOfferRev( Log(lsINFO) << boost::str(boost::format("calcNodeOfferRev< uIndex=%d saPrvDlvReq=%s bSuccess=%d") % uIndex - % saPrvDlvReq.getText() + % saPrvDlvReq.getFullText() % bSuccess); return bSuccess; @@ -2134,7 +2141,7 @@ bool TransactionEngine::calcNodeOfferFwd( // Do a directory. // - Drive on computing saPrvDlvAct to derive saCurDlvAct. SLE::pointer sleDirectDir = entryCache(ltDIR_NODE, uDirectTip); - STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip), uCurCurrencyID); // For correct ratio + STAmount saOfrRate = STAmount::setRate(Ledger::getQuality(uDirectTip)); // For correct ratio unsigned int uEntry = 0; uint256 uCurIndex; @@ -2158,17 +2165,18 @@ bool TransactionEngine::calcNodeOfferFwd( : saTransferRate; const bool bFee = saFeeRate != saOne; - const STAmount saOutPass = STAmount::divide(saCurOfrInMax, saOfrRate, uCurCurrencyID); + const STAmount saOutPass = STAmount::divide(saCurOfrInMax, saOfrRate, uCurCurrencyID, uCurIssuerID); const STAmount saOutBase = MIN(saCurOfrOutReq, saOutPass); // Limit offer out by needed. const STAmount saOutCost = MIN( bFee - ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID) + ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutBase, saCurOfrFunds); // Limit cost by fees & funds. const STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID) + ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutCost; // Out amount after fees. - const STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uCurCurrencyID); // Compute input w/o fees required. + // Compute input w/o fees required. + const STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uCurCurrencyID, uCurIssuerID); saCurDlvAct += saOutDlvAct; // Portion of driver served. saPrvDlvAct += saInDlvAct; // Portion needed in previous. @@ -2216,18 +2224,18 @@ bool TransactionEngine::calcNodeOfferFwd( const bool bFee = saFeeRate != saOne; const STAmount saInBase = saCurOfrInMax-saCurOfrInAct; - const STAmount saOutPass = STAmount::divide(saInBase, saOfrRate, uCurCurrencyID); + 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) + ? STAmount::multiply(saOutBase, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutBase, saCurOfrFunds); // Limit cost by fees & funds. const STAmount saOutDlvAct = bFee - ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID) + ? STAmount::divide(saOutCost, saFeeRate, uCurCurrencyID, uCurIssuerID) : saOutCost; // Out amount after fees. - const STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID); // Compute input w/o fees required. + const STAmount saInDlvAct = STAmount::multiply(saOutDlvAct, saOfrRate, uPrvCurrencyID, uCurIssuerID); // Compute input w/o fees required. saCurOfrInAct += saOutDlvAct; // Portion of driver served. saPrvDlvAct += saOutDlvAct; // Portion needed in previous. @@ -2538,8 +2546,11 @@ void TransactionEngine::calcNodeRipple( // Fee. Log(lsINFO) << boost::str(boost::format("calcNodeRipple: Fee")); - uint160 uCurrencyID = saCur.getCurrency(); - STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID), uQualityIn, uCurrencyID); + const uint160 uCurrencyID = saCur.getCurrency(); + const uint160 uCurIssuerID = saCur.getIssuer(); + const uint160 uPrvIssuerID = saPrv.getIssuer(); + + STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID, uCurIssuerID), uQualityIn, uCurrencyID, uCurIssuerID); Log(lsINFO) << boost::str(boost::format("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCurIn=%s") % bPrvUnlimited % saPrv.getFullText() % saCurIn.getFullText()); if (bPrvUnlimited || saCurIn <= saPrv) @@ -2552,8 +2563,7 @@ Log(lsINFO) << boost::str(boost::format("calcNodeRipple:3c: saCurReq=%s saPrvAct else { // A part of cur. All of prv. (cur as driver) - uint160 uCurrencyID = saPrv.getCurrency(); - STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, uCurrencyID), uQualityOut, uCurrencyID); + STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, uCurrencyID, uCurIssuerID), uQualityOut, uCurrencyID, uCurIssuerID); Log(lsINFO) << boost::str(boost::format("calcNodeRipple:4: saCurReq=%s") % saCurReq.getFullText()); saCurAct += saCurOut; @@ -2597,25 +2607,32 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point const uint32 uQualityOut = uIndex != uLast ? rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID) : QUALITY_ONE; // For bPrvAccount - const STAmount saPrvBalance = uIndex && bPrvAccount ? rippleBalance(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID); - const STAmount saPrvLimit = uIndex && bPrvAccount ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID); + const STAmount saPrvOwed = uIndex && bPrvAccount // Previous account is owed. + ? rippleOwed(uCurAccountID, uPrvAccountID, uCurrencyID) + : STAmount(uCurrencyID, uCurAccountID); + const STAmount saPrvLimit = uIndex && bPrvAccount // Previous account may owe. + ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) + : STAmount(uCurrencyID, uCurAccountID); - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev> uIndex=%d/%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvBalance=%s saPrvLimit=%s") + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev> uIndex=%d/%d bPrvIssue=%d uPrvAccountID=%s uCurAccountID=%s uNxtAccountID=%s uCurrencyID=%s uQualityIn=%d uQualityOut=%d saPrvOwed=%s saPrvLimit=%s") % uIndex % uLast + % bPrvIssue % NewcoinAddress::createHumanAccountID(uPrvAccountID) % NewcoinAddress::createHumanAccountID(uCurAccountID) % NewcoinAddress::createHumanAccountID(uNxtAccountID) % STAmount::createHumanCurrency(uCurrencyID) % uQualityIn % uQualityOut - % saPrvBalance.getText() - % saPrvLimit.getText()); + % saPrvOwed.getFullText() + % saPrvLimit.getFullText()); - const STAmount saPrvRedeemReq = bPrvRedeem && saPrvBalance.isNegative() ? -saPrvBalance : STAmount(uCurrencyID, 0); + // Previous can redeem the owed IOUs it holds. + const STAmount saPrvRedeemReq = bPrvRedeem && saPrvOwed.isPositive() ? saPrvOwed : STAmount(uCurrencyID, 0); STAmount& saPrvRedeemAct = pnPrv.saRevRedeem; - const STAmount saPrvIssueReq = bPrvIssue ? saPrvLimit - saPrvBalance : STAmount(uCurrencyID); + // Previous can issue up to limit minus whatever portion of limit already used (not including redeemable amount). + const STAmount saPrvIssueReq = bPrvIssue && saPrvOwed.isNegative() ? saPrvLimit+saPrvOwed : saPrvLimit; STAmount& saPrvIssueAct = pnPrv.saRevIssue; // For !bPrvAccount @@ -2624,27 +2641,25 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point // For bNxtAccount const STAmount& saCurRedeemReq = pnCur.saRevRedeem; - STAmount saCurRedeemAct(saCurRedeemReq.getCurrency()); + STAmount saCurRedeemAct(saCurRedeemReq.getCurrency(), saCurRedeemReq.getIssuer()); const STAmount& saCurIssueReq = pnCur.saRevIssue; - STAmount saCurIssueAct(saCurIssueReq.getCurrency()); // Track progress. + STAmount saCurIssueAct(saCurIssueReq.getCurrency(), saCurIssueReq.getIssuer()); // Track progress. // For !bNxtAccount const STAmount& saCurDeliverReq = pnCur.saRevDeliver; - STAmount saCurDeliverAct(saCurDeliverReq.getCurrency()); + STAmount saCurDeliverAct(saCurDeliverReq.getCurrency(), saCurDeliverReq.getIssuer()); - // For uIndex == uLast - const STAmount& saCurWantedReq = pspCur->saOutReq; // XXX Credit limits? - // STAmount saPrvDeliverReq = saPrvBalance.isPositive() ? saPrvLimit - saPrvBalance : saPrvLimit; - STAmount saCurWantedAct(saCurWantedReq.getCurrency()); + // For uIndex == uLast, over all 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. + STAmount saCurWantedAct(saCurWantedReq.getCurrency(), saCurWantedReq.getIssuer()); - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: saPrvRedeemReq=%s/%s saPrvIssueReq=%s/%s saCurWantedReq=%s/%s") - % saPrvRedeemReq.getText() - % saPrvRedeemReq.getHumanCurrency() - % saPrvIssueReq.getText() - % saPrvIssueReq.getHumanCurrency() - % saCurWantedReq.getText() - % saCurWantedReq.getHumanCurrency()); + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: saPrvRedeemReq=%s saPrvIssueReq=%s saCurWantedReq=%s") + % saPrvRedeemReq.getFullText() + % saPrvIssueReq.getFullText() + % saCurWantedReq.getFullText()); Log(lsINFO) << pspCur->getJson(); @@ -2698,7 +2713,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point if (bPrvRedeem && bRedeem // Allowed to redeem. && saCurRedeemReq // Next wants us to redeem. - && saPrvBalance.isNegative()) // Previous has IOUs to redeem. + && saPrvOwed) // Previous has IOUs to redeem. { // Rate : 1.0 : quality out Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate : 1.0 : quality out")); @@ -2710,7 +2725,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point if (bPrvRedeem && bIssue // Allowed to issue. && saCurRedeemReq != saCurRedeemAct // Can only if issue if more can not be redeemed. - && saPrvBalance.isNegative() // Previous still has IOUs. + && saPrvOwed // Previous still has IOUs. && saCurIssueReq) // Need some issued. { // Rate : 1.0 : transfer_rate @@ -2723,7 +2738,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point if (bPrvIssue && bRedeem // Allowed to redeem. && saCurRedeemReq != saCurRedeemAct // Can only redeem if more to be redeemed. - && !saPrvBalance.isNegative()) // Previous has no IOUs. + && !saPrvOwed.isPositive()) // Previous has no IOUs. { // Rate: quality in : quality out Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: Rate: quality in : quality out")); @@ -2735,7 +2750,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point if (bPrvIssue && bIssue // Allowed to issue. && saCurRedeemReq == saCurRedeemAct // Can only if issue if more can not be redeemed. - && !saPrvBalance.isNegative() // Previous has no IOUs. + && !saPrvOwed.isPositive() // Previous has no IOUs. && saCurIssueReq != saCurIssueAct) // Need some issued. { // Rate: quality in : 1.0 @@ -2750,14 +2765,15 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point // terResult = tenBAD_AMOUNT; bSuccess = false; } - Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: ^|account --> ACCOUNT --> account : bPrvRedeem=%d bPrvIssue=%d bRedeem=%d bIssue=%d saCurRedeemReq=%s saCurIssueReq=%s saPrvBalance=%s saCurRedeemAct=%s saCurIssueAct=%s") + + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: ^|account --> ACCOUNT --> account : bPrvRedeem=%d bPrvIssue=%d bRedeem=%d bIssue=%d saCurRedeemReq=%s saCurIssueReq=%s saPrvOwed=%s saCurRedeemAct=%s saCurIssueAct=%s") % bPrvRedeem % bPrvIssue % bRedeem % bIssue % saCurRedeemReq.getFullText() % saCurIssueReq.getFullText() - % saPrvBalance.getFullText() + % saPrvOwed.getFullText() % saCurRedeemAct.getFullText() % saCurIssueAct.getFullText()); } @@ -2771,7 +2787,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point // redeem -> deliver/issue. if (bPrvRedeem && bIssue // Allowed to issue. - && saPrvBalance.isNegative() // Previous redeeming: Previous still has IOUs. + && saPrvOwed.isPositive() // Previous redeeming: Previous still has IOUs. && saCurDeliverReq) // Need some issued. { // Rate : 1.0 : transfer_rate @@ -2781,7 +2797,8 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point // issue -> deliver/issue if (bPrvIssue && bIssue // Allowed to issue. - && !saPrvBalance.isNegative() // Previous issuing: Previous has no IOUs. + && (!saPrvOwed.isPositive() // Previous issuing: Never had IOUs. + || saPrvOwed == saPrvRedeemAct) // Previous issuing: Previous has no IOUs left after redeeming. && saCurDeliverReq != saCurDeliverAct) // Need some issued. { // Rate: quality in : 1.0 @@ -2794,6 +2811,15 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point // terResult = tenBAD_AMOUNT; bSuccess = false; } + + Log(lsINFO) << boost::str(boost::format("calcNodeAccountRev: bPrvRedeem=%d bPrvIssue=%d bRedeem=%d bIssue=%d saCurDeliverReq=%s saCurDeliverAct=%s saPrvOwed=%s") + % bPrvRedeem + % bPrvIssue + % bRedeem + % bIssue + % saCurDeliverReq.getFullText() + % saCurDeliverAct.getFullText() + % saPrvOwed.getFullText()); } else if (!bPrvAccount && bNxtAccount) { @@ -2906,14 +2932,14 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point // For bNxtAccount const STAmount& saPrvRedeemReq = pnPrv.saFwdRedeem; - STAmount saPrvRedeemAct(saPrvRedeemReq.getCurrency()); + STAmount saPrvRedeemAct(saPrvRedeemReq.getCurrency(), saPrvRedeemReq.getIssuer()); const STAmount& saPrvIssueReq = pnPrv.saFwdIssue; - STAmount saPrvIssueAct(saPrvIssueReq.getCurrency()); + STAmount saPrvIssueAct(saPrvIssueReq.getCurrency(), saPrvIssueReq.getIssuer()); // For !bPrvAccount const STAmount& saPrvDeliverReq = pnPrv.saRevDeliver; - STAmount saPrvDeliverAct(saPrvDeliverReq.getCurrency()); + STAmount saPrvDeliverAct(saPrvDeliverReq.getCurrency(), saPrvDeliverReq.getIssuer()); // For bNxtAccount const STAmount& saCurRedeemReq = pnCur.saRevRedeem; @@ -3003,7 +3029,7 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point STAmount saIssueCrd = uQualityIn >= QUALITY_ONE ? saPrvIssueReq // No fee. - : STAmount::multiply(saPrvIssueReq, uQualityIn, uCurrencyID); // Fee. + : STAmount::multiply(saPrvIssueReq, uQualityIn, uCurrencyID, saPrvIssueReq.getIssuer()); // Fee. // Amount to credit. saCurReceive = saPrvRedeemReq+saIssueCrd; @@ -3259,8 +3285,8 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin pnCur.uAccountID = uAccountID; pnCur.uCurrencyID = bCurrency ? uCurrencyID : pnPrv.uCurrencyID; pnCur.uIssuerID = bIssuer ? uIssuerID : uAccountID; - pnCur.saRevRedeem = STAmount(uCurrencyID); - pnCur.saRevIssue = STAmount(uCurrencyID); + pnCur.saRevRedeem = STAmount(uCurrencyID, uAccountID); + pnCur.saRevIssue = STAmount(uCurrencyID, uAccountID); if (!bFirst) { diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index e6ff7d618..6204650fe 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -226,7 +226,7 @@ protected: void entryModify(SLE::pointer sleEntry); uint32 rippleTransferRate(const uint160& uIssuerID); - STAmount rippleBalance(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID); + STAmount rippleOwed(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, const SOE_Field sfLow=sfLowQualityIn, const SOE_Field sfHigh=sfHighQualityIn); uint32 rippleQualityOut(const uint160& uToAccountID, const uint160& uFromAccountID, const uint160& uCurrencyID)