Пример #1
0
TER
SetTrust::preflight (PreflightContext const& ctx)
{
    auto const ret = preflight1 (ctx);
    if (!isTesSuccess (ret))
        return ret;

    auto& tx = ctx.tx;
    auto& j = ctx.j;

    std::uint32_t const uTxFlags = tx.getFlags ();

    if (uTxFlags & tfTrustSetMask)
    {
        JLOG(j.trace) <<
            "Malformed transaction: Invalid flags set.";
        return temINVALID_FLAG;
    }

    STAmount const saLimitAmount (tx.getFieldAmount (sfLimitAmount));

    if (!isLegalNet (saLimitAmount))
        return temBAD_AMOUNT;

    if (saLimitAmount.native ())
    {
        JLOG(j.trace) <<
            "Malformed transaction: specifies native limit " <<
            saLimitAmount.getFullText ();
        return temBAD_LIMIT;
    }

    if (badCurrency() == saLimitAmount.getCurrency ())
    {
        JLOG(j.trace) <<
            "Malformed transaction: specifies XRP as IOU";
        return temBAD_CURRENCY;
    }

    if (saLimitAmount < zero)
    {
        JLOG(j.trace) <<
            "Malformed transaction: Negative credit limit.";
        return temBAD_LIMIT;
    }

    // Check if destination makes sense.
    auto const& issuer = saLimitAmount.getIssuer ();

    if (!issuer || issuer == noAccount())
    {
        JLOG(j.trace) <<
            "Malformed transaction: no destination account.";
        return temDST_NEEDED;
    }

    return preflight2 (ctx);
}
Пример #2
0
    TER preCheck () override
    {
        std::uint32_t const uTxFlags = mTxn.getFlags ();

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

        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);

        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);

        auto const& uSrcCurrency = maxSourceAmount.getCurrency ();
        auto const& uDstCurrency = saDstAmount.getCurrency ();

        // isZero() is XDV.  FIX!
        bool const bXDVDirect = uSrcCurrency.isZero () && uDstCurrency.isZero ();

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

        AccountID const uDstAccountID (mTxn.getFieldAccount160 (sfDestination));

        if (!uDstAccountID)
        {
            m_journal.trace << "Malformed transaction: " <<
                "Payment destination account not specified.";
            return temDST_NEEDED;
        }
        if (bMax && maxSourceAmount <= zero)
        {
            m_journal.trace << "Malformed transaction: " <<
                "bad max amount: " << maxSourceAmount.getFullText ();
            return temBAD_AMOUNT;
        }
        if (saDstAmount <= zero)
        {
            m_journal.trace << "Malformed transaction: "<<
                "bad dst amount: " << saDstAmount.getFullText ();
            return temBAD_AMOUNT;
        }
        if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency)
        {
            m_journal.trace <<"Malformed transaction: " <<
                "Bad currency.";
            return temBAD_CURRENCY;
        }
        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 payment from " << to_string (mTxnAccountID) <<
                " to self without path for " << to_string (uDstCurrency);
            return temREDUNDANT;
        }
        if (bXDVDirect && bMax)
        {
            // Consistent but redundant transaction.
            m_journal.trace << "Malformed transaction: " <<
                "SendMax specified for XDV to XDV.";
            return temBAD_SEND_XDV_MAX;
        }
        if (bXDVDirect && bPaths)
        {
            // XDV is sent without paths.
            m_journal.trace << "Malformed transaction: " <<
                "Paths specified for XDV to XDV.";
            return temBAD_SEND_XDV_PATHS;
        }
        if (bXDVDirect && partialPaymentAllowed)
        {
            // Consistent but redundant transaction.
            m_journal.trace << "Malformed transaction: " <<
                "Partial payment specified for XDV to XDV.";
            return temBAD_SEND_XDV_PARTIAL;
        }
        if (bXDVDirect && limitQuality)
        {
            // Consistent but redundant transaction.
            m_journal.trace << "Malformed transaction: " <<
                "Limit quality specified for XDV to XDV.";
            return temBAD_SEND_XDV_LIMIT;
        }
        if (bXDVDirect && !defaultPathsAllowed)
        {
            // Consistent but redundant transaction.
            m_journal.trace << "Malformed transaction: " <<
                "No divvy direct specified for XDV to XDV.";
            return temBAD_SEND_XDV_NO_DIRECT;
        }

        return Transactor::preCheck ();
    }
Пример #3
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;
    }
