From b04aa198e6df08c28b959194a66ea56e5045239b Mon Sep 17 00:00:00 2001 From: Arthur Britto Date: Mon, 17 Dec 2012 21:30:19 -0800 Subject: [PATCH] Revise trust set transactor for reserves. --- src/cpp/ripple/TransactionErr.cpp | 2 +- src/cpp/ripple/TransactionErr.h | 2 +- src/cpp/ripple/TrustSetTransactor.cpp | 261 +++++++++++++++++++------- 3 files changed, 196 insertions(+), 69 deletions(-) diff --git a/src/cpp/ripple/TransactionErr.cpp b/src/cpp/ripple/TransactionErr.cpp index a28ab5bc86..40aa6edff0 100644 --- a/src/cpp/ripple/TransactionErr.cpp +++ b/src/cpp/ripple/TransactionErr.cpp @@ -54,7 +54,7 @@ bool transResultInfo(TER terCode, std::string& strToken, std::string& strHuman) { terNO_DST, "terNO_DST", "Destination does not exist. Send XRP to create it." }, { terNO_DST_INSUF_XRP, "terNO_DST_INSUF_XRP", "Destination does not exist. Too little XRP sent to create it." }, { terNO_LINE, "terNO_LINE", "No such line." }, - { terNO_LINE_NO_ZERO, "terNO_LINE_NO_ZERO", "Can't zero non-existant line, destination might make it." }, + { terNO_LINE_REDUNDANT, "terNO_LINE_REDUNDANT", "Can't set non-existant line to default." }, { terPRE_SEQ, "terPRE_SEQ", "Missing/inapplicable prior transaction." }, { terSET_MISSING_DST, "terSET_MISSING_DST", "Can't set password, destination missing." }, { terUNFUNDED, "terUNFUNDED", "Source account had insufficient balance for transaction." }, diff --git a/src/cpp/ripple/TransactionErr.h b/src/cpp/ripple/TransactionErr.h index 8292dd3aaa..bed4e4f6e5 100644 --- a/src/cpp/ripple/TransactionErr.h +++ b/src/cpp/ripple/TransactionErr.h @@ -84,7 +84,7 @@ enum TER // aka TransactionEngineResult terNO_DST, terNO_DST_INSUF_XRP, terNO_LINE, - terNO_LINE_NO_ZERO, + terNO_LINE_REDUNDANT, terPRE_SEQ, terSET_MISSING_DST, terUNFUNDED, diff --git a/src/cpp/ripple/TrustSetTransactor.cpp b/src/cpp/ripple/TrustSetTransactor.cpp index 54e1efc124..7560bc0001 100644 --- a/src/cpp/ripple/TrustSetTransactor.cpp +++ b/src/cpp/ripple/TrustSetTransactor.cpp @@ -9,13 +9,19 @@ TER TrustSetTransactor::doApply() const STAmount saLimitAmount = mTxn.getFieldAmount(sfLimitAmount); const bool bQualityIn = mTxn.isFieldPresent(sfQualityIn); - const uint32 uQualityIn = bQualityIn ? mTxn.getFieldU32(sfQualityIn) : 0; const bool bQualityOut = mTxn.isFieldPresent(sfQualityOut); - const uint32 uQualityOut = bQualityIn ? mTxn.getFieldU32(sfQualityOut) : 0; const uint160 uCurrencyID = saLimitAmount.getCurrency(); uint160 uDstAccountID = saLimitAmount.getIssuer(); const bool bFlipped = mTxnAccountID > uDstAccountID; // true, iff current is not lowest. - bool bDelIndex = false; + + uint32 uQualityIn = bQualityIn ? mTxn.getFieldU32(sfQualityIn) : 0; + uint32 uQualityOut = bQualityIn ? mTxn.getFieldU32(sfQualityOut) : 0; + + if (bQualityIn && QUALITY_ONE == uQualityIn) + uQualityIn = 0; + + if (bQualityOut && QUALITY_ONE == uQualityOut) + uQualityOut = 0; // Check if destination makes sense. @@ -52,77 +58,198 @@ TER TrustSetTransactor::doApply() SLE::pointer sleRippleState = mEngine->entryCache(ltRIPPLE_STATE, Ledger::getRippleStateIndex(mTxnAccountID, uDstAccountID, uCurrencyID)); if (sleRippleState) { - // A line exists in one or more directions. + STAmount saLowBalance; + STAmount saLowLimit; + STAmount saHighBalance; + STAmount saHighLimit; + uint32 uLowQualityIn; + uint32 uLowQualityOut; + uint32 uHighQualityIn; + uint32 uHighQualityOut; + const uint160& uLowAccountID = !bFlipped ? mTxnAccountID : uDstAccountID; + const uint160& uHighAccountID = bFlipped ? mTxnAccountID : uDstAccountID; + SLE::ref sleLowAccount = !bFlipped ? mTxnAccount : sleDst; + SLE::ref sleHighAccount = bFlipped ? mTxnAccount : sleDst; -#if 0 - // We might delete a ripple state node if everything is set to defaults. - // However, this is problematic as it may make predicting reserve amounts harder for users. - // The code here is incomplete. + // + // Balances + // - if (!saLimitAmount) + saLowBalance = sleRippleState->getFieldAmount(sfBalance); + saHighBalance = saLowBalance; + + if (bFlipped) { - // Zeroing line. - uint160 uLowID = sleRippleState->getFieldAmount(sfLowLimit).getIssuer(); - uint160 uHighID = sleRippleState->getFieldAmount(sfHighLimit).getIssuer(); - bool bLow = uLowID == uSrcAccountID; - bool bHigh = uLowID == uDstAccountID; - bool bBalanceZero = !sleRippleState->getFieldAmount(sfBalance); - STAmount saDstLimit = sleRippleState->getFieldAmount(bSendLow ? sfLowLimit : sfHighLimit); - bool bDstLimitZero = !saDstLimit; - - assert(bLow || bHigh); - - if (bBalanceZero && bDstLimitZero) - { - // Zero balance and eliminating last limit. - - bDelIndex = true; - terResult = dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(mTxnAccountID), sleRippleState->getIndex(), false); - } + saLowBalance.negate(); } -#endif - - if (!bDelIndex) + else { - sleRippleState->setFieldAmount(bFlipped ? sfHighLimit: sfLowLimit, saLimitAllow); + saHighBalance.negate(); + } - if (!bQualityIn) - { - nothing(); - } - else if (uQualityIn) - { - sleRippleState->setFieldU32(bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn); - } - else - { - sleRippleState->makeFieldAbsent(bFlipped ? sfLowQualityIn : sfHighQualityIn); - } + // + // Limits + // - if (!bQualityOut) - { - nothing(); - } - else if (uQualityOut) - { - sleRippleState->setFieldU32(bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut); - } - else - { - sleRippleState->makeFieldAbsent(bFlipped ? sfLowQualityOut : sfHighQualityOut); - } + if (bFlipped) + { + sleRippleState->setFieldAmount(sfHighLimit, saLimitAllow); + saLowLimit = sleRippleState->getFieldAmount(sfLowLimit); + saHighLimit = saLimitAllow; + } + else + { + sleRippleState->setFieldAmount(sfLowLimit, saLimitAllow); + + saLowLimit = saLimitAllow; + saHighLimit = 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(!bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn); + + uLowQualityIn = !bFlipped ? uQualityIn : sleRippleState->getFieldU32(sfLowQualityIn); + uHighQualityIn = bFlipped ? uQualityIn : sleRippleState->getFieldU32(sfHighQualityIn); + } + else + { + // Clearing. + + sleRippleState->makeFieldAbsent(!bFlipped ? sfLowQualityIn : sfHighQualityIn); + + uLowQualityIn = !bFlipped ? 0 : sleRippleState->getFieldU32(sfLowQualityIn); + uHighQualityIn = bFlipped ? 0 : sleRippleState->getFieldU32(sfHighQualityIn); + } + + // + // Quality out + // + + if (!bQualityOut) + { + // Not setting. Just get it. + + uLowQualityOut = sleRippleState->getFieldU32(sfLowQualityOut); + uHighQualityOut = sleRippleState->getFieldU32(sfHighQualityOut); + } + else if (uQualityOut) + { + // Setting. + + sleRippleState->setFieldU32(!bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut); + + uLowQualityOut = !bFlipped ? uQualityOut : sleRippleState->getFieldU32(sfLowQualityOut); + uHighQualityOut = bFlipped ? uQualityOut : sleRippleState->getFieldU32(sfHighQualityOut); + } + else + { + // Clearing. + + sleRippleState->makeFieldAbsent(!bFlipped ? sfLowQualityOut : sfHighQualityOut); + + uLowQualityOut = !bFlipped ? 0 : sleRippleState->getFieldU32(sfLowQualityOut); + uHighQualityOut = bFlipped ? 0 : sleRippleState->getFieldU32(sfHighQualityOut); + } + + if (QUALITY_ONE == uLowQualityIn) uLowQualityIn = 0; + if (QUALITY_ONE == uHighQualityIn) uHighQualityIn = 0; + if (QUALITY_ONE == uLowQualityOut) uLowQualityOut = 0; + if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0; + + const bool bLowReserveSet = uLowQualityIn || uLowQualityOut || !!saLowLimit || saLowBalance.isPositive(); + const bool bLowReserveClear = !uLowQualityIn && !uLowQualityOut && !saLowLimit && !saLowBalance.isPositive(); + + const bool bHighReserveSet = uHighQualityIn || uHighQualityOut || !!saHighLimit || saHighBalance.isPositive(); + const bool bHighReserveClear = !uHighQualityIn && !uHighQualityOut && !saHighLimit && !saHighBalance.isPositive(); + 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); + + if (bLowReserveSet && !bLowReserved) + { + // Set reserve for low account. + + terResult = mEngine->getNodes().ownerCountAdjust(uLowAccountID, 1, sleLowAccount); + uFlagsOut |= lsfLowReserve; + } + + if (bLowReserveClear && bLowReserved) + { + // Clear reserve for low account. + + terResult = mEngine->getNodes().ownerCountAdjust(uLowAccountID, -1, sleLowAccount); + uFlagsOut &= ~lsfLowReserve; + } + + if (bHighReserveSet && !bHighReserved) + { + // Set reserve for high account. + + terResult = mEngine->getNodes().ownerCountAdjust(uHighAccountID, 1, sleHighAccount); + uFlagsOut |= lsfHighReserve; + } + + if (bHighReserveClear && bHighReserved) + { + // Clear reserve for high account. + + terResult = mEngine->getNodes().ownerCountAdjust(uHighAccountID, -1, sleHighAccount); + uFlagsOut &= ~lsfHighReserve; + } + + if (uFlagsIn != uFlagsOut) + sleRippleState->setFieldU32(sfFlags, uFlagsOut); + + if (bDefault) + { + // Can delete. + + uint64 uSrcRef; // <-- Ignored, dirs never delete. + + terResult = mEngine->getNodes().dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(uLowAccountID), sleRippleState->getIndex(), false); + + if (tesSUCCESS == terResult) + terResult = mEngine->getNodes().dirDelete(false, uSrcRef, Ledger::getOwnerDirIndex(uHighAccountID), sleRippleState->getIndex(), false); + + mEngine->entryDelete(sleRippleState); + + Log(lsINFO) << "doTrustSet: Deleting ripple line"; + } + else + { mEngine->entryModify(sleRippleState); - } - Log(lsINFO) << "doTrustSet: Modifying ripple line: bDelIndex=" << bDelIndex; + Log(lsINFO) << "doTrustSet: Modify ripple line"; + } } // Line does not exist. - else if (!saLimitAmount) + else if (!saLimitAmount // Setting default limit. + && bQualityIn && !uQualityIn // Setting default quality in. + && bQualityOut && !uQualityOut // Setting default quality out. + ) { - Log(lsINFO) << "doTrustSet: Redundant: Setting non-existent ripple line to 0."; + Log(lsINFO) << "doTrustSet: Redundant: Setting non-existent ripple line to defaults."; - return terNO_LINE_NO_ZERO; + return terNO_LINE_REDUNDANT; } else { @@ -132,13 +259,16 @@ TER TrustSetTransactor::doApply() Log(lsINFO) << "doTrustSet: Creating ripple line: " << sleRippleState->getIndex().ToString(); sleRippleState->setFieldAmount(sfBalance, STAmount(uCurrencyID, ACCOUNT_ONE)); // Zero balance in currency. - sleRippleState->setFieldAmount(bFlipped ? sfHighLimit : sfLowLimit, saLimitAllow); - sleRippleState->setFieldAmount(bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID, uDstAccountID)); + sleRippleState->setFieldAmount(!bFlipped ? sfLowLimit : sfHighLimit, saLimitAllow); + sleRippleState->setFieldAmount( bFlipped ? sfLowLimit : sfHighLimit, STAmount(uCurrencyID, uDstAccountID)); if (uQualityIn) - sleRippleState->setFieldU32(bFlipped ? sfHighQualityIn : sfLowQualityIn, uQualityIn); + sleRippleState->setFieldU32(!bFlipped ? sfLowQualityIn : sfHighQualityIn, uQualityIn); + if (uQualityOut) - sleRippleState->setFieldU32(bFlipped ? sfHighQualityOut : sfLowQualityOut, uQualityOut); + sleRippleState->setFieldU32(!bFlipped ? sfLowQualityOut : sfHighQualityOut, uQualityOut); + + sleRippleState->setFieldU32(sfFlags, !bFlipped ? lsfLowReserve : lsfHighReserve); uint64 uSrcRef; // <-- Ignored, dirs never delete. @@ -157,9 +287,6 @@ TER TrustSetTransactor::doApply() Ledger::getOwnerDirIndex(uDstAccountID), sleRippleState->getIndex(), boost::bind(&Ledger::ownerDirDescriber, _1, uDstAccountID)); - - if (tesSUCCESS == terResult) - terResult = mEngine->getNodes().ownerCountAdjust(uDstAccountID, 1, sleDst); } Log(lsINFO) << "doTrustSet<";