#include "PFRequest.h" #include "NetworkOPs.h" #include "RPCErr.h" #include "Ledger.h" #include "Application.h" #include "Pathfinder.h" #include "RippleCalc.h" #include "LedgerFormats.h" SETUP_LOG(); boost::recursive_mutex PFRequest::sLock; std::set PFRequest::sRequests; PFRequest::PFRequest(const boost::shared_ptr& subscriber) : wpSubscriber(subscriber), jvStatus(Json::objectValue), bValid(false), bNew(true) { ; } bool PFRequest::isValid() { boost::recursive_mutex::scoped_lock sl(mLock); return bValid; } bool PFRequest::isNew() { boost::recursive_mutex::scoped_lock sl(mLock); return bNew; } bool PFRequest::isValid(Ledger::ref lrLedger) { boost::recursive_mutex::scoped_lock sl(mLock); bValid = raSrcAccount.isSet() && raDstAccount.isSet() && saDstAmount.isPositive(); if (bValid) { AccountState::pointer asSrc = theApp->getOPs().getAccountState(lrLedger, raSrcAccount); if (!asSrc) { // no source account bValid = false; jvStatus = rpcError(rpcSRC_ACT_NOT_FOUND); } else { AccountState::pointer asDst = theApp->getOPs().getAccountState(lrLedger, raDstAccount); Json::Value jvDestCur; if (!asDst) { // no destination account jvDestCur.append(Json::Value("XRP")); if(!saDstAmount.isNative()) { // only XRP can be send to a non-existent account bValid = false; jvStatus = rpcError(rpcACT_NOT_FOUND); } else if (saDstAmount < STAmount(lrLedger->getReserve(0))) { // payment must meet reserve bValid = false; jvStatus = rpcError(rpcDST_AMT_MALFORMED); } } else { boost::unordered_set usDestCurrID = usAccountDestCurrencies(raDstAccount, lrLedger, true); BOOST_FOREACH(const uint160& uCurrency, usDestCurrID) jvDestCur.append(STAmount::createHumanCurrency(uCurrency)); jvStatus["destination_tag"] = (asDst->peekSLE().getFlags() & lsfRequireDestTag) != 0; } jvStatus["destination_currencies"] = jvDestCur; } } jvStatus["ledger_hash"] = lrLedger->getHash().GetHex(); jvStatus["ledger_index"] = lrLedger->getLedgerSeq(); return bValid; } Json::Value PFRequest::doCreate(Ledger::ref lrLedger, const Json::Value& value) { assert(lrLedger->isClosed()); Json::Value status; bool mValid; { boost::recursive_mutex::scoped_lock sl(mLock); if (parseJson(value, true) != PFR_PJ_INVALID) { mValid = isValid(lrLedger); if (mValid) { RLCache::pointer cache = boost::make_shared(lrLedger); doUpdate(cache, true); } } else mValid = false; } if (mValid) { cLog(lsINFO) << "Request created: " << raSrcAccount.humanAccountID() << " -> " << raDstAccount.humanAccountID(); cLog(lsINFO) << "Deliver: " << saDstAmount.getFullText(); boost::recursive_mutex::scoped_lock sl(sLock); sRequests.insert(shared_from_this()); } return jvStatus; } int PFRequest::parseJson(const Json::Value& jvParams, bool complete) { int ret = PFR_PJ_NOCHANGE; if (jvParams.isMember("source_account")) { if (!raSrcAccount.setAccountID(jvParams["source_account"].asString())) { jvStatus = rpcError(rpcSRC_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError(rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember("destination_account")) { if (!raDstAccount.setAccountID(jvParams["destination_account"].asString())) { jvStatus = rpcError(rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError(rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember("destination_amount")) { if (!saDstAmount.bSetJson(jvParams["destination_amount"]) || (saDstAmount.getCurrency().isZero() && saDstAmount.getIssuer().isNonZero()) || (saDstAmount.getCurrency() == CURRENCY_BAD) || !saDstAmount.isPositive()) { jvStatus = rpcError(rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError(rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember("source_currencies")) { const Json::Value& jvSrcCur = jvParams["source_currencies"]; if (!jvSrcCur.isArray()) { jvStatus = rpcError(rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.clear(); for (unsigned i = 0; i < jvSrcCur.size(); ++i) { const Json::Value& jvCur = jvSrcCur[i]; uint160 uCur, uIss; if (!jvCur.isMember("currency") || !STAmount::currencyFromString(uCur, jvCur["currency"].asString())) { jvStatus = rpcError(rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } if (jvCur.isMember("issuer") && !STAmount::issuerFromString(uIss, jvCur["issuer"].asString())) { jvStatus = rpcError(rpcSRC_ISR_MALFORMED); } if (uCur.isZero() && uIss.isNonZero()) { jvStatus = rpcError(rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.insert(currIssuer_t(uCur, uIss)); } } if (jvParams.isMember("id")) jvId = jvParams["id"]; return ret; } Json::Value PFRequest::doClose(const Json::Value&) { boost::recursive_mutex::scoped_lock sl(mLock); return jvStatus; } Json::Value PFRequest::doStatus(const Json::Value&) { boost::recursive_mutex::scoped_lock sl(mLock); return jvStatus; } bool PFRequest::doUpdate(RLCache::ref cache, bool fast) { boost::recursive_mutex::scoped_lock sl(mLock); jvStatus = Json::objectValue; if (!isValid(cache->getLedger())) return false; if (!fast) bNew = false; std::set sourceCurrencies(sciSourceCurrencies); if (sourceCurrencies.empty()) { boost::unordered_set usCurrencies = usAccountSourceCurrencies(raSrcAccount, cache->getLedger(), true); bool sameAccount = raSrcAccount == raDstAccount; BOOST_FOREACH(const uint160& c, usCurrencies) { if (!sameAccount || (c != saDstAmount.getCurrency())) { if (c.isZero()) sourceCurrencies.insert(std::make_pair(c, ACCOUNT_XRP)); else sourceCurrencies.insert(std::make_pair(c, raSrcAccount.getAccountID())); } } } jvStatus["source_account"] = raSrcAccount.humanAccountID(); jvStatus["destination_account"] = raDstAccount.humanAccountID(); jvStatus["destination_amount"] = saDstAmount.getJson(0); if (!jvId.isNull()) jvStatus["id"] = jvId; Json::Value jvArray = Json::arrayValue; BOOST_FOREACH(const currIssuer_t& currIssuer, sourceCurrencies) { { STAmount test(currIssuer.first, currIssuer.second, 1); cLog(lsDEBUG) << "Trying to find paths: " << test.getFullText(); } bool valid; STPathSet spsPaths; Pathfinder pf(cache, raSrcAccount, raDstAccount, currIssuer.first, currIssuer.second, saDstAmount, valid); tLog(!valid, lsINFO) << "PF request not valid"; if (valid && pf.findPaths(theConfig.PATH_SEARCH_SIZE - (fast ? 0 : 1), 3, spsPaths)) { LedgerEntrySet lesSandbox(cache->getLedger(), tapNONE); std::vector vpsExpanded; STAmount saMaxAmountAct; STAmount saDstAmountAct; STAmount saMaxAmount(currIssuer.first, currIssuer.second.isNonZero() ? currIssuer.second : (currIssuer.first.isZero() ? ACCOUNT_XRP : raSrcAccount.getAccountID()), 1); saMaxAmount.negate(); cLog(lsDEBUG) << "Paths found, calling rippleCalc"; TER terResult = RippleCalc::rippleCalc(lesSandbox, saMaxAmountAct, saDstAmountAct, vpsExpanded, saMaxAmount, saDstAmount, raDstAccount.getAccountID(), raSrcAccount.getAccountID(), spsPaths, false, false, false, true); if (terResult == tesSUCCESS) { Json::Value jvEntry(Json::objectValue); jvEntry["source_amount"] = saMaxAmountAct.getJson(0); jvEntry["paths_computed"] = spsPaths.getJson(0); jvArray.append(jvEntry); } else { cLog(lsINFO) << "rippleCalc returns " << transHuman(terResult); } } else { cLog(lsINFO) << "No paths found"; } } jvStatus["alternatives"] = jvArray; return true; } void PFRequest::updateAll(Ledger::ref ledger, bool newOnly) { std::set requests; { boost::recursive_mutex::scoped_lock sl(sLock); requests = sRequests; } if (requests.empty()) return; RLCache::pointer cache = boost::make_shared(ledger); BOOST_FOREACH(wref wRequest, requests) { bool remove = true; PFRequest::pointer pRequest = wRequest.lock(); if (pRequest && (!newOnly || pRequest->isNew())) { InfoSub::pointer ipSub = pRequest->wpSubscriber.lock(); if (ipSub) { Json::Value update; { boost::recursive_mutex::scoped_lock sl(pRequest->mLock); pRequest->doUpdate(cache, false); update = pRequest->jvStatus; } update["type"] = "path_find"; ipSub->send(update, false); remove = false; } } if (remove) { boost::recursive_mutex::scoped_lock sl(sLock); sRequests.erase(wRequest); } } } // vim:ts=4