Beispiel #1
0
bool Log::ShouldLog(LogFilterType type, LogLevel level) const
{
    LoggerMap::const_iterator it = loggers.begin();
    while (it != loggers.end() && it->second.getType() != type)
        ++it;

    if (it != loggers.end())
    {
        LogLevel loggerLevel = it->second.getLogLevel();
        return loggerLevel && loggerLevel <= level;
    }

    if (type != LOG_FILTER_GENERAL)
        return ShouldLog(LOG_FILTER_GENERAL, level);

    return false;
}
Beispiel #2
0
void Log::outCharDump(char const* str, uint32 accountId, uint32 guid, char const* name)
{
    if (!str || !ShouldLog("entities.player.dump", LOG_LEVEL_INFO))
        return;

    std::ostringstream ss;
    ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name
        << ")\n" << str << "\n== END DUMP ==\n";

    LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, "entities.player.dump", ss.str());
    std::ostringstream param;
    param << guid << '_' << name;

    msg->param1 = param.str();

    write(msg);
}
Beispiel #3
0
void Log::outCharDump(char const* str, uint32 accountId, uint32 guid, char const* name)
{
    if (!str || !ShouldLog(LOG_FILTER_PLAYER_DUMP, LOG_LEVEL_INFO))
        return;

    std::ostringstream ss;
    ss << "== START DUMP == (account: " << accountId << " guid: " << guid << " name: " << name
       << ")\n" << str << "\n== END DUMP ==\n";

    LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, LOG_FILTER_PLAYER_DUMP, ss.str());
    ss.clear();
    ss << guid << '_' << name;

    msg->param1 = ss.str();

    write(msg);
}
Beispiel #4
0
void Log::outCommand(uint32 account, const char * str, ...)
{
    if (!str || !ShouldLog(LOG_FILTER_GMCOMMAND, LOG_LEVEL_INFO))
        return;

    va_list ap;
    va_start(ap, str);
    char text[MAX_QUERY_LEN];
    vsnprintf(text, MAX_QUERY_LEN, str, ap);
    va_end(ap);

    LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, LOG_FILTER_GMCOMMAND, text);

    std::ostringstream ss;
    ss << account;
    msg->param1 = ss.str();

    write(msg);
}
/**
 * Emit lsTRACE-level logging of a count of the objects in a
 * ledger's account-state SHAMap, grouped by LedgerEntryType.
 */
static void
traceLedgerContents(Ledger& ledger)
{
    if (ShouldLog (lsTRACE, LedgerDump))
    {
        std::map<LedgerEntryType, int> entryTypeCounts;
        ledger.peekAccountStateMap ()->visitLeaves (
            [&](SHAMapItem::ref item)
            {
                uint256 idx = item->getTag ();
                Serializer ser = item->peekSerializer ();
                SerializedLedgerEntry sle (ser, idx);
                entryTypeCounts[sle.getType ()]++;
            });
        WriteLog (lsTRACE, LedgerDump) << "Entry-type counts in account state map:";
        for (auto const& etc : entryTypeCounts)
        {
            WriteLog (lsTRACE, LedgerDump) << entryTypeNames[etc.first]
                                      << ": " << etc.second;
        }
    }
}
/**
 * Instantiate an application and replay a ledger history out
 * of the dump file `filename`.
 */
