Ejemplo n.º 1
0
STTx
getPaymentTxWithPath(TestAccount& from, TestAccount const& to,
std::string const& currency, std::string const& amount,
Ledger::pointer const& ledger, bool sign)
{
    auto amountJson = Amount(std::stod(amount), currency, to).getJson();
    Json::Value tx_json = getPaymentJson(from, to, amountJson);

    // Find path. Note that the sign command can do this transparently
    // with the "build_path" field, but we don't have that here.
    auto cache = std::make_shared<RippleLineCache>(ledger);
    STPathSet pathSet;
    STPath fullLiquidityPath;
    auto stDstAmount = amountFromJson(sfGeneric, amountJson);
    Issue srcIssue = Issue(stDstAmount.getCurrency(), from.pk.getAccountID());

    auto found = findPathsForOneIssuer(cache, from.pk.getAccountID(), to.pk.getAccountID(),
        srcIssue, stDstAmount, 7, 4, pathSet, fullLiquidityPath);
    if (!found)
        throw std::runtime_error(
        "!found");
    if (pathSet.isDefault())
        throw std::runtime_error(
        "pathSet.isDefault()");

    tx_json[jss::Paths] = pathSet.getJson(0);

    return parseTransaction(from, tx_json, sign);
}
Ejemplo n.º 2
0
void
stpathset_append(STPathSet& st,
    STPath const& p, Args const&... args)
{
    st.push_back(p);
    stpathset_append(st, args...);
}
Ejemplo n.º 3
0
    TER doApply () override
    {
        // Divvy if source or destination is non-native or if there are paths.
        std::uint32_t const uTxFlags = mTxn.getFlags ();
        bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
        bool const limitQuality = uTxFlags & tfLimitQuality;
        bool const defaultPathsAllowed = !(uTxFlags & tfNoDivvyDirect);
        bool const bPaths = mTxn.isFieldPresent (sfPaths);
        bool const bMax = mTxn.isFieldPresent (sfSendMax);
        AccountID const uDstAccountID (mTxn.getFieldAccount160 (sfDestination));
        STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount));
        STAmount maxSourceAmount;
        if (bMax)
            maxSourceAmount = mTxn.getFieldAmount (sfSendMax);
        else if (saDstAmount.native ())
            maxSourceAmount = saDstAmount;
        else
          maxSourceAmount = STAmount (
              {saDstAmount.getCurrency (), mTxnAccountID},
              saDstAmount.mantissa(), saDstAmount.exponent (),
              saDstAmount < zero);

        m_journal.trace <<
            "maxSourceAmount=" << maxSourceAmount.getFullText () <<
            " saDstAmount=" << saDstAmount.getFullText ();

        // Open a ledger for editing.
        auto const index = getAccountRootIndex (uDstAccountID);
        SLE::pointer sleDst (mEngine->view().entryCache (ltACCOUNT_ROOT, index));

        if (!sleDst)
        {
            // Destination account does not exist.
            if (!saDstAmount.native ())
            {
                m_journal.trace <<
                    "Delay transaction: Destination account does not exist.";

                // Another transaction could create the account and then this
                // transaction would succeed.
                return tecNO_DST;
            }
            else if (mParams & tapOPEN_LEDGER && partialPaymentAllowed)
            {
                // You cannot fund an account with a partial payment.
                // Make retry work smaller, by rejecting this.
                m_journal.trace <<
                    "Delay transaction: Partial payment not allowed to create account.";


                // Another transaction could create the account and then this
                // transaction would succeed.
                return telNO_DST_PARTIAL;
            }
            else if (saDstAmount < STAmount (mEngine->getLedger ()->getReserve (0)))
            {
                // getReserve() is the minimum amount that an account can have.
                // Reserve is not scaled by load.
                m_journal.trace <<
                    "Delay transaction: Destination account does not exist. " <<
                    "Insufficent payment to create account.";

                // TODO: dedupe
                // Another transaction could create the account and then this
                // transaction would succeed.
                return tecNO_DST_INSUF_XDV;
            }

            // Create the account.
            sleDst = std::make_shared<SLE>(ltACCOUNT_ROOT,
                getAccountRootIndex (uDstAccountID));
            sleDst->setFieldAccount (sfAccount, uDstAccountID);
            sleDst->setFieldU32 (sfSequence, 1);
            mEngine->view().entryCreate(sleDst);
        }
        else if ((sleDst->getFlags () & lsfRequireDestTag) &&
                 !mTxn.isFieldPresent (sfDestinationTag))
        {
            // The tag is basically account-specific information we don't
            // understand, but we can require someone to fill it in.

            // We didn't make this test for a newly-formed account because there's
            // no way for this field to be set.
            m_journal.trace << "Malformed transaction: DestinationTag required.";

            return tecDST_TAG_NEEDED;
        }
        else
        {
            // Tell the engine that we are intending to change the the destination
            // account.  The source account gets always charged a fee so it's always
            // marked as modified.
            mEngine->view().entryModify (sleDst);
        }

        TER terResult;

        bool const bDivvy = bPaths || bMax || !saDstAmount.native ();
        // XXX Should bMax be sufficient to imply divvy?

        if (bDivvy)
        {
            // Divvy payment with at least one intermediate step and uses
            // transitive balances.

            // Copy paths into an editable class.
            STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);

            try
            {
                path::DivvyCalc::Input rcInput;
                rcInput.partialPaymentAllowed = partialPaymentAllowed;
                rcInput.defaultPathsAllowed = defaultPathsAllowed;
                rcInput.limitQuality = limitQuality;
                rcInput.deleteUnfundedOffers = true;
                rcInput.isLedgerOpen = static_cast<bool>(mParams & tapOPEN_LEDGER);

                bool pathTooBig = spsPaths.size () > MaxPathSize;

                for (auto const& path : spsPaths)
                    if (path.size () > MaxPathLength)
                        pathTooBig = true;

                if (rcInput.isLedgerOpen && pathTooBig)
                {
                    terResult = telBAD_PATH_COUNT; // Too many paths for proposed ledger.
                }
                else
                {

                    path::DivvyCalc::Output rc;
                    {
                        ScopedDeferCredits g (mEngine->view ());
                        rc = path::DivvyCalc::divvyCalculate (
                            mEngine->view (),
                            maxSourceAmount,
                            saDstAmount,
                            uDstAccountID,
                            mTxnAccountID,
                            spsPaths,
                            &rcInput);
                    }

                    // TODO: is this right?  If the amount is the correct amount, was
                    // the delivered amount previously set?
                    if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount)
                        mEngine->view ().setDeliveredAmount (rc.actualAmountOut);

                    terResult = rc.result ();
                }

                // TODO(tom): what's going on here?
                if (isTerRetry (terResult))
                    terResult = tecPATH_DRY;

            }
            catch (std::exception const& e)
            {
                m_journal.trace <<
                    "Caught throw: " << e.what ();

                terResult = tefEXCEPTION;
            }
        }
        else
        {
            // Direct XDV payment.

            // uOwnerCount is the number of entries in this legder for this
            // account that require a reserve.
            auto const uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount);

            // This is the total reserve in drops.
            std::uint64_t const uReserve =
                mEngine->getLedger ()->getReserve (uOwnerCount);

            // mPriorBalance is the balance on the sending account BEFORE the
            // fees were charged. We want to make sure we have enough reserve
            // to send. Allow final spend to use reserve for fee.
            auto const mmm = std::max(mTxn.getTransactionFee (),
                STAmount (uReserve));

            if (mPriorBalance < saDstAmount + mmm)
            {
                // Vote no. However the transaction might succeed, if applied in
                // a different order.
                m_journal.trace << "Delay transaction: Insufficient funds: " <<
                    " " << mPriorBalance.getText () <<
                    " / " << (saDstAmount + mmm).getText () <<
                    " (" << uReserve << ")";

                terResult = tecUNFUNDED_PAYMENT;
            }
            else
            {
                // The source account does have enough money, so do the
                // arithmetic for the transfer and make the ledger change.
                mTxnAccount->setFieldAmount (sfBalance,
                    mSourceBalance - saDstAmount);
                sleDst->setFieldAmount (sfBalance,
                    sleDst->getFieldAmount (sfBalance) + saDstAmount);

                // Re-arm the password change fee if we can and need to.
                if ((sleDst->getFlags () & lsfPasswordSpent))
                    sleDst->clearFlag (lsfPasswordSpent);

                terResult = tesSUCCESS;
            }
        }

        std::string strToken;
        std::string strHuman;

        if (transResultInfo (terResult, strToken, strHuman))
        {
            m_journal.trace <<
                strToken << ": " << strHuman;
        }
        else
        {
            assert (false);
        }

        return terResult;
    }
