Exemple #1
0
TER Transactor::payFee()
{
	STAmount saPaid = mTxn.getTransactionFee();

	// Only check fee is sufficient when the ledger is open.
	if (isSetBit(mParams, tapOPEN_LEDGER) && saPaid < mFeeDue)
	{
		cLog(lsINFO) << "applyTransaction: insufficient fee";

		return telINSUF_FEE_P;
	}

	if (saPaid.isNegative() || !saPaid.isNative())
		return temBAD_FEE;

	if (!saPaid) return tesSUCCESS;

	// Deduct the fee, so it's not available during the transaction.
	// Will only write the account back, if the transaction succeeds.
	if (mSourceBalance < saPaid)
	{
		cLog(lsINFO)
			<< boost::str(boost::format("applyTransaction: Delay: insufficient balance: balance=%s paid=%s")
			% mSourceBalance.getText()
			% saPaid.getText());

		return terINSUF_FEE_B;
	}

	mSourceBalance -= saPaid;
	mTxnAccount->setFieldAmount(sfBalance, mSourceBalance);

	return tesSUCCESS;
}
Exemple #2
0
// check stuff before you bother to lock the ledger
TER Transactor::preCheck()
{

	mTxnAccountID	= mTxn.getSourceAccount().getAccountID();
	if (!mTxnAccountID)
	{
		cLog(lsWARNING) << "applyTransaction: bad source id";

		return temBAD_SRC_ACCOUNT;
	}

	// Extract signing key
	// Transactions contain a signing key.  This allows us to trivially verify a transaction has at least been properly signed
	// without going to disk.  Each transaction also notes a source account id.  This is used to verify that the signing key is
	// associated with the account.
	// XXX This could be a lot cleaner to prevent unnecessary copying.
	mSigningPubKey	= RippleAddress::createAccountPublic(mTxn.getSigningPubKey());

	// Consistency: really signed.
	if ( !isSetBit(mParams, tapNO_CHECK_SIGN) && !mTxn.checkSign(mSigningPubKey))
	{
		cLog(lsWARNING) << "applyTransaction: Invalid transaction: bad signature";

		return temINVALID;
	}

	return tesSUCCESS;
}
Exemple #3
0
TER ChangeTransactor::preCheck ()
{
    mTxnAccountID   = mTxn.getSourceAccount ().getAccountID ();

    if (mTxnAccountID.isNonZero ())
    {
        WriteLog (lsWARNING, ChangeTransactor) << "applyTransaction: bad source id";

        return temBAD_SRC_ACCOUNT;
    }

    if (isSetBit (mParams, tapOPEN_LEDGER))
    {
        WriteLog (lsWARNING, ChangeTransactor) << "Change transaction against open ledger";
        return temINVALID;
    }

    return tesSUCCESS;
}
Exemple #4
0
// This is for debugging not end users. Output names can be changed without warning.
Json::Value PathState::Node::getJson () const
{
    Json::Value jvNode (Json::objectValue);
    Json::Value jvFlags (Json::arrayValue);

    jvNode["type"]  = uFlags;

    if (isSetBit (uFlags, STPathElement::typeAccount) || !!uAccountID)
        jvFlags.append (!!isSetBit (uFlags, STPathElement::typeAccount) == !!uAccountID ? "account" : "-account");

    if (isSetBit (uFlags, STPathElement::typeCurrency) || !!uCurrencyID)
        jvFlags.append (!!isSetBit (uFlags, STPathElement::typeCurrency) == !!uCurrencyID ? "currency" : "-currency");

    if (isSetBit (uFlags, STPathElement::typeIssuer) || !!uIssuerID)
        jvFlags.append (!!isSetBit (uFlags, STPathElement::typeIssuer) == !!uIssuerID ? "issuer" : "-issuer");

    jvNode["flags"] = jvFlags;

    if (!!uAccountID)
        jvNode["account"]   = RippleAddress::createHumanAccountID (uAccountID);

    if (!!uCurrencyID)
        jvNode["currency"]  = STAmount::createHumanCurrency (uCurrencyID);

    if (!!uIssuerID)
        jvNode["issuer"]    = RippleAddress::createHumanAccountID (uIssuerID);

    if (saRevRedeem)
        jvNode["rev_redeem"]    = saRevRedeem.getFullText ();

    if (saRevIssue)
        jvNode["rev_issue"]     = saRevIssue.getFullText ();

    if (saRevDeliver)
        jvNode["rev_deliver"]   = saRevDeliver.getFullText ();

    if (saFwdRedeem)
        jvNode["fwd_redeem"]    = saFwdRedeem.getFullText ();

    if (saFwdIssue)
        jvNode["fwd_issue"]     = saFwdIssue.getFullText ();

    if (saFwdDeliver)
        jvNode["fwd_deliver"]   = saFwdDeliver.getFullText ();

    return jvNode;
}
TER AccountSetTransactor::doApply ()
{
    WriteLog (lsINFO, AccountSetTransactor) << "AccountSet>";

    const uint32    uTxFlags    = mTxn.getFlags ();

    const uint32    uFlagsIn    = mTxnAccount->getFieldU32 (sfFlags);
    uint32          uFlagsOut   = uFlagsIn;

    const uint32    uSetFlag    = mTxn.getFieldU32 (sfSetFlag);
    const uint32    uClearFlag  = mTxn.getFieldU32 (sfClearFlag);

    // legacy AccountSet flags
    bool      bSetRequireDest   = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest);
    bool      bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest);
    bool      bSetRequireAuth   = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth);
    bool      bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth);
    bool      bSetDisallowXRP   = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP);
    bool      bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP);

    if (uTxFlags & tfAccountSetMask)
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Invalid flags set.";

        return temINVALID_FLAG;
    }

    //
    // RequireAuth
    //

    if (bSetRequireAuth && bClearRequireAuth)
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";

        return temINVALID_FLAG;
    }

    if (bSetRequireAuth && !isSetBit (uFlagsIn, lsfRequireAuth))
    {
        if (mTxnAccount->getFieldU32 (sfOwnerCount))
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Retry: OwnerCount not zero.";

            return isSetBit(mParams, tapRETRY) ? terOWNERS : tecOWNERS;
        }

        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set RequireAuth.";

        uFlagsOut   |= lsfRequireAuth;
    }

    if (bClearRequireAuth && isSetBit (uFlagsIn, lsfRequireAuth))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear RequireAuth.";

        uFlagsOut   &= ~lsfRequireAuth;
    }

    //
    // RequireDestTag
    //

    if (bSetRequireDest && bClearRequireDest)
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";

        return temINVALID_FLAG;
    }

    if (bSetRequireDest && !isSetBit (uFlagsIn, lsfRequireDestTag))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set lsfRequireDestTag.";

        uFlagsOut   |= lsfRequireDestTag;
    }

    if (bClearRequireDest && isSetBit (uFlagsIn, lsfRequireDestTag))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear lsfRequireDestTag.";

        uFlagsOut   &= ~lsfRequireDestTag;
    }

    //
    // DisallowXRP
    //

    if (bSetDisallowXRP && bClearDisallowXRP)
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";

        return temINVALID_FLAG;
    }

    if (bSetDisallowXRP && !isSetBit (uFlagsIn, lsfDisallowXRP))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set lsfDisallowXRP.";

        uFlagsOut   |= lsfDisallowXRP;
    }

    if (bClearDisallowXRP && isSetBit (uFlagsIn, lsfDisallowXRP))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear lsfDisallowXRP.";

        uFlagsOut   &= ~lsfDisallowXRP;
    }

    //
    // DisableMaster
    //

    if ((uSetFlag == asfDisableMaster) && (uClearFlag == asfDisableMaster))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Malformed transaction: Contradictory flags set.";

        return temINVALID_FLAG;
    }

    if ((uSetFlag == asfDisableMaster) && !isSetBit (uFlagsIn, lsfDisableMaster))
    {
        if (!mTxnAccount->isFieldPresent (sfRegularKey))
            return tecNO_REGULAR_KEY;

        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Set lsfDisableMaster.";

        uFlagsOut   |= lsfDisableMaster;
    }

    if ((uClearFlag == asfDisableMaster) && isSetBit (uFlagsIn, lsfDisableMaster))
    {
        WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: Clear lsfDisableMaster.";

        uFlagsOut   &= ~lsfDisableMaster;
    }

    //
    // EmailHash
    //

    if (mTxn.isFieldPresent (sfEmailHash))
    {
        uint128     uHash   = mTxn.getFieldH128 (sfEmailHash);

        if (!uHash)
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset email hash";

            mTxnAccount->makeFieldAbsent (sfEmailHash);
        }
        else
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set email hash";

            mTxnAccount->setFieldH128 (sfEmailHash, uHash);
        }
    }

    //
    // WalletLocator
    //

    if (mTxn.isFieldPresent (sfWalletLocator))
    {
        uint256     uHash   = mTxn.getFieldH256 (sfWalletLocator);

        if (!uHash)
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset wallet locator";

            mTxnAccount->makeFieldAbsent (sfEmailHash);
        }
        else
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set wallet locator";

            mTxnAccount->setFieldH256 (sfWalletLocator, uHash);
        }
    }

    //
    // MessageKey
    //

    if (mTxn.isFieldPresent (sfMessageKey))
    {
        Blob    vucPublic   = mTxn.getFieldVL (sfMessageKey);

        if (vucPublic.size () > PUBLIC_BYTES_MAX)
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: message key too long";

            return telBAD_PUBLIC_KEY;
        }
        else
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set message key";

            mTxnAccount->setFieldVL (sfMessageKey, vucPublic);
        }
    }

    //
    // Domain
    //

    if (mTxn.isFieldPresent (sfDomain))
    {
        Blob    vucDomain   = mTxn.getFieldVL (sfDomain);

        if (vucDomain.empty ())
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset domain";

            mTxnAccount->makeFieldAbsent (sfDomain);
        }
        else if (vucDomain.size () > DOMAIN_BYTES_MAX)
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: domain too long";

            return telBAD_DOMAIN;
        }
        else
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set domain";

            mTxnAccount->setFieldVL (sfDomain, vucDomain);
        }
    }

    //
    // TransferRate
    //

    if (mTxn.isFieldPresent (sfTransferRate))
    {
        uint32      uRate   = mTxn.getFieldU32 (sfTransferRate);

        if (!uRate || uRate == QUALITY_ONE)
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: unset transfer rate";

            mTxnAccount->makeFieldAbsent (sfTransferRate);
        }
        else if (uRate > QUALITY_ONE)
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: set transfer rate";

            mTxnAccount->setFieldU32 (sfTransferRate, uRate);
        }
        else
        {
            WriteLog (lsINFO, AccountSetTransactor) << "AccountSet: bad transfer rate";

            return temBAD_TRANSFER_RATE;
        }
    }

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

    WriteLog (lsINFO, AccountSetTransactor) << "AccountSet<";

    return tesSUCCESS;
}
Exemple #6
0
TER TrustSetTransactor::doApply ()
{
    TER         terResult       = tesSUCCESS;
    WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet>";

    const STAmount      saLimitAmount   = mTxn.getFieldAmount (sfLimitAmount);
    const bool          bQualityIn      = mTxn.isFieldPresent (sfQualityIn);
    const bool          bQualityOut     = mTxn.isFieldPresent (sfQualityOut);
    const uint160       uCurrencyID     = saLimitAmount.getCurrency ();
    uint160             uDstAccountID   = saLimitAmount.getIssuer ();
    const bool          bHigh           = mTxnAccountID > uDstAccountID;        // true, iff current is high account.

    uint32              uQualityIn      = bQualityIn ? mTxn.getFieldU32 (sfQualityIn) : 0;
    uint32              uQualityOut     = bQualityOut ? mTxn.getFieldU32 (sfQualityOut) : 0;

    if (!saLimitAmount.isLegalNet ())
        return temBAD_AMOUNT;

    if (bQualityIn && QUALITY_ONE == uQualityIn)
        uQualityIn  = 0;

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

    const uint32        uTxFlags        = mTxn.getFlags ();

    if (uTxFlags & tfTrustSetMask)
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Invalid flags set.";

        return temINVALID_FLAG;
    }

    const bool          bSetAuth        = isSetBit (uTxFlags, tfSetfAuth);
    const bool          bSetNoRipple    = isSetBit (uTxFlags, tfSetNoRipple);
    const bool          bClearNoRipple  = isSetBit (uTxFlags, tfClearNoRipple);

    if (bSetAuth && !isSetBit (mTxnAccount->getFieldU32 (sfFlags), lsfRequireAuth))
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Retry: Auth not required.";

        return tefNO_AUTH_REQUIRED;
    }

    if (saLimitAmount.isNative ())
    {
        WriteLog (lsINFO, TrustSetTransactor) << boost::str (boost::format ("doTrustSet: Malformed transaction: Native credit limit: %s")
                                              % saLimitAmount.getFullText ());

        return temBAD_LIMIT;
    }

    if (saLimitAmount.isNegative ())
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Negative credit limit.";

        return temBAD_LIMIT;
    }

    // Check if destination makes sense.
    if (!uDstAccountID || uDstAccountID == ACCOUNT_ONE)
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Destination account not specified.";

        return temDST_NEEDED;
    }

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

        if (selDelete)
        {
            WriteLog (lsWARNING, TrustSetTransactor) << "doTrustSet: Clearing redundant line.";

            return mEngine->getNodes ().trustDelete (selDelete, mTxnAccountID, uDstAccountID);
        }
        else
        {
            WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Malformed transaction: Can not extend credit to self.";

            return temDST_IS_SRC;
        }
    }

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

    if (!sleDst)
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Delay transaction: Destination account does not exist.";

        return tecNO_DST;
    }

    const uint32        uOwnerCount     = mTxnAccount->getFieldU32 (sfOwnerCount);
    // The reserve required to create the line.
    const uint64        uReserveCreate  = (uOwnerCount < 2) ? 0 : mEngine->getLedger ()->getReserve (uOwnerCount + 1);

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

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

    if (sleRippleState)
    {
        STAmount        saLowBalance;
        STAmount        saLowLimit;
        STAmount        saHighBalance;
        STAmount        saHighLimit;
        uint32          uLowQualityIn;
        uint32          uLowQualityOut;
        uint32          uHighQualityIn;
        uint32          uHighQualityOut;
        const uint160&  uLowAccountID   = !bHigh ? mTxnAccountID : uDstAccountID;
        const uint160&  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);
        }

        const uint32    uFlagsIn        = sleRippleState->getFieldU32 (sfFlags);
        uint32          uFlagsOut       = uFlagsIn;

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

        if (QUALITY_ONE == uLowQualityOut)  uLowQualityOut  = 0;

        if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0;


        const bool  bLowReserveSet      = uLowQualityIn || uLowQualityOut ||
                                          isSetBit (uFlagsOut, lsfLowNoRipple) ||
                                          !!saLowLimit || saLowBalance.isPositive ();
        const bool  bLowReserveClear    = !bLowReserveSet;

        const bool  bHighReserveSet     = uHighQualityIn || uHighQualityOut ||
                                          isSetBit (uFlagsOut, lsfHighNoRipple) ||
                                          !!saHighLimit || saHighBalance.isPositive ();
        const bool  bHighReserveClear   = !bHighReserveSet;

        const bool  bDefault            = bLowReserveClear && bHighReserveClear;

        const bool  bLowReserved        = isSetBit (uFlagsIn, lsfLowReserve);
        const bool  bHighReserved       = isSetBit (uFlagsIn, lsfHighReserve);

        bool        bReserveIncrease    = false;

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

        if (bLowReserveSet && !bLowReserved)
        {
            // Set reserve for low account.

            mEngine->getNodes ().ownerCountAdjust (uLowAccountID, 1, sleLowAccount);
            uFlagsOut           |= lsfLowReserve;

            if (!bHigh)
                bReserveIncrease    = true;
        }

        if (bLowReserveClear && bLowReserved)
        {
            // Clear reserve for low account.

            mEngine->getNodes ().ownerCountAdjust (uLowAccountID, -1, sleLowAccount);
            uFlagsOut   &= ~lsfLowReserve;
        }

        if (bHighReserveSet && !bHighReserved)
        {
            // Set reserve for high account.

            mEngine->getNodes ().ownerCountAdjust (uHighAccountID, 1, sleHighAccount);
            uFlagsOut   |= lsfHighReserve;

            if (bHigh)
                bReserveIncrease    = true;
        }

        if (bHighReserveClear && bHighReserved)
        {
            // Clear reserve for high account.

            mEngine->getNodes ().ownerCountAdjust (uHighAccountID, -1, sleHighAccount);
            uFlagsOut   &= ~lsfHighReserve;
        }

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

        if (bDefault || CURRENCY_BAD == uCurrencyID)
        {
            // Delete.

            terResult   = mEngine->getNodes ().trustDelete (sleRippleState, uLowAccountID, uHighAccountID);
        }
        else if (bReserveIncrease
                 && mPriorBalance.getNValue () < uReserveCreate) // Reserve is not scaled by load.
        {
            WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: 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);

            WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: 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.
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Redundant: Setting non-existent ripple line to defaults.";

        return tecNO_LINE_REDUNDANT;
    }
    else if (mPriorBalance.getNValue () < uReserveCreate)       // Reserve is not scaled by load.
    {
        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: 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 (CURRENCY_BAD == uCurrencyID)
    {
        terResult   = temBAD_CURRENCY;
    }
    else
    {
        STAmount    saBalance   = STAmount (uCurrencyID, ACCOUNT_ONE);  // Zero balance in currency.

        WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet: Creating ripple line: "
                                              << Ledger::getRippleStateIndex (mTxnAccountID, uDstAccountID, uCurrencyID).ToString ();

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

    WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet<";

    return terResult;
}
Exemple #7
0
void Transactor::calculateFee ()
{
    mFeeDue = STAmount (mEngine->getLedger ()->scaleFeeLoad (calculateBaseFee (), isSetBit (mParams, tapADMIN)));
}
Exemple #8
0
// Set to a canonical path.
// - Remove extra elements
// - Assumes path is expanded.
//
// We do canonicalization to:
// - Prevent waste in the ledger.
// - Allow longer paths to be specified than would otherwise be allowed.
//
// Optimization theory:
// - Can omit elements that the expansion routine derives.
// - Can pack some elements into other elements.
//
// Rules:
// - SendMax if not specified, defaults currency to send and if not sending XRP defaults issuer to sender.
// - All paths start with the sender account.
//   - Currency and issuer is from SendMax.
// - All paths end with the destination account.
//
// Optimization:
// - An XRP output implies an offer node or destination node is next.
// - A change in currency implies an offer node.
// - A change in issuer...
void PathState::setCanonical (
    const PathState&    psExpanded
)
{
    assert (false);
    saInAct     = psExpanded.saInAct;
    saOutAct    = psExpanded.saOutAct;

    const uint160   uMaxCurrencyID  = saInAct.getCurrency ();
    const uint160   uMaxIssuerID    = saInAct.getIssuer ();

    const uint160   uOutCurrencyID  = saOutAct.getCurrency ();
    const uint160   uOutIssuerID    = saOutAct.getIssuer ();

    unsigned int    uNode       = 0;

    unsigned int    uEnd        = psExpanded.vpnNodes.size ();  // The node, indexed by 0, not to include.

    uint160         uDstAccountID   = psExpanded.vpnNodes[uEnd].uAccountID; // FIXME: This can't be right

    uint160         uAccountID      = psExpanded.vpnNodes[0].uAccountID;
    uint160         uCurrencyID     = uMaxCurrencyID;
    uint160         uIssuerID       = uMaxIssuerID;

    // Node 0 is a composite of the sending account and saInAct.
    ++uNode;    // skip node 0

    // Last node is implied: Always skip last node
    --uEnd;     // skip last node

    // saInAct
    // - currency is always the same as vpnNodes[0].
#if 1

    if (uNode != uEnd && uMaxIssuerID != uAccountID)
    {
        // saInAct issuer is not the sender. This forces an implied node.
        // WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: in diff: uNode=%d uEnd=%d") % uNode % uEnd);

        // skip node 1

        uIssuerID   = psExpanded.vpnNodes[uNode].uIssuerID;

        ++uNode;
    }

#else

    if (uNode != uEnd)
    {
        // Have another node
        bool    bKeep   = false;

        if (uMaxIssuerID != uAccountID)
        {
        }

        if (uMaxCurrencyID)                         // Not sending XRP.
        {
            // Node 1 must be an account.

            if (uMaxIssuerID != uAccountID)
            {
                // Node 1 is required to specify issuer.

                bKeep   = true;
            }
            else
            {
                // Node 1 must be an account
            }
        }
        else
        {
            // Node 1 must be an order book.

            bKeep           = true;
        }

        if (bKeep)
        {
            uCurrencyID = psExpanded.vpnNodes[uNode].uCurrencyID;
            uIssuerID   = psExpanded.vpnNodes[uNode].uIssuerID;
            ++uNode;        // Keep it.
        }
    }

#endif

    if (uNode != uEnd && !!uOutCurrencyID && uOutIssuerID != uDstAccountID)
    {
        // WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out diff: uNode=%d uEnd=%d") % uNode % uEnd);
        // The next to last node is saOutAct if an issuer different from receiver is supplied.
        // The next to last node can be implied.

        --uEnd;
    }

    const Node&  pnEnd   = psExpanded.vpnNodes[uEnd];

    if (uNode != uEnd
            && !pnEnd.uAccountID && pnEnd.uCurrencyID == uOutCurrencyID && pnEnd.uIssuerID == uOutIssuerID)
    {
        // The current end node is an offer converting to saOutAct's currency and issuer and can be implied.
        // WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: out offer: uNode=%d uEnd=%d") % uNode % uEnd);

        --uEnd;
    }

    // Do not include uEnd.
    for (; uNode != uEnd; ++uNode)
    {
        // WriteLog (lsDEBUG, RippleCalc) << boost::str(boost::format("setCanonical: loop: uNode=%d uEnd=%d") % uNode % uEnd);
        const Node&  pnPrv   = psExpanded.vpnNodes[uNode - 1];
        const Node&  pnCur   = psExpanded.vpnNodes[uNode];
        const Node&  pnNxt   = psExpanded.vpnNodes[uNode + 1];

        const bool      bCurAccount     = isSetBit (pnCur.uFlags, STPathElement::typeAccount);

        bool            bSkip   = false;

        if (bCurAccount)
        {
            // Currently at an account.

            // Output is non-XRP and issuer is account.
            if (!!pnCur.uCurrencyID && pnCur.uIssuerID == pnCur.uAccountID)
            {
                // Account issues itself.
                // XXX Not good enough. Previous account must mention it.

                bSkip   = true;
            }
        }
        else
        {
            // Currently at an offer.
            const bool      bPrvAccount     = isSetBit (pnPrv.uFlags, STPathElement::typeAccount);
            const bool      bNxtAccount     = isSetBit (pnNxt.uFlags, STPathElement::typeAccount);

            if (bPrvAccount && bNxtAccount                  // Offer surrounded by accounts.
                    && pnPrv.uCurrencyID != pnNxt.uCurrencyID)
            {
                // Offer can be implied by currency change.
                // XXX What about issuer?

                bSkip   = true;
            }
        }

        if (!bSkip)
        {
            // Copy node
            Node     pnNew;

            bool            bSetAccount     = bCurAccount;
            bool            bSetCurrency    = uCurrencyID != pnCur.uCurrencyID;
            // XXX What if we need the next account because we want to skip it?
            bool            bSetIssuer      = !uCurrencyID && uIssuerID != pnCur.uIssuerID;

            pnNew.uFlags    = (bSetAccount ? STPathElement::typeAccount : 0)
                              | (bSetCurrency ? STPathElement::typeCurrency : 0)
                              | (bSetIssuer ? STPathElement::typeIssuer : 0);

            if (bSetAccount)
                pnNew.uAccountID    = pnCur.uAccountID;

            if (bSetCurrency)
            {
                pnNew.uCurrencyID   = pnCur.uCurrencyID;
                uCurrencyID         = pnNew.uCurrencyID;
            }

            if (bSetIssuer)
                pnNew.uIssuerID     = pnCur.uIssuerID;

            // XXX ^^^ What about setting uIssuerID?

            if (bSetCurrency && !uCurrencyID)
                uIssuerID.zero ();

            vpnNodes.push_back (pnNew);
        }
    }

    WriteLog (lsDEBUG, RippleCalc) << boost::str (boost::format ("setCanonical: in=%s/%s out=%s/%s %s")
                                   % STAmount::createHumanCurrency (uMaxCurrencyID)
                                   % RippleAddress::createHumanAccountID (uMaxIssuerID)
                                   % STAmount::createHumanCurrency (uOutCurrencyID)
                                   % RippleAddress::createHumanAccountID (uOutIssuerID)
                                   % getJson ());
}
Exemple #9
0
// Append a node and insert before it any implied nodes.
// Offers may go back to back.
// <-- terResult: tesSUCCESS, temBAD_PATH, terNO_ACCOUNT, terNO_AUTH, terNO_LINE, tecPATH_DRY
TER PathState::pushNode (
    const int iType,
    const uint160& uAccountID,
    const uint160& uCurrencyID,
    const uint160& uIssuerID)
{
    Node                pnCur;
    const bool          bFirst      = vpnNodes.empty ();
    const Node&         pnPrv       = bFirst ? Node () : vpnNodes.back ();
    // true, iff node is a ripple account. false, iff node is an offer node.
    const bool          bAccount    = isSetBit (iType, STPathElement::typeAccount);
    // true, iff currency supplied.
    // Currency is specified for the output of the current node.
    const bool          bCurrency   = isSetBit (iType, STPathElement::typeCurrency);
    // Issuer is specified for the output of the current node.
    const bool          bIssuer     = isSetBit (iType, STPathElement::typeIssuer);
    TER                 terResult   = tesSUCCESS;

    WriteLog (lsTRACE, RippleCalc) << "pushNode> "
                                   << iType
                                   << ": " << (bAccount ? RippleAddress::createHumanAccountID (uAccountID) : "-")
                                   << " " << (bCurrency ? STAmount::createHumanCurrency (uCurrencyID) : "-")
                                   << "/" << (bIssuer ? RippleAddress::createHumanAccountID (uIssuerID) : "-");

    pnCur.uFlags        = iType;
    pnCur.uCurrencyID   = bCurrency ? uCurrencyID : pnPrv.uCurrencyID;

    if (iType & ~STPathElement::typeValidBits)
    {
        WriteLog (lsDEBUG, RippleCalc) << "pushNode: bad bits.";

        terResult   = temBAD_PATH;
    }
    else if (bIssuer && !pnCur.uCurrencyID)
    {
        WriteLog (lsDEBUG, RippleCalc) << "pushNode: issuer specified for XRP.";

        terResult   = temBAD_PATH;
    }
    else if (bIssuer && !uIssuerID)
    {
        WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad issuer.";

        terResult   = temBAD_PATH;
    }
    else if (!bAccount && !bCurrency && !bIssuer)
    {
        WriteLog (lsDEBUG, RippleCalc) << "pushNode: offer must specify at least currency or issuer.";

        terResult   = temBAD_PATH;
    }
    else if (bAccount)
    {
        // Account link

        pnCur.uAccountID    = uAccountID;
        pnCur.uIssuerID     = bIssuer
                              ? uIssuerID
                              : !!pnCur.uCurrencyID
                              ? uAccountID
                              : ACCOUNT_XRP;
        pnCur.saRevRedeem   = STAmount (pnCur.uCurrencyID, uAccountID);
        pnCur.saRevIssue    = STAmount (pnCur.uCurrencyID, uAccountID);
        pnCur.saRevDeliver  = STAmount (pnCur.uCurrencyID, pnCur.uIssuerID);
        pnCur.saFwdDeliver  = pnCur.saRevDeliver;

        if (bFirst)
        {
            // The first node is always correct as is.

            nothing ();
        }
        else if (!uAccountID)
        {
            WriteLog (lsDEBUG, RippleCalc) << "pushNode: specified bad account.";

            terResult   = temBAD_PATH;
        }
        else
        {
            // Add required intermediate nodes to deliver to current account.
            WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for account.";

            terResult   = pushImply (
                              pnCur.uAccountID,                                   // Current account.
                              pnCur.uCurrencyID,                                  // Wanted currency.
                              !!pnCur.uCurrencyID ? uAccountID : ACCOUNT_XRP);    // Account as wanted issuer.

            // Note: pnPrv may no longer be the immediately previous node.
        }

        if (tesSUCCESS == terResult && !vpnNodes.empty ())
        {
            const Node&     pnBck       = vpnNodes.back ();
            bool            bBckAccount = isSetBit (pnBck.uFlags, STPathElement::typeAccount);

            if (bBckAccount)
            {
                SLE::pointer    sleRippleState  = lesEntries.entryCache (ltRIPPLE_STATE, Ledger::getRippleStateIndex (pnBck.uAccountID, pnCur.uAccountID, pnPrv.uCurrencyID));

                if (!sleRippleState)
                {
                    WriteLog (lsTRACE, RippleCalc) << "pushNode: No credit line between "
                                                   << RippleAddress::createHumanAccountID (pnBck.uAccountID)
                                                   << " and "
                                                   << RippleAddress::createHumanAccountID (pnCur.uAccountID)
                                                   << " for "
                                                   << STAmount::createHumanCurrency (pnCur.uCurrencyID)
                                                   << "." ;

                    WriteLog (lsTRACE, RippleCalc) << getJson ();

                    terResult   = terNO_LINE;
                }
                else
                {
                    WriteLog (lsTRACE, RippleCalc) << "pushNode: Credit line found between "
                                                   << RippleAddress::createHumanAccountID (pnBck.uAccountID)
                                                   << " and "
                                                   << RippleAddress::createHumanAccountID (pnCur.uAccountID)
                                                   << " for "
                                                   << STAmount::createHumanCurrency (pnCur.uCurrencyID)
                                                   << "." ;

                    SLE::pointer        sleBck  = lesEntries.entryCache (ltACCOUNT_ROOT, Ledger::getAccountRootIndex (pnBck.uAccountID));
                    bool                bHigh   = pnBck.uAccountID > pnCur.uAccountID;

                    if (!sleBck)
                    {
                        WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from non-existent issuer: " << RippleAddress::createHumanAccountID (pnBck.uAccountID);

                        terResult   = terNO_ACCOUNT;
                    }
                    else if ((isSetBit (sleBck->getFieldU32 (sfFlags), lsfRequireAuth)
                             && !isSetBit (sleRippleState->getFieldU32 (sfFlags), (bHigh ? lsfHighAuth : lsfLowAuth)))
                             && sleRippleState->getFieldAmount(sfBalance).isZero()) // CHECKME
                    {
                        WriteLog (lsWARNING, RippleCalc) << "pushNode: delay: can't receive IOUs from issuer without auth.";

                        terResult   = terNO_AUTH;
                    }
                    else if (isSetBit (sleRippleState->getFieldU32 (sfFlags), bHigh ? lsfHighNoRipple : lsfLowNoRipple) &&
                            (vpnNodes.size() > 1))
                    { // If the link leaves the side that set no ripple, it must be the first link
                        WriteLog (lsWARNING, RippleCalc) << "pushNode: illegal use of noRipple link";

                        terResult   = terNO_AUTH;
                    }

                    if (tesSUCCESS == terResult)
                    {
                        STAmount    saOwed  = lesEntries.rippleOwed (pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID);
                        STAmount    saLimit;

                        if (!saOwed.isPositive ()
                                && -saOwed >= (saLimit = lesEntries.rippleLimit (pnCur.uAccountID, pnBck.uAccountID, pnCur.uCurrencyID)))
                        {
                            WriteLog (lsWARNING, RippleCalc) << boost::str (boost::format ("pushNode: dry: saOwed=%s saLimit=%s")
                                                             % saOwed
                                                             % saLimit);

                            terResult   = tecPATH_DRY;
                        }
                    }
                }
            }
        }

        if (tesSUCCESS == terResult)
        {
            vpnNodes.push_back (pnCur);
        }
    }
    else
    {
        // Offer link
        // Offers bridge a change in currency & issuer or just a change in issuer.
        pnCur.uIssuerID     = bIssuer
                              ? uIssuerID
                              : !!pnCur.uCurrencyID
                              ? !!pnPrv.uIssuerID
                              ? pnPrv.uIssuerID   // Default to previous issuer
                              : pnPrv.uAccountID  // Or previous account if no previous issuer.
                      : ACCOUNT_XRP;
        pnCur.saRateMax     = saZero;
        pnCur.saRevDeliver  = STAmount (pnCur.uCurrencyID, pnCur.uIssuerID);
        pnCur.saFwdDeliver  = pnCur.saRevDeliver;

        if (!!pnCur.uCurrencyID != !!pnCur.uIssuerID)
        {
            WriteLog (lsDEBUG, RippleCalc) << "pushNode: currency is inconsistent with issuer.";

            terResult   = temBAD_PATH;
        }
        else if (!!pnPrv.uAccountID)
        {
            // Previous is an account.
            WriteLog (lsTRACE, RippleCalc) << "pushNode: imply for offer.";

            // Insert intermediary issuer account if needed.
            terResult   = pushImply (
                              ACCOUNT_XRP,                // Rippling, but offers don't have an account.
                              pnPrv.uCurrencyID,
                              pnPrv.uIssuerID);
        }

        if (tesSUCCESS == terResult)
        {
            vpnNodes.push_back (pnCur);
        }
    }

    WriteLog (lsTRACE, RippleCalc) << boost::str (boost::format ("pushNode< : %s") % transToken (terResult));

    return terResult;
}
TER AccountSetTransactor::doApply()
{
	cLog(lsINFO) << "AccountSet>";

	const uint32	uTxFlags	= mTxn.getFlags();

	const uint32	uFlagsIn	= mTxnAccount->getFieldU32(sfFlags);
	uint32			uFlagsOut	= uFlagsIn;

	if (uTxFlags & tfAccountSetMask)
	{
		cLog(lsINFO) << "AccountSet: Malformed transaction: Invalid flags set.";

		return temINVALID_FLAG;
	}

	//
	// RequireAuth
	//

	if ((tfRequireAuth|tfOptionalAuth) == (uTxFlags & (tfRequireAuth|tfOptionalAuth)))
	{
		cLog(lsINFO) << "AccountSet: Malformed transaction: Contradictory flags set.";

		return temINVALID_FLAG;
	}

	if ((uTxFlags & tfRequireAuth) && !isSetBit(uFlagsIn, lsfRequireAuth))
	{
		if (mTxn.getFieldU32(sfOwnerCount))
		{
			cLog(lsINFO) << "AccountSet: Retry: OwnerCount not zero.";

			return terOWNERS;
		}

		cLog(lsINFO) << "AccountSet: Set RequireAuth.";

		uFlagsOut	|= lsfRequireAuth;
	}

	if (uTxFlags & tfOptionalAuth)
	{
		cLog(lsINFO) << "AccountSet: Clear RequireAuth.";

		uFlagsOut	&= ~lsfRequireAuth;
	}

	//
	// RequireDestTag
	//

	if ((tfRequireDestTag|tfOptionalDestTag) == (uTxFlags & (tfRequireDestTag|tfOptionalDestTag)))
	{
		cLog(lsINFO) << "AccountSet: Malformed transaction: Contradictory flags set.";

		return temINVALID_FLAG;
	}

	if (uTxFlags & tfRequireDestTag)
	{
		cLog(lsINFO) << "AccountSet: Set RequireDestTag.";

		uFlagsOut	|= lsfRequireDestTag;
	}

	if (uTxFlags & tfOptionalDestTag)
	{
		cLog(lsINFO) << "AccountSet: Clear RequireDestTag.";

		uFlagsOut	&= ~lsfRequireDestTag;
	}

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

	//
	// EmailHash
	//

	if (mTxn.isFieldPresent(sfEmailHash))
	{
		uint128		uHash	= mTxn.getFieldH128(sfEmailHash);

		if (!uHash)
		{
			cLog(lsINFO) << "AccountSet: unset email hash";

			mTxnAccount->makeFieldAbsent(sfEmailHash);
		}
		else
		{
			cLog(lsINFO) << "AccountSet: set email hash";

			mTxnAccount->setFieldH128(sfEmailHash, uHash);
		}
	}

	//
	// WalletLocator
	//

	if (mTxn.isFieldPresent(sfWalletLocator))
	{
		uint256		uHash	= mTxn.getFieldH256(sfWalletLocator);

		if (!uHash)
		{
			cLog(lsINFO) << "AccountSet: unset wallet locator";

			mTxnAccount->makeFieldAbsent(sfEmailHash);
		}
		else
		{
			cLog(lsINFO) << "AccountSet: set wallet locator";

			mTxnAccount->setFieldH256(sfWalletLocator, uHash);
		}
	}

	//
	// MessageKey
	//

	if (mTxn.isFieldPresent(sfMessageKey))
	{
		std::vector<unsigned char>	vucPublic	= mTxn.getFieldVL(sfMessageKey);

		if (vucPublic.size() > PUBLIC_BYTES_MAX)
		{
			cLog(lsINFO) << "AccountSet: message key too long";

			return telBAD_PUBLIC_KEY;
		}
		else
		{
			cLog(lsINFO) << "AccountSet: set message key";

			mTxnAccount->setFieldVL(sfMessageKey, vucPublic);
		}
	}

	//
	// Domain
	//

	if (mTxn.isFieldPresent(sfDomain))
	{
		std::vector<unsigned char>	vucDomain	= mTxn.getFieldVL(sfDomain);

		if (vucDomain.empty())
		{
			cLog(lsINFO) << "AccountSet: unset domain";

			mTxnAccount->makeFieldAbsent(sfDomain);
		}
		else if (vucDomain.size() > DOMAIN_BYTES_MAX)
		{
			cLog(lsINFO) << "AccountSet: domain too long";

			return telBAD_DOMAIN;
		}
		else
		{
			cLog(lsINFO) << "AccountSet: set domain";

			mTxnAccount->setFieldVL(sfDomain, vucDomain);
		}
	}

	//
	// TransferRate
	//

	if (mTxn.isFieldPresent(sfTransferRate))
	{
		uint32		uRate	= mTxn.getFieldU32(sfTransferRate);

		if (!uRate || uRate == QUALITY_ONE)
		{
			cLog(lsINFO) << "AccountSet: unset transfer rate";

			mTxnAccount->makeFieldAbsent(sfTransferRate);
		}
		else if (uRate > QUALITY_ONE)
		{
			cLog(lsINFO) << "AccountSet: set transfer rate";

			mTxnAccount->setFieldU32(sfTransferRate, uRate);
		}
		else
		{
			cLog(lsINFO) << "AccountSet: bad transfer rate";

			return temBAD_TRANSFER_RATE;
		}
	}

	cLog(lsINFO) << "AccountSet<";

	return tesSUCCESS;
}