void
LedgerDump::loadTransactions (std::string const& filename)
{
    std::ifstream in (filename);
    require ((bool)in, "opening file");

    std::unique_ptr <Application> app (make_Application ());
    app->setup ();
    auto &lm = app->getLedgerMaster ();
    WriteLog (lsINFO, LedgerDump) << "Loading ledgers from " << filename;

    auto nTxs = 0;

    // app->setup() when called with START_UP == Config::FRESH calls
    // ApplicationImp::startNewLedger(). Unfortunately it starts the new
    // ledger at the wrong timestamp, so we call it again once we've read
    // the first ledger we're going to apply. However, it's worth
    // understanding that startNewLedger() establishes 3 ledgers:
    //
    // Ledger 0 is the genesis ledger, it's not a real ledger, just a
    //          number.
    //
    // Ledger 1 is the root-account deposit ledger, with a single pile of
    //          currency owned by a single account generated by the seed
    //          "masterpassword".
    //
    // Ledger 2 is created and closed immediately on start, not sure why.
    //
    // Ledger 3 is a new ledger chained to #2 and left open in
    //          ledgermaster.
    //
    // Ledger 3 is where replay should be starting, so (once we call
    // startNewLedger() again) we pull out ledger #2 and use it as the
    // parent of the new ledger 3 we're replaying, and throw away the #3
    // that was made by startNewLedger().

    Ledger::pointer parentLedger;

    while (in)
    {
        if ((gLedgerSeq & 0xfff) == 0) {
            Job j;
            app->doSweep (j);
        }

        Json::Value j = loadJsonRecord (in);

        Ledger::pointer deserializedLedger;
        SHAMap::pointer txSet;
        std::vector<uint256> txOrder;
        std::tie (deserializedLedger, txSet, txOrder) =
            loadLedgerAndTransactionsFromJSON (*app, j);

        if (!parentLedger)
        {
            if (getConfig ().START_LEDGER.empty ())
            {
                require (deserializedLedger->getLedgerSeq () == 3,
                         "Initial ledger isn't seq #3");

                // On first iteration, restart the app's view of the ledger
                // history at the same instant as the parent close time of the
                // first ledger (ledger #3).
                app->startNewLedger (deserializedLedger->getParentCloseTimeNC ());
                parentLedger = lm.getClosedLedger ();
                require (parentLedger->getLedgerSeq () == 2,
                         "Initial ledger parent isn't seq #2");
            }
            else
            {
                // We're being invoked with --ledger, which is where we
                // will start replay from.
                require (app->loadOldLedger (getConfig ().START_LEDGER, false),
                         "Reloading old ledger failed.");
                parentLedger = lm.getClosedLedger ();
            }

            auto const parentSeq = parentLedger->getLedgerSeq ();
            auto seq = j["seq"].asUInt ();
            while (parentSeq + 1 > seq)
            {
                // Fast-scan JSON records until we hit the right one.
                WriteLog (lsINFO, LedgerDump) << "scanning past ledger: "
                                              << seq;
                j = loadJsonRecord (in);
                seq = j["seq"].asUInt ();
                if (parentSeq + 1 <= seq)
                {
                    require (parentSeq + 1 == seq,
                             "Missing ledgers between loaded and replay-start");
                    std::tie (deserializedLedger, txSet, txOrder) =
                        loadLedgerAndTransactionsFromJSON (*app, j);
                }
            }
            gLedgerSeq = parentSeq;
            require(parentLedger->getLedgerSeq () + 1 ==
                    deserializedLedger->getLedgerSeq (),
                    "Mismatched ledger-sequence prefix.");
        }

        Ledger::pointer currentLedger =
            boost::make_shared<Ledger> (true, *parentLedger);
        currentLedger->setCloseTime (deserializedLedger->getCloseTimeNC ());
        currentLedger->setCloseFlags (deserializedLedger->getCloseFlags ());
        currentLedger->setParentHash (deserializedLedger->getParentHash ());

        WriteLog (lsINFO, LedgerDump) << "loading ledger: "
                                      << currentLedger->getLedgerSeq();

        if (ShouldLog (lsTRACE, LedgerDump))
        {
            WriteLog (lsTRACE, LedgerDump) << "expecting next ledger:";
            WriteLog (lsTRACE, LedgerDump) << deserializedLedger->getJson (0);
            WriteLog (lsTRACE, LedgerDump) << "synthetic next ledger:";
            WriteLog (lsTRACE, LedgerDump) << currentLedger->getJson (0);
        }

        gLedgerSeq++;

        // Apply transactions, transitioning from one ledger state to next.
        WriteLog (lsDEBUG, LedgerDump)
            << "Applying set of " << txOrder.size() << " transactions";
        CanonicalTXSet retriableTransactions (txSet->getHash ());
        std::set<uint256> failedTransactions;
        LedgerConsensus::applyTransactions (txSet, currentLedger, currentLedger,
                                            retriableTransactions, failedTransactions,
                                            false, txOrder);

        require (retriableTransactions.empty (), "failed retriable tx set is not empty");
        require (failedTransactions.empty (), "failed tx set is not empty");

        currentLedger->updateSkipList ();
        currentLedger->setClosed ();
        currentLedger->setCloseTime (deserializedLedger->getCloseTimeNC ());

        int asf = currentLedger->peekAccountStateMap ()->flushDirty (
            hotACCOUNT_NODE, currentLedger->getLedgerSeq());
        int tmf = currentLedger->peekTransactionMap ()->flushDirty (
            hotTRANSACTION_NODE, currentLedger->getLedgerSeq());
        nTxs += tmf;

        WriteLog (lsDEBUG, LedgerDump) << "Flushed " << asf << " account "
                                  << "and " << tmf << "transaction nodes";

        // Finalize with the LedgerMaster.
        currentLedger->setAccepted ();
        bool alreadyHadLedger = lm.storeLedger (currentLedger);
        assert (! alreadyHadLedger);
        lm.pushLedger (currentLedger);

        WriteLog (lsTRACE, LedgerDump) << "parent ledger:";
        traceLedgerContents (*parentLedger);
        WriteLog (lsTRACE, LedgerDump) << "current ledger:";
        traceLedgerContents (*currentLedger);

        try
        {
            checkLedgersEqual (*deserializedLedger, *currentLedger);
        }
        catch (...)
        {
            WriteLog (lsINFO, LedgerDump) << "bad ledger:";
            std::cerr << currentLedger->getJson (LEDGER_JSON_FULL);
            throw;
        }
        parentLedger = currentLedger;
    }

    WriteLog (lsINFO, LedgerDump) << "Loaded "
                             << gLedgerSeq << "ledgers, "
                             << nTxs << " transactions from "
                             << filename;
    exit (0);
}
// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed.
TER RippleCalc::rippleCalculate ()
{
    JLOG (j_.trace)
        << "rippleCalc>"
        << " saMaxAmountReq_:" << saMaxAmountReq_
        << " saDstAmountReq_:" << saDstAmountReq_;

    TER resultCode = temUNCERTAIN;
    permanentlyUnfundedOffers_.clear ();
    mumSource_.clear ();

    // YYY Might do basic checks on src and dst validity as per doPayment.

    // Incrementally search paths.
    if (inputFlags.defaultPathsAllowed)
    {
        if (!addPathState (STPath(), resultCode))
            return resultCode;
    }
    else if (spsPaths_.empty ())
    {
        JLOG (j_.debug)
            << "rippleCalc: Invalid transaction:"
            << "No paths and direct ripple not allowed.";

        return temRIPPLE_EMPTY;
    }

    // Build a default path.  Use saDstAmountReq_ and saMaxAmountReq_ to imply
    // nodes.
    // XXX Might also make a XRP bridge by default.

    JLOG (j_.trace)
        << "rippleCalc: Paths in set: " << spsPaths_.size ();

    // Now expand the path state.
    for (auto const& spPath: spsPaths_)
    {
        if (!addPathState (spPath, resultCode))
            return resultCode;
    }

    if (resultCode != tesSUCCESS)
        return (resultCode == temUNCERTAIN) ? terNO_LINE : resultCode;

    resultCode = temUNCERTAIN;

    actualAmountIn_ = saMaxAmountReq_.zeroed();
    actualAmountOut_ = saDstAmountReq_.zeroed();

    // When processing, we don't want to complicate directory walking with
    // deletion.
    const std::uint64_t uQualityLimit = inputFlags.limitQuality ?
            getRate (saDstAmountReq_, saMaxAmountReq_) : 0;

    // Offers that became unfunded.
    std::set<uint256> unfundedOffersFromBestPaths;

    int iPass = 0;

    while (resultCode == temUNCERTAIN)
    {
        int iBest = -1;
        int iDry = 0;

        // True, if ever computed multi-quality.
        bool multiQuality = false;

        // Find the best path.
        for (auto pathState : pathStateList_)
        {
            if (pathState->quality())
                // Only do active paths.
            {
                // If computing the only non-dry path, compute multi-quality.
                multiQuality = ((pathStateList_.size () - iDry) == 1);

                // Update to current amount processed.
                pathState->reset (actualAmountIn_, actualAmountOut_);

                // Error if done, output met.
                PathCursor pc(*this, *pathState, multiQuality, j_);
                pc.nextIncrement ();

                // Compute increment.
                JLOG (j_.debug)
                    << "rippleCalc: AFTER:"
                    << " mIndex=" << pathState->index()
                    << " uQuality=" << pathState->quality()
                    << " rate=" << amountFromRate (pathState->quality());

                if (!pathState->quality())
                {
                    // Path was dry.

                    ++iDry;
                }
                else if (pathState->outPass() == zero)
                {
                    // Path is not dry, but moved no funds
                    // This should never happen. Consider the path dry

                    JLOG (j_.warning)
                        << "rippelCalc: Non-dry path moves no funds";

                    assert (false);

                    pathState->setQuality (0);
                    ++iDry;
                }
                else
                {
                    CondLog (!pathState->inPass() || !pathState->outPass(),
                             lsDEBUG, RippleCalc)
                        << "rippleCalc: better:"
                        << " uQuality="
                        << amountFromRate (pathState->quality())
                        << " inPass()=" << pathState->inPass()
                        << " saOutPass="******"rippleCalc: better:"
                            << " mIndex=" << pathState->index()
                            << " uQuality=" << pathState->quality()
                            << " rate="
                            << amountFromRate (pathState->quality())
                            << " inPass()=" << pathState->inPass()
                            << " saOutPass="******"rippleCalc: Summary:"
                << " Pass: "******" Dry: " << iDry
                << " Paths: " << pathStateList_.size ();
            for (auto pathState: pathStateList_)
            {
                JLOG (j_.debug)
                    << "rippleCalc: "
                    << "Summary: " << pathState->index()
                    << " rate: "
                    << amountFromRate (pathState->quality())
                    << " quality:" << pathState->quality()
                    << " best: " << (iBest == pathState->index ());
            }
        }

        if (iBest >= 0)
        {
            // Apply best path.
            auto pathState = pathStateList_[iBest];

            JLOG (j_.debug)
                << "rippleCalc: best:"
                << " uQuality="
                << amountFromRate (pathState->quality())
                << " inPass()=" << pathState->inPass()
                << " saOutPass="******"rippleCalc: TOO MUCH:"
                    << " actualAmountOut_:" << actualAmountOut_
                    << " saDstAmountReq_:" << saDstAmountReq_;

                return tefEXCEPTION;  // TEMPORARY
                assert (false);
            }
            else if (actualAmountIn_ != saMaxAmountReq_ &&
                     iDry != pathStateList_.size ())
            {
                // Have not met requested amount or max send, try to do
                // more. Prepare for next pass.
                //
                // Merge best pass' umReverse.
                mumSource_.insert (
                    pathState->reverse().begin (), pathState->reverse().end ());

                if (iPass >= PAYMENT_MAX_LOOPS)
                {
                    // This payment is taking too many passes

                    JLOG (j_.error)
                       << "rippleCalc: pass limit";

                    resultCode = telFAILED_PROCESSING;
                }

            }
            else if (!inputFlags.partialPaymentAllowed)
            {
                // Have sent maximum allowed. Partial payment not allowed.

                resultCode   = tecPATH_PARTIAL;
            }
            else
            {
                // Have sent maximum allowed. Partial payment allowed.  Success.

                resultCode   = tesSUCCESS;
            }
        }
        // Not done and ran out of paths.
        else if (!inputFlags.partialPaymentAllowed)
        {
            // Partial payment not allowed.
            resultCode = tecPATH_PARTIAL;
        }
        // Partial payment ok.
        else if (!actualAmountOut_)
        {
            // No payment at all.
            resultCode = tecPATH_DRY;
        }
        else
        {
            // Don't apply any payment increments
            resultCode   = tesSUCCESS;
        }
    }

    if (resultCode == tesSUCCESS)
    {
        auto viewJ = logs_.journal ("View");
        resultCode = deleteOffers(view, unfundedOffersFromBestPaths, viewJ);
        if (resultCode == tesSUCCESS)
            resultCode = deleteOffers(view, permanentlyUnfundedOffers_, viewJ);
    }

    // If isOpenLedger, then ledger is not final, can vote no.
    if (resultCode == telFAILED_PROCESSING && !inputFlags.isLedgerOpen)
        return tecFAILED_PROCESSING;
    return resultCode;
}
Beispiel #8
0
TER TransactionEngine::applyTransaction (
    STTx const& txn,
    TransactionEngineParams params,
    bool& didApply)
{
    WriteLog (lsTRACE, TransactionEngine) << "applyTransaction>";
    didApply = false;
    assert (mLedger);
    uint256 const& txID = txn.getTransactionID ();
    mNodes.init (mLedger, txID, mLedger->getLedgerSeq (), params);

#ifdef BEAST_DEBUG
    if (1)
    {
        Serializer ser;
        txn.add (ser);
        SerializerIterator sit (ser);
        STTx s2 (sit);

        if (!s2.isEquivalent (txn))
        {
            WriteLog (lsFATAL, TransactionEngine) <<
                "Transaction serdes mismatch";
            WriteLog (lsINFO, TransactionEngine) << txn.getJson (0);
            WriteLog (lsFATAL, TransactionEngine) << s2.getJson (0);
            assert (false);
        }
    }
#endif

    if (!txID)
    {
        WriteLog (lsWARNING, TransactionEngine) <<
            "applyTransaction: invalid transaction id";
        return temINVALID;
    }

    TER terResult = Transactor::transact (txn, params, this);

    if (terResult == temUNKNOWN)
    {
        WriteLog (lsWARNING, TransactionEngine) <<
            "applyTransaction: Invalid transaction: unknown transaction type";
        return temUNKNOWN;
    }

    if (ShouldLog (lsDEBUG, TransactionEngine))
    {
        std::string strToken;
        std::string strHuman;

        transResultInfo (terResult, strToken, strHuman);

        WriteLog (lsDEBUG, TransactionEngine) <<
            "applyTransaction: terResult=" << strToken <<
            " : " << terResult <<
            " : " << strHuman;
    }

    if (isTesSuccess (terResult))
        didApply = true;
    else if (isTecClaim (terResult) && !(params & tapRETRY))
    {
        // only claim the transaction fee
        WriteLog (lsDEBUG, TransactionEngine) << "Reprocessing to only claim fee";
        mNodes.clear ();

        SLE::pointer txnAcct = entryCache (ltACCOUNT_ROOT,
            getAccountRootIndex (txn.getSourceAccount ()));

        if (!txnAcct)
            terResult = terNO_ACCOUNT;
        else
        {
            std::uint32_t t_seq = txn.getSequence ();
            std::uint32_t a_seq = txnAcct->getFieldU32 (sfSequence);

            if (a_seq < t_seq)
                terResult = terPRE_SEQ;
            else if (a_seq > t_seq)
                terResult = tefPAST_SEQ;
            else
            {
                STAmount fee        = txn.getTransactionFee ();
                STAmount balance    = txnAcct->getFieldAmount (sfBalance);
                STAmount balanceVBC = txnAcct->getFieldAmount(sfBalanceVBC);

                // We retry/reject the transaction if the account
                // balance is zero or we're applying against an open
                // ledger and the balance is less than the fee
                if ((balance == zero) || (balanceVBC.getNValue() == 0) ||
                    ((params & tapOPEN_LEDGER) && (balance < fee)))
                {
                    // Account has no funds or ledger is open
                    terResult = terINSUF_FEE_B;
                }
                else
                {
                    if (fee > balance)
                        fee = balance;
                    txnAcct->setFieldAmount (sfBalance, balance - fee);
                    txnAcct->setFieldAmount(sfBalanceVBC, balanceVBC);
                    txnAcct->setFieldU32 (sfSequence, t_seq + 1);
                    entryModify (txnAcct);
                    didApply = true;
                }
            }
        }
    }
    else
        WriteLog (lsDEBUG, TransactionEngine) << "Not applying transaction " << txID;

    if (didApply)
    {
        if (!checkInvariants (terResult, txn, params))
        {
            WriteLog (lsFATAL, TransactionEngine) <<
                "Transaction violates invariants";
            WriteLog (lsFATAL, TransactionEngine) <<
                txn.getJson (0);
            WriteLog (lsFATAL, TransactionEngine) <<
                transToken (terResult) << ": " << transHuman (terResult);
            WriteLog (lsFATAL, TransactionEngine) <<
                mNodes.getJson (0);
            didApply = false;
            terResult = tefINTERNAL;
        }
        else
        {
            // Transaction succeeded fully or (retries are not allowed and the
            // transaction could claim a fee)
            Serializer m;
            mNodes.calcRawMeta (m, terResult, mTxnSeq++);

            txnWrite ();

            Serializer s;
            txn.add (s);

            if (params & tapOPEN_LEDGER)
            {
                if (!mLedger->addTransaction (txID, s))
                {
                    WriteLog (lsFATAL, TransactionEngine) <<
                        "Tried to add transaction to open ledger that already had it";
                    assert (false);
                    throw std::runtime_error ("Duplicate transaction applied");
                }
            }
            else
            {
                if (!mLedger->addTransaction (txID, s, m))
                {
                    WriteLog (lsFATAL, TransactionEngine) <<
                        "Tried to add transaction to ledger that already had it";
                    assert (false);
                    throw std::runtime_error ("Duplicate transaction applied to closed ledger");
                }

                // Charge whatever fee they specified.
                STAmount saPaid = txn.getTransactionFee ();
                mLedger->destroyCoins (saPaid.getNValue ());
            }
        }
    }

    mTxnAccount.reset ();
    mNodes.clear ();

    if (!(params & tapOPEN_LEDGER) && isTemMalformed (terResult))
    {
        // XXX Malformed or failed transaction in closed ledger must bow out.
    }

    return terResult;
}
Beispiel #9
0
std::string HTTPReply (int nStatus, std::string const& strMsg)
{
    if (ShouldLog (lsTRACE, RPC))
    {
        WriteLog (lsTRACE, RPC) << "HTTP Reply " << nStatus << " " << strMsg;
    }

    std::string ret;

    if (nStatus == 401)
    {
        ret.reserve (512);

        ret.append ("HTTP/1.0 401 Authorization Required\r\n");
        ret.append (getHTTPHeaderTimestamp ());

        // CHECKME this returns a different version than the replies below. Is
        //         this by design or an accident or should it be using
        //         BuildInfo::getFullVersionString () as well?
        ret.append ("Server: " SYSTEM_NAME "-json-rpc/");
        ret.append (FormatFullVersion ());
        ret.append ("\r\n");

        // Be careful in modifying this! If you change the contents you MUST
        // update the Content-Length header as well to indicate the correct
        // size of the data.
        ret.append ("WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
                    "Content-Type: text/html\r\n"
                    "Content-Length: 296\r\n"
                    "\r\n"
                    "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
                    "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
                    "<HTML>\r\n"
                    "<HEAD>\r\n"
                    "<TITLE>Error</TITLE>\r\n"
                    "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
                    "</HEAD>\r\n"
                    "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n");

        return ret;
    }

    ret.reserve(256 + strMsg.length());

    switch (nStatus)
    {
    case 200:
        ret.append ("HTTP/1.1 200 OK\r\n");
        break;
    case 400:
        ret.append ("HTTP/1.1 400 Bad Request\r\n");
        break;
    case 403:
        ret.append ("HTTP/1.1 403 Forbidden\r\n");
        break;
    case 404:
        ret.append ("HTTP/1.1 404 Not Found\r\n");
        break;
    case 500:
        ret.append ("HTTP/1.1 500 Internal Server Error\r\n");
        break;
    }

    ret.append (getHTTPHeaderTimestamp ());

    ret.append ("Connection: Keep-Alive\r\n");

    if (getConfig ().RPC_ALLOW_REMOTE)
        ret.append ("Access-Control-Allow-Origin: *\r\n");

    ret.append ("Content-Length: ");
    ret.append (std::to_string(strMsg.size () + 2));
    ret.append ("\r\n");

    ret.append ("Content-Type: application/json; charset=UTF-8\r\n");

    ret.append ("Server: " SYSTEM_NAME "-json-rpc/");
    ret.append (BuildInfo::getFullVersionString ());
    ret.append ("\r\n");

    ret.append ("\r\n");
    ret.append (strMsg);
    ret.append ("\r\n");

    return ret;
}
Beispiel #10
0
// <-- TER: Only returns tepPATH_PARTIAL if partialPaymentAllowed.
TER DivvyCalc::divvyCalculate ()
{
    assert (mActiveLedger.isValid ());
    WriteLog (lsTRACE, DivvyCalc)
        << "divvyCalc>"
        << " saMaxAmountReq_:" << saMaxAmountReq_
        << " saDstAmountReq_:" << saDstAmountReq_;

    TER resultCode = temUNCERTAIN;
    permanentlyUnfundedOffers_.clear ();
    mumSource_.clear ();

    // YYY Might do basic checks on src and dst validity as per doPayment.

    // Incrementally search paths.
    if (inputFlags.defaultPathsAllowed)
    {
        if (!addPathState (STPath(), resultCode))
            return resultCode;
    }
    else if (spsPaths_.empty ())
    {
        WriteLog (lsDEBUG, DivvyCalc)
            << "divvyCalc: Invalid transaction:"
            << "No paths and direct divvy not allowed.";

        return temRIPPLE_EMPTY;
    }

    // Build a default path.  Use saDstAmountReq_ and saMaxAmountReq_ to imply
    // nodes.
    // XXX Might also make a XDV bridge by default.

    WriteLog (lsTRACE, DivvyCalc)
        << "divvyCalc: Paths in set: " << spsPaths_.size ();

    // Now expand the path state.
    for (auto const& spPath: spsPaths_)
    {
        if (!addPathState (spPath, resultCode))
            return resultCode;
    }

    if (resultCode != tesSUCCESS)
        return (resultCode == temUNCERTAIN) ? terNO_LINE : resultCode;

    resultCode = temUNCERTAIN;

    actualAmountIn_ = saMaxAmountReq_.zeroed();
    actualAmountOut_ = saDstAmountReq_.zeroed();

    // When processing, we don't want to complicate directory walking with
    // deletion.
    const std::uint64_t uQualityLimit = inputFlags.limitQuality ?
            getRate (saDstAmountReq_, saMaxAmountReq_) : 0;

    // Offers that became unfunded.
    OfferSet unfundedOffersFromBestPaths;

    int iPass = 0;

    while (resultCode == temUNCERTAIN)
    {
        int iBest = -1;
        LedgerEntrySet lesCheckpoint = mActiveLedger;
        int iDry = 0;

        // True, if ever computed multi-quality.
        bool multiQuality = false;

        // Find the best path.
        for (auto pathState : pathStateList_)
        {
            if (pathState->quality())
                // Only do active paths.
            {
                // If computing the only non-dry path, compute multi-quality.
                multiQuality = ((pathStateList_.size () - iDry) == 1);

                // Update to current amount processed.
                pathState->reset (actualAmountIn_, actualAmountOut_);

                // Error if done, output met.
                PathCursor pc(*this, *pathState, multiQuality);
                pc.nextIncrement (lesCheckpoint);

                // Compute increment.
                WriteLog (lsDEBUG, DivvyCalc)
                    << "divvyCalc: AFTER:"
                    << " mIndex=" << pathState->index()
                    << " uQuality=" << pathState->quality()
                    << " rate=" << amountFromRate (pathState->quality());

                if (!pathState->quality())
                {
                    // Path was dry.

                    ++iDry;
                }
                else if (pathState->outPass() == zero)
                {
                    // Path is not dry, but moved no funds
                    // This should never happen. Consider the path dry

                    WriteLog (lsWARNING, DivvyCalc)
                        << "rippelCalc: Non-dry path moves no funds";

                    assert (false);

                    pathState->setQuality (0);
                    ++iDry;
                }
                else
                {
                    CondLog (!pathState->inPass() || !pathState->outPass(),
                             lsDEBUG, DivvyCalc)
                        << "divvyCalc: better:"
                        << " uQuality="
                        << amountFromRate (pathState->quality())
                        << " inPass()=" << pathState->inPass()
                        << " saOutPass="******"divvyCalc: better:"
                            << " mIndex=" << pathState->index()
                            << " uQuality=" << pathState->quality()
                            << " rate="
                            << amountFromRate (pathState->quality())
                            << " inPass()=" << pathState->inPass()
                            << " saOutPass="******"divvyCalc: Summary:"
                << " Pass: "******" Dry: " << iDry
                << " Paths: " << pathStateList_.size ();
            for (auto pathState: pathStateList_)
            {
                WriteLog (lsDEBUG, DivvyCalc)
                    << "divvyCalc: "
                    << "Summary: " << pathState->index()
                    << " rate: "
                    << amountFromRate (pathState->quality())
                    << " quality:" << pathState->quality()
                    << " best: " << (iBest == pathState->index ());
            }
        }

        if (iBest >= 0)
        {
            // Apply best path.
            auto pathState = pathStateList_[iBest];

            WriteLog (lsDEBUG, DivvyCalc)
                << "divvyCalc: best:"
                << " uQuality="
                << amountFromRate (pathState->quality())
                << " inPass()=" << pathState->inPass()
                << " saOutPass="******"divvyCalc: TOO MUCH:"
                    << " actualAmountOut_:" << actualAmountOut_
                    << " saDstAmountReq_:" << saDstAmountReq_;

                return tefEXCEPTION;  // TEMPORARY
                assert (false);
            }
            else if (actualAmountIn_ != saMaxAmountReq_ &&
                     iDry != pathStateList_.size ())
            {
                // Have not met requested amount or max send, try to do
                // more. Prepare for next pass.
                //
                // Merge best pass' umReverse.
                mumSource_.insert (
                    pathState->reverse().begin (), pathState->reverse().end ());

                if (iPass >= PAYMENT_MAX_LOOPS)
                {
                    // This payment is taking too many passes

                    WriteLog (lsERROR, DivvyCalc)
                       << "divvyCalc: pass limit";

                    resultCode = telFAILED_PROCESSING;
                }

            }
            else if (!inputFlags.partialPaymentAllowed)
            {
                // Have sent maximum allowed. Partial payment not allowed.

                resultCode   = tecPATH_PARTIAL;
            }
            else
            {
                // Have sent maximum allowed. Partial payment allowed.  Success.

                resultCode   = tesSUCCESS;
            }
        }
        // Not done and ran out of paths.
        else if (!inputFlags.partialPaymentAllowed)
        {
            // Partial payment not allowed.
            resultCode = tecPATH_PARTIAL;
        }
        // Partial payment ok.
        else if (!actualAmountOut_)
        {
            // No payment at all.
            resultCode = tecPATH_DRY;
        }
        else
        {
            // We must restore the activeLedger from lesCheckpoint in the case
            // when iBest is -1 and just before the result is set to tesSUCCESS.

            mActiveLedger.swapWith (lesCheckpoint);
            resultCode   = tesSUCCESS;
        }
    }

    if (resultCode == tesSUCCESS)
    {
        resultCode = deleteOffers(mActiveLedger, unfundedOffersFromBestPaths);
        if (resultCode == tesSUCCESS)
            resultCode = deleteOffers(mActiveLedger, permanentlyUnfundedOffers_);
    }

    // If isOpenLedger, then ledger is not final, can vote no.
    if (resultCode == telFAILED_PROCESSING && !inputFlags.isLedgerOpen)
        return tecFAILED_PROCESSING;
    return resultCode;
}