Ejemplo n.º 4
0
    TER doApply () override
    {
        // Ripple if source or destination is non-native or if there are paths.
        std::uint32_t const uTxFlags = mTxn.getFlags ();
        bool const partialPaymentAllowed = uTxFlags & tfPartialPayment;
        bool const limitQuality = uTxFlags & tfLimitQuality;
        bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect);
        bool const bPaths = mTxn.isFieldPresent (sfPaths);
        bool const bMax = mTxn.isFieldPresent (sfSendMax);
        Account const uDstAccountID (mTxn.getFieldAccount160 (sfDestination));
        STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount));
        STAmount maxSourceAmount;
        if (bMax)
            maxSourceAmount = mTxn.getFieldAmount (sfSendMax);
        else if (saDstAmount.isNative ())
            maxSourceAmount = saDstAmount;
        else
          maxSourceAmount = STAmount (
              {saDstAmount.getCurrency (), mTxnAccountID},
              saDstAmount.mantissa(), saDstAmount.exponent (),
              saDstAmount < zero);
        auto const& uSrcCurrency = maxSourceAmount.getCurrency ();
        auto const& uDstCurrency = saDstAmount.getCurrency ();

        // isZero() is XRP.  FIX!
        bool const bXRPDirect = uSrcCurrency.isZero () && uDstCurrency.isZero ();

        m_journal.trace <<
            "maxSourceAmount=" << maxSourceAmount.getFullText () <<
            " saDstAmount=" << saDstAmount.getFullText ();

        if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount))
            return temBAD_AMOUNT;

        if (uTxFlags & tfPaymentMask)
        {
            m_journal.trace <<
                "Malformed transaction: Invalid flags set.";

            return temINVALID_FLAG;
        }
        else if (!uDstAccountID)
        {
            m_journal.trace <<
                "Malformed transaction: Payment destination account not specified.";

            return temDST_NEEDED;
        }
        else if (bMax && maxSourceAmount <= zero)
        {
            m_journal.trace <<
                "Malformed transaction: bad max amount: " << maxSourceAmount.getFullText ();

            return temBAD_AMOUNT;
        }
        else if (saDstAmount <= zero)
        {
            m_journal.trace <<
                "Malformed transaction: bad dst amount: " << saDstAmount.getFullText ();

            return temBAD_AMOUNT;
        }
        else if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency)
        {
            m_journal.trace <<
                "Malformed transaction: Bad currency.";

            return temBAD_CURRENCY;
        }
        else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths)
        {
            // You're signing yourself a payment.
            // If bPaths is true, you might be trying some arbitrage.
            m_journal.trace <<
                "Malformed transaction: Redundant transaction:" <<
                " src=" << to_string (mTxnAccountID) <<
                " dst=" << to_string (uDstAccountID) <<
                " src_cur=" << to_string (uSrcCurrency) <<
                " dst_cur=" << to_string (uDstCurrency);

            return temREDUNDANT;
        }
        else if (bMax && maxSourceAmount == saDstAmount &&
                 maxSourceAmount.getCurrency () == saDstAmount.getCurrency ())
        {
            // Consistent but redundant transaction.
            m_journal.trace <<
                "Malformed transaction: Redundant SendMax.";

            return temREDUNDANT_SEND_MAX;
        }
        else if (bXRPDirect && bMax)
        {
            // Consistent but redundant transaction.
            m_journal.trace <<
                "Malformed transaction: SendMax specified for XRP to XRP.";

            return temBAD_SEND_XRP_MAX;
        }
        else if (bXRPDirect && bPaths)
        {
            // XRP is sent without paths.
            m_journal.trace <<
                "Malformed transaction: Paths specified for XRP to XRP.";

            return temBAD_SEND_XRP_PATHS;
        }
        else if (bXRPDirect && partialPaymentAllowed)
        {
            // Consistent but redundant transaction.
            m_journal.trace <<
                "Malformed transaction: Partial payment specified for XRP to XRP.";

            return temBAD_SEND_XRP_PARTIAL;
        }
        else if (bXRPDirect && limitQuality)
        {
            // Consistent but redundant transaction.
            m_journal.trace <<
                "Malformed transaction: Limit quality specified for XRP to XRP.";

            return temBAD_SEND_XRP_LIMIT;
        }
        else if (bXRPDirect && !defaultPathsAllowed)
        {
            // Consistent but redundant transaction.
            m_journal.trace <<
                "Malformed transaction: No ripple direct specified for XRP to XRP.";

            return temBAD_SEND_XRP_NO_DIRECT;
        }

        //
        // Open a ledger for editing.
        auto const index = getAccountRootIndex (uDstAccountID);
        SLE::pointer sleDst (mEngine->entryCache (ltACCOUNT_ROOT, index));

        if (!sleDst)
        {
            // Destination account does not exist.
            if (!saDstAmount.isNative ())
            {
                m_journal.trace <<
                    "Delay transaction: Destination account does not exist.";

                // Another transaction could create the account and then this
                // transaction would succeed.
                return tecNO_DST;
            }
            else if (mParams & tapOPEN_LEDGER && partialPaymentAllowed)
            {
                // You cannot fund an account with a partial payment.
                // Make retry work smaller, by rejecting this.
                m_journal.trace <<
                    "Delay transaction: Partial payment not allowed to create account.";


                // Another transaction could create the account and then this
                // transaction would succeed.
                return telNO_DST_PARTIAL;
            }
            else if (saDstAmount.getNValue () < mEngine->getLedger ()->getReserve (0))
            {
                // getReserve() is the minimum amount that an account can have.
                // Reserve is not scaled by load.
                m_journal.trace <<
                    "Delay transaction: Destination account does not exist. " <<
                    "Insufficent payment to create account.";

                // TODO: dedupe
                // Another transaction could create the account and then this
                // transaction would succeed.
                return tecNO_DST_INSUF_XRP;
            }

            // Create the account.
            auto const newIndex = getAccountRootIndex (uDstAccountID);
            sleDst = mEngine->entryCreate (ltACCOUNT_ROOT, newIndex);
            sleDst->setFieldAccount (sfAccount, uDstAccountID);
            sleDst->setFieldU32 (sfSequence, 1);
        }
        else if ((sleDst->getFlags () & lsfRequireDestTag) &&
                 !mTxn.isFieldPresent (sfDestinationTag))
        {
            // The tag is basically account-specific information we don't
            // understand, but we can require someone to fill it in.

            // We didn't make this test for a newly-formed account because there's
            // no way for this field to be set.
            m_journal.trace << "Malformed transaction: DestinationTag required.";

            return tefDST_TAG_NEEDED;
        }
        else
        {
            // Tell the engine that we are intending to change the the destination
            // account.  The source account gets always charged a fee so it's always
            // marked as modified.
            mEngine->entryModify (sleDst);
        }

        TER terResult;

        bool const bRipple = bPaths || bMax || !saDstAmount.isNative ();
        // XXX Should bMax be sufficient to imply ripple?

        if (bRipple)
        {
            // Ripple payment with at least one intermediate step and uses
            // transitive balances.

            // Copy paths into an editable class.
            STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);

            try
            {
                path::RippleCalc::Input rcInput;
                rcInput.partialPaymentAllowed = partialPaymentAllowed;
                rcInput.defaultPathsAllowed = defaultPathsAllowed;
                rcInput.limitQuality = limitQuality;
                rcInput.deleteUnfundedOffers = true;
                rcInput.isLedgerOpen = static_cast<bool>(mParams & tapOPEN_LEDGER);

                bool pathTooBig = spsPaths.size () > MaxPathSize;

                for (auto const& path : spsPaths)
                    if (path.size () > MaxPathLength)
                        pathTooBig = true;

                if (rcInput.isLedgerOpen && pathTooBig)
                {
                    terResult = telBAD_PATH_COUNT; // Too many paths for proposed ledger.
                }
                else
                {
                    auto rc = path::RippleCalc::rippleCalculate (
                        mEngine->view (),
                        maxSourceAmount,
                        saDstAmount,
                        uDstAccountID,
                        mTxnAccountID,
                        spsPaths,
                        &rcInput);

                    // TODO: is this right?  If the amount is the correct amount, was
                    // the delivered amount previously set?
                    if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount)
                        mEngine->view ().setDeliveredAmount (rc.actualAmountOut);

                    terResult = rc.result ();
                }

                // TODO(tom): what's going on here?
                if (isTerRetry (terResult))
                    terResult = tecPATH_DRY;

            }
            catch (std::exception const& e)
            {
                m_journal.trace <<
                    "Caught throw: " << e.what ();

                terResult = tefEXCEPTION;
            }
        }
        else
        {
            // Direct XRP payment.

            // uOwnerCount is the number of entries in this legder for this account
            // that require a reserve.

            std::uint32_t const uOwnerCount (mTxnAccount->getFieldU32 (sfOwnerCount));

            // This is the total reserve in drops.
            // TODO(tom): there should be a class for this.
            std::uint64_t const uReserve (mEngine->getLedger ()->getReserve (uOwnerCount));

            // mPriorBalance is the balance on the sending account BEFORE the fees were charged.
            //
            // Make sure have enough reserve to send. Allow final spend to use
            // reserve for fee.
            auto const mmm = std::max(uReserve, mTxn.getTransactionFee ().getNValue ());
            if (mPriorBalance < saDstAmount + mmm)
            {
                // Vote no.
                // However, transaction might succeed, if applied in a different order.
                m_journal.trace << "Delay transaction: Insufficient funds: " <<
                    " " << mPriorBalance.getText () <<
                    " / " << (saDstAmount + uReserve).getText () <<
                    " (" << uReserve << ")";

                terResult = tecUNFUNDED_PAYMENT;
            }
            else
            {
                // The source account does have enough money, so do the arithmetic
                // for the transfer and make the ledger change.
                mTxnAccount->setFieldAmount (sfBalance, mSourceBalance - saDstAmount);
                sleDst->setFieldAmount (sfBalance, sleDst->getFieldAmount (sfBalance) + saDstAmount);

                // Re-arm the password change fee if we can and need to.
                if ((sleDst->getFlags () & lsfPasswordSpent))
                    sleDst->clearFlag (lsfPasswordSpent);

                terResult = tesSUCCESS;
            }
        }

        std::string strToken;
        std::string strHuman;

        if (transResultInfo (terResult, strToken, strHuman))
        {
            m_journal.trace <<
                strToken << ": " << strHuman;
        }
        else
        {
            assert (false);
        }

        return terResult;
    }
