diff --git a/src/Amount.cpp b/src/Amount.cpp index 1e2db3124c..e6616a9c35 100644 --- a/src/Amount.cpp +++ b/src/Amount.cpp @@ -880,6 +880,30 @@ STAmount STAmount::deserialize(SerializerIterator& it) } std::string STAmount::getFullText() const +{ + if (mIsNative) + { + return str(boost::format("%s/" SYSTEM_CURRENCY_CODE) % getText()); + } + else if (!mIssuer) + { + return str(boost::format("%s/%s/0") % getText() % getHumanCurrency()); + } + else if (mIssuer == ACCOUNT_ONE) + { + return str(boost::format("%s/%s/1") % getText() % getHumanCurrency()); + } + else + { + return str(boost::format("%s/%s/%s") + % getText() + % getHumanCurrency() + % NewcoinAddress::createHumanAccountID(mIssuer)); + } +} + +#if 0 +std::string STAmount::getExtendedText() const { if (mIsNative) { @@ -887,7 +911,7 @@ std::string STAmount::getFullText() const } else { - return str(boost::format("%s %s/%s %dE%d" ) + return str(boost::format("%s/%s/%s %dE%d" ) % getText() % getHumanCurrency() % NewcoinAddress::createHumanAccountID(mIssuer) @@ -895,6 +919,7 @@ std::string STAmount::getFullText() const % getExponent()); } } +#endif Json::Value STAmount::getJson(int) const { diff --git a/src/SNTPClient.cpp b/src/SNTPClient.cpp index 444b7b6c73..d204b6a8ec 100644 --- a/src/SNTPClient.cpp +++ b/src/SNTPClient.cpp @@ -6,6 +6,7 @@ #include #include "utils.h" +#include "Config.h" #include "Log.h" // #define SNTP_DEBUG @@ -43,13 +44,16 @@ static uint8_t SNTPQueryData[48] = SNTPClient::SNTPClient(boost::asio::io_service& service) : mSocket(service), mTimer(service), mResolver(service), mOffset(0), mLastOffsetUpdate((time_t) -1), mReceiveBuffer(256) { - mSocket.open(boost::asio::ip::udp::v4()); - mSocket.async_receive_from(boost::asio::buffer(mReceiveBuffer, 256), mReceiveEndpoint, - boost::bind(&SNTPClient::receivePacket, this, boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - - mTimer.expires_from_now(boost::posix_time::seconds(NTP_QUERY_FREQUENCY)); - mTimer.async_wait(boost::bind(&SNTPClient::timerEntry, this, boost::asio::placeholders::error)); + if (!theConfig.RUN_STANDALONE) + { + mSocket.open(boost::asio::ip::udp::v4()); + mSocket.async_receive_from(boost::asio::buffer(mReceiveBuffer, 256), mReceiveEndpoint, + boost::bind(&SNTPClient::receivePacket, this, boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred)); + + mTimer.expires_from_now(boost::posix_time::seconds(NTP_QUERY_FREQUENCY)); + mTimer.async_wait(boost::bind(&SNTPClient::timerEntry, this, boost::asio::placeholders::error)); + } } void SNTPClient::resolveComplete(const boost::system::error_code& error, boost::asio::ip::udp::resolver::iterator it) @@ -247,3 +251,4 @@ bool SNTPClient::doQuery() #endif return true; } +// vim:ts=4 diff --git a/src/SNTPClient.h b/src/SNTPClient.h index 51adf3ceb2..c2bf75ec63 100644 --- a/src/SNTPClient.h +++ b/src/SNTPClient.h @@ -56,3 +56,4 @@ public: }; #endif +// vim:ts=4 diff --git a/src/SerializedTypes.h b/src/SerializedTypes.h index 63977102ca..b1a264cde4 100644 --- a/src/SerializedTypes.h +++ b/src/SerializedTypes.h @@ -50,6 +50,12 @@ enum PathFlags PF_ISSUE = 0x80, }; +#define QUALITY_ONE 100000000 // 10e9 +#define CURRENCY_XNS uint160(0) +#define CURRENCY_ONE uint160(1) // Used as a place holder +#define ACCOUNT_XNS uint160(0) +#define ACCOUNT_ONE uint160(1) // Used as a place holder + class SerializedType { protected: diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 45f1e5a113..17299b346b 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -21,12 +21,6 @@ #define DIR_NODE_MAX 2 #define RIPPLE_PATHS_MAX 3 -#define QUALITY_ONE 100000000 // 10e9 -#define CURRENCY_XNS uint160(0) -#define CURRENCY_ONE uint160(1) // Used as a place holder -#define ACCOUNT_XNS uint160(0) -#define ACCOUNT_ONE uint160(1) // Used as a place holder - static STAmount saZero(CURRENCY_ONE, 0, 0); static STAmount saOne(CURRENCY_ONE, 1, 0); @@ -113,13 +107,24 @@ STAmount TransactionEngine::rippleBalance(const uint160& uToAccountID, const uin 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(); } + else + { + Log(lsINFO) << "rippleBalance: No credit line between " + << NewcoinAddress::createHumanAccountID(uFromAccountID) + << " and " + << NewcoinAddress::createHumanAccountID(uToAccountID) + << " for " + << STAmount::createHumanCurrency(uCurrencyID) + << "." ; + + assert(false); + } return saBalance; @@ -164,7 +169,13 @@ uint32 TransactionEngine::rippleQualityIn(const uint160& uToAccountID, const uin if (sleRippleState) { - uQualityIn = sleRippleState->getIFieldU32(uToAccountID < uFromAccountID ? sfLowQualityIn : sfHighQualityIn); + SOE_Field sfField = uToAccountID < uFromAccountID ? sfLowQualityIn : sfHighQualityIn; + + uQualityIn = sleRippleState->getIFieldPresent(sfField) + ? sleRippleState->getIFieldU32(sfField) + : QUALITY_ONE; + if (!uQualityIn) + uQualityIn = 1; } else { @@ -190,6 +201,8 @@ uint32 TransactionEngine::rippleQualityOut(const uint160& uToAccountID, const ui if (sleRippleState) { uQualityOut = sleRippleState->getIFieldU32(uToAccountID < uFromAccountID ? sfLowQualityOut : sfHighQualityOut); + if (!uQualityOut) + uQualityOut = 1; } else { @@ -305,11 +318,11 @@ STAmount TransactionEngine::rippleTransfer(const uint160& uSenderID, const uint1 } // Direct send w/o fees: redeeming IOUs and/or sending own IOUs. -void TransactionEngine::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount) +void TransactionEngine::rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer) { uint160 uIssuerID = saAmount.getIssuer(); - assert(uSenderID == uIssuerID || uReceiverID == uIssuerID); + assert(!bCheckIssuer || uSenderID == uIssuerID || uReceiverID == uIssuerID); bool bFlipped = uSenderID > uReceiverID; uint256 uIndex = Ledger::getRippleStateIndex(uSenderID, uReceiverID, saAmount.getCurrency()); @@ -2505,6 +2518,7 @@ void TransactionEngine::calcNodeOffer( // actual send toward the reciver. // 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. +// XXX Deal with uQualityIn or uQualityOut = 0 void TransactionEngine::calcNodeRipple( const uint32 uQualityIn, const uint32 uQualityOut, @@ -2513,10 +2527,29 @@ void TransactionEngine::calcNodeRipple( STAmount& saPrvAct, // <-> in limit including achieved STAmount& saCurAct) // <-> out limit achieved. { + Log(lsINFO) << str(boost::format("calcNodeRipple> uQualityIn=%d uQualityOut=%d saPrvReq=%s saCurReq=%s saPrvAct=%s saCurAct=%s") + % uQualityIn + % uQualityOut + % saPrvReq.getFullText() + % saCurReq.getFullText() + % saPrvAct.getFullText() + % saCurAct.getFullText()); + + assert(saPrvReq.getCurrency() == saCurReq.getCurrency()); + bool bPrvUnlimited = saPrvReq.isNegative(); - STAmount saPrv = bPrvUnlimited ? saZero : saPrvReq-saPrvAct; + STAmount saPrv = bPrvUnlimited ? STAmount(saPrvReq) : saPrvReq-saPrvAct; STAmount saCur = saCurReq-saCurAct; + Log(lsINFO) << str(boost::format("calcNodeRipple:1: saCurReq=%s") % saCurReq.getFullText()); + +#if 0 + Log(lsINFO) << str(boost::format("calcNodeRipple: bPrvUnlimited=%d saPrv=%s saCur=%s") + % bPrvUnlimited + % saPrv.getFullText() + % saCur.getFullText()); +#endif + if (uQualityIn >= uQualityOut) { // No fee. @@ -2528,23 +2561,37 @@ void TransactionEngine::calcNodeRipple( else { // Fee. - STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, CURRENCY_ONE), uQualityIn, CURRENCY_ONE); + uint160 uCurrencyID = saCur.getCurrency(); + STAmount saCurIn = STAmount::divide(STAmount::multiply(saCur, uQualityOut, uCurrencyID), uQualityIn, uCurrencyID); +Log(lsINFO) << str(boost::format("calcNodeRipple:2: saCurReq=%s") % saCurReq.getFullText()); if (bPrvUnlimited || saCurIn >= saPrv) { // All of cur. Some amount of prv. +Log(lsINFO) << str(boost::format("calcNodeRipple:3a: saCurReq=%s") % saCurReq.getFullText()); saCurAct = saCurReq; saPrvAct += saCurIn; +Log(lsINFO) << str(boost::format("calcNodeRipple:3c: saCurReq=%s saPrvAct=%s") % saCurReq.getFullText() % saPrvAct.getFullText()); } else { // A part of cur. All of prv. (cur as driver) - STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, CURRENCY_ONE), uQualityOut, CURRENCY_ONE); + uint160 uCurrencyID = saPrv.getCurrency(); + STAmount saCurOut = STAmount::divide(STAmount::multiply(saPrv, uQualityIn, uCurrencyID), uQualityOut, uCurrencyID); +Log(lsINFO) << str(boost::format("calcNodeRipple:4: saCurReq=%s") % saCurReq.getFullText()); saCurAct += saCurOut; saPrvAct = saPrvReq; } } + + Log(lsINFO) << str(boost::format("calcNodeRipple< uQualityIn=%d uQualityOut=%d saPrvReq=%s saCurReq=%s saPrvAct=%s saCurAct=%s") + % uQualityIn + % uQualityOut + % saPrvReq.getFullText() + % saCurReq.getFullText() + % saPrvAct.getFullText() + % saCurAct.getFullText()); } // Calculate saPrvRedeemReq, saPrvIssueReq, saPrvDeliver; @@ -2561,59 +2608,80 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point bool bPrvRedeem = !!(prvPN.uFlags & STPathElement::typeRedeem); bool bIssue = !!(curPN.uFlags & STPathElement::typeIssue); bool bPrvIssue = !!(prvPN.uFlags & STPathElement::typeIssue); - bool bPrvAccount = !!(prvPN.uFlags & STPathElement::typeAccount); - bool bNxtAccount = !!(nxtPN.uFlags & STPathElement::typeAccount); + bool bPrvAccount = !uIndex || !!(prvPN.uFlags & STPathElement::typeAccount); + bool bNxtAccount = uIndex == uLast || !!(nxtPN.uFlags & STPathElement::typeAccount); - uint160& uPrvAccountID = prvPN.uAccountID; - uint160& uCurAccountID = curPN.uAccountID; - uint160& uNxtAccountID = bNxtAccount ? nxtPN.uAccountID : uCurAccountID; // Offers are always issue. + const uint160& uPrvAccountID = prvPN.uAccountID; + const uint160& uCurAccountID = curPN.uAccountID; + const uint160& uNxtAccountID = bNxtAccount ? nxtPN.uAccountID : uCurAccountID; // Offers are always issue. - uint160& uCurrencyID = curPN.uCurrencyID; + const uint160& uCurrencyID = curPN.uCurrencyID; - uint32 uQualityIn = rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID); - uint32 uQualityOut = rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID); + const uint32 uQualityIn = uIndex ? rippleQualityIn(uCurAccountID, uPrvAccountID, uCurrencyID) : 1; + const uint32 uQualityOut = uIndex != uLast ? rippleQualityOut(uCurAccountID, uNxtAccountID, uCurrencyID) : 1; // For bPrvAccount - STAmount saPrvBalance = bPrvAccount ? rippleBalance(uCurAccountID, uPrvAccountID, uCurrencyID) : saZero; - STAmount saPrvLimit = bPrvAccount ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) : saZero; + const STAmount saPrvBalance = uIndex && bPrvAccount ? rippleBalance(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID); + const STAmount saPrvLimit = uIndex && bPrvAccount ? rippleLimit(uCurAccountID, uPrvAccountID, uCurrencyID) : STAmount(uCurrencyID); - STAmount saPrvRedeemReq = bPrvRedeem && saPrvBalance.isNegative() ? -saPrvBalance : STAmount(uCurrencyID, 0); + const STAmount saPrvRedeemReq = bPrvRedeem && saPrvBalance.isNegative() ? -saPrvBalance : STAmount(uCurrencyID, 0); STAmount& saPrvRedeemAct = prvPN.saRevRedeem; - STAmount saPrvIssueReq = bPrvIssue && saPrvLimit - saPrvBalance; + const STAmount saPrvIssueReq = bPrvIssue ? saPrvLimit - saPrvBalance : STAmount(uCurrencyID); STAmount& saPrvIssueAct = prvPN.saRevIssue; // For !bPrvAccount - STAmount saPrvDeliverReq = STAmount(uCurrencyID, -1); // Unlimited. + const STAmount saPrvDeliverReq = STAmount(uCurrencyID, -1); // Unlimited. STAmount& saPrvDeliverAct = prvPN.saRevDeliver; // For bNxtAccount const STAmount& saCurRedeemReq = curPN.saRevRedeem; - STAmount saCurRedeemAct; + STAmount saCurRedeemAct(saCurRedeemReq.getCurrency()); const STAmount& saCurIssueReq = curPN.saRevIssue; - STAmount saCurIssueAct; // Track progress. + STAmount saCurIssueAct(saCurIssueReq.getCurrency()); // Track progress. // For !bNxtAccount const STAmount& saCurDeliverReq = curPN.saRevDeliver; - STAmount saCurDeliverAct; + STAmount saCurDeliverAct(saCurDeliverReq.getCurrency()); // For uIndex == uLast const STAmount& saCurWantedReq = pspCur->saOutReq; // XXX Credit limits? // STAmount saPrvDeliverReq = saPrvBalance.isPositive() ? saPrvLimit - saPrvBalance : saPrvLimit; - STAmount saCurWantedAct; + STAmount saCurWantedAct(saCurWantedReq.getCurrency()); + + Log(lsINFO) << str(boost::format("calcNodeAccountRev> uIndex=%d/%d saPrvRedeemReq=%s/%s saPrvIssueReq=%s/%s saCurWantedReq=%s/%s") + % uIndex + % uLast + % saPrvRedeemReq.getText() + % saPrvRedeemReq.getHumanCurrency() + % saPrvIssueReq.getText() + % saPrvIssueReq.getHumanCurrency() + % saCurWantedReq.getText() + % saCurWantedReq.getHumanCurrency()); + + Log(lsINFO) << pspCur->getJson(); if (bPrvAccount && bNxtAccount) { - if (uIndex == uLast) + if (!uIndex) + { + // ^ --> ACCOUNT --> account|offer + // Nothing to do, there is no previous to adjust. + nothing(); + } + else if (uIndex == uLast) { // account --> ACCOUNT --> $ + Log(lsINFO) << str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> $")); // Calculate redeem if (bRedeem && saPrvRedeemReq) // Previous has IOUs to redeem. { // Redeem at 1:1 + Log(lsINFO) << str(boost::format("calcNodeAccountRev: Redeem at 1:1")); + saCurWantedAct = MIN(saPrvRedeemReq, saCurWantedReq); saPrvRedeemAct = saCurWantedAct; } @@ -2624,6 +2692,8 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point && saPrvIssueReq) // Will accept IOUs. { // Rate: quality in : 1.0 + Log(lsINFO) << str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0")); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurWantedReq, saPrvIssueAct, saCurWantedAct); } @@ -2636,7 +2706,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point } else { - // account --> ACCOUNT --> account + // ^|account --> ACCOUNT --> account // redeem (part 1) -> redeem if (bPrvRedeem @@ -2645,6 +2715,8 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point && saPrvBalance.isNegative()) // Previous has IOUs to redeem. { // Rate : 1.0 : quality out + Log(lsINFO) << str(boost::format("calcNodeAccountRev: Rate : 1.0 : quality out")); + calcNodeRipple(QUALITY_ONE, uQualityOut, saPrvRedeemReq, saCurRedeemReq, saPrvRedeemAct, saCurRedeemAct); } @@ -2656,6 +2728,8 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point && saCurIssueReq) // Need some issued. { // Rate : 1.0 : transfer_rate + Log(lsINFO) << str(boost::format("calcNodeAccountRev: Rate : 1.0 : transfer_rate")); + calcNodeRipple(QUALITY_ONE, rippleTransfer(uCurAccountID), saPrvRedeemReq, saCurIssueReq, saPrvRedeemAct, saCurIssueAct); } @@ -2666,17 +2740,21 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point && !saPrvBalance.isNegative()) // Previous has no IOUs. { // Rate: quality in : quality out + Log(lsINFO) << str(boost::format("calcNodeAccountRev: Rate: quality in : quality out")); + calcNodeRipple(uQualityIn, uQualityOut, saPrvIssueReq, saCurRedeemReq, saPrvIssueAct, saCurRedeemAct); } // issue (part 2) -> issue if (bPrvIssue && bIssue // Allowed to issue. - && saCurRedeemReq != saCurRedeemAct // Can only if issue if more can not be redeemed. + && 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 + Log(lsINFO) << str(boost::format("calcNodeAccountRev: Rate: quality in : 1.0")); + calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvIssueReq, saCurIssueReq, saPrvIssueAct, saCurIssueAct); } @@ -2686,12 +2764,23 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point // terResult = tenBAD_AMOUNT; bSuccess = false; } + Log(lsINFO) << 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") + % bPrvRedeem + % bPrvIssue + % bRedeem + % bIssue + % saCurRedeemReq.getFullText() + % saCurIssueReq.getFullText() + % saPrvBalance.getFullText() + % saCurRedeemAct.getFullText() + % saCurIssueAct.getFullText()); } } else if (bPrvAccount && !bNxtAccount) { // account --> ACCOUNT --> offer // Note: deliver is always issue as ACCOUNT is the issuer for the offer input. + Log(lsINFO) << str(boost::format("calcNodeAccountRev: account --> ACCOUNT --> offer")); // redeem -> deliver/issue. if (bPrvRedeem @@ -2725,6 +2814,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point if (uIndex == uLast) { // offer --> ACCOUNT --> $ + Log(lsINFO) << str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> $")); // Rate: quality in : 1.0 calcNodeRipple(uQualityIn, QUALITY_ONE, saPrvDeliverReq, saCurWantedReq, saPrvDeliverAct, saCurWantedAct); @@ -2740,6 +2830,7 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point { // offer --> ACCOUNT --> account // Note: offer is always deliver/redeeming as account is issuer. + Log(lsINFO) << str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> account")); // deliver -> redeem if (bRedeem // Allowed to redeem. @@ -2772,6 +2863,8 @@ bool TransactionEngine::calcNodeAccountRev(unsigned int uIndex, PathState::point { // offer --> ACCOUNT --> offer // deliver/redeem -> deliver/issue. + Log(lsINFO) << str(boost::format("calcNodeAccountRev: offer --> ACCOUNT --> offer")); + if (bIssue // Allowed to issue. && saCurDeliverReq != saCurDeliverAct) // Can only if issue if more can not be redeemed. { @@ -2821,14 +2914,14 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point // For bNxtAccount const STAmount& saPrvRedeemReq = prvPN.saFwdRedeem; - STAmount saPrvRedeemAct; + STAmount saPrvRedeemAct(saPrvRedeemReq.getCurrency()); const STAmount& saPrvIssueReq = prvPN.saFwdIssue; - STAmount saPrvIssueAct; + STAmount saPrvIssueAct(saPrvIssueReq.getCurrency()); // For !bPrvAccount const STAmount& saPrvDeliverReq = prvPN.saRevDeliver; - STAmount saPrvDeliverAct; + STAmount saPrvDeliverAct(saPrvDeliverReq.getCurrency()); // For bNxtAccount const STAmount& saCurRedeemReq = curPN.saRevRedeem; @@ -2843,6 +2936,16 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point STAmount& saCurReceive = pspCur->saOutAct; + Log(lsINFO) << str(boost::format("calcNodeAccountFwd> uIndex=%d/%d saCurRedeemReq=%s/%s saCurIssueReq=%s/%s saCurDeliverReq=%s/%s") + % uIndex + % uLast + % saCurRedeemReq.getText() + % saCurRedeemReq.getHumanCurrency() + % saCurIssueReq.getText() + % saCurIssueReq.getHumanCurrency() + % saCurDeliverReq.getText() + % saCurDeliverReq.getHumanCurrency()); + // Ripple through account. if (bPrvAccount && bNxtAccount) @@ -2860,26 +2963,49 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point STAmount& saCurIssueReq = curPN.saRevIssue; STAmount& saCurIssueAct = curPN.saFwdIssue; - STAmount& saCurSendMaxReq = pspCur->saInReq; - STAmount& saCurSendMaxAct = pspCur->saInAct; + STAmount& saCurSendMaxReq = pspCur->saInReq; // Negative for no limit, doing a calculation. + STAmount& saCurSendMaxAct = pspCur->saInAct; // Report to user how much this sends. if (saCurRedeemReq) { // Redeem requested. - saCurRedeemAct = MIN(saCurRedeemAct, saCurSendMaxReq); - saCurSendMaxAct = saCurRedeemAct; + saCurRedeemAct = saCurRedeemReq.isNegative() + ? saCurRedeemReq + : MIN(saCurRedeemReq, saCurSendMaxReq); } + else + { + saCurRedeemAct = STAmount(saCurRedeemReq); + } + saCurSendMaxAct = saCurRedeemAct; - if (saCurIssueReq && saCurSendMaxReq != saCurRedeemAct) + if (saCurIssueReq && (saCurSendMaxReq.isNegative() || saCurSendMaxReq != saCurRedeemAct)) { // Issue requested and not over budget. - saCurIssueAct = MIN(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); - // saCurSendMaxAct += saCurIssueReq; // Not needed. + saCurIssueAct = saCurSendMaxReq.isNegative() + ? saCurIssueReq + : MIN(saCurSendMaxReq-saCurRedeemAct, saCurIssueReq); } + else + { + saCurIssueAct = STAmount(saCurIssueReq); + } + saCurSendMaxAct += saCurIssueAct; + + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: ^ --> ACCOUNT --> account : saCurSendMaxReq=%s saCurRedeemAct=%s saCurIssueReq=%s saCurIssueAct=%s") + % saCurSendMaxReq.getFullText() + % saCurRedeemAct.getFullText() + % saCurIssueReq.getFullText() + % saCurIssueAct.getFullText()); } else if (uIndex == uLast) { // account --> ACCOUNT --> $ + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> $ : uPrvAccountID=%s uCurAccountID=%s saPrvRedeemReq=%s saPrvIssueReq=%s") + % NewcoinAddress::createHumanAccountID(uPrvAccountID) + % NewcoinAddress::createHumanAccountID(uCurAccountID) + % saPrvRedeemReq.getFullText() + % saPrvIssueReq.getFullText()); // Last node. Accept all funds. Calculate amount actually to credit. @@ -2891,11 +3017,12 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point saCurReceive = saPrvRedeemReq+saIssueCrd; // Actually receive. - rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq+saPrvIssueReq); + rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq+saPrvIssueReq, false); } else { // account --> ACCOUNT --> account + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> account")); // Previous redeem part 1: redeem -> redeem if (bRedeem // Can redeem. @@ -2936,12 +3063,13 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point // Adjust prv --> cur balance : take all inbound // XXX Currency must be in amount. - rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq); + rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false); } } else if (bPrvAccount && !bNxtAccount) { // account --> ACCOUNT --> offer + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: account --> ACCOUNT --> offer")); // redeem -> issue. // wants to redeem and current would and can issue. @@ -2962,13 +3090,14 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point // Adjust prv --> cur balance : take all inbound // XXX Currency must be in amount. - rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq); + rippleCredit(uPrvAccountID, uCurAccountID, saPrvRedeemReq + saPrvIssueReq, false); } else if (!bPrvAccount && bNxtAccount) { if (uIndex == uLast) { // offer --> ACCOUNT --> $ + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> $")); // Amount to credit. saCurReceive = saPrvDeliverAct; @@ -2978,6 +3107,7 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point else { // offer --> ACCOUNT --> account + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> account")); // deliver -> redeem if (bRedeem // Allowed to redeem. @@ -3005,6 +3135,8 @@ bool TransactionEngine::calcNodeAccountFwd(unsigned int uIndex, PathState::point { // offer --> ACCOUNT --> offer // deliver/redeem -> deliver/issue. + Log(lsINFO) << str(boost::format("calcNodeAccountFwd: offer --> ACCOUNT --> offer")); + if (bIssue // Allowed to issue. && saPrvDeliverReq // Previous wants to deliver && saCurIssueReq) // Current wants issue. @@ -3134,6 +3266,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); if (!bFirst) { @@ -3144,6 +3278,40 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XNS); // Account as issuer. } + if (bValid && !vpnNodes.empty()) + { + const paymentNode& pnBck = vpnNodes.back(); + bool bBckAccount = !!(pnBck.uFlags & STPathElement::typeAccount); + + if (bBckAccount) + { + SLE::pointer sleRippleState = mLedger->getSLE(Ledger::getRippleStateIndex(pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID)); + + if (!sleRippleState) + { + Log(lsINFO) << "pushNode: No credit line between " + << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) + << " and " + << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) + << " for " + << STAmount::createHumanCurrency(pnPrv.uCurrencyID) + << "." ; + + bValid = false; + } + else + { + Log(lsINFO) << "pushNode: Credit line found between " + << NewcoinAddress::createHumanAccountID(pnBck.uAccountID) + << " and " + << NewcoinAddress::createHumanAccountID(pnCur.uAccountID) + << " for " + << STAmount::createHumanCurrency(pnPrv.uCurrencyID) + << "." ; + } + } + } + if (bValid) vpnNodes.push_back(pnCur); } @@ -3187,11 +3355,11 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin if (bValid) { // Verify that previous account is allowed to issue. - const paymentNode& pnLst = vpnNodes.back(); - bool bLstAccount = !!(pnLst.uFlags & STPathElement::typeAccount); - bool bLstIssue = !!(pnLst.uFlags & STPathElement::typeIssue); + const paymentNode& pnBck = vpnNodes.back(); + bool bBckAccount = !!(pnBck.uFlags & STPathElement::typeAccount); + bool bBckIssue = !!(pnBck.uFlags & STPathElement::typeIssue); - if (bLstAccount && !bLstIssue) + if (bBckAccount && !bBckIssue) { Log(lsINFO) << "pushNode: previous account must be allowed to issue."; @@ -3210,6 +3378,7 @@ bool PathState::pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uin // XXX Disallow loops in ripple paths PathState::PathState( + Ledger::pointer lpLedger, int iIndex, const LedgerEntrySet& lesSource, const STPath& spSourcePath, @@ -3219,7 +3388,7 @@ PathState::PathState( STAmount saSendMax, bool bPartialPayment ) - : mIndex(iIndex), uQuality(0) + : mLedger(lpLedger), mIndex(iIndex), uQuality(0) { uint160 uInCurrencyID = saSendMax.getCurrency(); uint160 uOutCurrencyID = saSend.getCurrency(); @@ -3231,6 +3400,7 @@ PathState::PathState( saInReq = saSendMax; saOutReq = saSend; + // Push sending node. bValid = pushNode( STPathElement::typeAccount | STPathElement::typeRedeem @@ -3276,13 +3446,16 @@ Json::Value PathState::getJson() const { Json::Value jvNode(Json::objectValue); - Json::Value jvFlags(Json::objectValue); + Json::Value jvFlags(Json::arrayValue); + + if (pnNode.uFlags & STPathElement::typeAccount) + jvFlags.append("account"); if (pnNode.uFlags & STPathElement::typeRedeem) - jvFlags["redeem"] = 1; + jvFlags.append("redeem"); if (pnNode.uFlags & STPathElement::typeIssue) - jvFlags["issue"] = 1; + jvFlags.append("issue"); jvNode["flags"] = jvFlags; @@ -3295,6 +3468,24 @@ Json::Value PathState::getJson() const if (!!pnNode.uIssuerID) jvNode["issuer"] = NewcoinAddress::createHumanAccountID(pnNode.uIssuerID); + // if (!!pnNode.saRevRedeem) + jvNode["rev_redeem"] = pnNode.saRevRedeem.getFullText(); + + // if (!!pnNode.saRevIssue) + jvNode["rev_issue"] = pnNode.saRevIssue.getFullText(); + + // if (!!pnNode.saRevDeliver) + jvNode["rev_deliver"] = pnNode.saRevDeliver.getFullText(); + + // if (!!pnNode.saFwdRedeem) + jvNode["fwd_redeem"] = pnNode.saFwdRedeem.getFullText(); + + // if (!!pnNode.saFwdIssue) + jvNode["fwd_issue"] = pnNode.saFwdIssue.getFullText(); + + // if (!!pnNode.saFwdDeliver) + jvNode["fwd_deliver"] = pnNode.saFwdDeliver.getFullText(); + jvNodes.append(jvNode); } @@ -3323,7 +3514,7 @@ Json::Value PathState::getJson() const // Calculate a node and its previous nodes. // 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 / liqudity. +// <-- bValid: true=success, false=insufficient funds / liqudity. // <-> pnNodes: // --> [end]saWanted.mAmount // --> [all]saWanted.mCurrency @@ -3334,28 +3525,28 @@ bool TransactionEngine::calcNode(unsigned int uIndex, PathState::pointer pspCur, { paymentNode& curPN = pspCur->vpnNodes[uIndex]; bool bCurAccount = !!(curPN.uFlags & STPathElement::typeAccount); - bool bSuccess; + bool bValid; // Do current node reverse. - bSuccess = bCurAccount - ? calcNodeAccountRev(uIndex, pspCur, bMultiQuality) - : calcNodeOfferRev(uIndex, pspCur, bMultiQuality); + bValid = bCurAccount + ? calcNodeAccountRev(uIndex, pspCur, bMultiQuality) + : calcNodeOfferRev(uIndex, pspCur, bMultiQuality); // Do previous. - if (bSuccess && uIndex) + if (bValid && uIndex) { - bSuccess = calcNode(uIndex-1, pspCur, bMultiQuality); + bValid = calcNode(uIndex-1, pspCur, bMultiQuality); } // Do current node forward. - if (bSuccess) + if (bValid) { - bSuccess = bCurAccount - ? calcNodeAccountFwd(uIndex, pspCur, bMultiQuality) - : calcNodeOfferFwd(uIndex, pspCur, bMultiQuality); + bValid = bCurAccount + ? calcNodeAccountFwd(uIndex, pspCur, bMultiQuality) + : calcNodeOfferFwd(uIndex, pspCur, bMultiQuality); } - return bSuccess; + return bValid; } // Calculate the next increment of a path. @@ -3372,9 +3563,14 @@ void TransactionEngine::pathNext(PathState::pointer pspCur, int iPaths) assert(pspCur->vpnNodes.size() >= 2); - bool bZero = !calcNode(uLast, pspCur, iPaths == 1); + bool bValid = calcNode(uLast, pspCur, iPaths == 1); - pspCur->uQuality = bZero + Log(lsINFO) << "pathNext: bValid=" + << bValid + << " saOutAct=" << pspCur->saOutAct.getText() + << " saInAct=" << pspCur->saInAct.getText(); + + pspCur->uQuality = bValid ? STAmount::getRate(pspCur->saOutAct, pspCur->saInAct) // Calculate relative quality. : 0; // Mark path as inactive. @@ -3591,6 +3787,7 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction // Direct path. Log(lsINFO) << "doPayment: Build direct:"; vpsPaths.push_back(PathState::createPathState( + mLedger, vpsPaths.size(), mNodes, STPath(), @@ -3606,6 +3803,7 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction { Log(lsINFO) << "doPayment: Build path:"; vpsPaths.push_back(PathState::createPathState( + mLedger, vpsPaths.size(), mNodes, spPath, @@ -3671,9 +3869,19 @@ TransactionEngineResult TransactionEngine::doPayment(const SerializedTransaction } } - Log(lsINFO) << "doPayment: Delay transaction: No ripple paths could be satisfied."; + std::string strToken; + std::string strHuman; - return terBAD_RIPPLE; + if (transResultInfo(terResult, strToken, strHuman)) + { + Log(lsINFO) << str(boost::format("doPayment: %s: %s") % strToken % strHuman); + } + else + { + assert(false); + } + + return terResult; } TransactionEngineResult TransactionEngine::doWalletAdd(const SerializedTransaction& txn) diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index 546287c03e..717074194b 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -104,7 +104,7 @@ typedef struct { uint16 uFlags; // --> From path. uint160 uAccountID; // --> Recieving/sending account. - uint160 uCurrencyID; // --> Currency to recieve. + uint160 uCurrencyID; // --> Accounts: receive and send, Offers: send. // --- For offer's next has currency out. uint160 uIssuerID; // --> Currency's issuer @@ -125,6 +125,8 @@ typedef struct { class PathState { protected: + Ledger::pointer mLedger; + bool pushNode(int iType, uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID); bool pushImply(uint160 uAccountID, uint160 uCurrencyID, uint160 uIssuerID); @@ -143,6 +145,7 @@ public: STAmount saOutAct; // Amount actually sent (calc output). PathState( + Ledger::pointer lpLedger, int iIndex, const LedgerEntrySet& lesSource, const STPath& spSourcePath, @@ -156,6 +159,7 @@ public: Json::Value getJson() const; static PathState::pointer createPathState( + Ledger::pointer lpLedger, int iIndex, const LedgerEntrySet& lesSource, const STPath& spSourcePath, @@ -165,7 +169,7 @@ public: STAmount saSendMax, bool bPartialPayment ) - { return boost::make_shared(iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax, bPartialPayment); }; + { return boost::make_shared(lpLedger, iIndex, lesSource, spSourcePath, uReceiverID, uSenderID, saSend, saSendMax, bPartialPayment); }; static bool lessPriority(const PathState::pointer& lhs, const PathState::pointer& rhs); }; @@ -225,7 +229,7 @@ protected: STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID); STAmount rippleTransfer(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount); - void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); + void rippleCredit(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount, bool bCheckIssuer=true); STAmount rippleSend(const uint160& uSenderID, const uint160& uReceiverID, const STAmount& saAmount); STAmount accountHolds(const uint160& uAccountID, const uint160& uCurrencyID, const uint160& uIssuerID);