Exemple #1
0
CurrencySet accountDestCurrencies (
    RippleAddress const& raAccountID,
    RippleLineCache::ref lrCache,
    bool includeXRP)
{
    CurrencySet currencies;

    if (includeXRP)
        currencies.insert (xrpCurrency());
    // Even if account doesn't exist

    // List of ripple lines.
    auto& rippleLines (lrCache->getRippleLines (raAccountID.getAccountID ()));

    for (auto const& item : rippleLines)
    {
        auto rspEntry = (RippleState*) item.get ();
        assert (rspEntry);
        if (!rspEntry)
            continue;

        auto& saBalance  = rspEntry->getBalance ();

        if (saBalance < rspEntry->getLimit ())                  // Can take more
            currencies.insert (saBalance.getCurrency ());
    }

    currencies.erase (badCurrency());
    return currencies;
}
hash_set<Currency> accountDestCurrencies (
    AccountID const& account,
    RippleLineCache::ref lrCache,
    bool includeXRP)
{
    hash_set<Currency> currencies;

    if (includeXRP) {
        currencies.insert (xrpCurrency());
        currencies.insert (vbcCurrency());
    }
    // Even if account doesn't exist

    // List of ripple lines.
    auto& rippleLines = lrCache->getRippleLines (account);

    for (auto const& item : rippleLines)
    {
        auto rspEntry = (RippleState*) item.get ();
        assert (rspEntry);
        if (!rspEntry)
            continue;

        auto& saBalance  = rspEntry->getBalance ();

        if (saBalance < rspEntry->getLimit ())                  // Can take more
            currencies.insert (saBalance.getCurrency ());
    }

    currencies.erase (badCurrency());
    return currencies;
}
Exemple #3
0
Pathfinder::Pathfinder (RippleLineCache::ref cache,
                        const RippleAddress& uSrcAccountID, const RippleAddress& uDstAccountID,
                        const uint160& uSrcCurrencyID, const uint160& uSrcIssuerID, const STAmount& saDstAmount, bool& bValid)
    :   mSrcAccountID (uSrcAccountID.getAccountID ()),
        mDstAccountID (uDstAccountID.getAccountID ()),
        mDstAmount (saDstAmount),
        mSrcCurrencyID (uSrcCurrencyID),
        mSrcIssuerID (uSrcIssuerID),
        mSrcAmount (uSrcCurrencyID, uSrcIssuerID, 1u, 0, true),
        mLedger (cache->getLedger ()), mRLCache (cache)
{

    if (((mSrcAccountID == mDstAccountID) && (mSrcCurrencyID == mDstAmount.getCurrency ())) || mDstAmount.isZero ())
    {
        // no need to send to same account with same currency, must send non-zero
        bValid = false;
        mLedger.reset ();
        return;
    }

    bValid = true;

    m_loadEvent = getApp().getJobQueue ().getLoadEvent (jtPATH_FIND, "FindPath");

    bool bIssuer = mSrcCurrencyID.isNonZero() && mSrcIssuerID.isNonZero() && (mSrcIssuerID != mSrcAccountID);
    mSource = STPathElement(                       // Where does an empty path start?
        bIssuer ? mSrcIssuerID : mSrcAccountID,    // On the source account or issuer account
        mSrcCurrencyID,                            // In the source currency
        mSrcCurrencyID.isZero() ? uint160() : (bIssuer ? mSrcIssuerID : mSrcAccountID));

}
hash_set<Currency> accountSourceCurrencies (
    AccountID const& account,
    RippleLineCache::ref lrCache,
    bool includeXRP)
{
    hash_set<Currency> currencies;

    // YYY Only bother if they are above reserve
    if (includeXRP) {
        currencies.insert (xrpCurrency());
        currencies.insert (vbcCurrency());
    }

    // List of ripple lines.
    auto& rippleLines = lrCache->getRippleLines (account);

    for (auto const& item : rippleLines)
    {
        auto rspEntry = (RippleState*) item.get ();
        assert (rspEntry);
        if (!rspEntry)
            continue;

        auto& saBalance = rspEntry->getBalance ();

        // Filter out non
        if (saBalance > zero
            // Have IOUs to send.
            || (rspEntry->getLimitPeer ()
                // Peer extends credit.
                && ((-saBalance) < rspEntry->getLimitPeer ()))) // Credit left.
        {
            currencies.insert (saBalance.getCurrency ());
        }
    }

    currencies.erase (badCurrency());
    return currencies;
}
Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
{
    m_journal.debug << iIdentifier << " update " << (fast ? "fast" : "normal");

    ScopedLockType sl (mLock);

    if (!isValid (cache))
        return jvStatus;
    jvStatus = Json::objectValue;

    auto sourceCurrencies = sciSourceCurrencies;

    if (sourceCurrencies.empty ())
    {
        auto usCurrencies =
                usAccountSourceCurrencies (raSrcAccount, cache, true);
        bool sameAccount = raSrcAccount == raDstAccount;
        for (auto const& c: usCurrencies)
        {
            if (!sameAccount || (c != saDstAmount.getCurrency ()))
            {
                if (c.isZero ())
                    sourceCurrencies.insert (std::make_pair (c, xrpAccount()));
                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;

    int iLevel = iLastLevel;
    bool loaded = getApp().getFeeTrack().isLoadedLocal();

    if (iLevel == 0)
    { // first pass
        if (loaded || fast)
            iLevel = getConfig().PATH_SEARCH_FAST;
        else
            iLevel = getConfig().PATH_SEARCH;
    }
    else if ((iLevel == getConfig().PATH_SEARCH_FAST) && !fast)
    { // leaving fast pathfinding
        iLevel = getConfig().PATH_SEARCH;
        if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
            --iLevel;
    }
    else if (bLastSuccess)
    { // decrement, if possible
        if (iLevel > getConfig().PATH_SEARCH ||
            (loaded && (iLevel > getConfig().PATH_SEARCH_FAST)))
            --iLevel;
    }
    else
    { // adjust as needed
        if (!loaded && (iLevel < getConfig().PATH_SEARCH_MAX))
            ++iLevel;
        if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
            --iLevel;
    }

    m_journal.debug << iIdentifier << " processing at level " << iLevel;

    bool found = false;

    for (auto const& currIssuer: sourceCurrencies)
    {
        {
            STAmount test ({currIssuer.first, currIssuer.second}, 1);
            if (m_journal.debug)
            {
                m_journal.debug
                        << iIdentifier
                        << " Trying to find paths: " << test.getFullText ();
            }
        }
        bool valid;
        STPathSet& spsPaths = mContext[currIssuer];
        Pathfinder pf (cache, raSrcAccount, raDstAccount,
                       currIssuer.first, currIssuer.second, saDstAmount, valid);
        CondLog (!valid, lsDEBUG, PathRequest)
                << iIdentifier << " PF request not valid";

        STPath extraPath;
        if (valid && pf.findPaths (iLevel, 4, spsPaths, extraPath))
        {
            LedgerEntrySet lesSandbox (cache->getLedger (), tapNONE);
            PathState::List pathStateList;
            STAmount saMaxAmountAct;
            STAmount saDstAmountAct;
            auto& account = currIssuer.second.isNonZero ()
                    ? Account(currIssuer.second)
                    : isXRP (currIssuer.first)
                        ? xrpAccount()
                        : raSrcAccount.getAccountID ();
            STAmount saMaxAmount ({currIssuer.first, account}, 1);

            saMaxAmount.negate ();
            m_journal.debug << iIdentifier << " Paths found, calling rippleCalc";
            TER resultCode = path::rippleCalculate (
                lesSandbox, saMaxAmountAct, saDstAmountAct,
                pathStateList, saMaxAmount, saDstAmount,
                raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
                spsPaths, false, false, false, true);

            if ((extraPath.size() > 0) && ((resultCode == terNO_LINE) || (resultCode == tecPATH_PARTIAL)))
            {
                m_journal.debug
                        << iIdentifier << " Trying with an extra path element";
                spsPaths.addPath(extraPath);
                pathStateList.clear ();
                resultCode = path::rippleCalculate (
                    lesSandbox, saMaxAmountAct, saDstAmountAct,
                    pathStateList, saMaxAmount, saDstAmount,
                    raDstAccount.getAccountID (), raSrcAccount.getAccountID (),
                    spsPaths, false, false, false, true);
                m_journal.debug
                        << iIdentifier << " Extra path element gives "
                        << transHuman (resultCode);
            }

            if (resultCode == tesSUCCESS)
            {
                Json::Value jvEntry (Json::objectValue);
                jvEntry["source_amount"]    = saMaxAmountAct.getJson (0);
                jvEntry["paths_computed"]   = spsPaths.getJson (0);
                found  = true;
                jvArray.append (jvEntry);
            }
            else
            {
                m_journal.debug << iIdentifier << " rippleCalc returns "
                                << transHuman (resultCode);
            }
        }
        else
        {
            m_journal.debug << iIdentifier << " No paths found";
        }
    }

    iLastLevel = iLevel;
    bLastSuccess = found;

    if (fast && ptQuickReply.is_not_a_date_time())
    {
        ptQuickReply = boost::posix_time::microsec_clock::universal_time();
        mOwner.reportFast ((ptQuickReply-ptCreated).total_milliseconds());
    }
    else if (!fast && ptFullReply.is_not_a_date_time())
    {
        ptFullReply = boost::posix_time::microsec_clock::universal_time();
        mOwner.reportFull ((ptFullReply-ptCreated).total_milliseconds());
    }

    jvStatus["alternatives"] = jvArray;
    return jvStatus;
}
bool PathRequest::isValid (RippleLineCache::ref crCache)
{
    ScopedLockType sl (mLock);
    bValid = raSrcAccount.isSet () && raDstAccount.isSet () && saDstAmount > zero;
    Ledger::pointer lrLedger = crCache->getLedger ();

    if (bValid)
    {
        AccountState::pointer asSrc = getApp().getOPs ().getAccountState (crCache->getLedger(), 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 = (jvStatus["destination_currencies"] = Json::arrayValue);

            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
            {
                bool const disallowXRP (
                    asDst->peekSLE ().getFlags() & lsfDisallowXRP);

                CurrencySet usDestCurrID =
                    usAccountDestCurrencies (raDstAccount, crCache, !disallowXRP);

                for (auto const& currency : usDestCurrID)
                    jvDestCur.append (to_string (currency));

                jvStatus["destination_tag"] = (asDst->peekSLE ().getFlags () & lsfRequireDestTag) != 0;
            }
        }
    }

    if (bValid)
    {
        jvStatus["ledger_hash"] = to_string (lrLedger->getHash ());
        jvStatus["ledger_index"] = lrLedger->getLedgerSeq ();
    }
    return bValid;
}
Exemple #7
0
Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast)
{
    m_journal.debug << iIdentifier << " update " << (fast ? "fast" : "normal");

    ScopedLockType sl (mLock);

    if (!isValid (cache))
        return jvStatus;
    jvStatus = Json::objectValue;

    auto sourceCurrencies = sciSourceCurrencies;

    if (sourceCurrencies.empty ())
    {
        auto usCurrencies =
                accountSourceCurrencies (*raSrcAccount, cache, true);
        bool sameAccount = *raSrcAccount == *raDstAccount;
        for (auto const& c: usCurrencies)
        {
            if (!sameAccount || (c != saDstAmount.getCurrency ()))
            {
                if (c.isZero ())
                    sourceCurrencies.insert ({c, xrpAccount()});
                else
                    sourceCurrencies.insert ({c, *raSrcAccount});
            }
        }
    }

    if (hasCompletion ())
    {
        // Old ripple_path_find API gives destination_currencies
        auto& destCurrencies = (jvStatus[jss::destination_currencies] = Json::arrayValue);
        auto usCurrencies = accountDestCurrencies (*raDstAccount, cache, true);
        for (auto const& c : usCurrencies)
            destCurrencies.append (to_string (c));
    }

    jvStatus[jss::source_account] = getApp().accountIDCache().toBase58(*raSrcAccount);
    jvStatus[jss::destination_account] = getApp().accountIDCache().toBase58(*raDstAccount);
    jvStatus[jss::destination_amount] = saDstAmount.getJson (0);
    jvStatus[jss::full_reply] = ! fast;

    if (jvId)
        jvStatus["id"] = jvId;

    Json::Value jvArray = Json::arrayValue;

    int iLevel = iLastLevel;
    bool loaded = getApp().getFeeTrack().isLoadedLocal();

    if (iLevel == 0)
    {
        // first pass
        if (loaded || fast)
            iLevel = getConfig().PATH_SEARCH_FAST;
        else
            iLevel = getConfig().PATH_SEARCH;
    }
    else if ((iLevel == getConfig().PATH_SEARCH_FAST) && !fast)
    {
        // leaving fast pathfinding
        iLevel = getConfig().PATH_SEARCH;
        if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
            --iLevel;
    }
    else if (bLastSuccess)
    {
        // decrement, if possible
        if (iLevel > getConfig().PATH_SEARCH ||
            (loaded && (iLevel > getConfig().PATH_SEARCH_FAST)))
            --iLevel;
    }
    else
    {
        // adjust as needed
        if (!loaded && (iLevel < getConfig().PATH_SEARCH_MAX))
            ++iLevel;
        if (loaded && (iLevel > getConfig().PATH_SEARCH_FAST))
            --iLevel;
    }

    m_journal.debug << iIdentifier << " processing at level " << iLevel;

    bool found = false;

    FindPaths fp (
        cache,
        *raSrcAccount,
        *raDstAccount,
        saDstAmount,
        iLevel,
        4);  // iMaxPaths
    for (auto const& currIssuer: sourceCurrencies)
    {
        {
            STAmount test (currIssuer, 1);
            if (m_journal.debug)
            {
                m_journal.debug
                        << iIdentifier
                        << " Trying to find paths: " << test.getFullText ();
            }
        }
        STPathSet& spsPaths = mContext[currIssuer];
        STPath fullLiquidityPath;
        auto valid = fp.findPathsForIssue (
            currIssuer,
            spsPaths,
            fullLiquidityPath);
        CondLog (!valid, lsDEBUG, PathRequest)
                << iIdentifier << " PF request not valid";

        if (valid)
        {
            boost::optional<PaymentSandbox> sandbox;
            sandbox.emplace(&*cache->getLedger(), tapNONE);

            auto& sourceAccount = !isXRP (currIssuer.account)
                    ? currIssuer.account
                    : isXRP (currIssuer.currency)
                        ? xrpAccount()
                        : *raSrcAccount;
            STAmount saMaxAmount ({currIssuer.currency, sourceAccount}, 1);

            saMaxAmount.negate ();
            m_journal.debug << iIdentifier
                            << " Paths found, calling rippleCalc";
            auto rc = path::RippleCalc::rippleCalculate (
                *sandbox,
                saMaxAmount,
                saDstAmount,
                *raDstAccount,
                *raSrcAccount,
                spsPaths);

            if (!fullLiquidityPath.empty() &&
                (rc.result () == terNO_LINE || rc.result () == tecPATH_PARTIAL))
            {
                m_journal.debug
                        << iIdentifier << " Trying with an extra path element";
                spsPaths.push_back (fullLiquidityPath);
                sandbox.emplace(&*cache->getLedger(), tapNONE);
                rc = path::RippleCalc::rippleCalculate (
                    *sandbox,
                    saMaxAmount,
                    saDstAmount,
                    *raDstAccount,
                    *raSrcAccount,
                    spsPaths);
                if (rc.result () != tesSUCCESS)
                    m_journal.warning
                        << iIdentifier << " Failed with covering path "
                        << transHuman (rc.result ());
                else
                    m_journal.debug
                        << iIdentifier << " Extra path element gives "
                        << transHuman (rc.result ());
            }

            if (rc.result () == tesSUCCESS)
            {
                Json::Value jvEntry (Json::objectValue);
                rc.actualAmountIn.setIssuer (sourceAccount);

                jvEntry[jss::source_amount] = rc.actualAmountIn.getJson (0);
                jvEntry[jss::paths_computed] = spsPaths.getJson (0);

                if (hasCompletion ())
                {
                    // Old ripple_path_find API requires this
                    jvEntry[jss::paths_canonical] = Json::arrayValue;
                }

                found  = true;
                jvArray.append (jvEntry);
            }
            else
            {
                m_journal.debug << iIdentifier << " rippleCalc returns "
                    << transHuman (rc.result ());
            }
        }
        else
        {
            m_journal.debug << iIdentifier << " No paths found";
        }
    }

    iLastLevel = iLevel;
    bLastSuccess = found;

    if (fast && ptQuickReply.is_not_a_date_time())
    {
        ptQuickReply = boost::posix_time::microsec_clock::universal_time();
        mOwner.reportFast ((ptQuickReply-ptCreated).total_milliseconds());
    }
    else if (!fast && ptFullReply.is_not_a_date_time())
    {
        ptFullReply = boost::posix_time::microsec_clock::universal_time();
        mOwner.reportFull ((ptFullReply-ptCreated).total_milliseconds());
    }

    jvStatus[jss::alternatives] = jvArray;
    return jvStatus;
}
Exemple #8
0
bool PathRequest::isValid (RippleLineCache::ref crCache)
{
    ScopedLockType sl (mLock);
    bValid = raSrcAccount && raDstAccount &&
            saDstAmount > zero;
    auto const& lrLedger = crCache->getLedger ();

    if (bValid)
    {
        if (! crCache->getLedger()->exists(
                keylet::account(*raSrcAccount)))
        {
            // no source account
            bValid = false;
            jvStatus = rpcError (rpcSRC_ACT_NOT_FOUND);
        }
    }

    if (bValid)
    {
        auto const sleDest = crCache->getLedger()->read(
            keylet::account(*raDstAccount));

        Json::Value& jvDestCur =
                (jvStatus[jss::destination_currencies] = Json::arrayValue);

        if (!sleDest)
        {
            // no destination account
            jvDestCur.append (Json::Value ("XRP"));

            if (!saDstAmount.native ())
            {
                // only XRP can be send to a non-existent account
                bValid = false;
                jvStatus = rpcError (rpcACT_NOT_FOUND);
            }
            else if (saDstAmount < STAmount (lrLedger->fees().accountReserve (0)))
            {
                // payment must meet reserve
                bValid = false;
                jvStatus = rpcError (rpcDST_AMT_MALFORMED);
            }
        }
        else
        {
            bool const disallowXRP (
                sleDest->getFlags() & lsfDisallowXRP);

            auto usDestCurrID = accountDestCurrencies (
                    *raDstAccount, crCache, !disallowXRP);

            for (auto const& currency : usDestCurrID)
                jvDestCur.append (to_string (currency));

            jvStatus["destination_tag"] =
                    (sleDest->getFlags () & lsfRequireDestTag)
                    != 0;
        }
    }

    if (bValid)
    {
        jvStatus[jss::ledger_hash] = to_string (lrLedger->info().hash);
        jvStatus[jss::ledger_index] = lrLedger->seq();
    }
    return bValid;
}