Ejemplo n.º 5
0
// This interface is deprecated.
Json::Value doRipplePathFind (RPC::Context& context)
{
    RPC::LegacyPathFind lpf (context.role == Role::ADMIN);
    if (!lpf.isOk ())
        return rpcError (rpcTOO_BUSY);

    context.loadType = Resource::feeHighBurdenRPC;

    RippleAddress raSrc;
    RippleAddress raDst;
    STAmount saDstAmount;
    Ledger::pointer lpLedger;

    Json::Value jvResult;

    if (getConfig().RUN_STANDALONE ||
        context.params.isMember(jss::ledger) ||
        context.params.isMember(jss::ledger_index) ||
        context.params.isMember(jss::ledger_hash))
    {
        // The caller specified a ledger
        jvResult = RPC::lookupLedger (
            context.params, lpLedger, context.netOps);
        if (!lpLedger)
            return jvResult;
    }

    if (!context.params.isMember ("source_account"))
    {
        jvResult = rpcError (rpcSRC_ACT_MISSING);
    }
    else if (!context.params["source_account"].isString ()
             || !raSrc.setAccountID (
                 context.params["source_account"].asString ()))
    {
        jvResult = rpcError (rpcSRC_ACT_MALFORMED);
    }
    else if (!context.params.isMember ("destination_account"))
    {
        jvResult = rpcError (rpcDST_ACT_MISSING);
    }
    else if (!context.params["destination_account"].isString ()
             || !raDst.setAccountID (
                 context.params["destination_account"].asString ()))
    {
        jvResult = rpcError (rpcDST_ACT_MALFORMED);
    }
    else if (
        // Parse saDstAmount.
        !context.params.isMember ("destination_amount")
        || ! amountFromJsonNoThrow(saDstAmount, context.params["destination_amount"])
        || saDstAmount <= zero
        || (!isXRP(saDstAmount.getCurrency ())
            && (!saDstAmount.getIssuer () ||
                noAccount() == saDstAmount.getIssuer ())))
    {
        WriteLog (lsINFO, RPCHandler) << "Bad destination_amount.";
        jvResult    = rpcError (rpcINVALID_PARAMS);
    }
    else if (
        // Checks on source_currencies.
        context.params.isMember ("source_currencies")
        && (!context.params["source_currencies"].isArray ()
            || !context.params["source_currencies"].size ())
        // Don't allow empty currencies.
    )
    {
        WriteLog (lsINFO, RPCHandler) << "Bad source_currencies.";
        jvResult    = rpcError (rpcINVALID_PARAMS);
    }
    else
    {
        context.loadType = Resource::feeHighBurdenRPC;
        RippleLineCache::pointer cache;

        if (lpLedger)
        {
            // The caller specified a ledger
            lpLedger = std::make_shared<Ledger> (std::ref (*lpLedger), false);
            cache = std::make_shared<RippleLineCache>(lpLedger);
        }
        else
        {
            // The closed ledger is recent and any nodes made resident
            // have the best chance to persist
            lpLedger = context.netOps.getClosedLedger();
            cache = getApp().getPathRequests().getLineCache(lpLedger, false);
        }

        Json::Value     jvSrcCurrencies;

        if (context.params.isMember ("source_currencies"))
        {
            jvSrcCurrencies = context.params["source_currencies"];
        }
        else
        {
            auto currencies = accountSourceCurrencies (raSrc, cache, true);
            jvSrcCurrencies = Json::Value (Json::arrayValue);

            for (auto const& uCurrency: currencies)
            {
                Json::Value jvCurrency (Json::objectValue);
                jvCurrency["currency"] = to_string(uCurrency);
                jvSrcCurrencies.append (jvCurrency);
            }
        }

        // Fill in currencies destination will accept
        Json::Value jvDestCur (Json::arrayValue);

        // TODO(tom): this could be optimized the same way that
        // PathRequest::doUpdate() is - if we don't obsolete this code first.
        auto usDestCurrID = accountDestCurrencies (raDst, cache, true);
        for (auto const& uCurrency: usDestCurrID)
                jvDestCur.append (to_string (uCurrency));

        jvResult["destination_currencies"] = jvDestCur;
        jvResult["destination_account"] = raDst.humanAccountID ();

        Json::Value jvArray (Json::arrayValue);

        int level = getConfig().PATH_SEARCH_OLD;
        if ((getConfig().PATH_SEARCH_MAX > level)
            && !getApp().getFeeTrack().isLoadedLocal())
        {
            ++level;
        }

        if (context.params.isMember("search_depth")
            && context.params["search_depth"].isIntegral())
        {
            int rLev = context.params["search_depth"].asInt ();
            if ((rLev < level) || (context.role == Role::ADMIN))
                level = rLev;
        }

        FindPaths fp (
            cache,
            raSrc.getAccountID(),
            raDst.getAccountID(),
            saDstAmount,
            level,
            4); // max paths

        for (unsigned int i = 0; i != jvSrcCurrencies.size (); ++i)
        {
            Json::Value jvSource        = jvSrcCurrencies[i];

            Currency uSrcCurrencyID;
            Account uSrcIssuerID;

            if (!jvSource.isObject ())
                return rpcError (rpcINVALID_PARAMS);

            // Parse mandatory currency.
            if (!jvSource.isMember ("currency")
                || !to_currency (
                    uSrcCurrencyID, jvSource["currency"].asString ()))
            {
                WriteLog (lsINFO, RPCHandler) << "Bad currency.";

                return rpcError (rpcSRC_CUR_MALFORMED);
            }

            if (uSrcCurrencyID.isNonZero ())
                uSrcIssuerID = raSrc.getAccountID ();

            // Parse optional issuer.
            if (jvSource.isMember ("issuer") &&
                ((!jvSource["issuer"].isString () ||
                  !to_issuer (uSrcIssuerID, jvSource["issuer"].asString ())) ||
                 (uSrcIssuerID.isZero () != uSrcCurrencyID.isZero ()) ||
                 (noAccount() == uSrcIssuerID)))
            {
                WriteLog (lsINFO, RPCHandler) << "Bad issuer.";
                return rpcError (rpcSRC_ISR_MALFORMED);
            }

            STPathSet spsComputed;
            if (context.params.isMember("paths"))
            {
                Json::Value pathSet = Json::objectValue;
                pathSet["Paths"] = context.params["paths"];
                STParsedJSONObject paths ("pathSet", pathSet);
                if (paths.object.get() == nullptr)
                    return paths.error;
                else
                {
                    spsComputed = paths.object.get()->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() :
                            Account (raSrc.getAccountID ())
                        : uSrcIssuerID;            // Use specifed issuer.

                STAmount saMaxAmount ({uSrcCurrencyID, issuer}, 1);
                saMaxAmount.negate ();

                LedgerEntrySet lesSandbox (lpLedger, tapNONE);

                auto rc = path::RippleCalc::rippleCalculate (
                    lesSandbox,
                    saMaxAmount,            // --> Amount to send is unlimited
                                            //     to get an estimate.
                    saDstAmount,            // --> Amount to deliver.
                    raDst.getAccountID (),  // --> Account to deliver to.
                    raSrc.getAccountID (),  // --> 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);
                    lesSandbox.clear ();
                    rc = path::RippleCalc::rippleCalculate (
                        lesSandbox,
                        saMaxAmount,            // --> Amount to send is unlimited
                        //     to get an estimate.
                        saDstAmount,            // --> Amount to deliver.
                        raDst.getAccountID (),  // --> Account to deliver to.
                        raSrc.getAccountID (),  // --> 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["source_amount"] = rc.actualAmountIn.getJson (0);
                    jvEntry["paths_canonical"]  = Json::arrayValue;
                    jvEntry["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);
                }
            }
        }

        // Each alternative differs by source currency.
        jvResult["alternatives"] = jvArray;
    }

    WriteLog (lsDEBUG, RPCHandler)
            << boost::str (boost::format ("ripple_path_find< %s")
                           % jvResult);

    return jvResult;
}
Ejemplo n.º 6
0
bool STParsedJSON::parse (std::string const& json_name,
    Json::Value const& json, SField::ref inName, int depth,
        std::unique_ptr <STObject>& sub_object)
{
    if (! json.isObject ())
    {
        error = not_an_object (json_name);
        return false;
    }

    SField::ptr name (&inName);

    boost::ptr_vector<SerializedType> data;
    Json::Value::Members members (json.getMemberNames ());

    for (Json::Value::Members::iterator it (members.begin ());
        it != members.end (); ++it)
    {
        std::string const& fieldName = *it;
        Json::Value const& value = json [fieldName];

        SField::ref field = SField::getField (fieldName);

        if (field == sfInvalid)
        {
            error = unknown_field (json_name, fieldName);
            return false;
        }

        switch (field.fieldType)
        {
        case STI_UINT8:
            try
            {
                if (value.isString ())
                {
                    // VFALCO TODO wtf?
                }
                else if (value.isInt ())
                {
                    if (value.asInt () < 0 || value.asInt () > 255)
                    {
                        error = out_of_range (json_name, fieldName);
                        return false;
                    }

                    data.push_back (new STUInt8 (field,
                        range_check_cast <unsigned char> (
                            value.asInt (), 0, 255)));
                }
                else if (value.isUInt ())
                {
                    if (value.asUInt () > 255)
                    {
                        error = out_of_range (json_name, fieldName);
                        return false;
                    }

                    data.push_back (new STUInt8 (field,
                        range_check_cast <unsigned char> (
                            value.asUInt (), 0, 255)));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_UINT16:
            try
            {
                if (value.isString ())
                {
                    std::string strValue = value.asString ();

                    if (! strValue.empty () &&
                        ((strValue[0] < '0') || (strValue[0] > '9')))
                    {
                        if (field == sfTransactionType)
                        {
                            TxType const txType (TxFormats::getInstance()->
                                findTypeByName (strValue));

                            data.push_back (new STUInt16 (field,
                                static_cast <std::uint16_t> (txType)));

                            if (*name == sfGeneric)
                                name = &sfTransaction;
                        }
                        else if (field == sfLedgerEntryType)
                        {
                            LedgerEntryType const type (LedgerFormats::getInstance()->
                                findTypeByName (strValue));

                            data.push_back (new STUInt16 (field,
                                static_cast <std::uint16_t> (type)));

                            if (*name == sfGeneric)
                                name = &sfLedgerEntry;
                        }
                        else
                        {
                            error = invalid_data (json_name, fieldName);
                            return false;
                        }
                    }
                    else
                    {
                        data.push_back (new STUInt16 (field,
                            beast::lexicalCastThrow <std::uint16_t> (strValue)));
                    }
                }
                else if (value.isInt ())
                {
                    data.push_back (new STUInt16 (field,
                        range_check_cast <std::uint16_t> (
                            value.asInt (), 0, 65535)));
                }
                else if (value.isUInt ())
                {
                    data.push_back (new STUInt16 (field,
                        range_check_cast <std::uint16_t> (
                            value.asUInt (), 0, 65535)));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_UINT32:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STUInt32 (field,
                        beast::lexicalCastThrow <std::uint32_t> (value.asString ())));
                }
                else if (value.isInt ())
                {
                    data.push_back (new STUInt32 (field,
                        range_check_cast <std::uint32_t> (value.asInt (), 0u, 4294967295u)));
                }
                else if (value.isUInt ())
                {
                    data.push_back (new STUInt32 (field,
                        static_cast <std::uint32_t> (value.asUInt ())));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_UINT64:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STUInt64 (field,
                        uintFromHex (value.asString ())));
                }
                else if (value.isInt ())
                {
                    data.push_back (new STUInt64 (field,
                        range_check_cast<std::uint64_t> (
                            value.asInt (), 0, 18446744073709551615ull)));
                }
                else if (value.isUInt ())
                {
                    data.push_back (new STUInt64 (field,
                        static_cast <std::uint64_t> (value.asUInt ())));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_HASH128:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STHash128 (field, value.asString ()));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_HASH160:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STHash160 (field, value.asString ()));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_HASH256:
            try
            {
                if (value.isString ())
                {
                    data.push_back (new STHash256 (field, value.asString ()));
                }
                else
                {
                    error = bad_type (json_name, fieldName);
                    return false;
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_VL:
            if (! value.isString ())
            {
                error = bad_type (json_name, fieldName);
                return false;
            }

            try
            {
                std::pair<Blob, bool> ret(strUnHex (value.asString ()));

                if (!ret.second)
                    throw std::invalid_argument ("invalid data");

                data.push_back (new STVariableLength (field, ret.first));
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_AMOUNT:
            try
            {
                data.push_back (new STAmount (field, value));
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_VECTOR256:
            if (! value.isArray ())
            {
                error = array_expected (json_name, fieldName);
                return false;
            }
            
            try
            {
                data.push_back (new STVector256 (field));
                STVector256* tail (dynamic_cast <STVector256*> (&data.back ()));
                assert (tail);

                for (Json::UInt i = 0; !json.isValidIndex (i); ++i)
                {
                    uint256 s;
                    s.SetHex (json[i].asString ());
                    tail->addValue (s);
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_PATHSET:
            if (!value.isArray ())
            {
                error = array_expected (json_name, fieldName);
                return false;
            }

            try
            {
                data.push_back (new STPathSet (field));
                STPathSet* tail = dynamic_cast <STPathSet*> (&data.back ());
                assert (tail);

                for (Json::UInt i = 0; value.isValidIndex (i); ++i)
                {
                    STPath p;

                    if (!value[i].isArray ())
                    {
                        std::stringstream ss;
                        ss << fieldName << "[" << i << "]";
                        error = array_expected (json_name, ss.str ());
                        return false;
                    }

                    for (Json::UInt j = 0; value[i].isValidIndex (j); ++j)
                    {
                        std::stringstream ss;
                        ss << fieldName << "[" << i << "][" << j << "]";
                        std::string const element_name (
                            json_name + "." + ss.str());

                        // each element in this path has some combination of account,
                        // currency, or issuer

                        Json::Value pathEl = value[i][j];

                        if (!pathEl.isObject ())
                        {
                            error = not_an_object (element_name);
                            return false;
                        }

                        const Json::Value& account  = pathEl["account"];
                        const Json::Value& currency = pathEl["currency"];
                        const Json::Value& issuer   = pathEl["issuer"];
                        bool hasCurrency            = false;
                        uint160 uAccount, uCurrency, uIssuer;

                        if (! account.isNull ())
                        {
                            // human account id
                            if (! account.isString ())
                            {
                                error = string_expected (element_name, "account");
                                return false;
                            }

                            std::string const strValue (account.asString ());

                            if (value.size () == 40) // 160-bit hex account value
                                uAccount.SetHex (strValue);

                            {
                                RippleAddress a;

                                if (! a.setAccountID (strValue))
                                {
                                    error = invalid_data (element_name, "account");
                                    return false;
                                }

                                uAccount = a.getAccountID ();
                            }
                        }

                        if (!currency.isNull ())
                        {
                            // human currency
                            if (!currency.isString ())
                            {
                                error = string_expected (element_name, "currency");
                                return false;
                            }

                            hasCurrency = true;

                            if (currency.asString ().size () == 40)
                            {
                                uCurrency.SetHex (currency.asString ());
                            }
                            else if (!STAmount::currencyFromString (
                                uCurrency, currency.asString ()))
                            {
                                error = invalid_data (element_name, "currency");
                                return false;
                            }
                        }

                        if (!issuer.isNull ())
                        {
                            // human account id
                            if (!issuer.isString ())
                            {
                                error = string_expected (element_name, "issuer");
                                return false;
                            }

                            if (issuer.asString ().size () == 40)
                            {
                                uIssuer.SetHex (issuer.asString ());
                            }
                            else
                            {
                                RippleAddress a;

                                if (!a.setAccountID (issuer.asString ()))
                                {
                                    error = invalid_data (element_name, "issuer");
                                    return false;
                                }

                                uIssuer = a.getAccountID ();
                            }
                        }

                        p.addElement (STPathElement (uAccount, uCurrency, uIssuer, hasCurrency));
                    }

                    tail->addPath (p);
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_ACCOUNT:
        {
            if (! value.isString ())
            {
                error = bad_type (json_name, fieldName);
                return false;
            }

            std::string strValue = value.asString ();

            try
            {
                if (value.size () == 40) // 160-bit hex account value
                {
                    uint160 v;
                    v.SetHex (strValue);
                    data.push_back (new STAccount (field, v));
                }
                else
                {
                    // ripple address
                    RippleAddress a;

                    if (!a.setAccountID (strValue))
                    {
                        error = invalid_data (json_name, fieldName);
                        return false;
                    }

                    data.push_back (new STAccount (field, a.getAccountID ()));
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }
        }
        break;

        case STI_OBJECT:
        case STI_TRANSACTION:
        case STI_LEDGERENTRY:
        case STI_VALIDATION:
            if (! value.isObject ())
            {
                error = not_an_object (json_name, fieldName);
                return false;
            }

            if (depth > 64)
            {
                error = too_deep (json_name, fieldName);
                return false;
            }

            try
            {
                std::unique_ptr <STObject> sub_object_;
                bool const success (parse (json_name + "." + fieldName,
                    value, field, depth + 1, sub_object_));
                if (! success)
                    return false;
                data.push_back (sub_object_.release ());
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        case STI_ARRAY:
            if (! value.isArray ())
            {
                error = array_expected (json_name, fieldName);
                return false;
            }

            try
            {
                data.push_back (new STArray (field));
                STArray* tail = dynamic_cast<STArray*> (&data.back ());
                assert (tail);

                for (Json::UInt i = 0; value.isValidIndex (i); ++i)
                {
                    bool const isObject (value[i].isObject());
                    bool const singleKey (isObject
                        ? value [i].size() == 1
                        : true);

                    if (!isObject || !singleKey)
                    {
                        std::stringstream ss;
                        ss << json_name << "." << fieldName << "[" << i << "]";
                        error = singleton_expected (ss.str ());
                        return false;
                    }

                    // TODO: There doesn't seem to be a nice way to get just the
                    // first/only key in an object without copying all keys into
                    // a vector
                    std::string const objectName (value[i].getMemberNames()[0]);;
                    SField::ref       nameField (SField::getField(objectName));

                    if (nameField == sfInvalid)
                    {
                        error = unknown_field (json_name, objectName);
                        return false;
                    }

                    Json::Value const objectFields (value[i][objectName]);

                    std::unique_ptr <STObject> sub_object_;
                    {
                        std::stringstream ss;
                        ss << json_name << "." << fieldName <<
                            "[" << i << "]." << objectName;
                        bool const success (parse (ss.str (), objectFields,
                            nameField, depth + 1, sub_object_));
                        if (! success)
                            return false;
                    }
                    tail->push_back (*sub_object_);
                }
            }
            catch (...)
            {
                error = invalid_data (json_name, fieldName);
                return false;
            }

            break;

        default:
            error = bad_type (json_name, fieldName);
            return false;
        }
    }

    sub_object.reset (new STObject (*name, data));
    return true;
}
Ejemplo n.º 7
0
TER PaymentTransactor::doApply ()
{
    // Ripple if source or destination is non-native or if there are paths.
    std::uint32_t const uTxFlags = mTxn.getFlags ();
	//bool const bPartialPayment = is_bit_set(uTxFlags, tfPartialPayment);
    bool const bLimitQuality = is_bit_set (uTxFlags, tfLimitQuality);
    bool const bNoRippleDirect = is_bit_set (uTxFlags, tfNoRippleDirect);
    bool const bPaths = mTxn.isFieldPresent (sfPaths);
    bool const bMax = mTxn.isFieldPresent (sfSendMax);
    uint160 const uDstAccountID = mTxn.getFieldAccount160 (sfDestination);
    STAmount const saDstAmount = mTxn.getFieldAmount (sfAmount);
    STAmount const saMaxAmount = bMax
                                   ? mTxn.getFieldAmount (sfSendMax)
                                   : saDstAmount.isNative ()
                                   ? saDstAmount
                                   : STAmount (saDstAmount.getCurrency (),
                                        mTxnAccountID,
                                        saDstAmount.getMantissa (),
                                        saDstAmount.getExponent (),
                                        saDstAmount < zero);
    uint160 const uSrcCurrency = saMaxAmount.getCurrency ();
    uint160 const uDstCurrency = saDstAmount.getCurrency ();
    bool const bSTRDirect = uSrcCurrency.isZero () && uDstCurrency.isZero ();

	
    m_journal.trace <<
        "saMaxAmount=" << saMaxAmount.getFullText () <<
        " saDstAmount=" << saDstAmount.getFullText ();

    if (!saDstAmount.isLegalNet () || !saMaxAmount.isLegalNet ())
        return temBAD_AMOUNT;

    if (uTxFlags & tfPaymentMask)
    {
        m_journal.trace <<
            "Malformed transaction: Invalid flags set.";

        return temINVALID_FLAG;
    }
    else if (!uDstAccountID)
    {
        m_journal.trace <<
            "Malformed transaction: Payment destination account not specified.";

        return temDST_NEEDED;
    }
    else if (bMax && saMaxAmount <= zero)
    {
        m_journal.trace <<
            "Malformed transaction: bad max amount: " << saMaxAmount.getFullText ();

        return temBAD_AMOUNT;
    }
    else if (saDstAmount <= zero)
    {
        m_journal.trace <<
            "Malformed transaction: bad dst amount: " << saDstAmount.getFullText ();

        return temBAD_AMOUNT;
    }
    else if (CURRENCY_BAD == uSrcCurrency || CURRENCY_BAD == uDstCurrency)
    {
        m_journal.trace <<
            "Malformed transaction: Bad currency.";

        return temBAD_CURRENCY;
    }
    else if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths)
    {
        m_journal.trace <<
            "Malformed transaction: Redundant transaction:" <<
            " src=" << to_string (mTxnAccountID) <<
            " dst=" << to_string (uDstAccountID) <<
            " src_cur=" << to_string (uSrcCurrency) <<
            " dst_cur=" << to_string (uDstCurrency);

        return temREDUNDANT;
    }
    else if (bMax && saMaxAmount == saDstAmount && saMaxAmount.getCurrency () == saDstAmount.getCurrency ())
    {
        m_journal.trace <<
            "Malformed transaction: Redundant SendMax.";

        return temREDUNDANT_SEND_MAX;
    }
    else if (bSTRDirect && bMax)
    {
        m_journal.trace <<
            "Malformed transaction: SendMax specified for STR to STR.";

        return temBAD_SEND_STR_MAX;
    }
    else if (bSTRDirect && bPaths)
    {
        m_journal.trace <<
            "Malformed transaction: Paths specified for STR to STR.";

        return temBAD_SEND_STR_PATHS;
    }
    else if (bSTRDirect && bLimitQuality)
    {
        m_journal.trace <<
            "Malformed transaction: Limit quality specified for STR to STR.";

        return temBAD_SEND_STR_LIMIT;
    }
    else if (bSTRDirect && bNoRippleDirect)
    {
        m_journal.trace <<
            "Malformed transaction: No ripple direct specified for STR to STR.";

        return temBAD_SEND_STR_NO_DIRECT;
    }

    SLE::pointer sleDst (mEngine->entryCache (
        ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID)));

    if (!sleDst)
    {
        // Destination account does not exist.

        if (!saDstAmount.isNative ())
        {
            m_journal.trace <<
                "Delay transaction: Destination account does not exist.";

            // Another transaction could create the account and then this transaction would succeed.
            return tecNO_DST;
        }
        
        // Note: Reserve is not scaled by load.
        else if (saDstAmount.getNValue () < mEngine->getLedger ()->getReserve (0))
        {
            m_journal.trace <<
                "Delay transaction: Destination account does not exist. " <<
                "Insufficient payment to create account.";

            // Another transaction could create the account and then this
            // transaction would succeed.
            return tecNO_DST_INSUF_STR;
        }

        // Create the account.
        sleDst = mEngine->entryCreate (
            ltACCOUNT_ROOT, Ledger::getAccountRootIndex (uDstAccountID));

        sleDst->setFieldAccount (sfAccount, uDstAccountID);
        sleDst->setFieldU32 (sfSequence, 1);
    }
    else if ((sleDst->getFlags () & lsfRequireDestTag) && !mTxn.isFieldPresent (sfDestinationTag))
    {
        m_journal.trace <<
            "Malformed transaction: DestinationTag required.";

        return tefDST_TAG_NEEDED;
    }
    else
    {
        mEngine->entryModify (sleDst);
    }

    TER terResult;
    // XXX Should bMax be sufficient to imply ripple?
    bool const bRipple = bPaths || bMax || !saDstAmount.isNative ();

    if (bRipple)
    {
        // Ripple payment

        STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths);
        std::vector<PathState::pointer> vpsExpanded;
        STAmount saMaxAmountAct;
        STAmount saDstAmountAct;

        try
        {
            bool const openLedger = is_bit_set (mParams, tapOPEN_LEDGER);
			bool tooManyPaths = false;
			if (spsPaths.size() > MAX_NUM_PATHS) tooManyPaths = true;
			else
			{
				for (auto const& path : spsPaths)
				{
					if (path.size() > MAX_PATH_SIZE)
					{
						tooManyPaths = true;
						break;
					}
				}
			}

			
			terResult = openLedger && tooManyPaths
				? telBAD_PATH_COUNT // Too many paths for proposed ledger.
				: RippleCalc::rippleCalc(
				mEngine->view(),
				saMaxAmountAct,
				saDstAmountAct,
				vpsExpanded,
				saMaxAmount,
				saDstAmount,
				uDstAccountID,
				mTxnAccountID,
				spsPaths,
				false,
				bLimitQuality,
				bNoRippleDirect, // Always compute for finalizing ledger.
				false, // Not standalone, delete unfundeds.
				is_bit_set(mParams, tapOPEN_LEDGER));

			if (isTerRetry(terResult))
				terResult = tecPATH_DRY;

			if ((tesSUCCESS == terResult) && (saDstAmountAct != saDstAmount))
				mEngine->view().setDeliveredAmount(saDstAmountAct);
			
        }
        catch (std::exception const& e)
        {
            m_journal.trace <<
                "Caught throw: " << e.what ();

            terResult   = tefEXCEPTION;
        }
    }
    else
    {
        // Direct STR payment.

        std::uint32_t const uOwnerCount (mTxnAccount->getFieldU32 (sfOwnerCount));
        std::uint64_t const uReserve (mEngine->getLedger ()->getReserve (uOwnerCount));

        // Make sure have enough reserve to send. Allow final spend to use reserve for fee.
        if (mPriorBalance < saDstAmount + std::max(uReserve, mTxn.getTransactionFee ().getNValue ()))
        {
            // Vote no. However, transaction might succeed, if applied in a different order.
            m_journal.trace << "Delay transaction: Insufficient funds: " <<
                " " << mPriorBalance.getText () <<
                " / " << (saDstAmount + uReserve).getText () <<
                " (" << uReserve << ")";

            terResult   = tecUNFUNDED_PAYMENT;
        }
        else
        {
            mTxnAccount->setFieldAmount (sfBalance, mSourceBalance - saDstAmount);
            sleDst->setFieldAmount (sfBalance, sleDst->getFieldAmount (sfBalance) + saDstAmount);


            terResult = tesSUCCESS;
        }
    }

    std::string strToken;
    std::string strHuman;

    if (transResultInfo (terResult, strToken, strHuman))
    {
        m_journal.trace <<
            strToken << ": " << strHuman;
    }
    else
    {
        assert (false);
    }

    return terResult;
}
Ejemplo n.º 8
0
static Json::Value signPayment(
    Json::Value const& params,
    Json::Value& tx_json,
    RippleAddress const& raSrcAddressID,
    RPCDetail::LedgerFacade& ledgerFacade,
    Role role)
{
    RippleAddress dstAccountID;

    if (!tx_json.isMember ("Amount"))
        return RPC::missing_field_error ("tx_json.Amount");

    STAmount amount;

    if (! amountFromJsonNoThrow (amount, tx_json ["Amount"]))
        return RPC::invalid_field_error ("tx_json.Amount");

    if (!tx_json.isMember ("Destination"))
        return RPC::missing_field_error ("tx_json.Destination");

    if (!dstAccountID.setAccountID (tx_json["Destination"].asString ()))
        return RPC::invalid_field_error ("tx_json.Destination");

    if (tx_json.isMember ("Paths") && params.isMember ("build_path"))
        return RPC::make_error (rpcINVALID_PARAMS,
            "Cannot specify both 'tx_json.Paths' and 'build_path'");

    if (!tx_json.isMember ("Paths")
        && tx_json.isMember ("Amount")
        && params.isMember ("build_path"))
    {
        // Need a ripple path.
        Currency uSrcCurrencyID;
        Account uSrcIssuerID;

        STAmount    saSendMax;

        if (tx_json.isMember ("SendMax"))
        {
            if (! amountFromJsonNoThrow (saSendMax, tx_json ["SendMax"]))
                return RPC::invalid_field_error ("tx_json.SendMax");
        }
        else
        {
            // If no SendMax, default to Amount with sender as issuer.
            saSendMax = amount;
            saSendMax.setIssuer (raSrcAddressID.getAccountID ());
        }

        if (saSendMax.isNative () && amount.isNative ())
            return RPC::make_error (rpcINVALID_PARAMS,
                "Cannot build XRP to XRP paths.");

        {
            LegacyPathFind lpf (role == Role::ADMIN);
            if (!lpf.isOk ())
                return rpcError (rpcTOO_BUSY);

            STPathSet spsPaths;
            STPath fullLiquidityPath;
            bool valid = ledgerFacade.findPathsForOneIssuer (
                dstAccountID,
                saSendMax.issue (),
                amount,
                getConfig ().PATH_SEARCH_OLD,
                4,  // iMaxPaths
                spsPaths,
                fullLiquidityPath);


            if (!valid)
            {
                WriteLog (lsDEBUG, RPCHandler)
                        << "transactionSign: build_path: No paths found.";
                return rpcError (rpcNO_PATH);
            }
            WriteLog (lsDEBUG, RPCHandler)
                    << "transactionSign: build_path: "
                    << spsPaths.getJson (0);

            if (!spsPaths.empty ())
                tx_json["Paths"] = spsPaths.getJson (0);
        }
    }
    return Json::Value();
}
Ejemplo n.º 9
0
std::pair<bool, Json::Value>
ripplePathFind(RippleLineCache::pointer const& cache, 
  RippleAddress const& raSrc, RippleAddress const& raDst,
    STAmount const& saDstAmount, Ledger::pointer const& lpLedger, 
      Json::Value const& jvSrcCurrencies, 
        boost::optional<Json::Value> const& contextPaths, int const& level)
{
    FindPaths fp(
        cache,
        raSrc.getAccountID(),
        raDst.getAccountID(),
        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;
        Account 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.getAccountID();

        // 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.get() == nullptr)
                return std::make_pair(false, paths.error);
            else
            {
                spsComputed = paths.object.get()->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() :
                Account(raSrc.getAccountID())
                : uSrcIssuerID;            // Use specifed issuer.

            STAmount saMaxAmount({ uSrcCurrencyID, issuer }, 1);
            saMaxAmount.negate();

            LedgerEntrySet lesSandbox(lpLedger, tapNONE);

            auto rc = path::RippleCalc::rippleCalculate(
                lesSandbox,
                saMaxAmount,            // --> Amount to send is unlimited
                //     to get an estimate.
                saDstAmount,            // --> Amount to deliver.
                raDst.getAccountID(),  // --> Account to deliver to.
                raSrc.getAccountID(),  // --> 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);
                lesSandbox.clear();
                rc = path::RippleCalc::rippleCalculate(
                    lesSandbox,
                    saMaxAmount,            // --> Amount to send is unlimited
                    //     to get an estimate.
                    saDstAmount,            // --> Amount to deliver.
                    raDst.getAccountID(),  // --> Account to deliver to.
                    raSrc.getAccountID(),  // --> 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);
}
Ejemplo n.º 10
0
static Json::Value signPayment(
    Json::Value const& params,
    Json::Value& tx_json,
    RippleAddress const& raSrcAddressID,
    Ledger::pointer lSnapshot,
    int role)
{
    RippleAddress dstAccountID;

    if (!tx_json.isMember ("Amount"))
        return RPC::missing_field_error ("tx_json.Amount");

    STAmount amount;

    if (!amount.bSetJson (tx_json ["Amount"]))
        return RPC::invalid_field_error ("tx_json.Amount");

    if (!tx_json.isMember ("Destination"))
        return RPC::missing_field_error ("tx_json.Destination");

    if (!dstAccountID.setAccountID (tx_json["Destination"].asString ()))
        return RPC::invalid_field_error ("tx_json.Destination");

    if (tx_json.isMember ("Paths") && params.isMember ("build_path"))
        return RPC::make_error (rpcINVALID_PARAMS,
            "Cannot specify both 'tx_json.Paths' and 'tx_json.build_path'");

    if (!tx_json.isMember ("Paths")
        && tx_json.isMember ("Amount")
        && params.isMember ("build_path"))
    {
        // Need a ripple path.
        STPathSet   spsPaths;
        uint160     uSrcCurrencyID;
        uint160     uSrcIssuerID;

        STAmount    saSendMax;

        if (tx_json.isMember ("SendMax"))
        {
            if (!saSendMax.bSetJson (tx_json ["SendMax"]))
                return RPC::invalid_field_error ("tx_json.SendMax");
        }
        else
        {
            // If no SendMax, default to Amount with sender as issuer.
            saSendMax = amount;
            saSendMax.setIssuer (raSrcAddressID.getAccountID ());
        }

        if (saSendMax.isNative () && amount.isNative ())
            return RPC::make_error (rpcINVALID_PARAMS,
                "Cannot build STR to STR paths.");

        {
            LegacyPathFind lpf (role == Config::ADMIN);
            if (!lpf.isOk ())
                return rpcError (rpcTOO_BUSY);

            bool bValid;
            auto cache = boost::make_shared<RippleLineCache> (lSnapshot);
            Pathfinder pf (cache, raSrcAddressID, dstAccountID,
                           saSendMax.getCurrency (), saSendMax.getIssuer (),
                           amount, bValid);

            STPath extraPath;
            if (!bValid || !pf.findPaths (getConfig ().PATH_SEARCH_OLD, 4, spsPaths, extraPath))
            {
                WriteLog (lsDEBUG, RPCHandler)
                        << "transactionSign: build_path: No paths found.";
                return rpcError (rpcNO_PATH);
            }
            WriteLog (lsDEBUG, RPCHandler)
                    << "transactionSign: build_path: "
                    << spsPaths.getJson (0);

            if (!spsPaths.isEmpty ())
                tx_json["Paths"] = spsPaths.getJson (0);
        }
    }
    return Json::Value();
}