std::pair<bool, Json::Value> ripplePathFind (RippleLineCache::pointer const& cache, AccountID const& raSrc, AccountID const& raDst, STAmount const& saDstAmount, Json::Value const& jvSrcCurrencies, boost::optional<Json::Value> const& contextPaths, int const& level) { FindPaths fp( cache, raSrc, raDst, saDstAmount, level, 4); // max paths Json::Value jvArray(Json::arrayValue); for (unsigned int i = 0; i != jvSrcCurrencies.size(); ++i) { Json::Value jvSource = jvSrcCurrencies[i]; Currency uSrcCurrencyID; AccountID uSrcIssuerID; if (!jvSource.isObject()) return std::make_pair(false, rpcError(rpcINVALID_PARAMS)); // Parse mandatory currency. if (!jvSource.isMember(jss::currency) || !to_currency( uSrcCurrencyID, jvSource[jss::currency].asString())) { WriteLog(lsINFO, RPCHandler) << "Bad currency."; return std::make_pair(false, rpcError(rpcSRC_CUR_MALFORMED)); } if (uSrcCurrencyID.isNonZero()) uSrcIssuerID = raSrc; // Parse optional issuer. if (jvSource.isMember(jss::issuer) && ((!jvSource[jss::issuer].isString() || !to_issuer(uSrcIssuerID, jvSource[jss::issuer].asString())) || (uSrcIssuerID.isZero() != uSrcCurrencyID.isZero()) || (noAccount() == uSrcIssuerID))) { WriteLog(lsINFO, RPCHandler) << "Bad issuer."; return std::make_pair(false, rpcError(rpcSRC_ISR_MALFORMED)); } STPathSet spsComputed; if (contextPaths) { Json::Value pathSet = Json::objectValue; pathSet[jss::Paths] = contextPaths.get(); STParsedJSONObject paths("pathSet", pathSet); if (! paths.object) return std::make_pair(false, paths.error); else { spsComputed = paths.object->getFieldPathSet(sfPaths); WriteLog(lsTRACE, RPCHandler) << "ripple_path_find: Paths: " << spsComputed.getJson(0); } } STPath fullLiquidityPath; auto valid = fp.findPathsForIssue( { uSrcCurrencyID, uSrcIssuerID }, spsComputed, fullLiquidityPath); if (!valid) { WriteLog(lsWARNING, RPCHandler) << "ripple_path_find: No paths found."; } else { auto& issuer = isXRP(uSrcIssuerID) ? isXRP(uSrcCurrencyID) ? // Default to source account. xrpAccount() : (raSrc) : uSrcIssuerID; // Use specifed issuer. STAmount saMaxAmount({ uSrcCurrencyID, issuer }, 1); saMaxAmount.negate(); boost::optional<PaymentSandbox> sandbox; sandbox.emplace(&*cache->getLedger(), tapNONE); assert(sandbox->open()); auto rc = path::RippleCalc::rippleCalculate( *sandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst, // --> Account to deliver to. raSrc, // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsWARNING, RPCHandler) << "ripple_path_find:" << " saMaxAmount=" << saMaxAmount << " saDstAmount=" << saDstAmount << " saMaxAmountAct=" << rc.actualAmountIn << " saDstAmountAct=" << rc.actualAmountOut; if (fullLiquidityPath.size() > 0 && (rc.result() == terNO_LINE || rc.result() == tecPATH_PARTIAL)) { WriteLog(lsDEBUG, PathRequest) << "Trying with an extra path element"; spsComputed.push_back(fullLiquidityPath); sandbox.emplace(&*cache->getLedger(), tapNONE); assert(sandbox->open()); rc = path::RippleCalc::rippleCalculate( *sandbox, saMaxAmount, // --> Amount to send is unlimited // to get an estimate. saDstAmount, // --> Amount to deliver. raDst, // --> Account to deliver to. raSrc, // --> Account sending from. spsComputed); // --> Path set. WriteLog(lsDEBUG, PathRequest) << "Extra path element gives " << transHuman(rc.result()); } if (rc.result() == tesSUCCESS) { Json::Value jvEntry(Json::objectValue); STPathSet spsCanonical; // Reuse the expanded as it would need to be calcuated // anyway to produce the canonical. (At least unless we // make a direct canonical.) jvEntry[jss::source_amount] = rc.actualAmountIn.getJson(0); jvEntry[jss::paths_canonical] = Json::arrayValue; jvEntry[jss::paths_computed] = spsComputed.getJson(0); jvArray.append(jvEntry); } else { std::string strToken; std::string strHuman; transResultInfo(rc.result(), strToken, strHuman); WriteLog(lsDEBUG, RPCHandler) << "ripple_path_find: " << strToken << " " << strHuman << " " << spsComputed.getJson(0); } } } return std::make_pair(true, jvArray); }
int PathRequest::parseJson (Json::Value const& jvParams) { if (! jvParams.isMember(jss::source_account)) { jvStatus = rpcError(rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (! jvParams.isMember(jss::destination_account)) { jvStatus = rpcError(rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (! jvParams.isMember(jss::destination_amount)) { jvStatus = rpcError(rpcDST_AMT_MISSING); return PFR_PJ_INVALID; } raSrcAccount = parseBase58<AccountID>( jvParams[jss::source_account].asString()); if (! raSrcAccount) { jvStatus = rpcError (rpcSRC_ACT_MALFORMED); return PFR_PJ_INVALID; } raDstAccount = parseBase58<AccountID>( jvParams[jss::destination_account].asString()); if (! raDstAccount) { jvStatus = rpcError (rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; } if (! amountFromJsonNoThrow ( saDstAmount, jvParams[jss::destination_amount])) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } convert_all_ = saDstAmount == STAmount(saDstAmount.issue(), 1u, 0, true); if ((saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) || (saDstAmount.getCurrency () == badCurrency ()) || (! convert_all_ && saDstAmount <= zero)) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } if (jvParams.isMember(jss::send_max)) { // Send_max requires destination amount to be -1. if (! convert_all_) { jvStatus = rpcError(rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } saSendMax.emplace(); if (! amountFromJsonNoThrow( *saSendMax, jvParams[jss::send_max]) || (saSendMax->getCurrency().isZero() && saSendMax->getIssuer().isNonZero()) || (saSendMax->getCurrency() == badCurrency()) || (*saSendMax <= zero && *saSendMax != STAmount(saSendMax->issue(), 1u, 0, true))) { jvStatus = rpcError(rpcSENDMAX_MALFORMED); return PFR_PJ_INVALID; } } if (jvParams.isMember (jss::source_currencies)) { Json::Value const& jvSrcCurrencies = jvParams[jss::source_currencies]; if (! jvSrcCurrencies.isArray() || jvSrcCurrencies.size() == 0 || jvSrcCurrencies.size() > RPC::Tuning::max_src_cur) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.clear (); for (auto const& c : jvSrcCurrencies) { // Mandatory currency Currency srcCurrencyID; if (! c.isObject() || ! c.isMember(jss::currency) || ! to_currency(srcCurrencyID, c[jss::currency].asString())) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } // Optional issuer AccountID srcIssuerID; if (c.isMember (jss::issuer) && (! c[jss::issuer].isString() || ! to_issuer(srcIssuerID, c[jss::issuer].asString()))) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); return PFR_PJ_INVALID; } if (srcCurrencyID.isZero()) { if (srcIssuerID.isNonZero()) { jvStatus = rpcError(rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } } else if (srcIssuerID.isZero()) { srcIssuerID = *raSrcAccount; } if (saSendMax) { // If the currencies don't match, ignore the source currency. if (srcCurrencyID == saSendMax->getCurrency()) { // If neither is the source and they are not equal, then the // source issuer is illegal. if (srcIssuerID != *raSrcAccount && saSendMax->getIssuer() != *raSrcAccount && srcIssuerID != saSendMax->getIssuer()) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); return PFR_PJ_INVALID; } // If both are the source, use the source. // Otherwise, use the one that's not the source. if (srcIssuerID != *raSrcAccount) { sciSourceCurrencies.insert( {srcCurrencyID, srcIssuerID}); } else if (saSendMax->getIssuer() != *raSrcAccount) { sciSourceCurrencies.insert( {srcCurrencyID, saSendMax->getIssuer()}); } else { sciSourceCurrencies.insert( {srcCurrencyID, *raSrcAccount}); } } } else { sciSourceCurrencies.insert({srcCurrencyID, srcIssuerID}); } } } if (jvParams.isMember ("id")) jvId = jvParams["id"]; return PFR_PJ_NOCHANGE; }
int PathRequest::parseJson (Json::Value const& jvParams, bool complete) { int ret = PFR_PJ_NOCHANGE; if (jvParams.isMember (jss::source_account)) { raSrcAccount = parseBase58<AccountID>( jvParams[jss::source_account].asString()); if (! raSrcAccount) { jvStatus = rpcError (rpcSRC_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcSRC_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember (jss::destination_account)) { raDstAccount = parseBase58<AccountID>( jvParams[jss::destination_account].asString()); if (! raDstAccount) { jvStatus = rpcError (rpcDST_ACT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember (jss::destination_amount)) { if (! amountFromJsonNoThrow ( saDstAmount, jvParams[jss::destination_amount]) || (saDstAmount.getCurrency ().isZero () && saDstAmount.getIssuer ().isNonZero ()) || (saDstAmount.getCurrency () == badCurrency ()) || saDstAmount <= zero) { jvStatus = rpcError (rpcDST_AMT_MALFORMED); return PFR_PJ_INVALID; } } else if (complete) { jvStatus = rpcError (rpcDST_ACT_MISSING); return PFR_PJ_INVALID; } if (jvParams.isMember (jss::source_currencies)) { Json::Value const& jvSrcCur = jvParams[jss::source_currencies]; if (!jvSrcCur.isArray ()) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } sciSourceCurrencies.clear (); for (unsigned i = 0; i < jvSrcCur.size (); ++i) { Json::Value const& jvCur = jvSrcCur[i]; Currency uCur; AccountID uIss; if (!jvCur.isObject() || !jvCur.isMember (jss::currency) || !to_currency (uCur, jvCur[jss::currency].asString ())) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } if (jvCur.isMember (jss::issuer) && !to_issuer (uIss, jvCur[jss::issuer].asString ())) { jvStatus = rpcError (rpcSRC_ISR_MALFORMED); } if (uCur.isZero () && uIss.isNonZero ()) { jvStatus = rpcError (rpcSRC_CUR_MALFORMED); return PFR_PJ_INVALID; } if (uCur.isNonZero() && uIss.isZero()) { uIss = *raSrcAccount; } sciSourceCurrencies.insert ({uCur, uIss}); } } if (jvParams.isMember ("id")) jvId = jvParams["id"]; return ret; }