//------------------------------------------------------------------------------ /* Copyright (c) 2011-2013, OpenCoin, Inc. */ //============================================================================== SETUP_LOG (TrustSetTransactor) 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); 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 = 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); } if (QUALITY_ONE == uLowQualityOut) uLowQualityOut = 0; if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0; const bool bLowReserveSet = uLowQualityIn || uLowQualityOut || !!saLowLimit || saLowBalance.isPositive (); const bool bLowReserveClear = !bLowReserveSet; const bool bHighReserveSet = uHighQualityIn || uHighQualityOut || !!saHighLimit || saHighBalance.isPositive (); const bool bHighReserveClear = !bHighReserveSet; const bool bDefault = bLowReserveClear && bHighReserveClear; const uint32 uFlagsIn = sleRippleState->getFieldU32 (sfFlags); uint32 uFlagsOut = uFlagsIn; 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, saBalance, saLimitAllow, // Limit for who is being charged. uQualityIn, uQualityOut); } WriteLog (lsINFO, TrustSetTransactor) << "doTrustSet<"; return terResult; } // vim:ts=4