mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
339 lines
12 KiB
C++
339 lines
12 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
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
|