Пример #4
0
    TER doApply () override
    {
        TER terResult = tesSUCCESS;

        STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount));
        bool const bQualityIn (mTxn.isFieldPresent (sfQualityIn));
        bool const bQualityOut (mTxn.isFieldPresent (sfQualityOut));

        Currency const currency (saLimitAmount.getCurrency ());
        Account uDstAccountID (saLimitAmount.getIssuer ());

        // true, iff current is high account.
        bool const bHigh = mTxnAccountID > uDstAccountID;

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

        // The reserve required to create the line. Note that we allow up to
        // two trust lines without requiring a reserve because being able to
        // exchange currencies is a powerful Ripple feature.
        //
        // This is also a security feature: if you're a gateway and you want to
        // be able to let someone use your services, you would otherwise have to
        // give them enough XRP to cover the incremental reserve for their trust
        // line. If they had no intention of using your services, they could use
        // the XRP for their own purposes. So we make it possible for gateways
        // to fund accounts in a way where there's no incentive to trick them
        // into creating an account you have no intention of using.

        std::uint64_t const uReserveCreate = (uOwnerCount < 2)
            ? 0
            : mEngine->getLedger ()->getReserve (uOwnerCount + 1);

        std::uint32_t uQualityIn (bQualityIn ? mTxn.getFieldU32 (sfQualityIn) : 0);
        std::uint32_t uQualityOut (bQualityOut ? mTxn.getFieldU32 (sfQualityOut) : 0);

        if (!isLegalNet (saLimitAmount))
            return temBAD_AMOUNT;

        if (bQualityOut && QUALITY_ONE == uQualityOut)
            uQualityOut = 0;

        std::uint32_t const uTxFlags = mTxn.getFlags ();

        if (uTxFlags & tfTrustSetMask)
        {
            m_journal.trace <<
                "Malformed transaction: Invalid flags set.";
            return temINVALID_FLAG;
        }

        bool const bSetAuth = (uTxFlags & tfSetfAuth);
        bool const bSetNoRipple = (uTxFlags & tfSetNoRipple);
        bool const bClearNoRipple  = (uTxFlags & tfClearNoRipple);
        bool const bSetFreeze = (uTxFlags & tfSetFreeze);
        bool const bClearFreeze = (uTxFlags & tfClearFreeze);

        if (bSetAuth && !(mTxnAccount->getFieldU32 (sfFlags) & lsfRequireAuth))
        {
            m_journal.trace <<
                "Retry: Auth not required.";
            return tefNO_AUTH_REQUIRED;
        }

        if (saLimitAmount.isNative ())
        {
            m_journal.trace <<
                "Malformed transaction: Native credit limit: " <<
                saLimitAmount.getFullText ();
            return temBAD_LIMIT;
        }

        if (saLimitAmount < zero)
        {
            m_journal.trace <<
                "Malformed transaction: Negative credit limit.";
            return temBAD_LIMIT;
        }

        // Check if destination makes sense.
        if (!uDstAccountID || uDstAccountID == noAccount())
        {
            m_journal.trace <<
                "Malformed transaction: Destination account not specified.";

            return temDST_NEEDED;
        }

        if (mTxnAccountID == uDstAccountID)
        {
            SLE::pointer selDelete (
                mEngine->entryCache (ltRIPPLE_STATE,
                    Ledger::getRippleStateIndex (
                        mTxnAccountID, uDstAccountID, currency)));

            if (selDelete)
            {
                m_journal.warning <<
                    "Clearing redundant line.";

                return mEngine->view ().trustDelete (
                    selDelete, mTxnAccountID, uDstAccountID);
            }
            else
            {
                m_journal.trace <<
                    "Malformed transaction: Can not extend credit to self.";
                return temDST_IS_SRC;
            }
        }

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

        if (!sleDst)
        {
            m_journal.trace <<
                "Delay transaction: Destination account does not exist.";
            return tecNO_DST;
        }

        STAmount saLimitAllow = saLimitAmount;
        saLimitAllow.setIssuer (mTxnAccountID);

        SLE::pointer sleRippleState (mEngine->entryCache (ltRIPPLE_STATE,
            Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, currency)));

        if (sleRippleState)
        {
            STAmount        saLowBalance;
            STAmount        saLowLimit;
            STAmount        saHighBalance;
            STAmount        saHighLimit;
            std::uint32_t   uLowQualityIn;
            std::uint32_t   uLowQualityOut;
            std::uint32_t   uHighQualityIn;
            std::uint32_t   uHighQualityOut;
            auto const& uLowAccountID   = !bHigh ? mTxnAccountID : uDstAccountID;
            auto const& uHighAccountID  =  bHigh ? mTxnAccountID : uDstAccountID;
            SLE::ref        sleLowAccount   = !bHigh ? mTxnAccount : sleDst;
            SLE::ref        sleHighAccount  =  bHigh ? mTxnAccount : sleDst;

            //
            // Balances
            //

            saLowBalance    = sleRippleState->getFieldAmount (sfBalance);
            saHighBalance   = -saLowBalance;

            //
            // Limits
            //

            sleRippleState->setFieldAmount (!bHigh ? sfLowLimit : sfHighLimit, saLimitAllow);

            saLowLimit  = !bHigh ? saLimitAllow : sleRippleState->getFieldAmount (sfLowLimit);
            saHighLimit =  bHigh ? saLimitAllow : sleRippleState->getFieldAmount (sfHighLimit);

            //
            // Quality in
            //

            if (!bQualityIn)
            {
                // Not setting. Just get it.

                uLowQualityIn   = sleRippleState->getFieldU32 (sfLowQualityIn);
                uHighQualityIn  = sleRippleState->getFieldU32 (sfHighQualityIn);
            }
            else if (uQualityIn)
            {
                // Setting.

                sleRippleState->setFieldU32 (!bHigh ? sfLowQualityIn : sfHighQualityIn, uQualityIn);

                uLowQualityIn   = !bHigh ? uQualityIn : sleRippleState->getFieldU32 (sfLowQualityIn);
                uHighQualityIn  =  bHigh ? uQualityIn : sleRippleState->getFieldU32 (sfHighQualityIn);
            }
            else
            {
                // Clearing.

                sleRippleState->makeFieldAbsent (!bHigh ? sfLowQualityIn : sfHighQualityIn);

                uLowQualityIn   = !bHigh ? 0 : sleRippleState->getFieldU32 (sfLowQualityIn);
                uHighQualityIn  =  bHigh ? 0 : sleRippleState->getFieldU32 (sfHighQualityIn);
            }

            if (QUALITY_ONE == uLowQualityIn)   uLowQualityIn   = 0;

            if (QUALITY_ONE == uHighQualityIn)  uHighQualityIn  = 0;

            //
            // Quality out
            //

            if (!bQualityOut)
            {
                // Not setting. Just get it.

                uLowQualityOut  = sleRippleState->getFieldU32 (sfLowQualityOut);
                uHighQualityOut = sleRippleState->getFieldU32 (sfHighQualityOut);
            }
            else if (uQualityOut)
            {
                // Setting.

                sleRippleState->setFieldU32 (!bHigh ? sfLowQualityOut : sfHighQualityOut, uQualityOut);

                uLowQualityOut  = !bHigh ? uQualityOut : sleRippleState->getFieldU32 (sfLowQualityOut);
                uHighQualityOut =  bHigh ? uQualityOut : sleRippleState->getFieldU32 (sfHighQualityOut);
            }
            else
            {
                // Clearing.

                sleRippleState->makeFieldAbsent (!bHigh ? sfLowQualityOut : sfHighQualityOut);

                uLowQualityOut  = !bHigh ? 0 : sleRippleState->getFieldU32 (sfLowQualityOut);
                uHighQualityOut =  bHigh ? 0 : sleRippleState->getFieldU32 (sfHighQualityOut);
            }

            std::uint32_t const uFlagsIn (sleRippleState->getFieldU32 (sfFlags));
            std::uint32_t uFlagsOut (uFlagsIn);

            if (bSetNoRipple && !bClearNoRipple && (bHigh ? saHighBalance : saLowBalance) >= zero)
            {
                uFlagsOut |= (bHigh ? lsfHighNoRipple : lsfLowNoRipple);
            }
            else if (bClearNoRipple && !bSetNoRipple)
            {
                uFlagsOut &= ~(bHigh ? lsfHighNoRipple : lsfLowNoRipple);
            }

            if (bSetFreeze && !bClearFreeze && !mTxnAccount->isFlag  (lsfNoFreeze))
            {
                uFlagsOut           |= (bHigh ? lsfHighFreeze : lsfLowFreeze);
            }
            else if (bClearFreeze && !bSetFreeze)
            {
                uFlagsOut           &= ~(bHigh ? lsfHighFreeze : lsfLowFreeze);
            }

            if (QUALITY_ONE == uLowQualityOut)  uLowQualityOut  = 0;

            if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0;


            bool const  bLowReserveSet      = uLowQualityIn || uLowQualityOut ||
                                              (uFlagsOut & lsfLowNoRipple) ||
                                              (uFlagsOut & lsfLowFreeze) ||
                                              saLowLimit || saLowBalance > zero;
            bool const  bLowReserveClear    = !bLowReserveSet;

            bool const  bHighReserveSet     = uHighQualityIn || uHighQualityOut ||
                                              (uFlagsOut & lsfHighNoRipple) ||
                                              (uFlagsOut & lsfHighFreeze) ||
                                              saHighLimit || saHighBalance > zero;
            bool const  bHighReserveClear   = !bHighReserveSet;

            bool const  bDefault            = bLowReserveClear && bHighReserveClear;

            bool const  bLowReserved = (uFlagsIn & lsfLowReserve);
            bool const  bHighReserved = (uFlagsIn & lsfHighReserve);

            bool        bReserveIncrease    = false;

            if (bSetAuth)
            {
                uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth);
            }

            if (bLowReserveSet && !bLowReserved)
            {
                // Set reserve for low account.
                mEngine->view ().incrementOwnerCount (sleLowAccount);
                uFlagsOut |= lsfLowReserve;

                if (!bHigh)
                    bReserveIncrease = true;
            }

            if (bLowReserveClear && bLowReserved)
            {
                // Clear reserve for low account.
                mEngine->view ().decrementOwnerCount (sleLowAccount);
                uFlagsOut &= ~lsfLowReserve;
            }

            if (bHighReserveSet && !bHighReserved)
            {
                // Set reserve for high account.
                mEngine->view ().incrementOwnerCount (sleHighAccount);
                uFlagsOut |= lsfHighReserve;

                if (bHigh)
                    bReserveIncrease    = true;
            }

            if (bHighReserveClear && bHighReserved)
            {
                // Clear reserve for high account.
                mEngine->view ().decrementOwnerCount (sleHighAccount);
                uFlagsOut &= ~lsfHighReserve;
            }

            if (uFlagsIn != uFlagsOut)
                sleRippleState->setFieldU32 (sfFlags, uFlagsOut);

            if (bDefault || badCurrency() == currency)
            {
                // Delete.

                terResult = mEngine->view ().trustDelete (sleRippleState, uLowAccountID, uHighAccountID);
            }
            else if (bReserveIncrease
                     && mPriorBalance.getNValue () < uReserveCreate) // Reserve is not scaled by load.
            {
                m_journal.trace <<
                    "Delay transaction: Insufficent reserve to add trust line.";

                // Another transaction could provide XRP to the account and then
                // this transaction would succeed.
                terResult = tecINSUF_RESERVE_LINE;
            }
            else
            {
                mEngine->entryModify (sleRippleState);

                m_journal.trace << "Modify ripple line";
            }
        }
        // Line does not exist.
        else if (!saLimitAmount                       // Setting default limit.
                 && (!bQualityIn || !uQualityIn)      // Not setting quality in or setting default quality in.
                 && (!bQualityOut || !uQualityOut))   // Not setting quality out or setting default quality out.
        {
            m_journal.trace <<
                "Redundant: Setting non-existent ripple line to defaults.";
            return tecNO_LINE_REDUNDANT;
        }
        else if (mPriorBalance.getNValue () < uReserveCreate) // Reserve is not scaled by load.
        {
            m_journal.trace <<
                "Delay transaction: Line does not exist. Insufficent reserve to create line.";

            // Another transaction could create the account and then this transaction would succeed.
            terResult = tecNO_LINE_INSUF_RESERVE;
        }
        else if (badCurrency() == currency)
        {
            terResult = temBAD_CURRENCY;
        }
        else
        {
            // Zero balance in currency.
            STAmount saBalance ({currency, noAccount()});

            uint256 index (Ledger::getRippleStateIndex (
                mTxnAccountID, uDstAccountID, currency));

            m_journal.trace <<
                "doTrustSet: Creating ripple line: " <<
                to_string (index);

            // Create a new ripple line.
            terResult = mEngine->view ().trustCreate (
                              bHigh,
                              mTxnAccountID,
                              uDstAccountID,
                              index,
                              mTxnAccount,
                              bSetAuth,
                              bSetNoRipple && !bClearNoRipple,
                              bSetFreeze && !bClearFreeze,
                              saBalance,
                              saLimitAllow,       // Limit for who is being charged.
                              uQualityIn,
                              uQualityOut);
        }

        return terResult;
    }