//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== SETUP_LOG (PathRequest) // VFALCO TODO Move these globals into a PathRequests collection inteface boost::recursive_mutex PathRequest::sLock; std::set PathRequest::sRequests; PathRequest::PathRequest (const boost::shared_ptr& subscriber) : wpSubscriber (subscriber) , jvStatus (Json::objectValue) , bValid (false) , bNew (true) { } bool PathRequest::isValid () { boost::recursive_mutex::scoped_lock sl (mLock); return bValid; } bool PathRequest::isNew () { boost::recursive_mutex::scoped_lock sl (mLock); return bNew; } bool PathRequest::isValid (Ledger::ref lrLedger) { boost::recursive_mutex::scoped_lock sl (mLock); bValid = raSrcAccount.isSet () && raDstAccount.isSet () && saDstAmount.isPositive (); if (bValid) { AccountState::pointer asSrc = getApp().getOPs ().getAccountState (lrLedger, raSrcAccount); if (!asSrc) { // no source account bValid = false; jvStatus = rpcError (rpcSRC_ACT_NOT_FOUND); } else { AccountState::pointer asDst = getApp().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 PathRequest::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) { RippleLineCache::pointer cache = boost::make_shared (lrLedger); doUpdate (cache, true); } } else mValid = false; } if (mValid) { WriteLog (lsINFO, PathRequest) << "Request created: " << raSrcAccount.humanAccountID () << " -> " << raDstAccount.humanAccountID (); WriteLog (lsINFO, PathRequest) << "Deliver: " << saDstAmount.getFullText (); boost::recursive_mutex::scoped_lock sl (sLock); sRequests.insert (shared_from_this ()); } return jvStatus; } int PathRequest::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.isObject() || !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 PathRequest::doClose (const Json::Value&) { boost::recursive_mutex::scoped_lock sl (mLock); return jvStatus; } Json::Value PathRequest::doStatus (const Json::Value&) { boost::recursive_mutex::scoped_lock sl (mLock); return jvStatus; } bool PathRequest::doUpdate (RippleLineCache::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); WriteLog (lsDEBUG, PathRequest) << "Trying to find paths: " << test.getFullText (); } bool valid; STPathSet spsPaths; Pathfinder pf (cache, raSrcAccount, raDstAccount, currIssuer.first, currIssuer.second, saDstAmount, valid); CondLog (!valid, lsINFO, PathRequest) << "PF request not valid"; if (valid && pf.findPaths (getConfig ().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 (); WriteLog (lsDEBUG, PathRequest) << "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 { WriteLog (lsINFO, PathRequest) << "rippleCalc returns " << transHuman (terResult); } } else { WriteLog (lsINFO, PathRequest) << "No paths found"; } } jvStatus["alternatives"] = jvArray; return true; } void PathRequest::updateAll (Ledger::ref ledger, bool newOnly) { std::set requests; { boost::recursive_mutex::scoped_lock sl (sLock); requests = sRequests; } if (requests.empty ()) return; RippleLineCache::pointer cache = boost::make_shared (ledger); BOOST_FOREACH (wref wRequest, requests) { bool remove = true; PathRequest::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