From a7efdb4e522a7d65a0035a462afacc5e3b47f6dd Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Sun, 22 Mar 2015 14:34:34 -0700 Subject: [PATCH] Improve version switchover semantics: * Support PreviousTxnID until the switchover * Implement "No Ripple" for issue_iou and redeem_iou. * Do not utilize issue_iou and redeem_iou from legacy code * Rename 0.27.x legacy files to account for VS build process * Misc. cleanups --- Builds/VisualStudio2013/RippleD.vcxproj | 16 +- .../VisualStudio2013/RippleD.vcxproj.filters | 17 +- src/BeastConfig.h | 7 - src/ripple/app/ledger/LedgerEntrySet.cpp | 165 +++++++++--------- src/ripple/app/ledger/LedgerEntrySet.h | 6 +- src/ripple/app/transactors/CreateOffer.cpp | 26 +-- src/ripple/app/transactors/CreateOffer.h | 2 - src/ripple/app/transactors/Transactor.cpp | 6 + .../{CreateOffer.cpp => CreateOffer27.cpp} | 0 src/ripple/legacy/0.27/Emulate027.cpp | 4 +- .../book/impl/{BookTip.cpp => BookTip27.cpp} | 0 .../{OfferStream.cpp => OfferStream27.cpp} | 0 .../book/impl/{Quality.cpp => Quality27.cpp} | 0 .../0.27/book/impl/{Taker.cpp => Taker27.cpp} | 0 src/ripple/protocol/impl/STTx.cpp | 14 +- src/ripple/protocol/impl/TxFormats.cpp | 1 + src/ripple/unity/legacy.cpp | 10 +- 17 files changed, 147 insertions(+), 127 deletions(-) rename src/ripple/legacy/0.27/{CreateOffer.cpp => CreateOffer27.cpp} (100%) rename src/ripple/legacy/0.27/book/impl/{BookTip.cpp => BookTip27.cpp} (100%) rename src/ripple/legacy/0.27/book/impl/{OfferStream.cpp => OfferStream27.cpp} (100%) rename src/ripple/legacy/0.27/book/impl/{Quality.cpp => Quality27.cpp} (100%) rename src/ripple/legacy/0.27/book/impl/{Taker.cpp => Taker27.cpp} (100%) diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 154daf6e2..4f766ac45 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -2442,19 +2442,19 @@ - + True True - + True True - + True True - + True True @@ -2468,12 +2468,12 @@ - + + + True True - - True True @@ -4496,8 +4496,6 @@ - - diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index c54cf2c14..a2815711d 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -3003,16 +3003,16 @@ ripple\legacy\0.27\book - + ripple\legacy\0.27\book\impl - + ripple\legacy\0.27\book\impl - + ripple\legacy\0.27\book\impl - + ripple\legacy\0.27\book\impl @@ -3030,12 +3030,12 @@ ripple\legacy\0.27\book - - ripple\legacy\0.27 - ripple\legacy\0.27 + + ripple\legacy\0.27 + ripple\legacy\0.27 @@ -5268,9 +5268,6 @@ soci\src\core - - soci\src\core - sqlite diff --git a/src/BeastConfig.h b/src/BeastConfig.h index 74833e2a4..15ad99372 100644 --- a/src/BeastConfig.h +++ b/src/BeastConfig.h @@ -180,13 +180,6 @@ #define RIPPLE_PROPOSE_AMENDMENTS 0 #endif -/** Config: RIPPLE_ENABLE_AUTOBRIDGING - This determines whether ripple implements offer autobridging via XRP. -*/ -#ifndef RIPPLE_ENABLE_AUTOBRIDGING -#define RIPPLE_ENABLE_AUTOBRIDGING 1 -#endif - /** Config: RIPPLE_SINGLE_IO_SERVICE_THREAD When set, restricts the number of threads calling io_service::run to one. This is useful when debugging. diff --git a/src/ripple/app/ledger/LedgerEntrySet.cpp b/src/ripple/app/ledger/LedgerEntrySet.cpp index 73bad24b1..030890d8e 100644 --- a/src/ripple/app/ledger/LedgerEntrySet.cpp +++ b/src/ripple/app/ledger/LedgerEntrySet.cpp @@ -1547,6 +1547,33 @@ TER LedgerEntrySet::rippleCredit ( return terResult; } +// Calculate the fee needed to transfer IOU assets between two parties. +STAmount LedgerEntrySet::rippleTransferFee ( + Account const& from, + Account const& to, + Account const& issuer, + STAmount const& saAmount) +{ + if (from != issuer && to != issuer) + { + std::uint32_t uTransitRate = rippleTransferRate (*this, issuer); + + if (QUALITY_ONE != uTransitRate) + { + STAmount saTransferTotal = multiply ( + saAmount, amountFromRate (uTransitRate), saAmount.issue ()); + STAmount saTransferFee = saTransferTotal - saAmount; + + WriteLog (lsDEBUG, LedgerEntrySet) << "rippleTransferFee:" << + " saTransferFee=" << saTransferFee.getFullText (); + + return saTransferFee; + } + } + + return saAmount.zeroed(); +} + // Send regardless of limits. // --> saAmount: Amount/currency/issuer to deliver to reciever. // <-- saActual: Amount actually cost. Sender pay's fees. @@ -1554,72 +1581,43 @@ TER LedgerEntrySet::rippleSend ( Account const& uSenderID, Account const& uReceiverID, STAmount const& saAmount, STAmount& saActual) { - auto const& issue = saAmount.issue (); - auto const& issuer = saAmount.issue ().account; + auto const issuer = saAmount.getIssuer (); + TER terResult; assert (!isXRP (uSenderID) && !isXRP (uReceiverID)); assert (uSenderID != uReceiverID); - // Issues and redeems are processed directly without any fees. - if (uSenderID == issue.account) + if (uSenderID == issuer || uReceiverID == issuer || issuer == noAccount()) { - assert (issue.account != noAccount ()); - saActual = saAmount; - return issue_iou (uReceiverID, saAmount, issue); - } - - if (uReceiverID == issue.account) - { - assert (issue.account != noAccount ()); - saActual = saAmount; - return redeem_iou (uSenderID, saAmount, issue); - } - - // NIKB TODO: What is going on here? Under what circumstances would we try - // to transfer some currency without a specified issuer? - if (issue.account == noAccount()) - { - WriteLog (lsDEBUG, LedgerEntrySet) << - "rippleSend> " << to_string (uSenderID) << - " - > " << to_string (uReceiverID) << - " : amount with issuer=1: " << saAmount.getFullText (); - // Direct send: redeeming IOUs and/or sending own IOUs. - TER result = rippleCredit (uSenderID, uReceiverID, saAmount, false); - saActual = saAmount; - - // NIKB CHECKME: Why are we setting terResult to tesSUCCESS here? - result = tesSUCCESS; - - return result; + terResult = rippleCredit (uSenderID, uReceiverID, saAmount, false); + saActual = saAmount; + terResult = tesSUCCESS; } - - assert (uSenderID != issue.account && uReceiverID != issue.account); - - // Sending IOUs to a third party: we must calculate the transit fee, by - // applying the transfer rate, if any. - saActual = saAmount; - - std::uint32_t const transit_rate = rippleTransferRate (*this, issuer); - - if (QUALITY_ONE != transit_rate) + else { - saActual = multiply (saActual, amountFromRate (transit_rate), - saActual.issue ()); + // Sending 3rd party IOUs: transit. + + STAmount saTransitFee = rippleTransferFee ( + uSenderID, uReceiverID, issuer, saAmount); + + saActual = !saTransitFee ? saAmount : saAmount + saTransitFee; + + saActual.setIssuer (issuer); // XXX Make sure this done in + above. + + WriteLog (lsDEBUG, LedgerEntrySet) << "rippleSend> " << + to_string (uSenderID) << + " - > " << to_string (uReceiverID) << + " : deliver=" << saAmount.getFullText () << + " fee=" << saTransitFee.getFullText () << + " cost=" << saActual.getFullText (); + + terResult = rippleCredit (issuer, uReceiverID, saAmount); + + if (tesSUCCESS == terResult) + terResult = rippleCredit (uSenderID, issuer, saActual); } - WriteLog (lsDEBUG, LedgerEntrySet) << - "rippleSend> " << to_string (uSenderID) << - " - > " << to_string (uReceiverID) << - " : deliver=" << saAmount.getFullText () << - " cost=" << saActual.getFullText () << - " fee=" << (saActual - saAmount).getFullText (); - - TER terResult = issue_iou (uReceiverID, saAmount, saAmount.issue ()); - - if (tesSUCCESS == terResult) - terResult = redeem_iou (uSenderID, saActual, saActual.issue ()); - return terResult; } @@ -1725,45 +1723,47 @@ TER LedgerEntrySet::accountSend ( bool LedgerEntrySet::checkState ( SLE::pointer state, - bool bIssuerHigh, + bool bSenderHigh, Account const& sender, STAmount const& before, STAmount const& after) { std::uint32_t const flags (state->getFieldU32 (sfFlags)); + auto sender_account = entryCache (ltACCOUNT_ROOT, + getAccountRootIndex (sender)); + assert (sender_account); + // YYY Could skip this if rippling in reverse. if (before > zero // Sender balance was positive. && after <= zero // Sender is zero or negative. - && (flags & (!bIssuerHigh ? lsfLowReserve : lsfHighReserve)) + && (flags & (!bSenderHigh ? lsfLowReserve : lsfHighReserve)) // Sender reserve is set. - && static_cast (flags & (!bIssuerHigh ? lsfLowNoRipple : lsfHighNoRipple)) != - static_cast (entryCache (ltACCOUNT_ROOT, - getAccountRootIndex (sender))->getFlags() & lsfDefaultRipple) - && !(flags & (!bIssuerHigh ? lsfLowFreeze : lsfHighFreeze)) + && static_cast (flags & (!bSenderHigh ? lsfLowNoRipple : lsfHighNoRipple)) != + static_cast (sender_account->getFlags() & lsfDefaultRipple) + && !(flags & (!bSenderHigh ? lsfLowFreeze : lsfHighFreeze)) && !state->getFieldAmount ( - !bIssuerHigh ? sfLowLimit : sfHighLimit) + !bSenderHigh ? sfLowLimit : sfHighLimit) // Sender trust limit is 0. && !state->getFieldU32 ( - !bIssuerHigh ? sfLowQualityIn : sfHighQualityIn) + !bSenderHigh ? sfLowQualityIn : sfHighQualityIn) // Sender quality in is 0. && !state->getFieldU32 ( - !bIssuerHigh ? sfLowQualityOut : sfHighQualityOut)) + !bSenderHigh ? sfLowQualityOut : sfHighQualityOut)) // Sender quality out is 0. { // Clear the reserve of the sender, possibly delete the line! - decrementOwnerCount (sender); + decrementOwnerCount (sender_account); // Clear reserve flag. - state->setFieldU32 ( - sfFlags, - flags & (!bIssuerHigh ? ~lsfLowReserve : ~lsfHighReserve)); + state->setFieldU32 (sfFlags, + flags & (!bSenderHigh ? ~lsfLowReserve : ~lsfHighReserve)); // Balance is zero, receiver reserve is clear. if (!after // Balance is zero. - && !(flags & (bIssuerHigh ? lsfLowReserve : lsfHighReserve))) + && !(flags & (bSenderHigh ? lsfLowReserve : lsfHighReserve))) return true; } @@ -1802,9 +1802,16 @@ TER LedgerEntrySet::issue_iou ( final_balance.setIssuer (noAccount()); + SLE::pointer receiverAccount = entryCache (ltACCOUNT_ROOT, + getAccountRootIndex (account)); + + bool noRipple = (receiverAccount->getFlags() & lsfDefaultRipple) == 0; + + if (ripple::legacy::emulate027 (mLedger)) + noRipple = false; + return trustCreate (bSenderHigh, issue.account, account, index, - entryCache (ltACCOUNT_ROOT, getAccountRootIndex (account)), - false, false, false, final_balance, limit); + receiverAccount, false, noRipple, false, final_balance, limit); } STAmount final_balance = state->getFieldAmount (sfBalance); @@ -1862,14 +1869,14 @@ TER LedgerEntrySet::redeem_iou ( if (!state) { - STAmount limit(issue); - STAmount final_balance = amount; + // In order to hold an IOU, a trust line *MUST* exist to track the + // balance. If it doesn't, then something is very wrong. Don't try + // to continue. + WriteLog (lsFATAL, LedgerEntrySet) << "redeem_iou: " << + to_string (account) << " attempts to redeem " << + amount.getFullText () << " but no trust line exists!"; - final_balance.setIssuer (noAccount()); - - return trustCreate (bSenderHigh, account, issue.account, index, - entryCache (ltACCOUNT_ROOT, getAccountRootIndex (issue.account)), - false, false, false, final_balance, limit); + return tefINTERNAL; } STAmount final_balance = state->getFieldAmount (sfBalance); diff --git a/src/ripple/app/ledger/LedgerEntrySet.h b/src/ripple/app/ledger/LedgerEntrySet.h index 91d569fd8..1f98b4c30 100644 --- a/src/ripple/app/ledger/LedgerEntrySet.h +++ b/src/ripple/app/ledger/LedgerEntrySet.h @@ -332,7 +332,11 @@ private: Account const& account, Currency const& currency, Account const& issuer, FreezeHandling zeroIfFrozen); - bool checkState (SLE::pointer state, bool bIssuerHigh, + STAmount rippleTransferFee ( + Account const& from, Account const& to, + Account const& issuer, STAmount const& saAmount); + + bool checkState (SLE::pointer state, bool bSenderHigh, Account const& sender, STAmount const& before, STAmount const& after); }; diff --git a/src/ripple/app/transactors/CreateOffer.cpp b/src/ripple/app/transactors/CreateOffer.cpp index 2201378ec..409a507ee 100644 --- a/src/ripple/app/transactors/CreateOffer.cpp +++ b/src/ripple/app/transactors/CreateOffer.cpp @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -150,7 +151,7 @@ private: { auto const& taker_amount = taker.original_offer (); - assert (!isXRP (taker_amount.in)&& !isXRP (taker_amount.in)); + assert (!isXRP (taker_amount.in) && !isXRP (taker_amount.out)); if (isXRP (taker_amount.in) || isXRP (taker_amount.out)) throw std::logic_error ("Bridging with XRP and an endpoint."); @@ -385,7 +386,7 @@ private: } // Fill offer as much as possible by consuming offers already on the books, - // and adjusting account balances accordingly. + // and adjusting account balances accordingly. // // Charges fees on top to taker. std::pair @@ -411,10 +412,8 @@ private: m_journal.debug << " Balance: " << format_amount (funds); } -#if RIPPLE_ENABLE_AUTOBRIDGING if (cross_type_ == core::CrossType::IouToIou) return bridged_cross (taker, view, cancel_view, when); -#endif return direct_cross (taker, view, cancel_view, when); } @@ -641,7 +640,7 @@ public: } // Process a cancellation request that's passed along with an offer. - if ((result == tesSUCCESS) && bHaveCancel) + if (bHaveCancel) { SLE::pointer sleCancel = mEngine->entryCache (ltOFFER, getOfferIndex (mTxnAccountID, uCancelSequence)); @@ -682,7 +681,7 @@ public: // empty (fully crossed), or something in-between. core::Amounts place_offer; - m_journal.debug << "Attempting cross: " << + m_journal.debug << "Attempting cross: " << to_string (taker_amount.in.issue ()) << " -> " << to_string (taker_amount.out.issue ()); @@ -724,7 +723,9 @@ public: // never be negative. If it is, something went very very wrong. if (place_offer.in < zero || place_offer.out < zero) { - m_journal.fatal << "Cross left offer negative!"; + m_journal.fatal << "Cross left offer negative!" << + " in: " << format_amount (place_offer.in) << + " out: " << format_amount (place_offer.out); return tefINTERNAL; } @@ -762,7 +763,7 @@ public: { m_journal.trace << "Fill or Kill: offer killed"; view.swapWith (view_checkpoint); - return result; + return tesSUCCESS; } // For 'immediate or cancel' offers, the amount remaining doesn't get @@ -770,17 +771,20 @@ public: if (bImmediateOrCancel) { m_journal.trace << "Immediate or cancel: offer cancelled"; - return result; + return tesSUCCESS; } if (mPriorBalance.getNValue () < getAccountReserve (sleCreator)) { // If we are here, the signing account had an insufficient reserve // *prior* to our processing. If something actually crossed, then - // allow this; otherwise, we just claim a fee. + // we allow this; otherwise, we just claim a fee. if (!crossed) result = tecINSUF_RESERVE_OFFER; + if (bOpenLedger && ripple::legacy::emulate027 (mEngine->getLedger())) + result = tecINSUF_RESERVE_OFFER; + if (result != tesSUCCESS) m_journal.debug << "final result: " << transToken (result); @@ -862,7 +866,7 @@ transact_CreateOffer ( { // Attempt to implement legacy offer creation semantics. If successful, // then return the result. Otherwise, attempt to process using the - // new semantics. + // new semantics. auto ret = ripple::legacy::transact_CreateOffer (txn, params, engine); if (ret.first) diff --git a/src/ripple/app/transactors/CreateOffer.h b/src/ripple/app/transactors/CreateOffer.h index 28299e5b8..c086bd373 100644 --- a/src/ripple/app/transactors/CreateOffer.h +++ b/src/ripple/app/transactors/CreateOffer.h @@ -36,9 +36,7 @@ class CreateOffer { private: // What kind of offer we are placing -#if RIPPLE_ENABLE_AUTOBRIDGING bool autobridging_; -#endif // Determine if we are authorized to hold the asset we want to get TER diff --git a/src/ripple/app/transactors/Transactor.cpp b/src/ripple/app/transactors/Transactor.cpp index 8015ffa92..563788214 100644 --- a/src/ripple/app/transactors/Transactor.cpp +++ b/src/ripple/app/transactors/Transactor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include namespace ripple { @@ -200,6 +201,11 @@ TER Transactor::checkSeq () return tefPAST_SEQ; } + if (ripple::legacy::emulate027 (mEngine->getLedger()) && + mTxn.isFieldPresent (sfPreviousTxnID) && + (mTxnAccount->getFieldH256 (sfPreviousTxnID) != mTxn.getFieldH256 (sfPreviousTxnID))) + return tefWRONG_PRIOR; + if (mTxn.isFieldPresent (sfAccountTxnID) && (mTxnAccount->getFieldH256 (sfAccountTxnID) != mTxn.getFieldH256 (sfAccountTxnID))) return tefWRONG_PRIOR; diff --git a/src/ripple/legacy/0.27/CreateOffer.cpp b/src/ripple/legacy/0.27/CreateOffer27.cpp similarity index 100% rename from src/ripple/legacy/0.27/CreateOffer.cpp rename to src/ripple/legacy/0.27/CreateOffer27.cpp diff --git a/src/ripple/legacy/0.27/Emulate027.cpp b/src/ripple/legacy/0.27/Emulate027.cpp index 6c9524e00..8de077a21 100644 --- a/src/ripple/legacy/0.27/Emulate027.cpp +++ b/src/ripple/legacy/0.27/Emulate027.cpp @@ -34,8 +34,8 @@ emulate027 (Ledger::ref ledger) return false; // The server also uses 0.28 semantics for all ledgers whose parent - // closed after 2015-03-30 13:00:00 PDT. - static std::uint32_t const legacy_cutoff = 481060800; + // closed after 2015-04-15 13:00:00 PDT. + static std::uint32_t const legacy_cutoff = 482442800; if (ledger->getParentCloseTimeNC () > legacy_cutoff) return false; diff --git a/src/ripple/legacy/0.27/book/impl/BookTip.cpp b/src/ripple/legacy/0.27/book/impl/BookTip27.cpp similarity index 100% rename from src/ripple/legacy/0.27/book/impl/BookTip.cpp rename to src/ripple/legacy/0.27/book/impl/BookTip27.cpp diff --git a/src/ripple/legacy/0.27/book/impl/OfferStream.cpp b/src/ripple/legacy/0.27/book/impl/OfferStream27.cpp similarity index 100% rename from src/ripple/legacy/0.27/book/impl/OfferStream.cpp rename to src/ripple/legacy/0.27/book/impl/OfferStream27.cpp diff --git a/src/ripple/legacy/0.27/book/impl/Quality.cpp b/src/ripple/legacy/0.27/book/impl/Quality27.cpp similarity index 100% rename from src/ripple/legacy/0.27/book/impl/Quality.cpp rename to src/ripple/legacy/0.27/book/impl/Quality27.cpp diff --git a/src/ripple/legacy/0.27/book/impl/Taker.cpp b/src/ripple/legacy/0.27/book/impl/Taker27.cpp similarity index 100% rename from src/ripple/legacy/0.27/book/impl/Taker.cpp rename to src/ripple/legacy/0.27/book/impl/Taker27.cpp diff --git a/src/ripple/protocol/impl/STTx.cpp b/src/ripple/protocol/impl/STTx.cpp index f2a5d875c..a6185918b 100644 --- a/src/ripple/protocol/impl/STTx.cpp +++ b/src/ripple/protocol/impl/STTx.cpp @@ -29,8 +29,10 @@ #include #include #include +#include #include #include +#include "boost/date_time/posix_time/posix_time.hpp" #include namespace ripple { @@ -290,7 +292,14 @@ isMemoOkay (STObject const& st, std::string& reason) if (!st.isFieldPresent (sfMemos)) return true; - const STArray& memos = st.getFieldArray (sfMemos); + // We switch to new semantics on April 15, 2015 at 1:00pm PDT: + static boost::posix_time::ptime const cutoff ( + boost::posix_time::time_from_string ("2015-04-15 20:00:00")); + + bool const emulate027 = + boost::posix_time::second_clock::universal_time () < cutoff; + + auto const& memos = st.getFieldArray (sfMemos); // The number 2048 is a preallocation hint, not a hard limit // to avoid allocate/copy/free's @@ -327,6 +336,9 @@ isMemoOkay (STObject const& st, std::string& reason) return false; } + if (emulate027) + continue; + // The raw data is stored as hex-octets, which we want to decode. auto data = strUnHex (memoElement.getText ()); diff --git a/src/ripple/protocol/impl/TxFormats.cpp b/src/ripple/protocol/impl/TxFormats.cpp index 4aceb6494..ee0cb093c 100644 --- a/src/ripple/protocol/impl/TxFormats.cpp +++ b/src/ripple/protocol/impl/TxFormats.cpp @@ -96,6 +96,7 @@ void TxFormats::addCommonFields (Item& item) << SOElement(sfSourceTag, SOE_OPTIONAL) << SOElement(sfAccount, SOE_REQUIRED) << SOElement(sfSequence, SOE_REQUIRED) + << SOElement(sfPreviousTxnID, SOE_OPTIONAL) // emulate027 << SOElement(sfLastLedgerSequence, SOE_OPTIONAL) << SOElement(sfAccountTxnID, SOE_OPTIONAL) << SOElement(sfFee, SOE_REQUIRED) diff --git a/src/ripple/unity/legacy.cpp b/src/ripple/unity/legacy.cpp index 7debd523c..4869ad569 100644 --- a/src/ripple/unity/legacy.cpp +++ b/src/ripple/unity/legacy.cpp @@ -19,10 +19,10 @@ #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include