From f96ac3db67ee757b8e52eaac07e0526bf7664dfc Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 26 Jun 2015 17:14:06 -0700 Subject: [PATCH] Refactor View, MetaView, and tx processing: This tidies up the View interface and makes transaction application a free function, with the removal of the TransactionEngine class. A new class ApplyContext provides all the state information needed to apply a Transactor. The Transactor is refactored to perform all the processing activities previously part of TransactionEngine. The calculation of metadata from a MetaView is improved. A new apply function performs all the steps for calculating and inserting metadata into the tx map. Transaction processing code path is passed a Config instead of retrieving the global, and uses the Journal supplied in the call to apply() consistently. To support transaction processing and RPC operations, a new POD type ViewInfo is added which consolidates static information about open and closed ledgers, such as the ledger sequence number or the closing times. Ledger and MetaView are refactored to use this info. The ViewInfo now contains the "open ledger" setting. The tapOPEN_LEDGER ViewFlag is removed. The view property of being an open ledger is obtained from the base or by using the MetaView constructor which presents a closed ledger as an open one. View, MetaView: * Fix missing includes * Add apply free function * Use Journal in TransactionEngine * Use BasicView in TransactionEngine * inline NetworkOPs::batchApply * Add shallow_copy, open_ledger MetaView ctor tags * Add ViewInfo with open flag, seq, close times * Make parent_ a reference * Tidy up ctor arguments and base_ name * Remove tapOPEN_LEDGER * add assert to MetaView::apply * ViewInfo comment * Throw, pass Journal in txInsert * Add BasicView::txCount TransactionEngine: * Add apply * Make TransactionEngine private * Refactor MetaView::apply and apply() * Rename to TxMeta * Refactor treatment of metadata in MetaView, TransactionEngine * Rename to ApplyContext * Use ApplyContext& in Transactor * Pass Config in ApplyContext * Declare Transactor classes in headers * Use view flags in Transactor --- Builds/VisualStudio2013/RippleD.vcxproj | 52 +- .../VisualStudio2013/RippleD.vcxproj.filters | 63 +- src/ripple/app/ledger/AcceptedLedgerTx.cpp | 4 +- src/ripple/app/ledger/AcceptedLedgerTx.h | 6 +- src/ripple/app/ledger/Ledger.cpp | 151 +- src/ripple/app/ledger/Ledger.h | 77 +- src/ripple/app/ledger/LedgerConsensus.h | 3 +- src/ripple/app/ledger/LedgerHistory.cpp | 6 +- src/ripple/app/ledger/LedgerToJson.h | 2 +- src/ripple/app/ledger/MetaView.h | 232 +-- .../{tx/TransactionMeta.h => ledger/TxMeta.h} | 17 +- .../app/ledger/impl/LedgerConsensusImp.cpp | 85 +- src/ripple/app/ledger/impl/LedgerMaster.cpp | 10 +- src/ripple/app/ledger/impl/MetaView.cpp | 507 +++--- .../impl/TxMeta.cpp} | 36 +- src/ripple/app/ledger/tests/MetaView_test.cpp | 47 +- src/ripple/app/ledger/tests/common_ledger.cpp | 11 +- src/ripple/app/main/Application.cpp | 12 +- src/ripple/app/misc/CanonicalTXSet.h | 3 +- src/ripple/app/misc/NetworkOPs.cpp | 71 +- src/ripple/app/misc/NetworkOPs.h | 2 +- src/ripple/app/misc/impl/AccountTxPaging.cpp | 2 +- src/ripple/app/paths/PathRequest.cpp | 8 +- src/ripple/app/paths/Pathfinder.cpp | 4 +- src/ripple/app/paths/cursor/AdvanceNode.cpp | 2 +- src/ripple/app/paths/impl/PaymentView.h | 4 +- src/ripple/app/tx/apply.h | 56 + src/ripple/app/tx/impl/ApplyContext.cpp | 55 + .../ApplyContext.h} | 81 +- src/ripple/app/tx/impl/CancelOffer.cpp | 107 +- src/ripple/app/tx/impl/CancelOffer.h | 48 + src/ripple/app/tx/impl/CancelTicket.cpp | 123 +- src/ripple/app/tx/impl/CancelTicket.h | 46 + src/ripple/app/tx/impl/Change.cpp | 301 ++-- src/ripple/app/tx/impl/Change.h | 63 + src/ripple/app/tx/impl/CreateOffer.cpp | 1443 ++++++++--------- src/ripple/app/tx/impl/CreateOffer.h | 119 ++ src/ripple/app/tx/impl/CreateTicket.cpp | 217 ++- src/ripple/app/tx/impl/CreateTicket.h | 54 + src/ripple/app/tx/impl/OfferStream.cpp | 24 +- src/ripple/app/tx/impl/OfferStream.h | 9 +- src/ripple/app/tx/impl/Payment.cpp | 640 ++++---- src/ripple/app/tx/impl/Payment.h | 55 + src/ripple/app/tx/impl/SetAccount.cpp | 676 ++++---- src/ripple/app/tx/impl/SetAccount.h | 52 + src/ripple/app/tx/impl/SetRegularKey.cpp | 110 +- src/ripple/app/tx/impl/SetRegularKey.h | 50 + src/ripple/app/tx/impl/SetSignerList.cpp | 109 +- src/ripple/app/tx/impl/SetSignerList.h | 78 + src/ripple/app/tx/impl/SetTrust.cpp | 758 +++++---- src/ripple/app/tx/impl/SetTrust.h | 49 + src/ripple/app/tx/impl/Taker.cpp | 32 +- src/ripple/app/tx/impl/Taker.h | 82 +- src/ripple/app/tx/impl/TransactionEngine.cpp | 254 --- src/ripple/app/tx/impl/Transactor.cpp | 318 ++-- src/ripple/app/tx/impl/Transactor.h | 89 +- src/ripple/app/tx/impl/apply.cpp | 86 + src/ripple/app/tx/tests/Offer.test.cpp | 2 +- src/ripple/app/tx/tests/common_transactor.cpp | 10 +- src/ripple/ledger/View.h | 185 ++- src/ripple/ledger/impl/View.cpp | 4 +- src/ripple/protocol/TER.h | 7 +- src/ripple/rpc/handlers/RipplePathFind.cpp | 6 +- src/ripple/rpc/handlers/TransactionEntry.cpp | 2 +- src/ripple/rpc/handlers/Tx.cpp | 2 +- src/ripple/rpc/impl/Utilities.cpp | 2 +- src/ripple/rpc/impl/Utilities.h | 2 +- src/ripple/test/jtx/Env.h | 7 +- src/ripple/test/jtx/impl/Env.cpp | 9 +- src/ripple/unity/app_ledger.cpp | 1 + src/ripple/unity/app_tx.cpp | 4 +- 71 files changed, 4229 insertions(+), 3615 deletions(-) rename src/ripple/app/{tx/TransactionMeta.h => ledger/TxMeta.h} (88%) rename src/ripple/app/{tx/impl/TransactionMeta.cpp => ledger/impl/TxMeta.cpp} (84%) create mode 100644 src/ripple/app/tx/apply.h create mode 100644 src/ripple/app/tx/impl/ApplyContext.cpp rename src/ripple/app/tx/{TransactionEngine.h => impl/ApplyContext.h} (53%) create mode 100644 src/ripple/app/tx/impl/CancelOffer.h create mode 100644 src/ripple/app/tx/impl/CancelTicket.h create mode 100644 src/ripple/app/tx/impl/Change.h create mode 100644 src/ripple/app/tx/impl/CreateOffer.h create mode 100644 src/ripple/app/tx/impl/CreateTicket.h create mode 100644 src/ripple/app/tx/impl/Payment.h create mode 100644 src/ripple/app/tx/impl/SetAccount.h create mode 100644 src/ripple/app/tx/impl/SetRegularKey.h create mode 100644 src/ripple/app/tx/impl/SetSignerList.h create mode 100644 src/ripple/app/tx/impl/SetTrust.h delete mode 100644 src/ripple/app/tx/impl/TransactionEngine.cpp create mode 100644 src/ripple/app/tx/impl/apply.cpp diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 303da0ca9..4dad3381b 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -1409,6 +1409,10 @@ True True + + True + True + @@ -1475,6 +1479,8 @@ + + True True @@ -1719,6 +1725,18 @@ True True + + + + True + True + + + True + True + + + True True @@ -1729,22 +1747,32 @@ True True + + True True + + True True + + True True + + True True + + True True @@ -1765,22 +1793,32 @@ True True + + True True + + True True + + True True + + True True + + True True @@ -1801,18 +1839,10 @@ True True - - True - True - True True - - True - True - True True @@ -1849,12 +1879,8 @@ - - - - @@ -1869,6 +1895,8 @@ + + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index 2d32fa1b4..a9039d96a 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -2145,6 +2145,9 @@ ripple\app\ledger\impl + + ripple\app\ledger\impl + ripple\app\ledger @@ -2217,6 +2220,9 @@ ripple\app\ledger + + ripple\app\ledger + ripple\app\main @@ -2454,6 +2460,18 @@ ripple\app\tests + + ripple\app\tx + + + ripple\app\tx\impl + + + ripple\app\tx\impl + + + ripple\app\tx\impl + ripple\app\tx\impl @@ -2463,18 +2481,33 @@ ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl @@ -2493,18 +2526,33 @@ ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl + + ripple\app\tx\impl + ripple\app\tx\impl @@ -2523,15 +2571,9 @@ ripple\app\tx\impl - - ripple\app\tx\impl - ripple\app\tx\impl - - ripple\app\tx\impl - ripple\app\tx\impl @@ -2568,15 +2610,9 @@ ripple\app\tx - - ripple\app\tx - ripple\app\tx - - ripple\app\tx - ripple\basics @@ -2598,6 +2634,9 @@ ripple\basics + + ripple\basics + ripple\basics diff --git a/src/ripple/app/ledger/AcceptedLedgerTx.cpp b/src/ripple/app/ledger/AcceptedLedgerTx.cpp index bb1158c27..aee8ea848 100644 --- a/src/ripple/app/ledger/AcceptedLedgerTx.cpp +++ b/src/ripple/app/ledger/AcceptedLedgerTx.cpp @@ -36,7 +36,7 @@ AcceptedLedgerTx::AcceptedLedgerTx (Ledger::ref ledger, SerialIter& sit) mTxn = std::make_shared (std::ref (txnIt)); mRawMeta = sit.getVL (); - mMeta = std::make_shared (mTxn->getTransactionID (), + mMeta = std::make_shared (mTxn->getTransactionID (), ledger->getLedgerSeq (), mRawMeta); mAffected = mMeta->getAffectedAccounts (); mResult = mMeta->getResultTER (); @@ -44,7 +44,7 @@ AcceptedLedgerTx::AcceptedLedgerTx (Ledger::ref ledger, SerialIter& sit) } AcceptedLedgerTx::AcceptedLedgerTx (Ledger::ref ledger, - STTx::ref txn, TransactionMetaSet::ref met) + STTx::ref txn, TxMeta::ref met) : mLedger (ledger) , mTxn (txn) , mMeta (met) diff --git a/src/ripple/app/ledger/AcceptedLedgerTx.h b/src/ripple/app/ledger/AcceptedLedgerTx.h index e91a9cc28..6bc6c59ef 100644 --- a/src/ripple/app/ledger/AcceptedLedgerTx.h +++ b/src/ripple/app/ledger/AcceptedLedgerTx.h @@ -53,14 +53,14 @@ public: public: AcceptedLedgerTx (Ledger::ref ledger, SerialIter& sit); AcceptedLedgerTx (Ledger::ref ledger, STTx::ref, - TransactionMetaSet::ref); + TxMeta::ref); AcceptedLedgerTx (Ledger::ref ledger, STTx::ref, TER result); STTx::ref getTxn () const { return mTxn; } - TransactionMetaSet::ref getMeta () const + TxMeta::ref getMeta () const { return mMeta; } @@ -105,7 +105,7 @@ public: private: Ledger::pointer mLedger; STTx::pointer mTxn; - TransactionMetaSet::pointer mMeta; + TxMeta::pointer mMeta; TER mResult; boost::container::flat_set mAffected; Blob mRawMeta; diff --git a/src/ripple/app/ledger/Ledger.cpp b/src/ripple/app/ledger/Ledger.cpp index 69440639c..a7e4882c5 100644 --- a/src/ripple/app/ledger/Ledger.cpp +++ b/src/ripple/app/ledger/Ledger.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -81,9 +82,6 @@ makeGenesisAccount (AccountID const& id, Ledger::Ledger (RippleAddress const& masterPublicKey, std::uint64_t balanceInDrops) : mTotCoins (balanceInDrops) - , seq_ (1) // First Ledger - , mCloseTime (0) - , mParentCloseTime (0) , mCloseResolution (ledgerDefaultTimeResolution) , mCloseFlags (0) , mImmutable (false) @@ -94,13 +92,15 @@ Ledger::Ledger (RippleAddress const& masterPublicKey, // VFALCO Needs audit , fees_(getFees(*this, getConfig())) { + // first ledger + info_.seq = 1; auto sle = makeGenesisAccount( calcAccountID(masterPublicKey), balanceInDrops); WriteLog (lsTRACE, Ledger) << "root account: " << sle->getJson(0); unchecked_insert(std::move(sle)); - stateMap_->flushDirty (hotACCOUNT_NODE, seq_); + stateMap_->flushDirty (hotACCOUNT_NODE, info_.seq); } Ledger::Ledger (uint256 const& parentHash, @@ -117,9 +117,6 @@ Ledger::Ledger (uint256 const& parentHash, , mTransHash (transHash) , mAccountHash (accountHash) , mTotCoins (totCoins) - , seq_ (ledgerSeq) - , mCloseTime (closeTime) - , mParentCloseTime (parentCloseTime) , mCloseResolution (closeResolution) , mCloseFlags (closeFlags) , mImmutable (true) @@ -131,6 +128,10 @@ Ledger::Ledger (uint256 const& parentHash, // VFALCO Needs audit , fees_(getFees(*this, getConfig())) { + info_.seq = ledgerSeq; + info_.parentCloseTime = parentCloseTime; + info_.closeTime = closeTime; + updateHash (); loaded = true; @@ -157,12 +158,8 @@ Ledger::Ledger (Ledger const& ledger, bool isMutable) : mParentHash (ledger.mParentHash) , mTotCoins (ledger.mTotCoins) - , seq_ (ledger.seq_) - , mCloseTime (ledger.mCloseTime) - , mParentCloseTime (ledger.mParentCloseTime) , mCloseResolution (ledger.mCloseResolution) , mCloseFlags (ledger.mCloseFlags) - , mClosed (ledger.mClosed) , mValidated (ledger.mValidated) , mAccepted (ledger.mAccepted) , mImmutable (!isMutable) @@ -170,16 +167,15 @@ Ledger::Ledger (Ledger const& ledger, , stateMap_ (ledger.stateMap_->snapShot (isMutable)) // VFALCO Needs audit , fees_(getFees(*this, getConfig())) + , info_ (ledger.info_) { updateHash (); } -// Create a new ledger that follows this one +// Create a new open ledger that follows this one Ledger::Ledger (bool /* dummy */, Ledger& prevLedger) : mTotCoins (prevLedger.mTotCoins) - , seq_ (prevLedger.seq_ + 1) - , mParentCloseTime (prevLedger.mCloseTime) , mCloseResolution (prevLedger.mCloseResolution) , mCloseFlags (0) , mImmutable (false) @@ -189,6 +185,10 @@ Ledger::Ledger (bool /* dummy */, // VFALCO Needs audit , fees_(getFees(*this, getConfig())) { + info_.open = true; + info_.seq = prevLedger.info_.seq + 1; + info_.parentCloseTime = + prevLedger.info_.closeTime; prevLedger.updateHash (); mParentHash = prevLedger.getHash (); @@ -196,16 +196,17 @@ Ledger::Ledger (bool /* dummy */, assert (mParentHash.isNonZero ()); mCloseResolution = getNextLedgerTimeResolution (prevLedger.mCloseResolution, - prevLedger.getCloseAgree (), seq_); + prevLedger.getCloseAgree (), info_.seq); - if (prevLedger.mCloseTime == 0) + if (prevLedger.info_.closeTime == 0) { - mCloseTime = roundCloseTime ( + info_.closeTime = roundCloseTime ( getApp().getOPs ().getCloseTimeNC (), mCloseResolution); } else { - mCloseTime = prevLedger.mCloseTime + mCloseResolution; + info_.closeTime = + prevLedger.info_.closeTime + mCloseResolution; } } @@ -220,9 +221,6 @@ Ledger::Ledger (void const* data, Ledger::Ledger (std::uint32_t ledgerSeq, std::uint32_t closeTime) : mTotCoins (0) - , seq_ (ledgerSeq) - , mCloseTime (closeTime) - , mParentCloseTime (0) , mCloseResolution (ledgerDefaultTimeResolution) , mCloseFlags (0) , mImmutable (false) @@ -235,6 +233,9 @@ Ledger::Ledger (std::uint32_t ledgerSeq, std::uint32_t closeTime) // VFALCO Needs audit , fees_(getFees(*this, getConfig())) { + info_.seq = ledgerSeq; + info_.parentCloseTime = 0; + info_.closeTime = closeTime; } //------------------------------------------------------------------------------ @@ -275,13 +276,13 @@ void Ledger::updateHash() // VFALCO This has to match addRaw mHash = sha512Half( HashPrefix::ledgerMaster, - std::uint32_t(seq_), + std::uint32_t(info_.seq), std::uint64_t(mTotCoins), mParentHash, mTransHash, mAccountHash, - std::uint32_t(mParentCloseTime), - std::uint32_t(mCloseTime), + std::uint32_t(info_.parentCloseTime), + std::uint32_t(info_.closeTime), std::uint8_t(mCloseResolution), std::uint8_t(mCloseFlags)); mValidHash = true; @@ -292,13 +293,13 @@ void Ledger::setRaw (SerialIter& sit, bool hasPrefix) if (hasPrefix) sit.get32 (); - seq_ = sit.get32 (); + info_.seq = sit.get32 (); mTotCoins = sit.get64 (); mParentHash = sit.get256 (); mTransHash = sit.get256 (); mAccountHash = sit.get256 (); - mParentCloseTime = sit.get32 (); - mCloseTime = sit.get32 (); + info_.parentCloseTime = sit.get32 (); + info_.closeTime = sit.get32 (); mCloseResolution = sit.get8 (); mCloseFlags = sit.get8 (); updateHash (); @@ -310,13 +311,13 @@ void Ledger::setRaw (SerialIter& sit, bool hasPrefix) void Ledger::addRaw (Serializer& s) const { - s.add32 (seq_); + s.add32 (info_.seq); s.add64 (mTotCoins); s.add256 (mParentHash); s.add256 (mTransHash); s.add256 (mAccountHash); - s.add32 (mParentCloseTime); - s.add32 (mCloseTime); + s.add32 (info_.parentCloseTime); + s.add32 (info_.closeTime); s.add8 (mCloseResolution); s.add8 (mCloseFlags); } @@ -326,9 +327,10 @@ void Ledger::setAccepted ( { // Used when we witnessed the consensus. Rounds the close time, updates the // hash, and sets the ledger accepted and immutable. - assert (mClosed && !mAccepted); - mCloseTime = correctCloseTime ? roundCloseTime (closeTime, closeResolution) - : closeTime; + assert (closed() && !mAccepted); + info_.closeTime = correctCloseTime + ? roundCloseTime (closeTime, closeResolution) + : closeTime; mCloseResolution = closeResolution; mCloseFlags = correctCloseTime ? 0 : sLCF_NoConsensusTime; mAccepted = true; @@ -338,9 +340,10 @@ void Ledger::setAccepted ( void Ledger::setAccepted () { // used when we acquired the ledger - // FIXME assert(mClosed && (mCloseTime != 0) && (mCloseResolution != 0)); + // FIXME assert(closed() && (info_.closeTime != 0) && (mCloseResolution != 0)); if ((mCloseFlags & sLCF_NoConsensusTime) == 0) - mCloseTime = roundCloseTime (mCloseTime, mCloseResolution); + info_.closeTime = roundCloseTime( + info_.closeTime, mCloseResolution); mAccepted = true; setImmutable (); @@ -396,7 +399,7 @@ getTransaction (Ledger const& ledger, bool getTransaction (Ledger const& ledger, uint256 const& txID, Transaction::pointer& txn, - TransactionMetaSet::pointer& meta, + TxMeta::pointer& meta, TransactionMaster& cache) { SHAMapTreeNode::TNType type; @@ -428,7 +431,7 @@ getTransaction (Ledger const& ledger, else it.getVL (); // skip transaction - meta = std::make_shared ( + meta = std::make_shared ( txID, ledger.seq(), it.getVL ()); } else @@ -442,7 +445,7 @@ getTransaction (Ledger const& ledger, } bool getTransactionMeta (Ledger const& ledger, - uint256 const& txID, TransactionMetaSet::pointer& meta) + uint256 const& txID, TxMeta::pointer& meta) { SHAMapTreeNode::TNType type; auto const item = @@ -456,7 +459,7 @@ bool getTransactionMeta (Ledger const& ledger, SerialIter it (item->slice()); it.getVL (); // skip transaction - meta = std::make_shared (txID, ledger.seq(), it.getVL ()); + meta = std::make_shared (txID, ledger.seq(), it.getVL ()); return true; } @@ -506,7 +509,7 @@ bool Ledger::saveValidatedLedger (bool current) WriteLog (lsFATAL, Ledger) << "sAL: " << getAccountHash () << " != " << stateMap_->getHash (); WriteLog (lsFATAL, Ledger) << "saveAcceptedLedger: seq=" - << seq_ << ", current=" << current; + << info_.seq << ", current=" << current; assert (false); } @@ -529,7 +532,7 @@ bool Ledger::saveValidatedLedger (bool current) catch (...) { WriteLog (lsWARNING, Ledger) << "An accepted ledger was missing nodes"; - getApp().getLedgerMaster().failedSave(seq_, mHash); + getApp().getLedgerMaster().failedSave(info_.seq, mHash); // Clients can now trust the database for information about this // ledger sequence. getApp().pendingSaves().erase(getLedgerSeq()); @@ -538,7 +541,7 @@ bool Ledger::saveValidatedLedger (bool current) { auto db = getApp().getLedgerDB ().checkoutDb(); - *db << boost::str (deleteLedger % seq_); + *db << boost::str (deleteLedger % info_.seq); } { @@ -604,7 +607,7 @@ bool Ledger::saveValidatedLedger (bool current) } else WriteLog (lsWARNING, Ledger) - << "Transaction in ledger " << seq_ + << "Transaction in ledger " << info_.seq << " affects no accounts"; *db << @@ -621,9 +624,9 @@ bool Ledger::saveValidatedLedger (bool current) // TODO(tom): ARG! *db << boost::str (addLedger % - to_string (getHash ()) % seq_ % to_string (mParentHash) % - beast::lexicalCastThrow (mTotCoins) % mCloseTime % - mParentCloseTime % mCloseResolution % mCloseFlags % + to_string (getHash ()) % info_.seq % to_string (mParentHash) % + beast::lexicalCastThrow (mTotCoins) % info_.closeTime % + info_.parentCloseTime % mCloseResolution % mCloseFlags % to_string (mAccountHash) % to_string (mTransHash)); } @@ -875,13 +878,13 @@ bool Ledger::isAcquiringAS (void) const boost::posix_time::ptime Ledger::getCloseTime () const { - return ptFromSeconds (mCloseTime); + return ptFromSeconds (info_.closeTime); } void Ledger::setCloseTime (boost::posix_time::ptime ptm) { assert (!mImmutable); - mCloseTime = iToSeconds (ptm); + info_.closeTime = iToSeconds (ptm); } //------------------------------------------------------------------------------ @@ -969,55 +972,50 @@ Ledger::unchecked_replace( auto const ours = std::move(sle); } +std::size_t +Ledger::txCount() const +{ + // Always zero for closed ledgers. + return 0; +} + bool Ledger::txExists (uint256 const& key) const { return txMap().hasItem (key); } -bool +void Ledger::txInsert (uint256 const& key, - std::shared_ptr const& txn, - std::shared_ptr const& metaData) + std::shared_ptr const& txn, std::shared_ptr< + Serializer const> const& metaData) { if (metaData) { // low-level - just add to table - Serializer s (txn->getDataLength () + metaData->getDataLength () + 16); + Serializer s(txn->getDataLength () + + metaData->getDataLength () + 16); s.addVL (txn->peekData ()); s.addVL (metaData->peekData ()); auto item = std::make_shared< SHAMapItem const> (key, std::move(s)); - - // VFALCO Should just terminate the app - // with a fatal error here. - - if (! txMap().addGiveItem (std::move(item), true, true)) - { - WriteLog (lsFATAL, Ledger) - << "Attempt to add transaction+MD to ledger that already had it"; - return false; - } - - auto const temp = std::move(*metaData); + if (! txMap().addGiveItem + (std::move(item), true, true)) + LogicError("duplicate_tx: " + to_string(key)); } else { // low-level - just add to table auto item = std::make_shared< - SHAMapItem const> (key, txn->peekData ()); - - if (! txMap().addGiveItem (std::move(item), true, false)) - { - WriteLog (lsWARNING, Ledger) - << "Attempt to add transaction to ledger that already had it"; - return false; - } + SHAMapItem const>(key, txn->peekData()); + if (! txMap().addGiveItem( + std::move(item), true, false)) + LogicError("duplicate_tx: " + to_string(key)); } // VFALCO TODO We could touch only the txMap touch(); - return true; } std::vector @@ -1074,7 +1072,7 @@ void Ledger::visitStateItems (std::function function) const if (mHash.isNonZero ()) { getApp().getInboundLedgers().acquire( - mHash, seq_, InboundLedger::fcGENERIC); + mHash, info_.seq, InboundLedger::fcGENERIC); } throw; } @@ -1164,10 +1162,10 @@ bool Ledger::assertSane () // VFALCO TODO Document this skip list concept void Ledger::updateSkipList () { - if (seq_ == 0) // genesis ledger has no previous ledger + if (info_.seq == 0) // genesis ledger has no previous ledger return; - std::uint32_t prevIndex = seq_ - 1; + std::uint32_t prevIndex = info_.seq - 1; // update record of every 256th ledger if ((prevIndex & 0xff) == 0) @@ -1287,6 +1285,7 @@ qualityDirDescriber ( sle->setFieldU64 (sfExchangeRate, uRate); if (isNew) { + // VFALCO NO! This shouldn't be done here! getApp().getOrderBookDB().addOrderBook( {{uTakerPaysCurrency, uTakerPaysIssuer}, {uTakerGetsCurrency, uTakerGetsIssuer}}); diff --git a/src/ripple/app/ledger/Ledger.h b/src/ripple/app/ledger/Ledger.h index 882abdf63..bf02f671f 100644 --- a/src/ripple/app/ledger/Ledger.h +++ b/src/ripple/app/ledger/Ledger.h @@ -20,16 +20,16 @@ #ifndef RIPPLE_APP_LEDGER_LEDGER_H_INCLUDED #define RIPPLE_APP_LEDGER_LEDGER_H_INCLUDED -#include -#include -#include +#include #include #include +#include +#include #include #include -#include #include #include +#include #include #include #include @@ -118,24 +118,18 @@ public: // //-------------------------------------------------------------------------- + ViewInfo const& + info() const + { + return info_; + } + Fees const& fees() const override { return fees_; } - LedgerIndex - seq() const override - { - return seq_; - } - - std::uint32_t - time() const override - { - return mParentCloseTime; - } - bool exists (Keylet const& k) const override; @@ -161,10 +155,13 @@ public: mTotCoins -= feeDrops; } + std::size_t + txCount() const override; + bool txExists (uint256 const& key) const override; - bool + void txInsert (uint256 const& key, std::shared_ptr const& txn, std::shared_ptr< @@ -187,9 +184,9 @@ public: mValidHash = false; } - void setClosed () + void setClosed() { - mClosed = true; + info_.open = false; } void setValidated() @@ -204,10 +201,10 @@ public: void setImmutable (); - // VFALCO Rename to closed + // DEPRECATED use closed() bool isClosed () const { - return mClosed; + return closed(); } bool isAccepted () const @@ -224,11 +221,14 @@ public: { return mImmutable; } - + + // Indicates that all ledger entries + // are available locally. For example, + // all in the NodeStore and memory. void setFull () { - txMap_->setLedgerSeq (seq_); - stateMap_->setLedgerSeq (seq_); + txMap_->setLedgerSeq (info_.seq); + stateMap_->setLedgerSeq (info_.seq); } // ledger signature operations @@ -266,20 +266,22 @@ public: mTotCoins = totCoins; } + // DEPRECATED std::uint32_t getCloseTimeNC () const { - return mCloseTime; + return info_.closeTime; } + // DEPRECATED Use parentCloseTime() std::uint32_t getParentCloseTimeNC () const { - return mParentCloseTime; + return info_.parentCloseTime; } - // DEPRECATED + // DEPRECATED Use seq() std::uint32_t getLedgerSeq () const { - return seq_; + return info_.seq; } int getCloseResolution () const @@ -293,10 +295,10 @@ public: } // close time functions - void setCloseTime (std::uint32_t ct) + void setCloseTime (std::uint32_t when) { assert (!mImmutable); - mCloseTime = ct; + info_.closeTime = when; } void setCloseTime (boost::posix_time::ptime); @@ -383,6 +385,7 @@ public: return mBaseFee; } + // DEPRECATED use fees() std::uint64_t getReserve (int increments) const { // Returns the required reserve in drops @@ -391,6 +394,7 @@ public: + mReserveBase; } + // DEPRECATED use fees() std::uint64_t getReserveInc () const { deprecatedUpdateCachedFees (); @@ -444,20 +448,12 @@ private: uint256 mTransHash; uint256 mAccountHash; std::uint64_t mTotCoins; - std::uint32_t seq_; - - // when this ledger closed - std::uint32_t mCloseTime; - - // when the previous ledger closed - std::uint32_t mParentCloseTime; // the resolution for this ledger close time (2-120 seconds) int mCloseResolution; // flags indicating how this ledger close took place std::uint32_t mCloseFlags; - bool mClosed = false; bool mValidated = false; bool mValidHash = false; bool mAccepted = false; @@ -470,6 +466,7 @@ private: std::mutex mutable mutex_; Fees fees_; + ViewInfo info_; // Ripple cost of the reference transaction std::uint64_t mutable mBaseFee = 0; @@ -561,13 +558,13 @@ getTransaction (Ledger const& ledger, bool getTransaction (Ledger const& ledger, uint256 const& transID, Transaction::pointer & txn, - TransactionMetaSet::pointer & txMeta, + TxMeta::pointer & txMeta, TransactionMaster& cache); bool getTransactionMeta (Ledger const&, uint256 const& transID, - TransactionMetaSet::pointer & txMeta); + TxMeta::pointer & txMeta); // VFALCO NOTE This is called from only one place bool diff --git a/src/ripple/app/ledger/LedgerConsensus.h b/src/ripple/app/ledger/LedgerConsensus.h index adab4590a..1d602af09 100644 --- a/src/ripple/app/ledger/LedgerConsensus.h +++ b/src/ripple/app/ledger/LedgerConsensus.h @@ -80,10 +80,9 @@ public: */ void applyTransactions ( SHAMap const* set, - Ledger::ref applyLedger, + BasicView& applyView, Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions, - bool openLgr, bool enableTesting = false); } // ripple diff --git a/src/ripple/app/ledger/LedgerHistory.cpp b/src/ripple/app/ledger/LedgerHistory.cpp index d66d78cdd..a011d016b 100644 --- a/src/ripple/app/ledger/LedgerHistory.cpp +++ b/src/ripple/app/ledger/LedgerHistory.cpp @@ -133,7 +133,7 @@ static void log_one(Ledger::pointer ledger, uint256 const& tx, char const* msg) { - TransactionMetaSet::pointer metaData; + TxMeta::pointer metaData; getTransactionMeta(*ledger, tx, metaData); if (metaData != nullptr) @@ -154,9 +154,9 @@ void log_metadata_difference(Ledger::pointer builtLedger, Ledger::pointer validLedger, uint256 const& tx) { - TransactionMetaSet::pointer validMetaData; + TxMeta::pointer validMetaData; getTransactionMeta(*validLedger, tx, validMetaData); - TransactionMetaSet::pointer builtMetaData; + TxMeta::pointer builtMetaData; getTransactionMeta(*builtLedger, tx, builtMetaData); assert(validMetaData != nullptr || builtMetaData != nullptr); diff --git a/src/ripple/app/ledger/LedgerToJson.h b/src/ripple/app/ledger/LedgerToJson.h index dc4355030..924255170 100644 --- a/src/ripple/app/ledger/LedgerToJson.h +++ b/src/ripple/app/ledger/LedgerToJson.h @@ -166,7 +166,7 @@ void fillJson (Object& json, LedgerFill const& fill) SerialIter tsit (make_Slice(vl)); STTx txn (tsit); - TransactionMetaSet meta ( + TxMeta meta ( item->getTag (), ledger.getLedgerSeq(), sit.getVL ()); auto&& txJson = appendObject (txns); diff --git a/src/ripple/app/ledger/MetaView.h b/src/ripple/app/ledger/MetaView.h index f91518729..81b452d5a 100644 --- a/src/ripple/app/ledger/MetaView.h +++ b/src/ripple/app/ledger/MetaView.h @@ -20,13 +20,14 @@ #ifndef RIPPLE_LEDGER_METAVIEW_H_INCLUDED #define RIPPLE_LEDGER_METAVIEW_H_INCLUDED -#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -35,6 +36,25 @@ namespace ripple { +/** Shallow-copy construction tag. + + When a MetaView is shallow-copied, the SLEs and + Serializers are shared between instances. It is + only safe to use BasicView interfaces, using + View members results in undefined behavior. +*/ +struct shallow_copy_t {}; +extern shallow_copy_t const shallow_copy; + +/** Open ledger construction tag. + + Views constructed with this tag will have the + rules of open ledgers applied during transaction + processing. +*/ +struct open_ledger_t {}; +extern open_ledger_t const open_ledger; + /** A MetaView can produce tx metadata and is attached to a parent. It's a view into a ledger used while a transaction is processing. @@ -70,99 +90,71 @@ private: // Note that this class needs to be // somewhat light-weight copy constructible. - BasicView const* parent_; + BasicView const& base_; ViewFlags flags_ = tapNONE; - LedgerIndex seq_; - std::uint32_t time_; // should be Clock::time_point + ViewInfo info_; tx_list txs_; item_list items_; - TransactionMetaSet meta_; std::uint32_t destroyedCoins_ = 0; + boost::optional deliverAmount_; public: MetaView() = delete; + MetaView(MetaView const&) = delete; MetaView& operator= (MetaView const&) = delete; /** Create a shallow copy of a MetaView. - The SLEs and Serializers in the created copy - are shared with the other view. + Effects: + Duplicates the information in the + passed MetaView. + + The SLEs and Serializers in the copy + are shared with the other view. + The copy has the same Info values. + + It is only safe to use the BasicView modification + functions. Using View modification functions will + break invariants. + */ + // VFALCO Refactor to disallow at compile time, + // breaking invariants on a shallow copy. + // + MetaView (shallow_copy_t, + MetaView const& other); + + /** Create a MetaView representing an open ledger. + + Preconditions: + + `prev` cannot represent an open ledger. + + Effects: + + The sequence number is set to the + sequence number of parent plus one. + + The parentCloseTime is set to the + closeTime of parent. It is only safe to use the BasicView modification functions. Using View modification functions will break invariants. - The seq, time, and flags are copied from `other`. - - @note This is used to apply new transactions to - the open MetaView. + @param parent A view representing the previous + ledger that this open ledger follows. */ - // VFALCO Refactor to disallow at compile time, - // breaking invariants on a shallow copy. - // - MetaView (MetaView const& other) = default; + MetaView (open_ledger_t, + BasicView const& parent); - /** Create a MetaView with a BasicView as its parent. + /** Create a nested MetaView. Effects: - The sequence number and time are set - from the passed parameters. - It is only safe to use the BasicView modification - functions. Using View modification functions will - break invariants. - - @note This is for converting a closed ledger - into an open ledger. - - @note A pointer is used to prevent confusion - with copy construction. + The ViewInfo is copied from the base. */ - // VFALCO Refactor to disallow at compile time, - // breaking invariants on a shallow copy. - // - MetaView (BasicView const* parent, - LedgerIndex seq, std::uint32_t time, - ViewFlags flags); - - /** Create a MetaView with a BasicView as its parent. - - Effects: - The sequence number and time are inherited - from the parent. - - The MetaSet is prepared to produce metadata - for a transaction with the specified key. - - @note This is for applying a particular transaction - and computing its metadata, or for applying - a transaction without extracting metadata. For - example, to calculate changes in a sandbox - and then throw the sandbox away. - - @note A pointer is used to prevent confusion - with copy construction. - */ - MetaView (BasicView const* parent, - ViewFlags flags, - boost::optional const& key = boost::none); - - /** Create a MetaView with a View as its parent. - - Effects: - The sequence number, time, and flags - are inherited from the parent. - - @note This is for stacking view for the purpose of - performing calculations or applying to an - underlying MetaView associated with a particular - transation. - - @note A pointer is used to prevent confusion - with copy construction. - */ - MetaView (View const* parent); + MetaView (BasicView const& base, + ViewFlags flags); //-------------------------------------------------------------------------- // @@ -170,22 +162,16 @@ public: // //-------------------------------------------------------------------------- + ViewInfo const& + info() const + { + return info_; + } + Fees const& fees() const override { - return parent_->fees(); - } - - LedgerIndex - seq() const override - { - return seq_; - } - - std::uint32_t - time() const override - { - return time_; + return base_.fees(); } bool @@ -213,10 +199,13 @@ public: void destroyCoins (std::uint64_t feeDrops) override; + std::size_t + txCount() const override; + bool txExists (uint256 const& key) const override; - bool + void txInsert (uint256 const& key, std::shared_ptr const& txn, std::shared_ptr< @@ -251,44 +240,77 @@ public: //-------------------------------------------------------------------------- - /** Apply changes to the parent View. + /** Apply changes to the base View. - `to` must contain contents identical to the parent - view passed upon construction, else undefined - behavior will result. + `to` must contain contents identical to the + parent view passed upon construction, else + undefined behavior will result. - After a call to apply(), the only valid operation that - may be performed on this is a call to the destructor. + After a call to apply(), the only valid operation + on the object is a call to the destructor. */ void - apply (BasicView& to); + apply (BasicView& to, + beast::Journal j = {}); + + /** Apply the results of a transaction to the base view. + + `to` must contain contents identical to the + parent view passed upon construction, else + undefined behavior will result. + + After a call to apply(), the only valid operation + on the object is a call to the destructor. + + Effects: + + The transaction is inserted to the tx map. + + If the base view represents a closed ledger, + the transaction metadata is computed and + inserted with the transaction. + + The metadata is computed by recording the + differences between the base view and the + modifications in this view. + + @param view The view to apply to. + @param tx The transaction that was processed. + @param ter The result of applying the transaction. + @param j Where to log. + */ + void + apply (BasicView& to, STTx const& tx, + TER result, beast::Journal j); // For diagnostics Json::Value getJson (int) const; - void calcRawMeta (Serializer&, TER result, std::uint32_t index); - void setDeliveredAmount (STAmount const& amt) { - meta_.setDeliveredAmount (amt); + deliverAmount_ = amt; } private: + static + bool + threadTx (TxMeta& meta, + std::shared_ptr const& to, + Mods& mods); + std::shared_ptr getForMod (uint256 const& key, - Mods& mods); + Mods& mods, beast::Journal j); bool - threadTx (AccountID const& to, - Mods& mods); + threadTx (TxMeta& meta, + AccountID const& to, Mods& mods, + beast::Journal j); bool - threadTx (std::shared_ptr const& to, - Mods& mods); - - bool - threadOwners (std::shared_ptr< - SLE const> const& sle, Mods& mods); + threadOwners (TxMeta& meta, std::shared_ptr< + SLE const> const& sle, Mods& mods, + beast::Journal j); }; } // ripple diff --git a/src/ripple/app/tx/TransactionMeta.h b/src/ripple/app/ledger/TxMeta.h similarity index 88% rename from src/ripple/app/tx/TransactionMeta.h rename to src/ripple/app/ledger/TxMeta.h index 0368e1be1..7a152f8cd 100644 --- a/src/ripple/app/tx/TransactionMeta.h +++ b/src/ripple/app/ledger/TxMeta.h @@ -28,26 +28,27 @@ namespace ripple { -class TransactionMetaSet +// VFALCO Move to ripple/app/ledger/detail, rename to TxMeta +class TxMeta { public: - using pointer = std::shared_ptr; + using pointer = std::shared_ptr; using ref = const pointer&; private: struct CtorHelper{}; template - TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, T const& data, + TxMeta (uint256 const& txID, std::uint32_t ledger, T const& data, CtorHelper); public: - TransactionMetaSet () + TxMeta () : mLedger (0) , mIndex (static_cast (-1)) , mResult (255) { } - TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, std::uint32_t index) + TxMeta (uint256 const& txID, std::uint32_t ledger, std::uint32_t index) : mTransactionID (txID) , mLedger (ledger) , mIndex (static_cast (-1)) @@ -55,15 +56,15 @@ public: { } - TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, Blob const&); - TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, std::string const&); + TxMeta (uint256 const& txID, std::uint32_t ledger, Blob const&); + TxMeta (uint256 const& txID, std::uint32_t ledger, std::string const&); void init (uint256 const& transactionID, std::uint32_t ledger); void clear () { mNodes.clear (); } - void swap (TransactionMetaSet&) noexcept; + void swap (TxMeta&) noexcept; uint256 const& getTxID () { diff --git a/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp b/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp index f9fb55245..7f972be93 100644 --- a/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp +++ b/src/ripple/app/ledger/impl/LedgerConsensusImp.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -979,15 +980,27 @@ void LedgerConsensusImp::accept (std::shared_ptr set) // Build the new last closed ledger auto newLCL = std::make_shared (false, *mPreviousLedger); + newLCL->setClosed (); // so applyTransactions sees a closed ledger // Set up to write SHAMap changes to our database, // perform updates, extract changes WriteLog (lsDEBUG, LedgerConsensus) << "Applying consensus set transactions to the" << " last closed ledger"; - applyTransactions (set.get(), newLCL, newLCL, retriableTransactions, false); + { + MetaView accum(*newLCL, tapNONE); + assert(accum.closed()); + applyTransactions (set.get(), accum, + newLCL, retriableTransactions); + accum.apply(*newLCL, + deprecatedLogs().journal("LedgerConsensus")); + } + + // retriableTransactions will include any transactions that + // made it into the consensus set but failed during application + // to the ledger. + newLCL->updateSkipList (); - newLCL->setClosed (); int asf = newLCL->stateMap().flushDirty ( hotACCOUNT_NODE, newLCL->getLedgerSeq()); @@ -1057,8 +1070,20 @@ void LedgerConsensusImp::accept (std::shared_ptr set) // Build new open ledger auto newOL = std::make_shared (true, *newLCL); + MetaView accum(*newOL, tapNONE); + assert(accum.open()); // Apply disputed transactions that didn't get in + // + // The first crack of transactions to get into the new + // open ledger goes to transactions proposed by a validator + // we trust but not included in the consensus set. + // + // These are done first because they are the most likely + // to receive agreement during consensus. They are also + // ordered logically "sooner" than transactions not mentioned + // in the previous consensus round. + // bool anyDisputes = false; for (auto& it : mDisputes) { @@ -1075,6 +1100,7 @@ void LedgerConsensusImp::accept (std::shared_ptr set) auto txn = std::make_shared(sit); retriableTransactions.push_back (txn); + anyDisputes = true; } catch (...) @@ -1087,7 +1113,8 @@ void LedgerConsensusImp::accept (std::shared_ptr set) if (anyDisputes) { - applyTransactions (nullptr, newOL, newLCL, retriableTransactions, true); + applyTransactions (nullptr, + accum, newLCL, retriableTransactions); } { @@ -1102,17 +1129,16 @@ void LedgerConsensusImp::accept (std::shared_ptr set) WriteLog (lsDEBUG, LedgerConsensus) << "Applying transactions from current open ledger"; applyTransactions (&oldOL->txMap(), - newOL, newLCL, retriableTransactions, true); + accum, newLCL, retriableTransactions); } // Apply local transactions - TransactionEngine engine (newOL); - for (auto it : m_localTX.getTxSet ()) { try { - engine.applyTransaction (*it.second, tapOPEN_LEDGER); + apply (accum, *it.second, tapNONE, getConfig(), + deprecatedLogs().journal("LedgerConsensus")); } catch (...) { @@ -1123,6 +1149,9 @@ void LedgerConsensusImp::accept (std::shared_ptr set) } } + accum.apply(*newOL, + deprecatedLogs().journal("LedgerConsensus")); + // We have a new Last Closed Ledger and new Open Ledger ledgerMaster_.pushLedger (newLCL, newOL); } @@ -1715,20 +1744,18 @@ make_LedgerConsensus (ConsensusImp& consensus, int previousProposers, @param engine The transaction engine containing the ledger. @param txn The transaction to be applied to ledger. - @param openLedger true if ledger is open @param retryAssured true if the transaction should be retried on failure. @return One of resultSuccess, resultFail or resultRetry. */ static -int applyTransaction ( - TransactionEngine& engine, - std::shared_ptr const& txn, - bool openLedger, - bool retryAssured, - bool enableTesting) +int +applyTransaction (BasicView& view, + STTx const& txn, + bool retryAssured, + bool enableTesting) { // Returns false if the transaction has need not be retried. - ViewFlags parms = openLedger ? tapOPEN_LEDGER : tapNONE; + ViewFlags parms = tapNONE; if (enableTesting) parms = parms | tapENABLE_TESTING; @@ -1738,21 +1765,22 @@ int applyTransaction ( parms = static_cast (parms | tapRETRY); } - if ((getApp().getHashRouter ().getFlags (txn->getTransactionID ()) + if ((getApp().getHashRouter ().getFlags (txn.getTransactionID ()) & SF_SIGGOOD) == SF_SIGGOOD) { parms = static_cast (parms | tapNO_CHECK_SIGN); } WriteLog (lsDEBUG, LedgerConsensus) << "TXN " - << txn->getTransactionID () - << (openLedger ? " open" : " closed") + << txn.getTransactionID () + //<< (engine.view().open() ? " open" : " closed") // because of the optional in engine << (retryAssured ? "/retry" : "/final"); - WriteLog (lsTRACE, LedgerConsensus) << txn->getJson (0); + WriteLog (lsTRACE, LedgerConsensus) << txn.getJson (0); try { - auto result = engine.applyTransaction (*txn, parms); + auto const result = apply(view, txn, parms, getConfig(), + deprecatedLogs().journal("LedgerConsensus")); if (result.second) { @@ -1783,14 +1811,11 @@ int applyTransaction ( void applyTransactions ( SHAMap const* set, - Ledger::ref applyLedger, + BasicView& applyView, Ledger::ref checkLedger, CanonicalTXSet& retriableTransactions, - bool openLgr, bool enableTesting) { - TransactionEngine engine (applyLedger); - if (set) { for (auto const item : *set) @@ -1805,9 +1830,11 @@ void applyTransactions ( try { SerialIter sit (item->slice()); - auto txn = std::make_shared(sit); + auto const txn = + std::make_shared(sit); - if (applyTransaction (engine, txn, openLgr, true, enableTesting) == LedgerConsensusImp::resultRetry) + if (applyTransaction(applyView, *txn, true, enableTesting) == + LedgerConsensusImp::resultRetry) { // On failure, stash the failed transaction for // later retry. @@ -1836,8 +1863,8 @@ void applyTransactions ( { try { - switch (applyTransaction (engine, it->second, - openLgr, certainRetry, enableTesting)) + switch (applyTransaction (applyView, + *it->second, certainRetry, enableTesting)) { case LedgerConsensusImp::resultSuccess: it = retriableTransactions.erase (it); diff --git a/src/ripple/app/ledger/impl/LedgerMaster.cpp b/src/ripple/app/ledger/impl/LedgerMaster.cpp index c894aaf8b..7b7b6688c 100644 --- a/src/ripple/app/ledger/impl/LedgerMaster.cpp +++ b/src/ripple/app/ledger/impl/LedgerMaster.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -352,20 +352,20 @@ public: // Start with a mutable snapshot of the open ledger auto const ledger = mCurrentLedger.getMutable(); - TransactionEngine engine (ledger); - int recovers = 0; for (auto const& it : mHeldTransactions) { try { - ViewFlags tepFlags = tapOPEN_LEDGER; + ViewFlags tepFlags = tapNONE; if (getApp().getHashRouter ().addSuppressionFlags (it.first.getTXID (), SF_SIGGOOD)) tepFlags = static_cast (tepFlags | tapNO_CHECK_SIGN); - auto ret = engine.applyTransaction (*it.second, tepFlags); + auto const ret = apply( + *ledger, *it.second, tepFlags, getConfig(), + deprecatedLogs().journal("LedgerMaster")); if (ret.second) ++recovers; diff --git a/src/ripple/app/ledger/impl/MetaView.cpp b/src/ripple/app/ledger/impl/MetaView.cpp index d72eeef81..63be9b007 100644 --- a/src/ripple/app/ledger/impl/MetaView.cpp +++ b/src/ripple/app/ledger/impl/MetaView.cpp @@ -18,10 +18,11 @@ //============================================================================== #include +#include +#include #include #include #include -#include #include #include #include @@ -42,34 +43,39 @@ namespace ripple { #define DIR_NODE_MAX 32 #endif -MetaView::MetaView (BasicView const* parent, - LedgerIndex seq, std::uint32_t time, +shallow_copy_t const shallow_copy {}; +open_ledger_t const open_ledger {}; + +MetaView::MetaView (shallow_copy_t, + MetaView const& other) + : base_ (other.base_) + , flags_ (other.flags_) + , info_ (other.info_) + , txs_ (other.txs_) + , items_ (other.items_) + , destroyedCoins_( + other.destroyedCoins_) +{ +} + +MetaView::MetaView (open_ledger_t, + BasicView const& parent) + : base_ (parent) + , flags_ (tapNONE) + , info_ (parent.info()) +{ + assert(! parent.open()); + info_.open = true; + info_.seq = parent.info().seq + 1; + info_.parentCloseTime = + parent.info().closeTime; +} + +MetaView::MetaView (BasicView const& base, ViewFlags flags) - : parent_ (parent) + : base_ (base) , flags_ (flags) - , seq_ (parent->seq()) - , time_ (parent->time()) -{ -} - -MetaView::MetaView (BasicView const* parent, - ViewFlags flags, boost::optional< - uint256> const& key) - : parent_ (parent) - , flags_ (flags) - , seq_ (parent->seq()) - , time_ (parent->time()) -{ - // VFALCO This needs to be refactored - if (key) - meta_.init (*key, seq_); -} - -MetaView::MetaView (View const* parent) - : parent_ (parent) - , flags_ (parent->flags()) - , seq_ (parent->seq()) - , time_ (parent->time()) + , info_ (base.info()) { } @@ -81,7 +87,7 @@ MetaView::exists (Keylet const& k) const assert(k.key.isNonZero()); auto const iter = items_.find(k.key); if (iter == items_.end()) - return parent_->exists(k); + return base_.exists(k); if (iter->second.first == taaDELETE) return false; if (! k.check(*iter->second.second)) @@ -99,11 +105,11 @@ MetaView::succ (uint256 const& key, { boost::optional next = key; item_list::const_iterator iter; - // Find parent successor that is + // Find base successor that is // not also deleted in our list do { - next = parent_->succ(*next, last); + next = base_.succ(*next, last); if (! next) break; iter = items_.find(*next); @@ -141,7 +147,7 @@ MetaView::read (Keylet const& k) const if (iter == items_.end()) { auto const sle = - parent_->read(k); + base_.read(k); if (! sle) return nullptr; return sle; @@ -163,13 +169,13 @@ MetaView::unchecked_erase (uint256 const& key) if (iter == items_.end() || iter->first != key) { - assert(parent_->exists( + assert(base_.exists( keylet::unchecked(key))); using namespace std; items_.emplace_hint(iter, piecewise_construct, forward_as_tuple(key), forward_as_tuple( taaDELETE, make_shared( - *parent_->read(keylet::unchecked(key))))); + *base_.read(keylet::unchecked(key))))); return true; } if (iter->second.first == taaCREATE) @@ -192,7 +198,7 @@ MetaView::unchecked_insert( iter->first != sle->key()) { // VFALCO return Keylet from SLE - assert(! parent_->exists(Keylet{ + assert(! base_.exists(Keylet{ sle->getType(), sle->key()})); using namespace std; items_.emplace_hint(iter, piecewise_construct, @@ -217,7 +223,7 @@ MetaView::unchecked_insert( break; }; // VFALCO return Keylet from SLE - assert(parent_->exists( + assert(base_.exists( Keylet{sle->getType(), sle->key()})); iter->second.first = taaMODIFY; iter->second.second = std::move(sle); @@ -232,7 +238,7 @@ MetaView::unchecked_replace (std::shared_ptr&& sle) iter->first != sle->key()) { // VFALCO return Keylet from SLE - assert(parent_->exists(Keylet{ + assert(base_.exists(Keylet{ sle->getType(), sle->key()})); using namespace std; items_.emplace_hint(iter, piecewise_construct, @@ -255,29 +261,33 @@ MetaView::destroyCoins (std::uint64_t feeDrops) destroyedCoins_ += feeDrops; } +std::size_t +MetaView::txCount() const +{ + return base_.txCount() + txs_.size(); +} + bool MetaView::txExists (uint256 const& key) const { if (txs_.count(key) > 0) return true; - return parent_->txExists(key); + return base_.txExists(key); } -bool +void MetaView::txInsert (uint256 const& key, - std::shared_ptr const& txn, - std::shared_ptr const& metaData) + std::shared_ptr const& txn, std::shared_ptr< + Serializer const> const& metaData) { - bool already = txs_.count(key); - if (! already) - already = parent_->txExists(key); - if (already) - return false; + if (txs_.count(key) || + base_.txExists(key)) + LogicError("duplicate_tx: " + to_string(key)); txs_.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple( txn, metaData)); - return true; } std::vector @@ -301,7 +311,7 @@ MetaView::peek (Keylet const& k) iter->first != k.key) { auto const sle = - parent_->read(k); + base_.read(k); if (! sle) return nullptr; // Make our own copy @@ -349,7 +359,7 @@ MetaView::insert (std::shared_ptr const& sle) iter->first != sle->key()) { // VFALCO return Keylet from SLE - assert(! parent_->exists( + assert(! base_.exists( Keylet{sle->getType(), sle->key()})); items_.emplace_hint(iter, std::piecewise_construct, std::forward_as_tuple(sle->getIndex()), @@ -372,7 +382,7 @@ MetaView::insert (std::shared_ptr const& sle) break; } // Existed in parent, deleted here - assert(parent_->exists( + assert(base_.exists( Keylet{sle->getType(), sle->key()})); iter->second.first = taaMODIFY; iter->second.second = sle; @@ -386,7 +396,7 @@ MetaView::update (std::shared_ptr const& sle) iter->first != sle->key()) { // VFALCO return Keylet from SLE - assert(parent_->exists( + assert(base_.exists( Keylet{sle->getType(), sle->key()})); items_.emplace_hint(iter, std::piecewise_construct, std::forward_as_tuple(sle->key()), @@ -404,9 +414,11 @@ MetaView::update (std::shared_ptr const& sle) //------------------------------------------------------------------------------ -void MetaView::apply (BasicView& to) +void MetaView::apply( + BasicView& to, beast::Journal j) { - assert(&to == parent_); + assert(&to == &base_); + assert(to.info().open == info_.open); // Write back the account states for (auto& item : items_) { @@ -521,23 +533,212 @@ Json::Value MetaView::getJson (int) const ret[jss::nodes] = nodes; - ret[jss::metaData] = meta_.getJson (0); + // VFALCO The meta only exists during apply() now + //ret[jss::metaData] = meta.getJson (0); return ret; } //------------------------------------------------------------------------------ +void +MetaView::apply (BasicView& to, + STTx const& tx, TER ter, beast::Journal j) +{ + auto const sTx = + std::make_shared(); + tx.add(*sTx); + + std::shared_ptr sMeta; + + if (closed()) + { + TxMeta meta; + // VFALCO Shouldn't TxMeta ctor do this? + meta.init (tx.getTransactionID(), seq()); + if (deliverAmount_) + meta.setDeliveredAmount( + *deliverAmount_); + + Mods newMod; + for (auto& it : items_) + { + auto type = &sfGeneric; + switch (it.second.first) + { + case taaMODIFY: + #ifdef META_DEBUG + JLOG(j.trace) << "modify " << it.first; + #endif + type = &sfModifiedNode; + break; + + case taaDELETE: + #ifdef META_DEBUG + JLOG(j.trace) << "delete " << it.first; + #endif + type = &sfDeletedNode; + break; + + case taaCREATE: + #ifdef META_DEBUG + JLOG(j.trace) << "insert " << it.first; + #endif + type = &sfCreatedNode; + break; + + default: // ignore these + break; + } + if (type == &sfGeneric) + continue; + auto const origNode = + base_.read(keylet::unchecked(it.first)); + auto curNode = it.second.second; + if ((type == &sfModifiedNode) && (*curNode == *origNode)) + continue; + std::uint16_t nodeType = curNode + ? curNode->getFieldU16 (sfLedgerEntryType) + : origNode->getFieldU16 (sfLedgerEntryType); + meta.setAffectedNode (it.first, *type, nodeType); + if (type == &sfDeletedNode) + { + assert (origNode && curNode); + threadOwners (meta, origNode, newMod, j); + + STObject prevs (sfPreviousFields); + for (auto const& obj : *origNode) + { + // go through the original node for + // modified fields saved on modification + if (obj.getFName().shouldMeta( + SField::sMD_ChangeOrig) && + ! curNode->hasMatchingEntry (obj)) + prevs.emplace_back (obj); + } + + if (!prevs.empty ()) + meta.getAffectedNode(it.first).emplace_back(std::move(prevs)); + + STObject finals (sfFinalFields); + for (auto const& obj : *curNode) + { + // go through the final node for final fields + if (obj.getFName().shouldMeta( + SField::sMD_Always | SField::sMD_DeleteFinal)) + finals.emplace_back (obj); + } + + if (!finals.empty ()) + meta.getAffectedNode (it.first).emplace_back (std::move(finals)); + } + else if (type == &sfModifiedNode) + { + assert (curNode && origNode); + + if (curNode->isThreadedType ()) // thread transaction to node it modified + threadTx (meta, curNode, newMod); + + STObject prevs (sfPreviousFields); + for (auto const& obj : *origNode) + { + // search the original node for values saved on modify + if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) + prevs.emplace_back (obj); + } + + if (!prevs.empty ()) + meta.getAffectedNode (it.first).emplace_back (std::move(prevs)); + + STObject finals (sfFinalFields); + for (auto const& obj : *curNode) + { + // search the final node for values saved always + if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_ChangeNew)) + finals.emplace_back (obj); + } + + if (!finals.empty ()) + meta.getAffectedNode (it.first).emplace_back (std::move(finals)); + } + else if (type == &sfCreatedNode) // if created, thread to owner(s) + { + assert (curNode && !origNode); + threadOwners (meta, curNode, newMod, j); + + if (curNode->isThreadedType ()) // always thread to self + threadTx (meta, curNode, newMod); + + STObject news (sfNewFields); + for (auto const& obj : *curNode) + { + // save non-default values + if (!obj.isDefault () && + obj.getFName().shouldMeta( + SField::sMD_Create | SField::sMD_Always)) + news.emplace_back (obj); + } + + if (!news.empty ()) + meta.getAffectedNode (it.first).emplace_back (std::move(news)); + } + else + { + assert (false); + } + } + + // add any new modified nodes to the modification set + for (auto& it : newMod) + update (it.second); + + sMeta = std::make_shared(); + meta.addRaw (*sMeta, ter, txCount()); + + // VFALCO For diagnostics do we want to show + // metadata even when the base view is open? + JLOG(j.trace) << + "metadata " << meta.getJson (0); + } + + txInsert (tx.getTransactionID(), + sTx, sMeta); + + apply(to); +} + +//------------------------------------------------------------------------------ + +bool +MetaView::threadTx (TxMeta& meta, + std::shared_ptr const& to, + Mods& mods) +{ + uint256 prevTxID; + std::uint32_t prevLgrID; + if (! to->thread(meta.getTxID(), + meta.getLgrSeq(), prevTxID, prevLgrID)) + return false; + if (prevTxID.isZero () || + TxMeta::thread( + meta.getAffectedNode(to, + sfModifiedNode), prevTxID, + prevLgrID)) + return true; + assert (false); + return false; +} + std::shared_ptr MetaView::getForMod (uint256 const& key, - Mods& mods) + Mods& mods, beast::Journal j) { auto iter = items_.find (key); if (iter != items_.end ()) { if (iter->second.first == taaDELETE) { - WriteLog (lsFATAL, View) << + JLOG(j.fatal) << "Trying to thread to deleted node"; return nullptr; } @@ -554,7 +755,7 @@ MetaView::getForMod (uint256 const& key, } } // VFALCO NOTE Should this be read() or peek()? - auto const csle = parent_->read( + auto const csle = base_.read( keylet::unchecked(key)); if (! csle) return nullptr; @@ -566,50 +767,33 @@ MetaView::getForMod (uint256 const& key, } bool -MetaView::threadTx (AccountID const& to, - Mods& mods) +MetaView::threadTx (TxMeta& meta, + AccountID const& to, Mods& mods, + beast::Journal j) { auto const sle = getForMod( - keylet::account(to).key, mods); + keylet::account(to).key, mods, j); #ifdef META_DEBUG - WriteLog (lsTRACE, View) << + JLOG(j.trace) << "Thread to " << toBase58(to); #endif assert(sle); if (! sle) { - WriteLog (lsFATAL, View) << + JLOG(j.fatal) << "Threading to non-existent account: " << toBase58(to); return false; } - return threadTx (sle, mods); + return threadTx (meta, sle, mods); } bool -MetaView::threadTx( - std::shared_ptr const& to, - Mods& mods) -{ - uint256 prevTxID; - std::uint32_t prevLgrID; - if (! to->thread(meta_.getTxID(), - meta_.getLgrSeq(), prevTxID, prevLgrID)) - return false; - if (prevTxID.isZero () || - TransactionMetaSet::thread( - meta_.getAffectedNode(to, - sfModifiedNode), prevTxID, - prevLgrID)) - return true; - assert (false); - return false; -} - -bool -MetaView::threadOwners(std::shared_ptr< - SLE const> const& sle, Mods& mods) +MetaView::threadOwners (TxMeta& meta, + std::shared_ptr< + SLE const> const& sle, Mods& mods, + beast::Journal j) { // thread new or modified sle to owner or owners // VFALCO Why not isFieldPresent? @@ -618,163 +802,24 @@ MetaView::threadOwners(std::shared_ptr< { // thread to owner's account #ifdef META_DEBUG - WriteLog (lsTRACE, View) << "Thread to single owner"; + JLOG(j.trace) << "Thread to single owner"; #endif - return threadTx (sle->getAccountID(sfAccount), mods); + return threadTx (meta, sle->getAccountID( + sfAccount), mods, j); } else if (sle->getType() == ltRIPPLE_STATE) { // thread to owner's accounts #ifdef META_DEBUG - WriteLog (lsTRACE, View) << "Thread to two owners"; + JLOG(j.trace) << "Thread to two owners"; #endif return - threadTx(sle->getFieldAmount(sfLowLimit).getIssuer(), mods) && - threadTx(sle->getFieldAmount(sfHighLimit).getIssuer(), mods); + threadTx (meta, sle->getFieldAmount( + sfLowLimit).getIssuer(), mods, j) && + threadTx (meta, sle->getFieldAmount( + sfHighLimit).getIssuer(), mods, j); } return false; } -void -MetaView::calcRawMeta (Serializer& s, - TER result, std::uint32_t index) -{ - // calculate the raw meta data and return it. This must be called before the set is committed - - // Entries modified only as a result of building the transaction metadata - Mods newMod; - - for (auto& it : items_) - { - auto type = &sfGeneric; - - switch (it.second.first) - { - case taaMODIFY: -#ifdef META_DEBUG - WriteLog (lsTRACE, View) << "Modified Node " << it.first; -#endif - type = &sfModifiedNode; - break; - - case taaDELETE: -#ifdef META_DEBUG - WriteLog (lsTRACE, View) << "Deleted Node " << it.first; -#endif - type = &sfDeletedNode; - break; - - case taaCREATE: -#ifdef META_DEBUG - WriteLog (lsTRACE, View) << "Created Node " << it.first; -#endif - type = &sfCreatedNode; - break; - - default: // ignore these - break; - } - - if (type == &sfGeneric) - continue; - - auto const origNode = - parent_->read(keylet::unchecked(it.first)); - auto curNode = it.second.second; - - if ((type == &sfModifiedNode) && (*curNode == *origNode)) - continue; - - std::uint16_t nodeType = curNode - ? curNode->getFieldU16 (sfLedgerEntryType) - : origNode->getFieldU16 (sfLedgerEntryType); - - meta_.setAffectedNode (it.first, *type, nodeType); - - if (type == &sfDeletedNode) - { - assert (origNode && curNode); - threadOwners (origNode, newMod); // thread transaction to owners - - STObject prevs (sfPreviousFields); - for (auto const& obj : *origNode) - { - // go through the original node for modified fields saved on modification - if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) - prevs.emplace_back (obj); - } - - if (!prevs.empty ()) - meta_.getAffectedNode (it.first).emplace_back (std::move(prevs)); - - STObject finals (sfFinalFields); - for (auto const& obj : *curNode) - { - // go through the final node for final fields - if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_DeleteFinal)) - finals.emplace_back (obj); - } - - if (!finals.empty ()) - meta_.getAffectedNode (it.first).emplace_back (std::move(finals)); - } - else if (type == &sfModifiedNode) - { - assert (curNode && origNode); - - if (curNode->isThreadedType ()) // thread transaction to node it modified - threadTx (curNode, newMod); - - STObject prevs (sfPreviousFields); - for (auto const& obj : *origNode) - { - // search the original node for values saved on modify - if (obj.getFName ().shouldMeta (SField::sMD_ChangeOrig) && !curNode->hasMatchingEntry (obj)) - prevs.emplace_back (obj); - } - - if (!prevs.empty ()) - meta_.getAffectedNode (it.first).emplace_back (std::move(prevs)); - - STObject finals (sfFinalFields); - for (auto const& obj : *curNode) - { - // search the final node for values saved always - if (obj.getFName ().shouldMeta (SField::sMD_Always | SField::sMD_ChangeNew)) - finals.emplace_back (obj); - } - - if (!finals.empty ()) - meta_.getAffectedNode (it.first).emplace_back (std::move(finals)); - } - else if (type == &sfCreatedNode) // if created, thread to owner(s) - { - assert (curNode && !origNode); - threadOwners (curNode, newMod); - - if (curNode->isThreadedType ()) // always thread to self - threadTx (curNode, newMod); - - STObject news (sfNewFields); - for (auto const& obj : *curNode) - { - // save non-default values - if (!obj.isDefault () && obj.getFName ().shouldMeta (SField::sMD_Create | SField::sMD_Always)) - news.emplace_back (obj); - } - - if (!news.empty ()) - meta_.getAffectedNode (it.first).emplace_back (std::move(news)); - } - else assert (false); - } - - // add any new modified nodes to the modification set - for (auto& it : newMod) - update (it.second); - - meta_.addRaw (s, result, index); - WriteLog (lsTRACE, View) << "Metadata:" << meta_.getJson (0); -} - } // ripple diff --git a/src/ripple/app/tx/impl/TransactionMeta.cpp b/src/ripple/app/ledger/impl/TxMeta.cpp similarity index 84% rename from src/ripple/app/tx/impl/TransactionMeta.cpp rename to src/ripple/app/ledger/impl/TxMeta.cpp index 0bcd4c67a..070d34168 100644 --- a/src/ripple/app/tx/impl/TransactionMeta.cpp +++ b/src/ripple/app/ledger/impl/TxMeta.cpp @@ -18,7 +18,7 @@ //============================================================================== #include -#include +#include #include #include #include @@ -29,7 +29,7 @@ namespace ripple { // VFALCO TODO rename class to TransactionMeta template -TransactionMetaSet::TransactionMetaSet (uint256 const& txid, +TxMeta::TxMeta (uint256 const& txid, std::uint32_t ledger, T const& data, CtorHelper) : mTransactionID (txid) , mLedger (ledger) @@ -46,21 +46,21 @@ TransactionMetaSet::TransactionMetaSet (uint256 const& txid, setDeliveredAmount (obj.getFieldAmount (sfDeliveredAmount)); } -TransactionMetaSet::TransactionMetaSet (uint256 const& txid, +TxMeta::TxMeta (uint256 const& txid, std::uint32_t ledger, Blob const& vec) - : TransactionMetaSet (txid, ledger, vec, CtorHelper ()) + : TxMeta (txid, ledger, vec, CtorHelper ()) { } -TransactionMetaSet::TransactionMetaSet (uint256 const& txid, +TxMeta::TxMeta (uint256 const& txid, std::uint32_t ledger, std::string const& data) - : TransactionMetaSet (txid, ledger, data, CtorHelper ()) + : TxMeta (txid, ledger, data, CtorHelper ()) { } -bool TransactionMetaSet::isNodeAffected (uint256 const& node) const +bool TxMeta::isNodeAffected (uint256 const& node) const { for (auto const& n : mNodes) { @@ -71,7 +71,7 @@ bool TransactionMetaSet::isNodeAffected (uint256 const& node) const return false; } -void TransactionMetaSet::setAffectedNode (uint256 const& node, SField const& type, +void TxMeta::setAffectedNode (uint256 const& node, SField const& type, std::uint16_t nodeType) { // make sure the node exists and force its type @@ -94,7 +94,7 @@ void TransactionMetaSet::setAffectedNode (uint256 const& node, SField const& typ } boost::container::flat_set -TransactionMetaSet::getAffectedAccounts() const +TxMeta::getAffectedAccounts() const { boost::container::flat_set list; list.reserve (10); @@ -137,7 +137,7 @@ TransactionMetaSet::getAffectedAccounts() const } else { - WriteLog (lsFATAL, TransactionMetaSet) << "limit is not amount " << field.getJson (0); + WriteLog (lsFATAL, TxMeta) << "limit is not amount " << field.getJson (0); } } } @@ -148,7 +148,7 @@ TransactionMetaSet::getAffectedAccounts() const return list; } -STObject& TransactionMetaSet::getAffectedNode (SLE::ref node, SField const& type) +STObject& TxMeta::getAffectedNode (SLE::ref node, SField const& type) { assert (&type); uint256 index = node->getIndex (); @@ -167,7 +167,7 @@ STObject& TransactionMetaSet::getAffectedNode (SLE::ref node, SField const& type return obj; } -STObject& TransactionMetaSet::getAffectedNode (uint256 const& node) +STObject& TxMeta::getAffectedNode (uint256 const& node) { for (auto& n : mNodes) { @@ -178,7 +178,7 @@ STObject& TransactionMetaSet::getAffectedNode (uint256 const& node) throw std::runtime_error ("Affected node not found"); } -const STObject& TransactionMetaSet::peekAffectedNode (uint256 const& node) const +const STObject& TxMeta::peekAffectedNode (uint256 const& node) const { for (auto const& n : mNodes) { @@ -189,7 +189,7 @@ const STObject& TransactionMetaSet::peekAffectedNode (uint256 const& node) const throw std::runtime_error ("Affected node not found"); } -void TransactionMetaSet::init (uint256 const& id, std::uint32_t ledger) +void TxMeta::init (uint256 const& id, std::uint32_t ledger) { mTransactionID = id; mLedger = ledger; @@ -197,13 +197,13 @@ void TransactionMetaSet::init (uint256 const& id, std::uint32_t ledger) mDelivered = boost::optional (); } -void TransactionMetaSet::swap (TransactionMetaSet& s) noexcept +void TxMeta::swap (TxMeta& s) noexcept { assert ((mTransactionID == s.mTransactionID) && (mLedger == s.mLedger)); mNodes.swap (s.mNodes); } -bool TransactionMetaSet::thread (STObject& node, uint256 const& prevTxID, std::uint32_t prevLgrID) +bool TxMeta::thread (STObject& node, uint256 const& prevTxID, std::uint32_t prevLgrID) { if (node.getFieldIndex (sfPreviousTxnID) == -1) { @@ -223,7 +223,7 @@ static bool compare (const STObject& o1, const STObject& o2) return o1.getFieldH256 (sfLedgerIndex) < o2.getFieldH256 (sfLedgerIndex); } -STObject TransactionMetaSet::getAsObject () const +STObject TxMeta::getAsObject () const { STObject metaData (sfTransactionMetaData); assert (mResult != 255); @@ -235,7 +235,7 @@ STObject TransactionMetaSet::getAsObject () const return metaData; } -void TransactionMetaSet::addRaw (Serializer& s, TER result, std::uint32_t index) +void TxMeta::addRaw (Serializer& s, TER result, std::uint32_t index) { mResult = static_cast (result); mIndex = index; diff --git a/src/ripple/app/ledger/tests/MetaView_test.cpp b/src/ripple/app/ledger/tests/MetaView_test.cpp index 8810a77c7..0a2f3b1d4 100644 --- a/src/ripple/app/ledger/tests/MetaView_test.cpp +++ b/src/ripple/app/ledger/tests/MetaView_test.cpp @@ -152,7 +152,7 @@ class MetaView_test using namespace jtx; Env env(*this); wipe(*env.ledger); - MetaView v(env.ledger.get(), tapNONE); + MetaView v(*env.ledger, tapNONE); succ(v, 0, boost::none); v.insert(sle(1)); expect(v.exists(k(1))); @@ -189,7 +189,7 @@ class MetaView_test v0.unchecked_insert(sle(4)); v0.unchecked_insert(sle(7)); { - MetaView v1(&v0, tapNONE); + MetaView v1(v0, tapNONE); v1.insert(sle(3)); v1.insert(sle(5)); v1.insert(sle(6)); @@ -252,7 +252,7 @@ class MetaView_test v0.unchecked_insert(sle(4, 4)); { - MetaView v1(&v0, tapNONE); + MetaView v1(v0, tapNONE); v1.erase(v1.peek(k(2))); v1.insert(sle(3, 3)); auto s = v1.peek(k(4)); @@ -263,7 +263,7 @@ class MetaView_test expect(seq(v1.read(k(3))) == 3); expect(seq(v1.read(k(4))) == 5); { - MetaView v2(&v1); + MetaView v2(v1, tapNONE); auto s = v2.peek(k(3)); seq(s, 6); v2.update(s); @@ -280,7 +280,7 @@ class MetaView_test expect(seq(v1.read(k(4))) == 5); { - MetaView v2(&v1); + MetaView v2(v1, tapNONE); auto s = v2.peek(k(3)); seq(s, 6); v2.update(s); @@ -311,36 +311,41 @@ class MetaView_test { Env env(*this); wipe(*env.ledger); - MetaView v0(env.ledger.get(), tapNONE); + MetaView v0(*env.ledger, tapNONE); expect(v0.seq() != 98); expect(v0.seq() == env.ledger->seq()); - expect(v0.time() != 99); - expect(v0.time() == env.ledger->time()); + expect(v0.parentCloseTime() != 99); + expect(v0.parentCloseTime() == + env.ledger->parentCloseTime()); expect(v0.flags() == tapNONE); { - // Shallow copy - MetaView v1(v0); + MetaView v1(shallow_copy, v0); expect (v1.seq() == v0.seq()); - expect (v1.time() == v1.time()); + expect (v1.parentCloseTime() == + v1.parentCloseTime()); expect (v1.flags() == tapNONE); - MetaView v2(&v1, tapNO_CHECK_SIGN); - expect(v2.time() == v1.time()); + MetaView v2(v1, tapNO_CHECK_SIGN); + expect(v2.parentCloseTime() == + v1.parentCloseTime()); expect(v2.seq() == v1.seq()); expect(v2.flags() == tapNO_CHECK_SIGN); - MetaView v3(&v2); + MetaView v3(v2, tapNONE); expect(v3.seq() == v2.seq()); - expect(v3.time() == v2.time()); - expect(v3.flags() == v2.flags()); + expect(v3.parentCloseTime() == + v2.parentCloseTime()); + expect(v3.flags() == tapNONE); } { - PaymentView v1(&v0, tapNO_CHECK_SIGN); + PaymentView v1(v0, tapNO_CHECK_SIGN); expect(v1.seq() == v0.seq()); - expect(v1.time() == v0.time()); + expect(v1.parentCloseTime() == + v0.parentCloseTime()); expect(v1.flags() == tapNO_CHECK_SIGN); PaymentView v2(&v1); expect(v2.seq() == v1.seq()); - expect(v2.time() == v1.time()); + expect(v2.parentCloseTime() == + v1.parentCloseTime()); expect(v2.flags() == v1.flags()); } } @@ -359,9 +364,9 @@ class MetaView_test wipe(*env.ledger); BasicView& v0 = *env.ledger; v0.unchecked_insert(sle(1)); - MetaView v1(&v0, tapNONE); + MetaView v1(v0, tapNONE); { - MetaView v2(&v1); + MetaView v2(v1, tapNONE); v2.erase(v2.peek(k(1))); v2.apply(v1); } diff --git a/src/ripple/app/ledger/tests/common_ledger.cpp b/src/ripple/app/ledger/tests/common_ledger.cpp index bac91d0bf..4613f6517 100644 --- a/src/ripple/app/ledger/tests/common_ledger.cpp +++ b/src/ripple/app/ledger/tests/common_ledger.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -127,9 +128,9 @@ parseTransaction(TestAccount& account, Json::Value const& tx_json, bool sign) void applyTransaction(Ledger::pointer const& ledger, STTx const& tx, bool check) { - TransactionEngine engine(ledger); - auto r = engine.applyTransaction(tx, - tapOPEN_LEDGER | (check ? tapNONE : tapNO_CHECK_SIGN)); + auto const r = apply (*ledger, tx, + check ? tapNONE : tapNO_CHECK_SIGN, getConfig(), + beast::Journal{}); if (r.first != tesSUCCESS) throw std::runtime_error("r != tesSUCCESS"); if (!r.second) @@ -441,9 +442,11 @@ close_and_advance(Ledger::pointer& ledger, std::shared_ptr& LCL) // that other Ledger constructor can take a const Ledger. Ledger oldLCL(*LCL, false); Ledger::pointer newLCL = std::make_shared(false, oldLCL); + MetaView accum(*newLCL, tapNONE); // Set up to write SHAMap changes to our database, // perform updates, extract changes - applyTransactions(&set, newLCL, newLCL, retriableTransactions, false); + applyTransactions(&set, accum, newLCL, retriableTransactions); + accum.apply(*newLCL, {}); newLCL->updateSkipList(); newLCL->setClosed(); newLCL->stateMap().flushDirty( diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 65bfaa176..79ce8e8d3 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -67,9 +67,9 @@ #include #include #include +#include #include #include -#include #include namespace ripple { @@ -1337,11 +1337,9 @@ bool ApplicationImp::loadOldLedger ( txn->getJson(0); Serializer s; txn->getSTransaction()->add(s); - if (! cur->txInsert(item->getTag(), - std::make_shared(std::move(s)), - nullptr)) - if (m_journal.warning) m_journal.warning << - "Unable to add transaction " << item->getTag(); + cur->txInsert(item->getTag(), + std::make_shared( + std::move(s)), nullptr); getApp().getHashRouter().setFlag (item->getTag(), SF_SIGGOOD); } @@ -1485,7 +1483,7 @@ static void addTxnSeqField () } else { - TransactionMetaSet m (transID, 0, txnMeta); + TxMeta m (transID, 0, txnMeta); txIDs.push_back (std::make_pair (transID, m.getIndex ())); } diff --git a/src/ripple/app/misc/CanonicalTXSet.h b/src/ripple/app/misc/CanonicalTXSet.h index 4c9a9e816..a392dcca8 100644 --- a/src/ripple/app/misc/CanonicalTXSet.h +++ b/src/ripple/app/misc/CanonicalTXSet.h @@ -35,7 +35,7 @@ namespace ripple { // VFALCO TODO rename to SortedTxSet class CanonicalTXSet { -public: +private: class Key { public: @@ -71,6 +71,7 @@ public: std::uint32_t mSeq; }; +public: using iterator = std::map >::iterator; using const_iterator = std::map >::const_iterator; diff --git a/src/ripple/app/misc/NetworkOPs.cpp b/src/ripple/app/misc/NetworkOPs.cpp index 467b1a53c..b29fd38e6 100644 --- a/src/ripple/app/misc/NetworkOPs.cpp +++ b/src/ripple/app/misc/NetworkOPs.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,7 @@ #include #include #include -#include +#include #include #include #include @@ -251,19 +252,6 @@ public: */ void apply (std::unique_lock& batchLock); - /** - * Apply each transaction to open ledger. - * - * @param ledger Open ledger. - * @param engine Engine that applies transactions to open ledger. - * @param transactions Batch of transactions to apply. - * @return Whether any transactions in batch succeeded. - */ - bool batchApply ( - Ledger::pointer& ledger, - TransactionEngine& engine, - std::vector& transactions); - // // Owner functions. // @@ -987,14 +975,35 @@ void NetworkOPsImp::apply (std::unique_lock& batchLock) batchLock.unlock(); - Ledger::pointer ledger; - TransactionEngine engine; - { + Ledger::pointer ledger; + bool applied = false; + boost::optional accum; auto lock = beast::make_lock(getApp().getMasterMutex()); - - if (batchApply (ledger, engine, transactions)) { + std::lock_guard lock ( + m_ledgerMaster.peekMutex()); + ledger = m_ledgerMaster.getCurrentLedgerHolder().getMutable(); + accum.emplace(*ledger, tapNONE); + for (TransactionStatus& e : transactions) + { + ViewFlags flags = tapNONE; + flags = flags | tapNO_CHECK_SIGN; + if (e.admin) + flags = flags | tapADMIN; + std::tie (e.result, e.applied) = + ripple::apply (*accum, + *e.transaction->getSTransaction(), flags, + getConfig(), deprecatedLogs().journal( + "NetworkOPs")); + applied |= e.applied; + } + } + + if (applied) + { + accum->apply(*ledger, + deprecatedLogs().journal("NetworkOPs")); ledger->setImmutable(); m_ledgerMaster.getCurrentLedgerHolder().set (ledger); } @@ -1101,28 +1110,6 @@ void NetworkOPsImp::apply (std::unique_lock& batchLock) mDispatchState = DispatchState::none; } -bool NetworkOPsImp::batchApply (Ledger::pointer& ledger, - TransactionEngine& engine, - std::vector& transactions) -{ - bool applied = false; - std::lock_guard lock (m_ledgerMaster.peekMutex()); - - ledger = m_ledgerMaster.getCurrentLedgerHolder().getMutable(); - engine.setLedger (ledger); - - for (TransactionStatus& e : transactions) - { - std::tie (e.result, e.applied) = engine.applyTransaction ( - *e.transaction->getSTransaction(), - e.admin ? (tapOPEN_LEDGER | tapNO_CHECK_SIGN | tapADMIN) : ( - tapOPEN_LEDGER | tapNO_CHECK_SIGN)); - applied |= e.applied; - } - - return applied; -} - // // Owner functions // @@ -1793,7 +1780,7 @@ NetworkOPs::AccountTxs NetworkOPsImp::getAccountTxs ( ledger->pendSaveValidated(false, false); } - ret.emplace_back (txn, std::make_shared ( + ret.emplace_back (txn, std::make_shared ( txn->getID (), txn->getLedger (), txnMeta)); } } diff --git a/src/ripple/app/misc/NetworkOPs.h b/src/ripple/app/misc/NetworkOPs.h index 94fceab18..c56032ff9 100644 --- a/src/ripple/app/misc/NetworkOPs.h +++ b/src/ripple/app/misc/NetworkOPs.h @@ -233,7 +233,7 @@ public: virtual std::size_t getLocalTxCount () = 0; // client information retrieval functions - using AccountTx = std::pair; + using AccountTx = std::pair; using AccountTxs = std::vector; virtual AccountTxs getAccountTxs ( diff --git a/src/ripple/app/misc/impl/AccountTxPaging.cpp b/src/ripple/app/misc/impl/AccountTxPaging.cpp index bda038e21..e22ad754e 100644 --- a/src/ripple/app/misc/impl/AccountTxPaging.cpp +++ b/src/ripple/app/misc/impl/AccountTxPaging.cpp @@ -46,7 +46,7 @@ convertBlobsToTxResult ( tr->setStatus (Transaction::sqlTransactionStatus(status)); tr->setLedger (ledger_index); - auto metaset = std::make_shared ( + auto metaset = std::make_shared ( tr->getID (), tr->getLedger (), rawMeta); to.emplace_back(std::move(tr), metaset); diff --git a/src/ripple/app/paths/PathRequest.cpp b/src/ripple/app/paths/PathRequest.cpp index 521408e67..dd987b8e1 100644 --- a/src/ripple/app/paths/PathRequest.cpp +++ b/src/ripple/app/paths/PathRequest.cpp @@ -525,8 +525,8 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) if (valid) { boost::optional sandbox; - sandbox.emplace(cache->getLedger().get(), - tapOPEN_LEDGER); + sandbox.emplace(*cache->getLedger(), + tapNONE); auto& sourceAccount = !isXRP (currIssuer.account) ? currIssuer.account @@ -552,8 +552,8 @@ Json::Value PathRequest::doUpdate (RippleLineCache::ref cache, bool fast) m_journal.debug << iIdentifier << " Trying with an extra path element"; spsPaths.push_back (fullLiquidityPath); - sandbox.emplace(cache->getLedger().get(), - tapOPEN_LEDGER); + sandbox.emplace(*cache->getLedger(), + tapNONE); rc = path::RippleCalc::rippleCalculate ( *sandbox, saMaxAmount, diff --git a/src/ripple/app/paths/Pathfinder.cpp b/src/ripple/app/paths/Pathfinder.cpp index 2201e5e68..dfa8e5b05 100644 --- a/src/ripple/app/paths/Pathfinder.cpp +++ b/src/ripple/app/paths/Pathfinder.cpp @@ -378,7 +378,7 @@ TER Pathfinder::getPathLiquidity ( path::RippleCalc::Input rcInput; rcInput.defaultPathsAllowed = false; - PaymentView sandbox (mLedger.get(), tapOPEN_LEDGER); + PaymentView sandbox (*mLedger, tapNONE); try { @@ -443,7 +443,7 @@ void Pathfinder::computePathRanks (int maxPaths) // Must subtract liquidity in default path from remaining amount. try { - PaymentView sandbox (mLedger.get(), tapOPEN_LEDGER); + PaymentView sandbox (*mLedger, tapNONE); path::RippleCalc::Input rcInput; rcInput.partialPaymentAllowed = true; diff --git a/src/ripple/app/paths/cursor/AdvanceNode.cpp b/src/ripple/app/paths/cursor/AdvanceNode.cpp index 3e8f17277..a9c4d5f88 100644 --- a/src/ripple/app/paths/cursor/AdvanceNode.cpp +++ b/src/ripple/app/paths/cursor/AdvanceNode.cpp @@ -212,7 +212,7 @@ TER PathCursor::advanceNode (bool const bReverse) const if (node().sleOffer->isFieldPresent (sfExpiration) && (node().sleOffer->getFieldU32 (sfExpiration) <= - view().time())) + view().parentCloseTime())) { // Offer is expired. WriteLog (lsTRACE, RippleCalc) diff --git a/src/ripple/app/paths/impl/PaymentView.h b/src/ripple/app/paths/impl/PaymentView.h index 0a652c778..e803dba02 100644 --- a/src/ripple/app/paths/impl/PaymentView.h +++ b/src/ripple/app/paths/impl/PaymentView.h @@ -73,14 +73,14 @@ public: /** @{ */ explicit PaymentView (PaymentView const* parent) - : ViewWrapper (parent) + : ViewWrapper (*parent, parent->flags()) , pv_ (parent) { } explicit PaymentView (PaymentView* parent) - : ViewWrapper (parent) + : ViewWrapper (*parent, parent->flags()) , pv_ (parent) { } diff --git a/src/ripple/app/tx/apply.h b/src/ripple/app/tx/apply.h new file mode 100644 index 000000000..b7a109eb8 --- /dev/null +++ b/src/ripple/app/tx/apply.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_APPLY_H_INCLUDED +#define RIPPLE_TX_APPLY_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** Apply a transaction to a BasicView. + + Throws: + + Exceptions are thrown on broken invariants. Callers + should catch these exceptions to protect the ledger + and the running process. + + std::logic_error + (any) + + @return A pair with the TER and a bool indicating + whether or not the transaction was applied. +*/ +// VFALCO Some call sites use try/catch some don't. +std::pair +apply (BasicView& view, + STTx const& tx, ViewFlags flags, + Config const& config, + beast::Journal journal); + +} // ripple + +#endif diff --git a/src/ripple/app/tx/impl/ApplyContext.cpp b/src/ripple/app/tx/impl/ApplyContext.cpp new file mode 100644 index 000000000..9b5f50f42 --- /dev/null +++ b/src/ripple/app/tx/impl/ApplyContext.cpp @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +ApplyContext::ApplyContext( + BasicView& base, STTx const& tx_, + ViewFlags flags, Config const& config_, + beast::Journal journal_) + : tx (tx_) + , config (config_) + , journal(journal_) + , base_ (base) + , flags_(flags) +{ + view_.emplace(base_, flags_); +} + +void +ApplyContext::discard() +{ + view_.emplace(base_, flags_); +} + +void +ApplyContext::apply(TER ter) +{ + view_->apply(base_, tx, ter, journal); +} + +} // ripple diff --git a/src/ripple/app/tx/TransactionEngine.h b/src/ripple/app/tx/impl/ApplyContext.h similarity index 53% rename from src/ripple/app/tx/TransactionEngine.h rename to src/ripple/app/tx/impl/ApplyContext.h index 03e2a98bb..223ba103e 100644 --- a/src/ripple/app/tx/TransactionEngine.h +++ b/src/ripple/app/tx/impl/ApplyContext.h @@ -17,11 +17,13 @@ */ //============================================================================== -#ifndef RIPPLE_APP_TX_TRANSACTIONENGINE_H_INCLUDED -#define RIPPLE_APP_TX_TRANSACTIONENGINE_H_INCLUDED +#ifndef RIPPLE_TX_APPLYCONTEXT_H_INCLUDED +#define RIPPLE_TX_APPLYCONTEXT_H_INCLUDED -#include #include +#include +#include +#include #include #include @@ -29,71 +31,54 @@ namespace ripple { // tx_enable_test -// One instance per ledger. -// Only one transaction applied at a time. -class TransactionEngine +/** State information when applying a tx. */ +class ApplyContext { - boost::optional mNodes; - - void txnWrite(); - -protected: - Ledger::pointer mLedger; - int mTxnSeq = 0; - public: - TransactionEngine() = default; - explicit - TransactionEngine (Ledger::ref ledger) - : mLedger (ledger) - { - assert (mLedger); - } + ApplyContext (BasicView& base, + STTx const& tx, ViewFlags flags, + Config const& config, + beast::Journal = {}); + + STTx const& tx; + Config const& config; + beast::Journal const journal; View& - view () + view() { - return *mNodes; + return *view_; } - Ledger::ref - getLedger() + View const& + view() const { - return mLedger; - } - - void - setLedger (Ledger::ref ledger) - { - assert (ledger); - mLedger = ledger; + return *view_; } /** Sets the DeliveredAmount field in the metadata */ void deliverAmount (STAmount const& delivered) { - mNodes->setDeliveredAmount(delivered); + view_->setDeliveredAmount(delivered); } - std::pair - applyTransaction (STTx const&, ViewFlags); + /** Discard changes and start fresh. */ + void + discard(); - bool - checkInvariants (TER result, STTx const& txn, ViewFlags params); + /** Apply the transaction result to the base. */ + void + apply (TER); + +private: + BasicView& base_; + ViewFlags flags_; + beast::Journal j_; + boost::optional view_; }; -inline ViewFlags operator| (const ViewFlags& l1, const ViewFlags& l2) -{ - return static_cast (static_cast (l1) | static_cast (l2)); -} - -inline ViewFlags operator& (const ViewFlags& l1, const ViewFlags& l2) -{ - return static_cast (static_cast (l1) & static_cast (l2)); -} - } // ripple #endif diff --git a/src/ripple/app/tx/impl/CancelOffer.cpp b/src/ripple/app/tx/impl/CancelOffer.cpp index cbace411e..fe9626310 100644 --- a/src/ripple/app/tx/impl/CancelOffer.cpp +++ b/src/ripple/app/tx/impl/CancelOffer.cpp @@ -1,4 +1,4 @@ -//------------------------------------------------------------------------------ + /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012, 2013 Ripple Labs Inc. @@ -18,87 +18,62 @@ //============================================================================== #include -#include +#include #include #include #include namespace ripple { -class CancelOffer - : public Transactor +TER +CancelOffer::preCheck () { -public: - CancelOffer ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("CancelOffer")) - { + std::uint32_t const uTxFlags (mTxn.getFlags ()); + if (uTxFlags & tfUniversalMask) + { + j_.trace << "Malformed transaction: " << + "Invalid flags set."; + return temINVALID_FLAG; } - TER preCheck () override + std::uint32_t const uOfferSequence = mTxn.getFieldU32 (sfOfferSequence); + + if (!uOfferSequence) { - std::uint32_t const uTxFlags (mTxn.getFlags ()); - - if (uTxFlags & tfUniversalMask) - { - m_journal.trace << "Malformed transaction: " << - "Invalid flags set."; - return temINVALID_FLAG; - } - - std::uint32_t const uOfferSequence = mTxn.getFieldU32 (sfOfferSequence); - - if (!uOfferSequence) - { - m_journal.trace << "Malformed transaction: " << - "No sequence specified."; - return temBAD_SEQUENCE; - } - - return Transactor::preCheck (); + j_.trace << "Malformed transaction: " << + "No sequence specified."; + return temBAD_SEQUENCE; } - TER doApply () override - { - std::uint32_t const uOfferSequence = mTxn.getFieldU32 (sfOfferSequence); - - if (mTxnAccount->getFieldU32 (sfSequence) - 1 <= uOfferSequence) - { - m_journal.trace << "Malformed transaction: " << - "Sequence " << uOfferSequence << " is invalid."; - return temBAD_SEQUENCE; - } - - uint256 const offerIndex (getOfferIndex (mTxnAccountID, uOfferSequence)); - - auto sleOffer = mEngine->view().peek ( - keylet::offer(offerIndex)); - - if (sleOffer) - { - m_journal.debug << "Trying to cancel offer #" << uOfferSequence; - return offerDelete (mEngine->view(), sleOffer); - } - - m_journal.debug << "Offer #" << uOfferSequence << " can't be found."; - return tesSUCCESS; - } -}; + return Transactor::preCheck (); +} TER -transact_CancelOffer ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +CancelOffer::doApply () { - return CancelOffer (txn, params, engine).apply (); + std::uint32_t const uOfferSequence = mTxn.getFieldU32 (sfOfferSequence); + + if (mTxnAccount->getFieldU32 (sfSequence) - 1 <= uOfferSequence) + { + j_.trace << "Malformed transaction: " << + "Sequence " << uOfferSequence << " is invalid."; + return temBAD_SEQUENCE; + } + + uint256 const offerIndex (getOfferIndex (mTxnAccountID, uOfferSequence)); + + auto sleOffer = view().peek ( + keylet::offer(offerIndex)); + + if (sleOffer) + { + j_.debug << "Trying to cancel offer #" << uOfferSequence; + return offerDelete (view(), sleOffer); + } + + j_.debug << "Offer #" << uOfferSequence << " can't be found."; + return tesSUCCESS; } } diff --git a/src/ripple/app/tx/impl/CancelOffer.h b/src/ripple/app/tx/impl/CancelOffer.h new file mode 100644 index 000000000..61a65c2f6 --- /dev/null +++ b/src/ripple/app/tx/impl/CancelOffer.h @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_CANCELOFFER_H_INCLUDED +#define RIPPLE_TX_CANCELOFFER_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class CancelOffer + : public Transactor +{ +public: + template + CancelOffer (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER preCheck () override; + + TER doApply () override; +}; + +} + +#endif diff --git a/src/ripple/app/tx/impl/CancelTicket.cpp b/src/ripple/app/tx/impl/CancelTicket.cpp index 8f03f4ccb..e7b52f3a1 100644 --- a/src/ripple/app/tx/impl/CancelTicket.cpp +++ b/src/ripple/app/tx/impl/CancelTicket.cpp @@ -18,89 +18,68 @@ //============================================================================== #include -#include +#include #include #include namespace ripple { -class CancelTicket - : public Transactor -{ -public: - CancelTicket ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("CancelTicket")) - { - - } - - TER doApply () override - { - assert (mTxnAccount); - - uint256 const ticketId = mTxn.getFieldH256 (sfTicketID); - - // VFALCO This is highly suspicious, we're requiring that the - // transaction provide the return value of getTicketIndex? - SLE::pointer sleTicket = mEngine->view().peek (keylet::ticket(ticketId)); - - if (!sleTicket) - return tecNO_ENTRY; - - auto const ticket_owner = - sleTicket->getAccountID (sfAccount); - - bool authorized = - mTxnAccountID == ticket_owner; - - // The target can also always remove a ticket - if (!authorized && sleTicket->isFieldPresent (sfTarget)) - authorized = (mTxnAccountID == sleTicket->getAccountID (sfTarget)); - - // And finally, anyone can remove an expired ticket - if (!authorized && sleTicket->isFieldPresent (sfExpiration)) - { - std::uint32_t const expiration = sleTicket->getFieldU32 (sfExpiration); - - if (mEngine->view().time() >= expiration) - authorized = true; - } - - if (!authorized) - return tecNO_PERMISSION; - - std::uint64_t const hint (sleTicket->getFieldU64 (sfOwnerNode)); - - TER const result = dirDelete (mEngine->view (), false, hint, - getOwnerDirIndex (ticket_owner), ticketId, false, (hint == 0)); - - adjustOwnerCount(mEngine->view(), mEngine->view().peek( - keylet::account(ticket_owner)), -1); - mEngine->view ().erase (sleTicket); - - return result; - } -}; - TER -transact_CancelTicket ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +CancelTicket::preCheck() { #if ! RIPPLE_ENABLE_TICKETS - if (! (engine->view().flags() & tapENABLE_TESTING)) + if (! (view().flags() & tapENABLE_TESTING)) return temDISABLED; #endif - return CancelTicket (txn, params, engine).apply(); + return Transactor::preCheck (); } +TER +CancelTicket::doApply () +{ + assert (mTxnAccount); + + uint256 const ticketId = mTxn.getFieldH256 (sfTicketID); + + // VFALCO This is highly suspicious, we're requiring that the + // transaction provide the return value of getTicketIndex? + SLE::pointer sleTicket = view().peek (keylet::ticket(ticketId)); + + if (!sleTicket) + return tecNO_ENTRY; + + auto const ticket_owner = + sleTicket->getAccountID (sfAccount); + + bool authorized = + mTxnAccountID == ticket_owner; + + // The target can also always remove a ticket + if (!authorized && sleTicket->isFieldPresent (sfTarget)) + authorized = (mTxnAccountID == sleTicket->getAccountID (sfTarget)); + + // And finally, anyone can remove an expired ticket + if (!authorized && sleTicket->isFieldPresent (sfExpiration)) + { + std::uint32_t const expiration = sleTicket->getFieldU32 (sfExpiration); + + if (view().parentCloseTime() >= expiration) + authorized = true; + } + + if (!authorized) + return tecNO_PERMISSION; + + std::uint64_t const hint (sleTicket->getFieldU64 (sfOwnerNode)); + + TER const result = dirDelete (ctx_.view (), false, hint, + getOwnerDirIndex (ticket_owner), ticketId, false, (hint == 0)); + + adjustOwnerCount(view(), view().peek( + keylet::account(ticket_owner)), -1); + ctx_.view ().erase (sleTicket); + + return result; +} } diff --git a/src/ripple/app/tx/impl/CancelTicket.h b/src/ripple/app/tx/impl/CancelTicket.h new file mode 100644 index 000000000..e92d0f48f --- /dev/null +++ b/src/ripple/app/tx/impl/CancelTicket.h @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2014 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_CANCELTICKET_H_INCLUDED +#define RIPPLE_TX_CANCELTICKET_H_INCLUDED + +#include +#include +#include + +namespace ripple { + +class CancelTicket + : public Transactor +{ +public: + template + CancelTicket (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER preCheck() override; + TER doApply () override; +}; + +} + +#endif diff --git a/src/ripple/app/tx/impl/Change.cpp b/src/ripple/app/tx/impl/Change.cpp index 9f1d64188..6c7e8772a 100644 --- a/src/ripple/app/tx/impl/Change.cpp +++ b/src/ripple/app/tx/impl/Change.cpp @@ -18,184 +18,157 @@ //============================================================================== #include +#include #include #include #include -#include #include #include namespace ripple { -class Change - : public Transactor +TER +Change::doApply() { -public: - Change ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("Change")) - { - } + if (mTxn.getTxnType () == ttAMENDMENT) + return applyAmendment (); - TER doApply () override - { - if (mTxn.getTxnType () == ttAMENDMENT) - return applyAmendment (); - - if (mTxn.getTxnType () == ttFEE) - return applyFee (); - - return temUNKNOWN; - } - - TER checkSign () override - { - if (mTxn.getAccountID (sfAccount).isNonZero ()) - { - m_journal.warning << "Bad source account"; - return temBAD_SRC_ACCOUNT; - } - - if (!mTxn.getSigningPubKey ().empty () || !mTxn.getSignature ().empty ()) - { - m_journal.warning << "Bad signature"; - return temBAD_SIGNATURE; - } - - return tesSUCCESS; - } - - TER checkSeq () override - { - if ((mTxn.getSequence () != 0) || mTxn.isFieldPresent (sfPreviousTxnID)) - { - m_journal.warning << "Bad sequence"; - return temBAD_SEQUENCE; - } - - return tesSUCCESS; - } - - TER payFee () override - { - if (mTxn.getTransactionFee () != STAmount ()) - { - m_journal.warning << "Non-zero fee"; - return temBAD_FEE; - } - - return tesSUCCESS; - } - - TER preCheck () override - { - mTxnAccountID = mTxn.getAccountID(sfAccount); - - if (mTxnAccountID.isNonZero ()) - { - m_journal.warning << "Bad source id"; - return temBAD_SRC_ACCOUNT; - } - - if (mParams & tapOPEN_LEDGER) - { - m_journal.warning << "Change transaction against open ledger"; - return temINVALID; - } - - return tesSUCCESS; - } - -private: - TER applyAmendment () - { - uint256 amendment (mTxn.getFieldH256 (sfAmendment)); - - auto const k = keylet::amendments(); - - SLE::pointer amendmentObject = - mEngine->view().peek (k); - - if (!amendmentObject) - { - amendmentObject = std::make_shared(k); - mEngine->view().insert(amendmentObject); - } - - STVector256 amendments = - amendmentObject->getFieldV256(sfAmendments); - - if (std::find (amendments.begin(), amendments.end(), - amendment) != amendments.end ()) - return tefALREADY; - - amendments.push_back (amendment); - amendmentObject->setFieldV256 (sfAmendments, amendments); - mEngine->view().update (amendmentObject); - - getApp().getAmendmentTable ().enable (amendment); - - if (!getApp().getAmendmentTable ().isSupported (amendment)) - getApp().getOPs ().setAmendmentBlocked (); - - return tesSUCCESS; - } - - TER applyFee () - { - auto const k = keylet::fees(); - - SLE::pointer feeObject = mEngine->view().peek (k); - - if (!feeObject) - { - feeObject = std::make_shared(k); - mEngine->view().insert(feeObject); - } - - // VFALCO-FIXME this generates errors - // m_journal.trace << - // "Previous fee object: " << feeObject->getJson (0); - - feeObject->setFieldU64 ( - sfBaseFee, mTxn.getFieldU64 (sfBaseFee)); - feeObject->setFieldU32 ( - sfReferenceFeeUnits, mTxn.getFieldU32 (sfReferenceFeeUnits)); - feeObject->setFieldU32 ( - sfReserveBase, mTxn.getFieldU32 (sfReserveBase)); - feeObject->setFieldU32 ( - sfReserveIncrement, mTxn.getFieldU32 (sfReserveIncrement)); - - mEngine->view().update (feeObject); - - // VFALCO-FIXME this generates errors - // m_journal.trace << - // "New fee object: " << feeObject->getJson (0); - m_journal.warning << "Fees have been changed"; - return tesSUCCESS; - } - - // VFALCO TODO Can this be removed? - bool mustHaveValidAccount () override - { - return false; - } -}; + if (mTxn.getTxnType () == ttFEE) + return applyFee (); + return temUNKNOWN; +} TER -transact_Change ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +Change::checkSign() { - return Change (txn, params, engine).apply (); + if (mTxn.getAccountID (sfAccount).isNonZero ()) + { + j_.warning << "Bad source account"; + return temBAD_SRC_ACCOUNT; + } + + if (!mTxn.getSigningPubKey ().empty () || !mTxn.getSignature ().empty ()) + { + j_.warning << "Bad signature"; + return temBAD_SIGNATURE; + } + + return tesSUCCESS; +} + +TER +Change::checkSeq() +{ + if ((mTxn.getSequence () != 0) || mTxn.isFieldPresent (sfPreviousTxnID)) + { + j_.warning << "Bad sequence"; + return temBAD_SEQUENCE; + } + + return tesSUCCESS; +} + +TER +Change::payFee() +{ + if (mTxn.getTransactionFee () != STAmount ()) + { + j_.warning << "Non-zero fee"; + return temBAD_FEE; + } + + return tesSUCCESS; +} + +TER +Change::preCheck() +{ + mTxnAccountID = mTxn.getAccountID(sfAccount); + + if (mTxnAccountID.isNonZero ()) + { + j_.warning << "Bad source id"; + return temBAD_SRC_ACCOUNT; + } + + if (view().open()) + { + j_.warning << "Change transaction against open ledger"; + return temINVALID; + } + + return tesSUCCESS; +} + +TER +Change::applyAmendment() +{ + uint256 amendment (mTxn.getFieldH256 (sfAmendment)); + + auto const k = keylet::amendments(); + + SLE::pointer amendmentObject = + view().peek (k); + + if (!amendmentObject) + { + amendmentObject = std::make_shared(k); + view().insert(amendmentObject); + } + + STVector256 amendments = + amendmentObject->getFieldV256(sfAmendments); + + if (std::find (amendments.begin(), amendments.end(), + amendment) != amendments.end ()) + return tefALREADY; + + amendments.push_back (amendment); + amendmentObject->setFieldV256 (sfAmendments, amendments); + view().update (amendmentObject); + + getApp().getAmendmentTable ().enable (amendment); + + if (!getApp().getAmendmentTable ().isSupported (amendment)) + getApp().getOPs ().setAmendmentBlocked (); + + return tesSUCCESS; +} + +TER +Change::applyFee() +{ + auto const k = keylet::fees(); + + SLE::pointer feeObject = view().peek (k); + + if (!feeObject) + { + feeObject = std::make_shared(k); + view().insert(feeObject); + } + + // VFALCO-FIXME this generates errors + // j_.trace << + // "Previous fee object: " << feeObject->getJson (0); + + feeObject->setFieldU64 ( + sfBaseFee, mTxn.getFieldU64 (sfBaseFee)); + feeObject->setFieldU32 ( + sfReferenceFeeUnits, mTxn.getFieldU32 (sfReferenceFeeUnits)); + feeObject->setFieldU32 ( + sfReserveBase, mTxn.getFieldU32 (sfReserveBase)); + feeObject->setFieldU32 ( + sfReserveIncrement, mTxn.getFieldU32 (sfReserveIncrement)); + + view().update (feeObject); + + // VFALCO-FIXME this generates errors + // j_.trace << + // "New fee object: " << feeObject->getJson (0); + j_.warning << "Fees have been changed"; + return tesSUCCESS; } } diff --git a/src/ripple/app/tx/impl/Change.h b/src/ripple/app/tx/impl/Change.h new file mode 100644 index 000000000..b97bb35ae --- /dev/null +++ b/src/ripple/app/tx/impl/Change.h @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_CHANGE_H_INCLUDED +#define RIPPLE_TX_CHANGE_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +class Change + : public Transactor +{ +public: + template + Change (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER doApply () override; + TER checkSign () override; + TER checkSeq () override; + TER payFee () override; + TER preCheck () override; + +private: + TER applyAmendment (); + + TER applyFee (); + + // VFALCO TODO Can this be removed? + bool mustHaveValidAccount () override + { + return false; + } +}; + +} + +#endif diff --git a/src/ripple/app/tx/impl/CreateOffer.cpp b/src/ripple/app/tx/impl/CreateOffer.cpp index 204e506b1..3c34b78c3 100644 --- a/src/ripple/app/tx/impl/CreateOffer.cpp +++ b/src/ripple/app/tx/impl/CreateOffer.cpp @@ -18,9 +18,10 @@ //============================================================================== #include +#include #include #include -#include +#include #include #include #include @@ -31,842 +32,804 @@ namespace ripple { -class CreateOffer - : public Transactor +TER +CreateOffer::checkAcceptAsset(IssueRef issue) const { -private: - // What kind of offer we are placing - CrossType cross_type_; + // Only valid for custom currencies + assert (!isXRP (issue.currency)); - /** Determine if we are authorized to hold the asset we want to get */ - TER - checkAcceptAsset(IssueRef issue) const + auto const issuerAccount = ctx_.view().read( + keylet::account(issue.account)); + + if (!issuerAccount) { - // Only valid for custom currencies - assert (!isXRP (issue.currency)); + if (j_.warning) j_.warning << + "delay: can't receive IOUs from non-existent issuer: " << + to_string (issue.account); - auto const issuerAccount = mEngine->view().read( - keylet::account(issue.account)); - - if (!issuerAccount) - { - if (m_journal.warning) m_journal.warning << - "delay: can't receive IOUs from non-existent issuer: " << - to_string (issue.account); - - return (mParams & tapRETRY) - ? terNO_ACCOUNT - : tecNO_ISSUER; - } - - if (issuerAccount->getFieldU32 (sfFlags) & lsfRequireAuth) - { - auto const trustLine = mEngine->view().read( - keylet::line(mTxnAccountID, issue.account, issue.currency)); - - if (!trustLine) - { - return (mParams & tapRETRY) - ? terNO_LINE - : tecNO_LINE; - } - - // Entries have a canonical representation, determined by a - // lexicographical "greater than" comparison employing strict weak - // ordering. Determine which entry we need to access. - bool const canonical_gt (mTxnAccountID > issue.account); - - bool const is_authorized (trustLine->getFieldU32 (sfFlags) & - (canonical_gt ? lsfLowAuth : lsfHighAuth)); - - if (!is_authorized) - { - if (m_journal.debug) m_journal.debug << - "delay: can't receive IOUs from issuer without auth."; - - return (mParams & tapRETRY) - ? terNO_AUTH - : tecNO_AUTH; - } - } - - return tesSUCCESS; + return (view().flags() & tapRETRY) + ? terNO_ACCOUNT + : tecNO_ISSUER; } - static - bool - dry_offer (View& view, Offer const& offer) + if (issuerAccount->getFieldU32 (sfFlags) & lsfRequireAuth) { - if (offer.fully_consumed ()) - return true; - auto const amount = accountFunds(view, offer.owner(), - offer.amount().out, fhZERO_IF_FROZEN, getConfig()); - return (amount <= zero); - } + auto const trustLine = ctx_.view().read( + keylet::line(mTxnAccountID, issue.account, issue.currency)); - static - std::pair - select_path ( - bool have_direct, OfferStream const& direct, - bool have_bridge, OfferStream const& leg1, OfferStream const& leg2) - { - // If we don't have any viable path, why are we here?! - assert (have_direct || have_bridge); - - // If there's no bridged path, the direct is the best by default. - if (!have_bridge) - return std::make_pair (true, direct.tip ().quality ()); - - Quality const bridged_quality (composed_quality ( - leg1.tip ().quality (), leg2.tip ().quality ())); - - if (have_direct) + if (!trustLine) { - // We compare the quality of the composed quality of the bridged - // offers and compare it against the direct offer to pick the best. - Quality const direct_quality (direct.tip ().quality ()); - - if (bridged_quality < direct_quality) - return std::make_pair (true, direct_quality); + return (view().flags() & tapRETRY) + ? terNO_LINE + : tecNO_LINE; } - // Either there was no direct offer, or it didn't have a better quality - // than the bridge. - return std::make_pair (false, bridged_quality); - } + // Entries have a canonical representation, determined by a + // lexicographical "greater than" comparison employing strict weak + // ordering. Determine which entry we need to access. + bool const canonical_gt (mTxnAccountID > issue.account); - std::pair - bridged_cross ( - Taker& taker, - View& view, - View& view_cancel, - Clock::time_point const when) - { - auto const& taker_amount = taker.original_offer (); + bool const is_authorized (trustLine->getFieldU32 (sfFlags) & + (canonical_gt ? lsfLowAuth : lsfHighAuth)); - 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."); - - OfferStream offers_direct (view, view_cancel, - Book (taker.issue_in (), taker.issue_out ()), when, m_journal); - - OfferStream offers_leg1 (view, view_cancel, - Book (taker.issue_in (), xrpIssue ()), when, m_journal); - - OfferStream offers_leg2 (view, view_cancel, - Book (xrpIssue (), taker.issue_out ()), when, m_journal); - - TER cross_result = tesSUCCESS; - - // Note the subtle distinction here: self-offers encountered in the - // bridge are taken, but self-offers encountered in the direct book - // are not. - bool have_bridge = offers_leg1.step () && offers_leg2.step (); - bool have_direct = step_account (offers_direct, taker); - int count = 0; - - // Modifying the order or logic of the operations in the loop will cause - // a protocol breaking change. - while (have_direct || have_bridge) + if (!is_authorized) { - bool leg1_consumed = false; - bool leg2_consumed = false; - bool direct_consumed = false; + if (j_.debug) j_.debug << + "delay: can't receive IOUs from issuer without auth."; - Quality quality; - bool use_direct; - - std::tie (use_direct, quality) = select_path ( - have_direct, offers_direct, - have_bridge, offers_leg1, offers_leg2); - - // We are always looking at the best quality; we are done with - // crossing as soon as we cross the quality boundary. - if (taker.reject(quality)) - break; - - count++; - - if (use_direct) - { - if (m_journal.debug) - { - m_journal.debug << count << " Direct:"; - m_journal.debug << " offer: " << offers_direct.tip (); - m_journal.debug << " in: " << offers_direct.tip ().amount().in; - m_journal.debug << " out: " << offers_direct.tip ().amount ().out; - m_journal.debug << " owner: " << offers_direct.tip ().owner (); - m_journal.debug << " funds: " << accountFunds(view, - offers_direct.tip ().owner (), - offers_direct.tip ().amount ().out, - fhIGNORE_FREEZE, - getConfig()); - } - - cross_result = taker.cross(offers_direct.tip ()); - - m_journal.debug << "Direct Result: " << transToken (cross_result); - - if (dry_offer (view, offers_direct.tip ())) - { - direct_consumed = true; - have_direct = step_account (offers_direct, taker); - } - } - else - { - if (m_journal.debug) - { - auto const owner1_funds_before = accountFunds(view, - offers_leg1.tip ().owner (), - offers_leg1.tip ().amount ().out, - fhIGNORE_FREEZE, - getConfig()); - - auto const owner2_funds_before = accountFunds(view, - offers_leg2.tip ().owner (), - offers_leg2.tip ().amount ().out, - fhIGNORE_FREEZE, - getConfig()); - - m_journal.debug << count << " Bridge:"; - m_journal.debug << " offer1: " << offers_leg1.tip (); - m_journal.debug << " in: " << offers_leg1.tip ().amount().in; - m_journal.debug << " out: " << offers_leg1.tip ().amount ().out; - m_journal.debug << " owner: " << offers_leg1.tip ().owner (); - m_journal.debug << " funds: " << owner1_funds_before; - m_journal.debug << " offer2: " << offers_leg2.tip (); - m_journal.debug << " in: " << offers_leg2.tip ().amount ().in; - m_journal.debug << " out: " << offers_leg2.tip ().amount ().out; - m_journal.debug << " owner: " << offers_leg2.tip ().owner (); - m_journal.debug << " funds: " << owner2_funds_before; - } - - cross_result = taker.cross(offers_leg1.tip (), offers_leg2.tip ()); - - m_journal.debug << "Bridge Result: " << transToken (cross_result); - - if (dry_offer (view, offers_leg1.tip ())) - { - leg1_consumed = true; - have_bridge = (have_bridge && offers_leg1.step ()); - } - if (dry_offer (view, offers_leg2.tip ())) - { - leg2_consumed = true; - have_bridge = (have_bridge && offers_leg2.step ()); - } - } - - if (cross_result != tesSUCCESS) - { - cross_result = tecFAILED_PROCESSING; - break; - } - - if (taker.done()) - { - m_journal.debug << "The taker reports he's done during crossing!"; - break; - } - - // Postcondition: If we aren't done, then we *must* have consumed at - // least one offer fully. - assert (direct_consumed || leg1_consumed || leg2_consumed); - - if (!direct_consumed && !leg1_consumed && !leg2_consumed) - throw std::logic_error ("bridged crossing: nothing was fully consumed."); + return (view().flags() & tapRETRY) + ? terNO_AUTH + : tecNO_AUTH; } - - return std::make_pair(cross_result, taker.remaining_offer ()); } - std::pair - direct_cross ( - Taker& taker, - View& view, - View& view_cancel, - Clock::time_point const when) + return tesSUCCESS; +} + +bool +CreateOffer::dry_offer (View& view, Offer const& offer) +{ + if (offer.fully_consumed ()) + return true; + auto const amount = accountFunds(view, offer.owner(), + offer.amount().out, fhZERO_IF_FROZEN, ctx_.config); + return (amount <= zero); +} + +std::pair +CreateOffer::select_path ( + bool have_direct, OfferStream const& direct, + bool have_bridge, OfferStream const& leg1, OfferStream const& leg2) +{ + // If we don't have any viable path, why are we here?! + assert (have_direct || have_bridge); + + // If there's no bridged path, the direct is the best by default. + if (!have_bridge) + return std::make_pair (true, direct.tip ().quality ()); + + Quality const bridged_quality (composed_quality ( + leg1.tip ().quality (), leg2.tip ().quality ())); + + if (have_direct) { - OfferStream offers ( - view, view_cancel, - Book (taker.issue_in (), taker.issue_out ()), - when, m_journal); + // We compare the quality of the composed quality of the bridged + // offers and compare it against the direct offer to pick the best. + Quality const direct_quality (direct.tip ().quality ()); - TER cross_result (tesSUCCESS); - int count = 0; + if (bridged_quality < direct_quality) + return std::make_pair (true, direct_quality); + } - bool have_offer = step_account (offers, taker); + // Either there was no direct offer, or it didn't have a better quality + // than the bridge. + return std::make_pair (false, bridged_quality); +} - // Modifying the order or logic of the operations in the loop will cause - // a protocol breaking change. - while (have_offer) +std::pair +CreateOffer::bridged_cross ( + Taker& taker, + View& view, + View& view_cancel, + Clock::time_point const when) +{ + auto const& taker_amount = taker.original_offer (); + + 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."); + + OfferStream offers_direct (view, view_cancel, + Book (taker.issue_in (), taker.issue_out ()), when, ctx_.config, j_); + + OfferStream offers_leg1 (view, view_cancel, + Book (taker.issue_in (), xrpIssue ()), when, ctx_.config, j_); + + OfferStream offers_leg2 (view, view_cancel, + Book (xrpIssue (), taker.issue_out ()), when, ctx_.config, j_); + + TER cross_result = tesSUCCESS; + + // Note the subtle distinction here: self-offers encountered in the + // bridge are taken, but self-offers encountered in the direct book + // are not. + bool have_bridge = offers_leg1.step () && offers_leg2.step (); + bool have_direct = step_account (offers_direct, taker); + int count = 0; + + // Modifying the order or logic of the operations in the loop will cause + // a protocol breaking change. + while (have_direct || have_bridge) + { + bool leg1_consumed = false; + bool leg2_consumed = false; + bool direct_consumed = false; + + Quality quality; + bool use_direct; + + std::tie (use_direct, quality) = select_path ( + have_direct, offers_direct, + have_bridge, offers_leg1, offers_leg2); + + // We are always looking at the best quality; we are done with + // crossing as soon as we cross the quality boundary. + if (taker.reject(quality)) + break; + + count++; + + if (use_direct) { - bool direct_consumed = false; - auto const& offer (offers.tip()); - - // We are done with crossing as soon as we cross the quality boundary - if (taker.reject (offer.quality())) - break; - - count++; - - if (m_journal.debug) + if (j_.debug) { - m_journal.debug << count << " Direct:"; - m_journal.debug << " offer: " << offer; - m_journal.debug << " in: " << offer.amount ().in; - m_journal.debug << " out: " << offer.amount ().out; - m_journal.debug << " owner: " << offer.owner (); - m_journal.debug << " funds: " << accountFunds(view, - offer.owner (), offer.amount ().out, fhIGNORE_FREEZE, getConfig()); + j_.debug << count << " Direct:"; + j_.debug << " offer: " << offers_direct.tip (); + j_.debug << " in: " << offers_direct.tip ().amount().in; + j_.debug << " out: " << offers_direct.tip ().amount ().out; + j_.debug << " owner: " << offers_direct.tip ().owner (); + j_.debug << " funds: " << accountFunds(view, + offers_direct.tip ().owner (), + offers_direct.tip ().amount ().out, + fhIGNORE_FREEZE, + ctx_.config); } - cross_result = taker.cross (offer); + cross_result = taker.cross(offers_direct.tip ()); - m_journal.debug << "Direct Result: " << transToken (cross_result); + j_.debug << "Direct Result: " << transToken (cross_result); - if (dry_offer (view, offer)) + if (dry_offer (view, offers_direct.tip ())) { direct_consumed = true; - have_offer = step_account (offers, taker); + have_direct = step_account (offers_direct, taker); } - - if (cross_result != tesSUCCESS) + } + else + { + if (j_.debug) { - cross_result = tecFAILED_PROCESSING; - break; + auto const owner1_funds_before = accountFunds(view, + offers_leg1.tip ().owner (), + offers_leg1.tip ().amount ().out, + fhIGNORE_FREEZE, + ctx_.config); + + auto const owner2_funds_before = accountFunds(view, + offers_leg2.tip ().owner (), + offers_leg2.tip ().amount ().out, + fhIGNORE_FREEZE, + ctx_.config); + + j_.debug << count << " Bridge:"; + j_.debug << " offer1: " << offers_leg1.tip (); + j_.debug << " in: " << offers_leg1.tip ().amount().in; + j_.debug << " out: " << offers_leg1.tip ().amount ().out; + j_.debug << " owner: " << offers_leg1.tip ().owner (); + j_.debug << " funds: " << owner1_funds_before; + j_.debug << " offer2: " << offers_leg2.tip (); + j_.debug << " in: " << offers_leg2.tip ().amount ().in; + j_.debug << " out: " << offers_leg2.tip ().amount ().out; + j_.debug << " owner: " << offers_leg2.tip ().owner (); + j_.debug << " funds: " << owner2_funds_before; } - if (taker.done()) + cross_result = taker.cross(offers_leg1.tip (), offers_leg2.tip ()); + + j_.debug << "Bridge Result: " << transToken (cross_result); + + if (dry_offer (view, offers_leg1.tip ())) { - m_journal.debug << "The taker reports he's done during crossing!"; - break; + leg1_consumed = true; + have_bridge = (have_bridge && offers_leg1.step ()); + } + if (dry_offer (view, offers_leg2.tip ())) + { + leg2_consumed = true; + have_bridge = (have_bridge && offers_leg2.step ()); } - - // Postcondition: If we aren't done, then we *must* have consumed the - // offer on the books fully! - assert (direct_consumed); - - if (!direct_consumed) - throw std::logic_error ("direct crossing: nothing was fully consumed."); } - return std::make_pair(cross_result, taker.remaining_offer ()); + if (cross_result != tesSUCCESS) + { + cross_result = tecFAILED_PROCESSING; + break; + } + + if (taker.done()) + { + j_.debug << "The taker reports he's done during crossing!"; + break; + } + + // Postcondition: If we aren't done, then we *must* have consumed at + // least one offer fully. + assert (direct_consumed || leg1_consumed || leg2_consumed); + + if (!direct_consumed && !leg1_consumed && !leg2_consumed) + throw std::logic_error ("bridged crossing: nothing was fully consumed."); } - // Step through the stream for as long as possible, skipping any offers - // that are from the taker or which cross the taker's threshold. - // Return false if the is no offer in the book, true otherwise. - static - bool - step_account (OfferStream& stream, Taker const& taker) + return std::make_pair(cross_result, taker.remaining_offer ()); +} + +std::pair +CreateOffer::direct_cross ( + Taker& taker, + View& view, + View& view_cancel, + Clock::time_point const when) +{ + OfferStream offers ( + view, view_cancel, + Book (taker.issue_in (), taker.issue_out ()), + when, ctx_.config, j_); + + TER cross_result (tesSUCCESS); + int count = 0; + + bool have_offer = step_account (offers, taker); + + // Modifying the order or logic of the operations in the loop will cause + // a protocol breaking change. + while (have_offer) { - while (stream.step ()) + bool direct_consumed = false; + auto const& offer (offers.tip()); + + // We are done with crossing as soon as we cross the quality boundary + if (taker.reject (offer.quality())) + break; + + count++; + + if (j_.debug) { - auto const& offer = stream.tip (); - - // This offer at the tip crosses the taker's threshold. We're done. - if (taker.reject (offer.quality ())) - return true; - - // This offer at the tip is not from the taker. We're done. - if (offer.owner () != taker.account ()) - return true; + j_.debug << count << " Direct:"; + j_.debug << " offer: " << offer; + j_.debug << " in: " << offer.amount ().in; + j_.debug << " out: " << offer.amount ().out; + j_.debug << " owner: " << offer.owner (); + j_.debug << " funds: " << accountFunds(view, + offer.owner (), offer.amount ().out, fhIGNORE_FREEZE, ctx_.config); } - // We ran out of offers. Can't advance. - return false; + cross_result = taker.cross (offer); + + j_.debug << "Direct Result: " << transToken (cross_result); + + if (dry_offer (view, offer)) + { + direct_consumed = true; + have_offer = step_account (offers, taker); + } + + if (cross_result != tesSUCCESS) + { + cross_result = tecFAILED_PROCESSING; + break; + } + + if (taker.done()) + { + j_.debug << "The taker reports he's done during crossing!"; + break; + } + + // Postcondition: If we aren't done, then we *must* have consumed the + // offer on the books fully! + assert (direct_consumed); + + if (!direct_consumed) + throw std::logic_error ("direct crossing: nothing was fully consumed."); } - // Fill offer as much as possible by consuming offers already on the books, - // and adjusting account balances accordingly. - // - // Charges fees on top to taker. - std::pair - cross ( - View& view, - View& cancel_view, - Amounts const& taker_amount) + return std::make_pair(cross_result, taker.remaining_offer ()); +} + +// Step through the stream for as long as possible, skipping any offers +// that are from the taker or which cross the taker's threshold. +// Return false if the is no offer in the book, true otherwise. +bool +CreateOffer::step_account (OfferStream& stream, Taker const& taker) +{ + while (stream.step ()) { - Clock::time_point const when = - mEngine->view().time(); + auto const& offer = stream.tip (); - beast::WrappedSink takerSink (m_journal, "Taker "); + // This offer at the tip crosses the taker's threshold. We're done. + if (taker.reject (offer.quality ())) + return true; - Taker taker (cross_type_, view, mTxnAccountID, taker_amount, - mTxn.getFlags(), beast::Journal (takerSink)); + // This offer at the tip is not from the taker. We're done. + if (offer.owner () != taker.account ()) + return true; + } - try + // We ran out of offers. Can't advance. + return false; +} + +// Fill offer as much as possible by consuming offers already on the books, +// and adjusting account balances accordingly. +// +// Charges fees on top to taker. +std::pair +CreateOffer::cross ( + View& view, + View& cancel_view, + Amounts const& taker_amount) +{ + Clock::time_point const when = + ctx_.view().parentCloseTime(); + + beast::WrappedSink takerSink (j_, "Taker "); + + Taker taker (cross_type_, view, mTxnAccountID, taker_amount, + mTxn.getFlags(), ctx_.config, beast::Journal (takerSink)); + + try + { + if (cross_type_ == CrossType::IouToIou) + return bridged_cross (taker, view, cancel_view, when); + + return direct_cross (taker, view, cancel_view, when); + } + catch (std::exception const& e) + { + j_.error << "Exception during offer crossing: " << e.what (); + return std::make_pair (tecINTERNAL, taker.remaining_offer ()); + } + catch (...) + { + j_.error << "Exception during offer crossing."; + return std::make_pair (tecINTERNAL, taker.remaining_offer ()); + } +} + +std::string +CreateOffer::format_amount (STAmount const& amount) +{ + std::string txt = amount.getText (); + txt += "/"; + txt += to_string (amount.issue().currency); + return txt; +} + +STAmount +CreateOffer::getAccountReserve (SLE::pointer account) +{ + return STAmount (ctx_.view().fees().accountReserve( + account->getFieldU32 (sfOwnerCount) + 1)); +} + +TER +CreateOffer::preCheck () +{ + cross_type_ = CrossType::IouToIou; + bool const pays_xrp = + mTxn.getFieldAmount (sfTakerPays).native (); + bool const gets_xrp = + mTxn.getFieldAmount (sfTakerGets).native (); + if (pays_xrp && !gets_xrp) + cross_type_ = CrossType::IouToXrp; + else if (gets_xrp && !pays_xrp) + cross_type_ = CrossType::XrpToIou; + + std::uint32_t const uTxFlags = mTxn.getFlags (); + + if (uTxFlags & tfOfferCreateMask) + { + if (j_.debug) j_.debug << + "Malformed transaction: Invalid flags set."; + return temINVALID_FLAG; + } + + bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel); + bool const bFillOrKill (uTxFlags & tfFillOrKill); + + if (bImmediateOrCancel && bFillOrKill) + { + if (j_.debug) j_.debug << + "Malformed transaction: both IoC and FoK set."; + return temINVALID_FLAG; + } + + bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration)); + + if (bHaveExpiration && (mTxn.getFieldU32 (sfExpiration) == 0)) + { + if (j_.debug) j_.warning << + "Malformed offer: bad expiration"; + return temBAD_EXPIRATION; + } + + bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence)); + + if (bHaveCancel && (mTxn.getFieldU32 (sfOfferSequence) == 0)) + { + if (j_.debug) j_.debug << + "Malformed offer: bad cancel sequence"; + return temBAD_SEQUENCE; + } + + STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays); + STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets); + + if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets)) + return temBAD_AMOUNT; + + if (saTakerPays.native () && saTakerGets.native ()) + { + if (j_.debug) j_.warning << + "Malformed offer: XRP for XRP"; + return temBAD_OFFER; + } + if (saTakerPays <= zero || saTakerGets <= zero) + { + if (j_.debug) j_.warning << + "Malformed offer: bad amount"; + return temBAD_OFFER; + } + + auto const& uPaysIssuerID = saTakerPays.getIssuer (); + auto const& uPaysCurrency = saTakerPays.getCurrency (); + + auto const& uGetsIssuerID = saTakerGets.getIssuer (); + auto const& uGetsCurrency = saTakerGets.getCurrency (); + + if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID) + { + if (j_.debug) j_.debug << + "Malformed offer: redundant offer"; + return temREDUNDANT; + } + // We don't allow a non-native currency to use the currency code XRP. + if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency) + { + if (j_.debug) j_.warning << + "Malformed offer: Bad currency."; + return temBAD_CURRENCY; + } + + if (saTakerPays.native () != !uPaysIssuerID || + saTakerGets.native () != !uGetsIssuerID) + { + if (j_.warning) j_.warning << + "Malformed offer: bad issuer"; + return temBAD_ISSUER; + } + + return Transactor::preCheck (); +} + +std::pair +CreateOffer::applyGuts (View& view, View& view_cancel) +{ + std::uint32_t const uTxFlags = mTxn.getFlags (); + + bool const bPassive (uTxFlags & tfPassive); + bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel); + bool const bFillOrKill (uTxFlags & tfFillOrKill); + bool const bSell (uTxFlags & tfSell); + + STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays); + STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets); + + if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets)) + return { temBAD_AMOUNT, true }; + + auto const& uPaysIssuerID = saTakerPays.getIssuer (); + auto const& uPaysCurrency = saTakerPays.getCurrency (); + + auto const& uGetsIssuerID = saTakerGets.getIssuer (); + + bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration)); + bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence)); + + std::uint32_t const uExpiration = mTxn.getFieldU32 (sfExpiration); + std::uint32_t const uCancelSequence = mTxn.getFieldU32 (sfOfferSequence); + + // FIXME understand why we use SequenceNext instead of current transaction + // sequence to determine the transaction. Why is the offer sequence + // number insufficient? + + std::uint32_t const uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence); + std::uint32_t const uSequence = mTxn.getSequence (); + + // This is the original rate of the offer, and is the rate at which + // it will be placed, even if crossing offers change the amounts that + // end up on the books. + std::uint64_t const uRate = getRate (saTakerGets, saTakerPays); + + TER result = tesSUCCESS; + + // This is the ledger view that we work against. Transactions are applied + // as we go on processing transactions. + + auto const sleCreator = view.peek ( + keylet::account(mTxnAccountID)); + + if (isGlobalFrozen (view, uPaysIssuerID) || isGlobalFrozen (view, uGetsIssuerID)) + { + if (j_.warning) j_.warning << + "Offer involves frozen asset"; + + result = tecFROZEN; + } + else if (accountFunds(view, mTxnAccountID, saTakerGets, + fhZERO_IF_FROZEN, ctx_.config) <= zero) + { + if (j_.debug) j_.debug << + "delay: Offers must be at least partially funded."; + + result = tecUNFUNDED_OFFER; + } + // This can probably be simplified to make sure that you cancel sequences + // before the transaction sequence number. + else if (bHaveCancel && (uAccountSequenceNext - 1 <= uCancelSequence)) + { + if (j_.debug) j_.debug << + "uAccountSequenceNext=" << uAccountSequenceNext << + " uOfferSequence=" << uCancelSequence; + + result = temBAD_SEQUENCE; + } + + if (result != tesSUCCESS) + { + j_.debug << "final result: " << transToken (result); + return { result, true }; + } + + // Process a cancellation request that's passed along with an offer. + if (bHaveCancel) + { + auto const sleCancel = view.peek( + keylet::offer(mTxnAccountID, uCancelSequence)); + + // It's not an error to not find the offer to cancel: it might have + // been consumed or removed. If it is found, however, it's an error + // to fail to delete it. + if (sleCancel) { - if (cross_type_ == CrossType::IouToIou) - return bridged_cross (taker, view, cancel_view, when); - - return direct_cross (taker, view, cancel_view, when); - } - catch (std::exception const& e) - { - m_journal.error << "Exception during offer crossing: " << e.what (); - return std::make_pair (tecINTERNAL, taker.remaining_offer ()); - } - catch (...) - { - m_journal.error << "Exception during offer crossing."; - return std::make_pair (tecINTERNAL, taker.remaining_offer ()); + j_.debug << "Create cancels order " << uCancelSequence; + result = offerDelete (view, sleCancel); } } - static - std::string - format_amount (STAmount const& amount) + // Expiration is defined in terms of the close time of the parent ledger, + // because we definitively know the time that it closed but we do not + // know the closing time of the ledger that is under construction. + if (bHaveExpiration && + (ctx_.view().parentCloseTime() >= uExpiration)) { - std::string txt = amount.getText (); - txt += "/"; - txt += to_string (amount.issue().currency); - return txt; + return { tesSUCCESS, true }; } -public: - CreateOffer ( - CrossType cross_type, - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("CreateOffer")) - , cross_type_ (cross_type) + // Make sure that we are authorized to hold what the taker will pay us. + if (result == tesSUCCESS && !saTakerPays.native ()) + result = checkAcceptAsset (Issue (uPaysCurrency, uPaysIssuerID)); + + bool const bOpenLedger = + ctx_.view().open(); + bool crossed = false; + + if (result == tesSUCCESS) { + // We reverse pays and gets because during crossing we are taking. + Amounts const taker_amount (saTakerGets, saTakerPays); - } + // The amount of the offer that is unfilled after crossing has been + // performed. It may be equal to the original amount (didn't cross), + // empty (fully crossed), or something in-between. + Amounts place_offer; - /** Returns the reserve the account would have if an offer was added. */ - STAmount - getAccountReserve (SLE::pointer account) - { - return STAmount (mEngine->view().fees().accountReserve( - account->getFieldU32 (sfOwnerCount) + 1)); - } + j_.debug << "Attempting cross: " << + to_string (taker_amount.in.issue ()) << " -> " << + to_string (taker_amount.out.issue ()); - TER - preCheck () override - { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - if (uTxFlags & tfOfferCreateMask) + if (j_.trace) { - if (m_journal.debug) m_journal.debug << - "Malformed transaction: Invalid flags set."; - return temINVALID_FLAG; + j_.debug << " mode: " << + (bPassive ? "passive " : "") << + (bSell ? "sell" : "buy"); + j_.trace <<" in: " << format_amount (taker_amount.in); + j_.trace << " out: " << format_amount (taker_amount.out); } - bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel); - bool const bFillOrKill (uTxFlags & tfFillOrKill); + std::tie(result, place_offer) = cross (view, view_cancel, taker_amount); + assert (result != tefINTERNAL); - if (bImmediateOrCancel && bFillOrKill) + if (j_.trace) { - if (m_journal.debug) m_journal.debug << - "Malformed transaction: both IoC and FoK set."; - return temINVALID_FLAG; + j_.trace << "Cross result: " << transToken (result); + j_.trace << " in: " << format_amount (place_offer.in); + j_.trace << " out: " << format_amount (place_offer.out); } - bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration)); - - if (bHaveExpiration && (mTxn.getFieldU32 (sfExpiration) == 0)) - { - if (m_journal.debug) m_journal.warning << - "Malformed offer: bad expiration"; - return temBAD_EXPIRATION; - } - - bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence)); - - if (bHaveCancel && (mTxn.getFieldU32 (sfOfferSequence) == 0)) - { - if (m_journal.debug) m_journal.debug << - "Malformed offer: bad cancel sequence"; - return temBAD_SEQUENCE; - } - - STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays); - STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets); - - if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets)) - return temBAD_AMOUNT; - - if (saTakerPays.native () && saTakerGets.native ()) - { - if (m_journal.debug) m_journal.warning << - "Malformed offer: XRP for XRP"; - return temBAD_OFFER; - } - if (saTakerPays <= zero || saTakerGets <= zero) - { - if (m_journal.debug) m_journal.warning << - "Malformed offer: bad amount"; - return temBAD_OFFER; - } - - auto const& uPaysIssuerID = saTakerPays.getIssuer (); - auto const& uPaysCurrency = saTakerPays.getCurrency (); - - auto const& uGetsIssuerID = saTakerGets.getIssuer (); - auto const& uGetsCurrency = saTakerGets.getCurrency (); - - if (uPaysCurrency == uGetsCurrency && uPaysIssuerID == uGetsIssuerID) - { - if (m_journal.debug) m_journal.debug << - "Malformed offer: redundant offer"; - return temREDUNDANT; - } - // We don't allow a non-native currency to use the currency code XRP. - if (badCurrency() == uPaysCurrency || badCurrency() == uGetsCurrency) - { - if (m_journal.debug) m_journal.warning << - "Malformed offer: Bad currency."; - return temBAD_CURRENCY; - } - - if (saTakerPays.native () != !uPaysIssuerID || - saTakerGets.native () != !uGetsIssuerID) - { - if (m_journal.warning) m_journal.warning << - "Malformed offer: bad issuer"; - return temBAD_ISSUER; - } - - return Transactor::preCheck (); - } - - std::pair - applyGuts (View& view, View& view_cancel) - { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - bool const bPassive (uTxFlags & tfPassive); - bool const bImmediateOrCancel (uTxFlags & tfImmediateOrCancel); - bool const bFillOrKill (uTxFlags & tfFillOrKill); - bool const bSell (uTxFlags & tfSell); - - STAmount saTakerPays = mTxn.getFieldAmount (sfTakerPays); - STAmount saTakerGets = mTxn.getFieldAmount (sfTakerGets); - - if (!isLegalNet (saTakerPays) || !isLegalNet (saTakerGets)) - return { temBAD_AMOUNT, true }; - - auto const& uPaysIssuerID = saTakerPays.getIssuer (); - auto const& uPaysCurrency = saTakerPays.getCurrency (); - - auto const& uGetsIssuerID = saTakerGets.getIssuer (); - - bool const bHaveExpiration (mTxn.isFieldPresent (sfExpiration)); - bool const bHaveCancel (mTxn.isFieldPresent (sfOfferSequence)); - - std::uint32_t const uExpiration = mTxn.getFieldU32 (sfExpiration); - std::uint32_t const uCancelSequence = mTxn.getFieldU32 (sfOfferSequence); - - // FIXME understand why we use SequenceNext instead of current transaction - // sequence to determine the transaction. Why is the offer sequence - // number insufficient? - - std::uint32_t const uAccountSequenceNext = mTxnAccount->getFieldU32 (sfSequence); - std::uint32_t const uSequence = mTxn.getSequence (); - - // This is the original rate of the offer, and is the rate at which - // it will be placed, even if crossing offers change the amounts that - // end up on the books. - std::uint64_t const uRate = getRate (saTakerGets, saTakerPays); - - TER result = tesSUCCESS; - - // This is the ledger view that we work against. Transactions are applied - // as we go on processing transactions. - - auto const sleCreator = view.peek ( - keylet::account(mTxnAccountID)); - - if (isGlobalFrozen (view, uPaysIssuerID) || isGlobalFrozen (view, uGetsIssuerID)) - { - if (m_journal.warning) m_journal.warning << - "Offer involves frozen asset"; - - result = tecFROZEN; - } - else if (accountFunds(view, mTxnAccountID, saTakerGets, - fhZERO_IF_FROZEN, getConfig()) <= zero) - { - if (m_journal.debug) m_journal.debug << - "delay: Offers must be at least partially funded."; - - result = tecUNFUNDED_OFFER; - } - // This can probably be simplified to make sure that you cancel sequences - // before the transaction sequence number. - else if (bHaveCancel && (uAccountSequenceNext - 1 <= uCancelSequence)) - { - if (m_journal.debug) m_journal.debug << - "uAccountSequenceNext=" << uAccountSequenceNext << - " uOfferSequence=" << uCancelSequence; - - result = temBAD_SEQUENCE; - } + if (result == tecFAILED_PROCESSING && bOpenLedger) + result = telFAILED_PROCESSING; if (result != tesSUCCESS) { - m_journal.debug << "final result: " << transToken (result); + j_.debug << "final result: " << transToken (result); return { result, true }; } - // Process a cancellation request that's passed along with an offer. - if (bHaveCancel) - { - auto const sleCancel = view.peek( - keylet::offer(mTxnAccountID, uCancelSequence)); + assert (saTakerGets.issue () == place_offer.in.issue ()); + assert (saTakerPays.issue () == place_offer.out.issue ()); - // It's not an error to not find the offer to cancel: it might have - // been consumed or removed. If it is found, however, it's an error - // to fail to delete it. - if (sleCancel) - { - m_journal.debug << "Create cancels order " << uCancelSequence; - result = offerDelete (view, sleCancel); - } + if (taker_amount != place_offer) + crossed = true; + + // The offer that we need to place after offer crossing should + // never be negative. If it is, something went very very wrong. + if (place_offer.in < zero || place_offer.out < zero) + { + j_.fatal << "Cross left offer negative!" << + " in: " << format_amount (place_offer.in) << + " out: " << format_amount (place_offer.out); + return { tefINTERNAL, true }; } - // Expiration is defined in terms of the close time of the parent ledger, - // because we definitively know the time that it closed but we do not - // know the closing time of the ledger that is under construction. - if (bHaveExpiration && - (mEngine->view().time() >= uExpiration)) + if (place_offer.in == zero || place_offer.out == zero) { - return { tesSUCCESS, true }; + j_.debug << "Offer fully crossed!"; + return { result, true }; } - // Make sure that we are authorized to hold what the taker will pay us. - if (result == tesSUCCESS && !saTakerPays.native ()) - result = checkAcceptAsset (Issue (uPaysCurrency, uPaysIssuerID)); + // We now need to adjust the offer to reflect the amount left after + // crossing. We reverse in and out here, since during crossing we + // were the taker. + saTakerPays = place_offer.out; + saTakerGets = place_offer.in; + } - bool const bOpenLedger (mParams & tapOPEN_LEDGER); - bool crossed = false; + assert (saTakerPays > zero && saTakerGets > zero); - if (result == tesSUCCESS) - { - // We reverse pays and gets because during crossing we are taking. - Amounts const taker_amount (saTakerGets, saTakerPays); + if (result != tesSUCCESS) + { + j_.debug << "final result: " << transToken (result); + return { result, true }; + } - // The amount of the offer that is unfilled after crossing has been - // performed. It may be equal to the original amount (didn't cross), - // empty (fully crossed), or something in-between. - Amounts place_offer; + if (j_.trace) + { + j_.trace << "Place" << (crossed ? " remaining " : " ") << "offer:"; + j_.trace << " Pays: " << saTakerPays.getFullText (); + j_.trace << " Gets: " << saTakerGets.getFullText (); + } - m_journal.debug << "Attempting cross: " << - to_string (taker_amount.in.issue ()) << " -> " << - to_string (taker_amount.out.issue ()); + // For 'fill or kill' offers, failure to fully cross means that the + // entire operation should be aborted, with only fees paid. + if (bFillOrKill) + { + j_.trace << "Fill or Kill: offer killed"; + return { tesSUCCESS, false }; + } - if (m_journal.trace) - { - m_journal.debug << " mode: " << - (bPassive ? "passive " : "") << - (bSell ? "sell" : "buy"); - m_journal.trace <<" in: " << format_amount (taker_amount.in); - m_journal.trace << " out: " << format_amount (taker_amount.out); - } + // For 'immediate or cancel' offers, the amount remaining doesn't get + // placed - it gets cancelled and the operation succeeds. + if (bImmediateOrCancel) + { + j_.trace << "Immediate or cancel: offer cancelled"; + return { tesSUCCESS, true }; + } - std::tie(result, place_offer) = cross (view, view_cancel, taker_amount); - assert (result != tefINTERNAL); - - if (m_journal.trace) - { - m_journal.trace << "Cross result: " << transToken (result); - m_journal.trace << " in: " << format_amount (place_offer.in); - m_journal.trace << " out: " << format_amount (place_offer.out); - } - - if (result == tecFAILED_PROCESSING && bOpenLedger) - result = telFAILED_PROCESSING; - - if (result != tesSUCCESS) - { - m_journal.debug << "final result: " << transToken (result); - return { result, true }; - } - - assert (saTakerGets.issue () == place_offer.in.issue ()); - assert (saTakerPays.issue () == place_offer.out.issue ()); - - if (taker_amount != place_offer) - crossed = true; - - // The offer that we need to place after offer crossing should - // 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!" << - " in: " << format_amount (place_offer.in) << - " out: " << format_amount (place_offer.out); - return { tefINTERNAL, true }; - } - - if (place_offer.in == zero || place_offer.out == zero) - { - m_journal.debug << "Offer fully crossed!"; - return { result, true }; - } - - // We now need to adjust the offer to reflect the amount left after - // crossing. We reverse in and out here, since during crossing we - // were the taker. - saTakerPays = place_offer.out; - saTakerGets = place_offer.in; - } - - assert (saTakerPays > zero && saTakerGets > zero); + if (mPriorBalance < getAccountReserve (sleCreator)) + { + // If we are here, the signing account had an insufficient reserve + // *prior* to our processing. If something actually crossed, then + // we allow this; otherwise, we just claim a fee. + if (!crossed) + result = tecINSUF_RESERVE_OFFER; if (result != tesSUCCESS) - { - m_journal.debug << "final result: " << transToken (result); - return { result, true }; - } - - if (m_journal.trace) - { - m_journal.trace << "Place" << (crossed ? " remaining " : " ") << "offer:"; - m_journal.trace << " Pays: " << saTakerPays.getFullText (); - m_journal.trace << " Gets: " << saTakerGets.getFullText (); - } - - // For 'fill or kill' offers, failure to fully cross means that the - // entire operation should be aborted, with only fees paid. - if (bFillOrKill) - { - m_journal.trace << "Fill or Kill: offer killed"; - return { tesSUCCESS, false }; - } - - // For 'immediate or cancel' offers, the amount remaining doesn't get - // placed - it gets cancelled and the operation succeeds. - if (bImmediateOrCancel) - { - m_journal.trace << "Immediate or cancel: offer cancelled"; - return { tesSUCCESS, true }; - } - - if (mPriorBalance < getAccountReserve (sleCreator)) - { - // If we are here, the signing account had an insufficient reserve - // *prior* to our processing. If something actually crossed, then - // we allow this; otherwise, we just claim a fee. - if (!crossed) - result = tecINSUF_RESERVE_OFFER; - - if (result != tesSUCCESS) - m_journal.debug << "final result: " << transToken (result); - - return { result, true }; - } - - // We need to place the remainder of the offer into its order book. - auto const offer_index = getOfferIndex (mTxnAccountID, uSequence); - - std::uint64_t uOwnerNode; - std::uint64_t uBookNode; - uint256 uDirectory; - - // Add offer to owner's directory. - result = dirAdd(view, uOwnerNode, - getOwnerDirIndex (mTxnAccountID), offer_index, - std::bind ( - &ownerDirDescriber, std::placeholders::_1, - std::placeholders::_2, mTxnAccountID)); - - if (result == tesSUCCESS) - { - // Update owner count. - adjustOwnerCount(view, sleCreator, 1); - - if (m_journal.trace) m_journal.trace << - "adding to book: " << to_string (saTakerPays.issue ()) << - " : " << to_string (saTakerGets.issue ()); - - uint256 const book_base (getBookBase ( - { saTakerPays.issue (), saTakerGets.issue () })); - - // We use the original rate to place the offer. - uDirectory = getQualityIndex (book_base, uRate); - - // Add offer to order book. - result = dirAdd (view, uBookNode, uDirectory, offer_index, - std::bind ( - &qualityDirDescriber, std::placeholders::_1, - std::placeholders::_2, saTakerPays.getCurrency (), - uPaysIssuerID, saTakerGets.getCurrency (), - uGetsIssuerID, uRate)); - } - - if (result == tesSUCCESS) - { - auto sleOffer = std::make_shared(ltOFFER, offer_index); - sleOffer->setAccountID (sfAccount, mTxnAccountID); - sleOffer->setFieldU32 (sfSequence, uSequence); - sleOffer->setFieldH256 (sfBookDirectory, uDirectory); - sleOffer->setFieldAmount (sfTakerPays, saTakerPays); - sleOffer->setFieldAmount (sfTakerGets, saTakerGets); - sleOffer->setFieldU64 (sfOwnerNode, uOwnerNode); - sleOffer->setFieldU64 (sfBookNode, uBookNode); - if (uExpiration) - sleOffer->setFieldU32 (sfExpiration, uExpiration); - if (bPassive) - sleOffer->setFlag (lsfPassive); - if (bSell) - sleOffer->setFlag (lsfSell); - view.insert(sleOffer); - } - - if (result != tesSUCCESS) - m_journal.debug << "final result: " << transToken (result); + j_.debug << "final result: " << transToken (result); return { result, true }; } - TER - doApply() override + // We need to place the remainder of the offer into its order book. + auto const offer_index = getOfferIndex (mTxnAccountID, uSequence); + + std::uint64_t uOwnerNode; + std::uint64_t uBookNode; + uint256 uDirectory; + + // Add offer to owner's directory. + result = dirAdd(view, uOwnerNode, + getOwnerDirIndex (mTxnAccountID), offer_index, + std::bind ( + &ownerDirDescriber, std::placeholders::_1, + std::placeholders::_2, mTxnAccountID)); + + if (result == tesSUCCESS) { - // This is the ledger view that we work against. Transactions are applied - // as we go on processing transactions. - MetaView view (&mEngine->view()); - // This is a checkpoint with just the fees paid. If something goes wrong - // with this transaction, we roll back to this ledger. - MetaView viewCancel (&mEngine->view()); - auto const result = applyGuts(view, viewCancel); - if (result.second) - view.apply(mEngine->view()); - else - viewCancel.apply(mEngine->view()); - return result.first; + // Update owner count. + adjustOwnerCount(view, sleCreator, 1); + + if (j_.trace) j_.trace << + "adding to book: " << to_string (saTakerPays.issue ()) << + " : " << to_string (saTakerGets.issue ()); + + uint256 const book_base (getBookBase ( + { saTakerPays.issue (), saTakerGets.issue () })); + + // We use the original rate to place the offer. + uDirectory = getQualityIndex (book_base, uRate); + + // Add offer to order book. + result = dirAdd (view, uBookNode, uDirectory, offer_index, + std::bind ( + &qualityDirDescriber, std::placeholders::_1, + std::placeholders::_2, saTakerPays.getCurrency (), + uPaysIssuerID, saTakerGets.getCurrency (), + uGetsIssuerID, uRate)); } -}; + + if (result == tesSUCCESS) + { + auto sleOffer = std::make_shared(ltOFFER, offer_index); + sleOffer->setAccountID (sfAccount, mTxnAccountID); + sleOffer->setFieldU32 (sfSequence, uSequence); + sleOffer->setFieldH256 (sfBookDirectory, uDirectory); + sleOffer->setFieldAmount (sfTakerPays, saTakerPays); + sleOffer->setFieldAmount (sfTakerGets, saTakerGets); + sleOffer->setFieldU64 (sfOwnerNode, uOwnerNode); + sleOffer->setFieldU64 (sfBookNode, uBookNode); + if (uExpiration) + sleOffer->setFieldU32 (sfExpiration, uExpiration); + if (bPassive) + sleOffer->setFlag (lsfPassive); + if (bSell) + sleOffer->setFlag (lsfSell); + view.insert(sleOffer); + } + + if (result != tesSUCCESS) + j_.debug << "final result: " << transToken (result); + + return { result, true }; +} TER -transact_CreateOffer ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +CreateOffer::doApply() { - CrossType cross_type = CrossType::IouToIou; - - bool const pays_xrp = txn.getFieldAmount (sfTakerPays).native (); - bool const gets_xrp = txn.getFieldAmount (sfTakerGets).native (); - - if (pays_xrp && !gets_xrp) - cross_type = CrossType::IouToXrp; - else if (gets_xrp && !pays_xrp) - cross_type = CrossType::XrpToIou; - - return CreateOffer (cross_type, txn, params, engine).apply (); + // This is the ledger view that we work against. Transactions are applied + // as we go on processing transactions. + MetaView view (ctx_.view(), ctx_.view().flags()); + // This is a checkpoint with just the fees paid. If something goes wrong + // with this transaction, we roll back to this ledger. + MetaView viewCancel (ctx_.view(), ctx_.view().flags()); + auto const result = applyGuts(view, viewCancel); + if (result.second) + view.apply(ctx_.view()); + else + viewCancel.apply(ctx_.view()); + return result.first; } } diff --git a/src/ripple/app/tx/impl/CreateOffer.h b/src/ripple/app/tx/impl/CreateOffer.h new file mode 100644 index 000000000..1257a28c2 --- /dev/null +++ b/src/ripple/app/tx/impl/CreateOffer.h @@ -0,0 +1,119 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_CREATEOFFER_H_INCLUDED +#define RIPPLE_TX_CREATEOFFER_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +class CreateOffer + : public Transactor +{ +public: + template + CreateOffer (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + /** Returns the reserve the account would have if an offer was added. */ + // VFALCO This function is not needed just inline the behavior + STAmount + getAccountReserve (SLE::pointer account); // const? + + TER + preCheck () override; + + std::pair + applyGuts (View& view, View& view_cancel); + + TER + doApply() override; + +private: + /** Determine if we are authorized to hold the asset we want to get */ + TER + checkAcceptAsset(IssueRef issue) const; + + bool + dry_offer (View& view, Offer const& offer); + + static + std::pair + select_path ( + bool have_direct, OfferStream const& direct, + bool have_bridge, OfferStream const& leg1, OfferStream const& leg2); + + std::pair + bridged_cross ( + Taker& taker, + View& view, + View& view_cancel, + Clock::time_point const when); + + std::pair + direct_cross ( + Taker& taker, + View& view, + View& view_cancel, + Clock::time_point const when); + + // Step through the stream for as long as possible, skipping any offers + // that are from the taker or which cross the taker's threshold. + // Return false if the is no offer in the book, true otherwise. + static + bool + step_account (OfferStream& stream, Taker const& taker); + + // Fill offer as much as possible by consuming offers already on the books, + // and adjusting account balances accordingly. + // + // Charges fees on top to taker. + std::pair + cross ( + View& view, + View& cancel_view, + Amounts const& taker_amount); + + static + std::string + format_amount (STAmount const& amount); + +private: + // What kind of offer we are placing + CrossType cross_type_; +}; + +} + +#endif diff --git a/src/ripple/app/tx/impl/CreateTicket.cpp b/src/ripple/app/tx/impl/CreateTicket.cpp index 5ec7a017a..070beb61c 100644 --- a/src/ripple/app/tx/impl/CreateTicket.cpp +++ b/src/ripple/app/tx/impl/CreateTicket.cpp @@ -18,137 +18,112 @@ //============================================================================== #include -#include +#include +#include #include #include namespace ripple { -class CreateTicket - : public Transactor -{ -public: - CreateTicket ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("CreateTicket")) - { - - } - - TER - preCheck () override - { - if (mTxn.isFieldPresent (sfExpiration)) - { - if (mTxn.getFieldU32 (sfExpiration) == 0) - { - m_journal.warning << - "Malformed transaction: bad expiration"; - return temBAD_EXPIRATION; - } - } - - return Transactor::preCheck (); - } - - /** Returns the reserve the account would have if an offer was added. */ - STAmount - getAccountReserve (SLE::pointer account) - { - return STAmount (mEngine->view().fees().accountReserve( - account->getFieldU32 (sfOwnerCount) + 1)); - } - - TER doApply () override - { - assert (mTxnAccount); - - // A ticket counts against the reserve of the issuing account, but we - // check the starting balance because we want to allow dipping into the - // reserve to pay fees. - if (mPriorBalance < getAccountReserve (mTxnAccount)) - return tecINSUFFICIENT_RESERVE; - - std::uint32_t expiration (0); - - if (mTxn.isFieldPresent (sfExpiration)) - { - expiration = mTxn.getFieldU32 (sfExpiration); - - if (mEngine->view().time() >= expiration) - return tesSUCCESS; - } - - SLE::pointer sleTicket = std::make_shared(ltTICKET, - getTicketIndex (mTxnAccountID, mTxn.getSequence ())); - sleTicket->setAccountID (sfAccount, mTxnAccountID); - sleTicket->setFieldU32 (sfSequence, mTxn.getSequence ()); - if (expiration != 0) - sleTicket->setFieldU32 (sfExpiration, expiration); - mEngine->view().insert (sleTicket); - - if (mTxn.isFieldPresent (sfTarget)) - { - AccountID const target_account (mTxn.getAccountID (sfTarget)); - - SLE::pointer sleTarget = mEngine->view().peek (keylet::account(target_account)); - - // Destination account does not exist. - if (!sleTarget) - return tecNO_TARGET; - - // The issuing account is the default account to which the ticket - // applies so don't bother saving it if that's what's specified. - if (target_account != mTxnAccountID) - sleTicket->setAccountID (sfTarget, target_account); - } - - std::uint64_t hint; - - auto describer = [&](SLE::pointer p, bool b) - { - ownerDirDescriber(p, b, mTxnAccountID); - }; - - TER result = dirAdd(mEngine->view(), - hint, - getOwnerDirIndex (mTxnAccountID), - sleTicket->getIndex (), - describer); - - if (m_journal.trace) m_journal.trace << - "Creating ticket " << to_string (sleTicket->getIndex ()) << - ": " << transHuman (result); - - if (result != tesSUCCESS) - return result; - - sleTicket->setFieldU64(sfOwnerNode, hint); - - // If we succeeded, the new entry counts agains the creator's reserve. - adjustOwnerCount(mEngine->view(), mTxnAccount, 1); - - return result; - } -}; - TER -transact_CreateTicket ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +CreateTicket::preCheck () { #if ! RIPPLE_ENABLE_TICKETS - if (! (engine->view().flags() & tapENABLE_TESTING)) + if (! (view().flags() & tapENABLE_TESTING)) return temDISABLED; #endif - return CreateTicket (txn, params, engine).apply (); + + if (mTxn.isFieldPresent (sfExpiration)) + { + if (mTxn.getFieldU32 (sfExpiration) == 0) + { + j_.warning << + "Malformed transaction: bad expiration"; + return temBAD_EXPIRATION; + } + } + + return Transactor::preCheck (); +} + +STAmount +CreateTicket::getAccountReserve (SLE::pointer account) +{ + return STAmount (view().fees().accountReserve( + account->getFieldU32 (sfOwnerCount) + 1)); +} + +TER +CreateTicket::doApply () +{ + assert (mTxnAccount); + + // A ticket counts against the reserve of the issuing account, but we + // check the starting balance because we want to allow dipping into the + // reserve to pay fees. + if (mPriorBalance < getAccountReserve (mTxnAccount)) + return tecINSUFFICIENT_RESERVE; + + std::uint32_t expiration (0); + + if (mTxn.isFieldPresent (sfExpiration)) + { + expiration = mTxn.getFieldU32 (sfExpiration); + + if (view().parentCloseTime() >= expiration) + return tesSUCCESS; + } + + SLE::pointer sleTicket = std::make_shared(ltTICKET, + getTicketIndex (mTxnAccountID, mTxn.getSequence ())); + sleTicket->setAccountID (sfAccount, mTxnAccountID); + sleTicket->setFieldU32 (sfSequence, mTxn.getSequence ()); + if (expiration != 0) + sleTicket->setFieldU32 (sfExpiration, expiration); + view().insert (sleTicket); + + if (mTxn.isFieldPresent (sfTarget)) + { + AccountID const target_account (mTxn.getAccountID (sfTarget)); + + SLE::pointer sleTarget = view().peek (keylet::account(target_account)); + + // Destination account does not exist. + if (!sleTarget) + return tecNO_TARGET; + + // The issuing account is the default account to which the ticket + // applies so don't bother saving it if that's what's specified. + if (target_account != mTxnAccountID) + sleTicket->setAccountID (sfTarget, target_account); + } + + std::uint64_t hint; + + auto describer = [&](SLE::pointer p, bool b) + { + ownerDirDescriber(p, b, mTxnAccountID); + }; + + TER result = dirAdd(view(), + hint, + getOwnerDirIndex (mTxnAccountID), + sleTicket->getIndex (), + describer); + + if (j_.trace) j_.trace << + "Creating ticket " << to_string (sleTicket->getIndex ()) << + ": " << transHuman (result); + + if (result != tesSUCCESS) + return result; + + sleTicket->setFieldU64(sfOwnerNode, hint); + + // If we succeeded, the new entry counts agains the creator's reserve. + adjustOwnerCount(view(), mTxnAccount, 1); + + return result; } } diff --git a/src/ripple/app/tx/impl/CreateTicket.h b/src/ripple/app/tx/impl/CreateTicket.h new file mode 100644 index 000000000..34a2b2a66 --- /dev/null +++ b/src/ripple/app/tx/impl/CreateTicket.h @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2014 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_CREATETICKET_H_INCLUDED +#define RIPPLE_TX_CREATETICKET_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class CreateTicket + : public Transactor +{ +public: + template + CreateTicket (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER + preCheck () override; + + /** Returns the reserve the account would have if an offer was added. */ + // VFALCO Not needed, just inline the behavior. + STAmount + getAccountReserve (SLE::pointer account); + + TER doApply () override; +}; + +} + +#endif diff --git a/src/ripple/app/tx/impl/OfferStream.cpp b/src/ripple/app/tx/impl/OfferStream.cpp index 96c183fd7..2840d6326 100644 --- a/src/ripple/app/tx/impl/OfferStream.cpp +++ b/src/ripple/app/tx/impl/OfferStream.cpp @@ -23,13 +23,15 @@ namespace ripple { OfferStream::OfferStream (View& view, View& view_cancel, - BookRef book, Clock::time_point when, beast::Journal journal) - : m_journal (journal) + BookRef book, Clock::time_point when, + Config const& config, beast::Journal journal) + : j_ (journal) , m_view (view) , m_view_cancel (view_cancel) , m_book (book) , m_when (when) , m_tip (view, book) + , config_ (config) { } @@ -46,7 +48,7 @@ OfferStream::erase (View& view) if (p == nullptr) { - if (m_journal.error) m_journal.error << + if (j_.error) j_.error << "Missing directory " << m_tip.dir() << " for offer " << m_tip.index(); return; @@ -57,7 +59,7 @@ OfferStream::erase (View& view) if (it == v.end()) { - if (m_journal.error) m_journal.error << + if (j_.error) j_.error << "Missing offer " << m_tip.index() << " for directory " << m_tip.dir(); return; @@ -67,7 +69,7 @@ OfferStream::erase (View& view) p->setFieldV256 (sfIndexes, v); view.update (p); - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Missing offer " << m_tip.index() << " removed from directory " << m_tip.dir(); } @@ -99,7 +101,7 @@ OfferStream::step () if (entry->isFieldPresent (sfExpiration) && entry->getFieldU32 (sfExpiration) <= m_when) { - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Removing expired offer " << entry->getIndex(); offerDelete (view_cancel(), view_cancel().peek( @@ -114,7 +116,7 @@ OfferStream::step () // Remove if either amount is zero if (amount.empty()) { - if (m_journal.warning) m_journal.warning << + if (j_.warning) j_.warning << "Removing bad offer " << entry->getIndex(); offerDelete (view_cancel(), view_cancel().peek( @@ -128,7 +130,7 @@ OfferStream::step () // looking up the funds twice? auto const owner_funds = accountFunds(view(), m_offer.owner(), amount.out, fhZERO_IF_FROZEN, - getConfig()); + config_); // Check for unfunded offer if (owner_funds <= zero) @@ -138,18 +140,18 @@ OfferStream::step () // offer is "found unfunded" versus "became unfunded" auto const original_funds = accountFunds(view_cancel(), m_offer.owner(), amount.out, fhZERO_IF_FROZEN, - getConfig()); + config_); if (original_funds == owner_funds) { offerDelete (view_cancel(), view_cancel().peek( keylet::offer(entry->key()))); - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Removing unfunded offer " << entry->getIndex(); } else { - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Removing became unfunded offer " << entry->getIndex(); } m_offer = Offer{}; diff --git a/src/ripple/app/tx/impl/OfferStream.h b/src/ripple/app/tx/impl/OfferStream.h index 37b8472c3..e824d7954 100644 --- a/src/ripple/app/tx/impl/OfferStream.h +++ b/src/ripple/app/tx/impl/OfferStream.h @@ -50,20 +50,23 @@ namespace ripple { class OfferStream { private: - beast::Journal m_journal; + beast::Journal j_; std::reference_wrapper m_view; std::reference_wrapper m_view_cancel; Book m_book; Clock::time_point m_when; BookTip m_tip; Offer m_offer; + Config const& config_; void erase (View& view); public: - OfferStream (View& view, View& view_cancel, BookRef book, - Clock::time_point when, beast::Journal journal); + OfferStream (View& view, View& view_cancel, + BookRef book, Clock::time_point when, + Config const& config, + beast::Journal journal); View& view () noexcept diff --git a/src/ripple/app/tx/impl/Payment.cpp b/src/ripple/app/tx/impl/Payment.cpp index b520200d7..ae2680c2d 100644 --- a/src/ripple/app/tx/impl/Payment.cpp +++ b/src/ripple/app/tx/impl/Payment.cpp @@ -18,8 +18,8 @@ //============================================================================== #include +#include #include -#include #include #include @@ -27,379 +27,349 @@ namespace ripple { // See https://ripple.com/wiki/Transaction_Format#Payment_.280.29 -class Payment - : public Transactor +TER +Payment::preCheck () { - /* The largest number of paths we allow */ - static std::size_t const MaxPathSize = 6; + std::uint32_t const uTxFlags = mTxn.getFlags (); - /* The longest path we allow */ - static std::size_t const MaxPathLength = 8; - -public: - Payment ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("Payment")) + if (uTxFlags & tfPaymentMask) { - + j_.trace << "Malformed transaction: " << + "Invalid flags set."; + return temINVALID_FLAG; } - TER preCheck () override + bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; + bool const limitQuality = uTxFlags & tfLimitQuality; + bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); + bool const bPaths = mTxn.isFieldPresent (sfPaths); + bool const bMax = mTxn.isFieldPresent (sfSendMax); + + STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount)); + + STAmount maxSourceAmount; + + if (bMax) + maxSourceAmount = mTxn.getFieldAmount (sfSendMax); + else if (saDstAmount.native ()) + maxSourceAmount = saDstAmount; + else + maxSourceAmount = STAmount ( + { saDstAmount.getCurrency (), mTxnAccountID }, + saDstAmount.mantissa(), saDstAmount.exponent (), + saDstAmount < zero); + + auto const& uSrcCurrency = maxSourceAmount.getCurrency (); + auto const& uDstCurrency = saDstAmount.getCurrency (); + + // isZero() is XRP. FIX! + bool const bXRPDirect = uSrcCurrency.isZero () && uDstCurrency.isZero (); + + if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount)) + return temBAD_AMOUNT; + + AccountID const uDstAccountID (mTxn.getAccountID (sfDestination)); + + if (!uDstAccountID) { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - if (uTxFlags & tfPaymentMask) - { - m_journal.trace << "Malformed transaction: " << - "Invalid flags set."; - return temINVALID_FLAG; - } - - bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; - bool const limitQuality = uTxFlags & tfLimitQuality; - bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); - bool const bPaths = mTxn.isFieldPresent (sfPaths); - bool const bMax = mTxn.isFieldPresent (sfSendMax); - - STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount)); - - STAmount maxSourceAmount; - - if (bMax) - maxSourceAmount = mTxn.getFieldAmount (sfSendMax); - else if (saDstAmount.native ()) - maxSourceAmount = saDstAmount; - else - maxSourceAmount = STAmount ( - { saDstAmount.getCurrency (), mTxnAccountID }, - saDstAmount.mantissa(), saDstAmount.exponent (), - saDstAmount < zero); - - auto const& uSrcCurrency = maxSourceAmount.getCurrency (); - auto const& uDstCurrency = saDstAmount.getCurrency (); - - // isZero() is XRP. FIX! - bool const bXRPDirect = uSrcCurrency.isZero () && uDstCurrency.isZero (); - - if (!isLegalNet (saDstAmount) || !isLegalNet (maxSourceAmount)) - return temBAD_AMOUNT; - - AccountID const uDstAccountID (mTxn.getAccountID (sfDestination)); - - if (!uDstAccountID) - { - m_journal.trace << "Malformed transaction: " << - "Payment destination account not specified."; - return temDST_NEEDED; - } - if (bMax && maxSourceAmount <= zero) - { - m_journal.trace << "Malformed transaction: " << - "bad max amount: " << maxSourceAmount.getFullText (); - return temBAD_AMOUNT; - } - if (saDstAmount <= zero) - { - m_journal.trace << "Malformed transaction: "<< - "bad dst amount: " << saDstAmount.getFullText (); - return temBAD_AMOUNT; - } - if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency) - { - m_journal.trace <<"Malformed transaction: " << - "Bad currency."; - return temBAD_CURRENCY; - } - if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) - { - // You're signing yourself a payment. - // If bPaths is true, you might be trying some arbitrage. - m_journal.trace << "Malformed transaction: " << - "Redundant payment from " << to_string (mTxnAccountID) << - " to self without path for " << to_string (uDstCurrency); - return temREDUNDANT; - } - if (bXRPDirect && bMax) - { - // Consistent but redundant transaction. - m_journal.trace << "Malformed transaction: " << - "SendMax specified for XRP to XRP."; - return temBAD_SEND_XRP_MAX; - } - if (bXRPDirect && bPaths) - { - // XRP is sent without paths. - m_journal.trace << "Malformed transaction: " << - "Paths specified for XRP to XRP."; - return temBAD_SEND_XRP_PATHS; - } - if (bXRPDirect && partialPaymentAllowed) - { - // Consistent but redundant transaction. - m_journal.trace << "Malformed transaction: " << - "Partial payment specified for XRP to XRP."; - return temBAD_SEND_XRP_PARTIAL; - } - if (bXRPDirect && limitQuality) - { - // Consistent but redundant transaction. - m_journal.trace << "Malformed transaction: " << - "Limit quality specified for XRP to XRP."; - return temBAD_SEND_XRP_LIMIT; - } - if (bXRPDirect && !defaultPathsAllowed) - { - // Consistent but redundant transaction. - m_journal.trace << "Malformed transaction: " << - "No ripple direct specified for XRP to XRP."; - return temBAD_SEND_XRP_NO_DIRECT; - } - - return Transactor::preCheck (); + j_.trace << "Malformed transaction: " << + "Payment destination account not specified."; + return temDST_NEEDED; + } + if (bMax && maxSourceAmount <= zero) + { + j_.trace << "Malformed transaction: " << + "bad max amount: " << maxSourceAmount.getFullText (); + return temBAD_AMOUNT; + } + if (saDstAmount <= zero) + { + j_.trace << "Malformed transaction: "<< + "bad dst amount: " << saDstAmount.getFullText (); + return temBAD_AMOUNT; + } + if (badCurrency() == uSrcCurrency || badCurrency() == uDstCurrency) + { + j_.trace <<"Malformed transaction: " << + "Bad currency."; + return temBAD_CURRENCY; + } + if (mTxnAccountID == uDstAccountID && uSrcCurrency == uDstCurrency && !bPaths) + { + // You're signing yourself a payment. + // If bPaths is true, you might be trying some arbitrage. + j_.trace << "Malformed transaction: " << + "Redundant payment from " << to_string (mTxnAccountID) << + " to self without path for " << to_string (uDstCurrency); + return temREDUNDANT; + } + if (bXRPDirect && bMax) + { + // Consistent but redundant transaction. + j_.trace << "Malformed transaction: " << + "SendMax specified for XRP to XRP."; + return temBAD_SEND_XRP_MAX; + } + if (bXRPDirect && bPaths) + { + // XRP is sent without paths. + j_.trace << "Malformed transaction: " << + "Paths specified for XRP to XRP."; + return temBAD_SEND_XRP_PATHS; + } + if (bXRPDirect && partialPaymentAllowed) + { + // Consistent but redundant transaction. + j_.trace << "Malformed transaction: " << + "Partial payment specified for XRP to XRP."; + return temBAD_SEND_XRP_PARTIAL; + } + if (bXRPDirect && limitQuality) + { + // Consistent but redundant transaction. + j_.trace << "Malformed transaction: " << + "Limit quality specified for XRP to XRP."; + return temBAD_SEND_XRP_LIMIT; + } + if (bXRPDirect && !defaultPathsAllowed) + { + // Consistent but redundant transaction. + j_.trace << "Malformed transaction: " << + "No ripple direct specified for XRP to XRP."; + return temBAD_SEND_XRP_NO_DIRECT; } - TER doApply () override + return Transactor::preCheck (); +} + +TER +Payment::doApply () +{ + // Ripple if source or destination is non-native or if there are paths. + std::uint32_t const uTxFlags = mTxn.getFlags (); + bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; + bool const limitQuality = uTxFlags & tfLimitQuality; + bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); + bool const bPaths = mTxn.isFieldPresent (sfPaths); + bool const bMax = mTxn.isFieldPresent (sfSendMax); + AccountID const uDstAccountID (mTxn.getAccountID (sfDestination)); + STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount)); + STAmount maxSourceAmount; + if (bMax) + maxSourceAmount = mTxn.getFieldAmount (sfSendMax); + else if (saDstAmount.native ()) + maxSourceAmount = saDstAmount; + else + maxSourceAmount = STAmount ( + {saDstAmount.getCurrency (), mTxnAccountID}, + saDstAmount.mantissa(), saDstAmount.exponent (), + saDstAmount < zero); + + j_.trace << + "maxSourceAmount=" << maxSourceAmount.getFullText () << + " saDstAmount=" << saDstAmount.getFullText (); + + // Open a ledger for editing. + auto const k = keylet::account(uDstAccountID); + SLE::pointer sleDst = view().peek (k); + + if (!sleDst) { - // Ripple if source or destination is non-native or if there are paths. - std::uint32_t const uTxFlags = mTxn.getFlags (); - bool const partialPaymentAllowed = uTxFlags & tfPartialPayment; - bool const limitQuality = uTxFlags & tfLimitQuality; - bool const defaultPathsAllowed = !(uTxFlags & tfNoRippleDirect); - bool const bPaths = mTxn.isFieldPresent (sfPaths); - bool const bMax = mTxn.isFieldPresent (sfSendMax); - AccountID const uDstAccountID (mTxn.getAccountID (sfDestination)); - STAmount const saDstAmount (mTxn.getFieldAmount (sfAmount)); - STAmount maxSourceAmount; - if (bMax) - maxSourceAmount = mTxn.getFieldAmount (sfSendMax); - else if (saDstAmount.native ()) - maxSourceAmount = saDstAmount; - else - maxSourceAmount = STAmount ( - {saDstAmount.getCurrency (), mTxnAccountID}, - saDstAmount.mantissa(), saDstAmount.exponent (), - saDstAmount < zero); - - m_journal.trace << - "maxSourceAmount=" << maxSourceAmount.getFullText () << - " saDstAmount=" << saDstAmount.getFullText (); - - // Open a ledger for editing. - auto const k = keylet::account(uDstAccountID); - SLE::pointer sleDst = mEngine->view().peek (k); - - if (!sleDst) + // Destination account does not exist. + if (!saDstAmount.native ()) { - // Destination account does not exist. - if (!saDstAmount.native ()) - { - m_journal.trace << - "Delay transaction: Destination account does not exist."; + j_.trace << + "Delay transaction: Destination account does not exist."; - // Another transaction could create the account and then this - // transaction would succeed. - return tecNO_DST; - } - else if (mParams & tapOPEN_LEDGER && partialPaymentAllowed) - { - // You cannot fund an account with a partial payment. - // Make retry work smaller, by rejecting this. - m_journal.trace << - "Delay transaction: Partial payment not allowed to create account."; - - - // Another transaction could create the account and then this - // transaction would succeed. - return telNO_DST_PARTIAL; - } - else if (saDstAmount < STAmount (mEngine->view().fees().accountReserve(0))) - { - // getReserve() is the minimum amount that an account can have. - // Reserve is not scaled by load. - m_journal.trace << - "Delay transaction: Destination account does not exist. " << - "Insufficent payment to create account."; - - // TODO: dedupe - // Another transaction could create the account and then this - // transaction would succeed. - return tecNO_DST_INSUF_XRP; - } - - // Create the account. - sleDst = std::make_shared(k); - sleDst->setAccountID (sfAccount, uDstAccountID); - sleDst->setFieldU32 (sfSequence, 1); - mEngine->view().insert(sleDst); + // Another transaction could create the account and then this + // transaction would succeed. + return tecNO_DST; } - else if ((sleDst->getFlags () & lsfRequireDestTag) && - !mTxn.isFieldPresent (sfDestinationTag)) + else if (view().open() + && partialPaymentAllowed) { - // The tag is basically account-specific information we don't - // understand, but we can require someone to fill it in. + // You cannot fund an account with a partial payment. + // Make retry work smaller, by rejecting this. + j_.trace << + "Delay transaction: Partial payment not allowed to create account."; - // We didn't make this test for a newly-formed account because there's - // no way for this field to be set. - m_journal.trace << "Malformed transaction: DestinationTag required."; - return tecDST_TAG_NEEDED; + // Another transaction could create the account and then this + // transaction would succeed. + return telNO_DST_PARTIAL; } - else + else if (saDstAmount < STAmount (view().fees().accountReserve(0))) { - // Tell the engine that we are intending to change the the destination - // account. The source account gets always charged a fee so it's always - // marked as modified. - mEngine->view().update (sleDst); + // getReserve() is the minimum amount that an account can have. + // Reserve is not scaled by load. + j_.trace << + "Delay transaction: Destination account does not exist. " << + "Insufficent payment to create account."; + + // TODO: dedupe + // Another transaction could create the account and then this + // transaction would succeed. + return tecNO_DST_INSUF_XRP; } - TER terResult; + // Create the account. + sleDst = std::make_shared(k); + sleDst->setAccountID (sfAccount, uDstAccountID); + sleDst->setFieldU32 (sfSequence, 1); + view().insert(sleDst); + } + else if ((sleDst->getFlags () & lsfRequireDestTag) && + !mTxn.isFieldPresent (sfDestinationTag)) + { + // The tag is basically account-specific information we don't + // understand, but we can require someone to fill it in. - bool const bRipple = bPaths || bMax || !saDstAmount.native (); - // XXX Should bMax be sufficient to imply ripple? + // We didn't make this test for a newly-formed account because there's + // no way for this field to be set. + j_.trace << "Malformed transaction: DestinationTag required."; - if (bRipple) + return tecDST_TAG_NEEDED; + } + else + { + // Tell the engine that we are intending to change the the destination + // account. The source account gets always charged a fee so it's always + // marked as modified. + view().update (sleDst); + } + + TER terResult; + + bool const bRipple = bPaths || bMax || !saDstAmount.native (); + // XXX Should bMax be sufficient to imply ripple? + + if (bRipple) + { + // Ripple payment with at least one intermediate step and uses + // transitive balances. + + // Copy paths into an editable class. + STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths); + + try { - // Ripple payment with at least one intermediate step and uses - // transitive balances. + path::RippleCalc::Input rcInput; + rcInput.partialPaymentAllowed = partialPaymentAllowed; + rcInput.defaultPathsAllowed = defaultPathsAllowed; + rcInput.limitQuality = limitQuality; + rcInput.deleteUnfundedOffers = true; + rcInput.isLedgerOpen = view().open(); - // Copy paths into an editable class. - STPathSet spsPaths = mTxn.getFieldPathSet (sfPaths); + bool pathTooBig = spsPaths.size () > MaxPathSize; - try + for (auto const& path : spsPaths) + if (path.size () > MaxPathLength) + pathTooBig = true; + + if (rcInput.isLedgerOpen && pathTooBig) { - path::RippleCalc::Input rcInput; - rcInput.partialPaymentAllowed = partialPaymentAllowed; - rcInput.defaultPathsAllowed = defaultPathsAllowed; - rcInput.limitQuality = limitQuality; - rcInput.deleteUnfundedOffers = true; - rcInput.isLedgerOpen = static_cast(mParams & tapOPEN_LEDGER); - - bool pathTooBig = spsPaths.size () > MaxPathSize; - - for (auto const& path : spsPaths) - if (path.size () > MaxPathLength) - pathTooBig = true; - - if (rcInput.isLedgerOpen && pathTooBig) - { - terResult = telBAD_PATH_COUNT; // Too many paths for proposed ledger. - } - else - { - path::RippleCalc::Output rc; - { - PaymentView view (&mEngine->view()); - rc = path::RippleCalc::rippleCalculate ( - view, - maxSourceAmount, - saDstAmount, - uDstAccountID, - mTxnAccountID, - spsPaths, - &rcInput); - // VFALCO NOTE We might not need to apply, depending - // on the TER. But always applying *should* - // be safe. - view.apply(mEngine->view()); - } - - // TODO: is this right? If the amount is the correct amount, was - // the delivered amount previously set? - if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount) - mEngine->deliverAmount (rc.actualAmountOut); - - terResult = rc.result (); - } - - // TODO(tom): what's going on here? - if (isTerRetry (terResult)) - terResult = tecPATH_DRY; - - } - catch (std::exception const& e) - { - m_journal.trace << - "Caught throw: " << e.what (); - - terResult = tefEXCEPTION; - } - } - else - { - // Direct XRP payment. - - // uOwnerCount is the number of entries in this legder for this - // account that require a reserve. - auto const uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount); - - // This is the total reserve in drops. - auto const uReserve = - mEngine->view().fees().accountReserve(uOwnerCount); - - // mPriorBalance is the balance on the sending account BEFORE the - // fees were charged. We want to make sure we have enough reserve - // to send. Allow final spend to use reserve for fee. - auto const mmm = std::max(mTxn.getTransactionFee (), - STAmount (uReserve)); - - if (mPriorBalance < saDstAmount + mmm) - { - // Vote no. However the transaction might succeed, if applied in - // a different order. - m_journal.trace << "Delay transaction: Insufficient funds: " << - " " << mPriorBalance.getText () << - " / " << (saDstAmount + mmm).getText () << - " (" << uReserve << ")"; - - terResult = tecUNFUNDED_PAYMENT; + terResult = telBAD_PATH_COUNT; // Too many paths for proposed ledger. } else { - // The source account does have enough money, so do the - // arithmetic for the transfer and make the ledger change. - mTxnAccount->setFieldAmount (sfBalance, - mSourceBalance - saDstAmount); - sleDst->setFieldAmount (sfBalance, - sleDst->getFieldAmount (sfBalance) + saDstAmount); + path::RippleCalc::Output rc; + { + PaymentView pv (view(), view().flags()); + rc = path::RippleCalc::rippleCalculate ( + pv, + maxSourceAmount, + saDstAmount, + uDstAccountID, + mTxnAccountID, + spsPaths, + &rcInput); + // VFALCO NOTE We might not need to apply, depending + // on the TER. But always applying *should* + // be safe. + pv.apply(view()); + } - // Re-arm the password change fee if we can and need to. - if ((sleDst->getFlags () & lsfPasswordSpent)) - sleDst->clearFlag (lsfPasswordSpent); + // TODO: is this right? If the amount is the correct amount, was + // the delivered amount previously set? + if (rc.result () == tesSUCCESS && rc.actualAmountOut != saDstAmount) + ctx_.deliverAmount (rc.actualAmountOut); - terResult = tesSUCCESS; + terResult = rc.result (); } + + // TODO(tom): what's going on here? + if (isTerRetry (terResult)) + terResult = tecPATH_DRY; + } - - std::string strToken; - std::string strHuman; - - if (transResultInfo (terResult, strToken, strHuman)) + catch (std::exception const& e) { - m_journal.trace << - strToken << ": " << strHuman; + j_.trace << + "Caught throw: " << e.what (); + + terResult = tefEXCEPTION; + } + } + else + { + // Direct XRP payment. + + // uOwnerCount is the number of entries in this legder for this + // account that require a reserve. + auto const uOwnerCount = mTxnAccount->getFieldU32 (sfOwnerCount); + + // This is the total reserve in drops. + auto const uReserve = + view().fees().accountReserve(uOwnerCount); + + // mPriorBalance is the balance on the sending account BEFORE the + // fees were charged. We want to make sure we have enough reserve + // to send. Allow final spend to use reserve for fee. + auto const mmm = std::max(mTxn.getTransactionFee (), + STAmount (uReserve)); + + if (mPriorBalance < saDstAmount + mmm) + { + // Vote no. However the transaction might succeed, if applied in + // a different order. + j_.trace << "Delay transaction: Insufficient funds: " << + " " << mPriorBalance.getText () << + " / " << (saDstAmount + mmm).getText () << + " (" << uReserve << ")"; + + terResult = tecUNFUNDED_PAYMENT; } else { - assert (false); + // The source account does have enough money, so do the + // arithmetic for the transfer and make the ledger change. + mTxnAccount->setFieldAmount (sfBalance, + mSourceBalance - saDstAmount); + sleDst->setFieldAmount (sfBalance, + sleDst->getFieldAmount (sfBalance) + saDstAmount); + + // Re-arm the password change fee if we can and need to. + if ((sleDst->getFlags () & lsfPasswordSpent)) + sleDst->clearFlag (lsfPasswordSpent); + + terResult = tesSUCCESS; } - - return terResult; } -}; -TER -transact_Payment ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) -{ - return Payment(txn, params, engine).apply (); + std::string strToken; + std::string strHuman; + + if (transResultInfo (terResult, strToken, strHuman)) + { + j_.trace << + strToken << ": " << strHuman; + } + else + { + assert (false); + } + + return terResult; } } // ripple diff --git a/src/ripple/app/tx/impl/Payment.h b/src/ripple/app/tx/impl/Payment.h new file mode 100644 index 000000000..9f0ea2c86 --- /dev/null +++ b/src/ripple/app/tx/impl/Payment.h @@ -0,0 +1,55 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_PAYMENT_H_INCLUDED +#define RIPPLE_TX_PAYMENT_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +// See https://ripple.com/wiki/Transaction_Format#Payment_.280.29 + +class Payment + : public Transactor +{ + /* The largest number of paths we allow */ + static std::size_t const MaxPathSize = 6; + + /* The longest path we allow */ + static std::size_t const MaxPathLength = 8; + +public: + template + Payment (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER preCheck () override; + TER doApply () override; +}; + +} // ripple + +#endif diff --git a/src/ripple/app/tx/impl/SetAccount.cpp b/src/ripple/app/tx/impl/SetAccount.cpp index 2cd8a5ce0..413344212 100644 --- a/src/ripple/app/tx/impl/SetAccount.cpp +++ b/src/ripple/app/tx/impl/SetAccount.cpp @@ -18,7 +18,7 @@ //============================================================================== #include -#include +#include #include #include #include @@ -27,370 +27,342 @@ namespace ripple { -class SetAccount - : public Transactor +TER +SetAccount::preCheck () { - static std::size_t const DOMAIN_BYTES_MAX = 256; - static std::size_t const PUBLIC_BYTES_MAX = 33; + std::uint32_t const uTxFlags = mTxn.getFlags (); -public: - SetAccount ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("SetAccount")) + if (uTxFlags & tfAccountSetMask) { - + j_.trace << "Malformed transaction: Invalid flags set."; + return temINVALID_FLAG; } - TER preCheck () override + std::uint32_t const uSetFlag = mTxn.getFieldU32 (sfSetFlag); + std::uint32_t const uClearFlag = mTxn.getFieldU32 (sfClearFlag); + + if ((uSetFlag != 0) && (uSetFlag == uClearFlag)) { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - if (uTxFlags & tfAccountSetMask) - { - m_journal.trace << "Malformed transaction: Invalid flags set."; - return temINVALID_FLAG; - } - - std::uint32_t const uSetFlag = mTxn.getFieldU32 (sfSetFlag); - std::uint32_t const uClearFlag = mTxn.getFieldU32 (sfClearFlag); - - if ((uSetFlag != 0) && (uSetFlag == uClearFlag)) - { - m_journal.trace << "Malformed transaction: Set and clear same flag."; - return temINVALID_FLAG; - } - - // - // RequireAuth - // - bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth); - bool bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth); - - if (bSetRequireAuth && bClearRequireAuth) - { - m_journal.trace << "Malformed transaction: Contradictory flags set."; - return temINVALID_FLAG; - } - - // - // RequireDestTag - // - bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest); - bool bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest); - - if (bSetRequireDest && bClearRequireDest) - { - m_journal.trace << "Malformed transaction: Contradictory flags set."; - return temINVALID_FLAG; - } - - // - // DisallowXRP - // - bool bSetDisallowXRP = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP); - bool bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP); - - if (bSetDisallowXRP && bClearDisallowXRP) - { - m_journal.trace << "Malformed transaction: Contradictory flags set."; - return temINVALID_FLAG; - } - - // TransferRate - if (mTxn.isFieldPresent (sfTransferRate)) - { - std::uint32_t uRate = mTxn.getFieldU32 (sfTransferRate); - - if (uRate && (uRate < QUALITY_ONE)) - { - m_journal.trace << "Malformed transaction: Bad transfer rate."; - return temBAD_TRANSFER_RATE; - } - } - - return Transactor::preCheck (); + j_.trace << "Malformed transaction: Set and clear same flag."; + return temINVALID_FLAG; } - TER doApply () override + // + // RequireAuth + // + bool bSetRequireAuth = (uTxFlags & tfRequireAuth) || (uSetFlag == asfRequireAuth); + bool bClearRequireAuth = (uTxFlags & tfOptionalAuth) || (uClearFlag == asfRequireAuth); + + if (bSetRequireAuth && bClearRequireAuth) { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - std::uint32_t const uFlagsIn = mTxnAccount->getFieldU32 (sfFlags); - std::uint32_t uFlagsOut = uFlagsIn; - - std::uint32_t const uSetFlag = mTxn.getFieldU32 (sfSetFlag); - std::uint32_t const 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); - - // - // RequireAuth - // - if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth)) - { - if (! dirIsEmpty (mEngine->view(), - keylet::ownerDir(mTxnAccountID))) - { - m_journal.trace << "Retry: Owner directory not empty."; - return (mParams & tapRETRY) ? terOWNERS : tecOWNERS; - } - - m_journal.trace << "Set RequireAuth."; - uFlagsOut |= lsfRequireAuth; - } - - if (bClearRequireAuth && (uFlagsIn & lsfRequireAuth)) - { - m_journal.trace << "Clear RequireAuth."; - uFlagsOut &= ~lsfRequireAuth; - } - - // - // RequireDestTag - // - if (bSetRequireDest && !(uFlagsIn & lsfRequireDestTag)) - { - m_journal.trace << "Set lsfRequireDestTag."; - uFlagsOut |= lsfRequireDestTag; - } - - if (bClearRequireDest && (uFlagsIn & lsfRequireDestTag)) - { - m_journal.trace << "Clear lsfRequireDestTag."; - uFlagsOut &= ~lsfRequireDestTag; - } - - // - // DisallowXRP - // - if (bSetDisallowXRP && !(uFlagsIn & lsfDisallowXRP)) - { - m_journal.trace << "Set lsfDisallowXRP."; - uFlagsOut |= lsfDisallowXRP; - } - - if (bClearDisallowXRP && (uFlagsIn & lsfDisallowXRP)) - { - m_journal.trace << "Clear lsfDisallowXRP."; - uFlagsOut &= ~lsfDisallowXRP; - } - - // - // DisableMaster - // - if ((uSetFlag == asfDisableMaster) && !(uFlagsIn & lsfDisableMaster)) - { - if (!mSigMaster) - { - m_journal.trace << "Must use master key to disable master key."; - return tecNEED_MASTER_KEY; - } - - if (!mTxnAccount->isFieldPresent (sfRegularKey)) - return tecNO_REGULAR_KEY; - - m_journal.trace << "Set lsfDisableMaster."; - uFlagsOut |= lsfDisableMaster; - } - - if ((uClearFlag == asfDisableMaster) && (uFlagsIn & lsfDisableMaster)) - { - m_journal.trace << "Clear lsfDisableMaster."; - uFlagsOut &= ~lsfDisableMaster; - } - - // - // DefaultRipple - // - if (uSetFlag == asfDefaultRipple) - { - uFlagsOut |= lsfDefaultRipple; - } - else if (uClearFlag == asfDefaultRipple) - { - uFlagsOut &= ~lsfDefaultRipple; - } - - // - // NoFreeze - // - if (uSetFlag == asfNoFreeze) - { - if (!mSigMaster && !(uFlagsIn & lsfDisableMaster)) - { - m_journal.trace << "Can't use regular key to set NoFreeze."; - return tecNEED_MASTER_KEY; - } - - m_journal.trace << "Set NoFreeze flag"; - uFlagsOut |= lsfNoFreeze; - } - - // Anyone may set global freeze - if (uSetFlag == asfGlobalFreeze) - { - m_journal.trace << "Set GlobalFreeze flag"; - uFlagsOut |= lsfGlobalFreeze; - } - - // If you have set NoFreeze, you may not clear GlobalFreeze - // This prevents those who have set NoFreeze from using - // GlobalFreeze strategically. - if ((uSetFlag != asfGlobalFreeze) && (uClearFlag == asfGlobalFreeze) && - ((uFlagsOut & lsfNoFreeze) == 0)) - { - m_journal.trace << "Clear GlobalFreeze flag"; - uFlagsOut &= ~lsfGlobalFreeze; - } - - // - // Track transaction IDs signed by this account in its root - // - if ((uSetFlag == asfAccountTxnID) && !mTxnAccount->isFieldPresent (sfAccountTxnID)) - { - m_journal.trace << "Set AccountTxnID"; - mTxnAccount->makeFieldPresent (sfAccountTxnID); - } - - if ((uClearFlag == asfAccountTxnID) && mTxnAccount->isFieldPresent (sfAccountTxnID)) - { - m_journal.trace << "Clear AccountTxnID"; - mTxnAccount->makeFieldAbsent (sfAccountTxnID); - } - - // - // EmailHash - // - if (mTxn.isFieldPresent (sfEmailHash)) - { - uint128 const uHash = mTxn.getFieldH128 (sfEmailHash); - - if (!uHash) - { - m_journal.trace << "unset email hash"; - mTxnAccount->makeFieldAbsent (sfEmailHash); - } - else - { - m_journal.trace << "set email hash"; - mTxnAccount->setFieldH128 (sfEmailHash, uHash); - } - } - - // - // WalletLocator - // - if (mTxn.isFieldPresent (sfWalletLocator)) - { - uint256 const uHash = mTxn.getFieldH256 (sfWalletLocator); - - if (!uHash) - { - m_journal.trace << "unset wallet locator"; - mTxnAccount->makeFieldAbsent (sfWalletLocator); - } - else - { - m_journal.trace << "set wallet locator"; - mTxnAccount->setFieldH256 (sfWalletLocator, uHash); - } - } - - // - // MessageKey - // - if (mTxn.isFieldPresent (sfMessageKey)) - { - Blob const messageKey = mTxn.getFieldVL (sfMessageKey); - - if (messageKey.size () > PUBLIC_BYTES_MAX) - { - m_journal.trace << "message key too long"; - return telBAD_PUBLIC_KEY; - } - - if (messageKey.empty ()) - { - m_journal.debug << "set message key"; - mTxnAccount->makeFieldAbsent (sfMessageKey); - } - else - { - m_journal.debug << "set message key"; - mTxnAccount->setFieldVL (sfMessageKey, messageKey); - } - } - - // - // Domain - // - if (mTxn.isFieldPresent (sfDomain)) - { - Blob const domain = mTxn.getFieldVL (sfDomain); - - if (domain.size () > DOMAIN_BYTES_MAX) - { - m_journal.trace << "domain too long"; - return telBAD_DOMAIN; - } - - if (domain.empty ()) - { - m_journal.trace << "unset domain"; - mTxnAccount->makeFieldAbsent (sfDomain); - } - else - { - m_journal.trace << "set domain"; - mTxnAccount->setFieldVL (sfDomain, domain); - } - } - - // - // TransferRate - // - if (mTxn.isFieldPresent (sfTransferRate)) - { - std::uint32_t uRate = mTxn.getFieldU32 (sfTransferRate); - - if (uRate == 0 || uRate == QUALITY_ONE) - { - m_journal.trace << "unset transfer rate"; - mTxnAccount->makeFieldAbsent (sfTransferRate); - } - else if (uRate > QUALITY_ONE) - { - m_journal.trace << "set transfer rate"; - mTxnAccount->setFieldU32 (sfTransferRate, uRate); - } - } - - if (uFlagsIn != uFlagsOut) - mTxnAccount->setFieldU32 (sfFlags, uFlagsOut); - - return tesSUCCESS; + j_.trace << "Malformed transaction: Contradictory flags set."; + return temINVALID_FLAG; } -}; + + // + // RequireDestTag + // + bool bSetRequireDest = (uTxFlags & TxFlag::requireDestTag) || (uSetFlag == asfRequireDest); + bool bClearRequireDest = (uTxFlags & tfOptionalDestTag) || (uClearFlag == asfRequireDest); + + if (bSetRequireDest && bClearRequireDest) + { + j_.trace << "Malformed transaction: Contradictory flags set."; + return temINVALID_FLAG; + } + + // + // DisallowXRP + // + bool bSetDisallowXRP = (uTxFlags & tfDisallowXRP) || (uSetFlag == asfDisallowXRP); + bool bClearDisallowXRP = (uTxFlags & tfAllowXRP) || (uClearFlag == asfDisallowXRP); + + if (bSetDisallowXRP && bClearDisallowXRP) + { + j_.trace << "Malformed transaction: Contradictory flags set."; + return temINVALID_FLAG; + } + + // TransferRate + if (mTxn.isFieldPresent (sfTransferRate)) + { + std::uint32_t uRate = mTxn.getFieldU32 (sfTransferRate); + + if (uRate && (uRate < QUALITY_ONE)) + { + j_.trace << "Malformed transaction: Bad transfer rate."; + return temBAD_TRANSFER_RATE; + } + } + + return Transactor::preCheck (); +} TER -transact_SetAccount ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +SetAccount::doApply () { - return SetAccount(txn, params, engine).apply (); + std::uint32_t const uTxFlags = mTxn.getFlags (); + + std::uint32_t const uFlagsIn = mTxnAccount->getFieldU32 (sfFlags); + std::uint32_t uFlagsOut = uFlagsIn; + + std::uint32_t const uSetFlag = mTxn.getFieldU32 (sfSetFlag); + std::uint32_t const 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); + + // + // RequireAuth + // + if (bSetRequireAuth && !(uFlagsIn & lsfRequireAuth)) + { + if (! dirIsEmpty (view(), + keylet::ownerDir(mTxnAccountID))) + { + j_.trace << "Retry: Owner directory not empty."; + return (view().flags() & tapRETRY) ? terOWNERS : tecOWNERS; + } + + j_.trace << "Set RequireAuth."; + uFlagsOut |= lsfRequireAuth; + } + + if (bClearRequireAuth && (uFlagsIn & lsfRequireAuth)) + { + j_.trace << "Clear RequireAuth."; + uFlagsOut &= ~lsfRequireAuth; + } + + // + // RequireDestTag + // + if (bSetRequireDest && !(uFlagsIn & lsfRequireDestTag)) + { + j_.trace << "Set lsfRequireDestTag."; + uFlagsOut |= lsfRequireDestTag; + } + + if (bClearRequireDest && (uFlagsIn & lsfRequireDestTag)) + { + j_.trace << "Clear lsfRequireDestTag."; + uFlagsOut &= ~lsfRequireDestTag; + } + + // + // DisallowXRP + // + if (bSetDisallowXRP && !(uFlagsIn & lsfDisallowXRP)) + { + j_.trace << "Set lsfDisallowXRP."; + uFlagsOut |= lsfDisallowXRP; + } + + if (bClearDisallowXRP && (uFlagsIn & lsfDisallowXRP)) + { + j_.trace << "Clear lsfDisallowXRP."; + uFlagsOut &= ~lsfDisallowXRP; + } + + // + // DisableMaster + // + if ((uSetFlag == asfDisableMaster) && !(uFlagsIn & lsfDisableMaster)) + { + if (!mSigMaster) + { + j_.trace << "Must use master key to disable master key."; + return tecNEED_MASTER_KEY; + } + + if (!mTxnAccount->isFieldPresent (sfRegularKey)) + return tecNO_REGULAR_KEY; + + j_.trace << "Set lsfDisableMaster."; + uFlagsOut |= lsfDisableMaster; + } + + if ((uClearFlag == asfDisableMaster) && (uFlagsIn & lsfDisableMaster)) + { + j_.trace << "Clear lsfDisableMaster."; + uFlagsOut &= ~lsfDisableMaster; + } + + // + // DefaultRipple + // + if (uSetFlag == asfDefaultRipple) + { + uFlagsOut |= lsfDefaultRipple; + } + else if (uClearFlag == asfDefaultRipple) + { + uFlagsOut &= ~lsfDefaultRipple; + } + + // + // NoFreeze + // + if (uSetFlag == asfNoFreeze) + { + if (!mSigMaster && !(uFlagsIn & lsfDisableMaster)) + { + j_.trace << "Can't use regular key to set NoFreeze."; + return tecNEED_MASTER_KEY; + } + + j_.trace << "Set NoFreeze flag"; + uFlagsOut |= lsfNoFreeze; + } + + // Anyone may set global freeze + if (uSetFlag == asfGlobalFreeze) + { + j_.trace << "Set GlobalFreeze flag"; + uFlagsOut |= lsfGlobalFreeze; + } + + // If you have set NoFreeze, you may not clear GlobalFreeze + // This prevents those who have set NoFreeze from using + // GlobalFreeze strategically. + if ((uSetFlag != asfGlobalFreeze) && (uClearFlag == asfGlobalFreeze) && + ((uFlagsOut & lsfNoFreeze) == 0)) + { + j_.trace << "Clear GlobalFreeze flag"; + uFlagsOut &= ~lsfGlobalFreeze; + } + + // + // Track transaction IDs signed by this account in its root + // + if ((uSetFlag == asfAccountTxnID) && !mTxnAccount->isFieldPresent (sfAccountTxnID)) + { + j_.trace << "Set AccountTxnID"; + mTxnAccount->makeFieldPresent (sfAccountTxnID); + } + + if ((uClearFlag == asfAccountTxnID) && mTxnAccount->isFieldPresent (sfAccountTxnID)) + { + j_.trace << "Clear AccountTxnID"; + mTxnAccount->makeFieldAbsent (sfAccountTxnID); + } + + // + // EmailHash + // + if (mTxn.isFieldPresent (sfEmailHash)) + { + uint128 const uHash = mTxn.getFieldH128 (sfEmailHash); + + if (!uHash) + { + j_.trace << "unset email hash"; + mTxnAccount->makeFieldAbsent (sfEmailHash); + } + else + { + j_.trace << "set email hash"; + mTxnAccount->setFieldH128 (sfEmailHash, uHash); + } + } + + // + // WalletLocator + // + if (mTxn.isFieldPresent (sfWalletLocator)) + { + uint256 const uHash = mTxn.getFieldH256 (sfWalletLocator); + + if (!uHash) + { + j_.trace << "unset wallet locator"; + mTxnAccount->makeFieldAbsent (sfWalletLocator); + } + else + { + j_.trace << "set wallet locator"; + mTxnAccount->setFieldH256 (sfWalletLocator, uHash); + } + } + + // + // MessageKey + // + if (mTxn.isFieldPresent (sfMessageKey)) + { + Blob const messageKey = mTxn.getFieldVL (sfMessageKey); + + if (messageKey.size () > PUBLIC_BYTES_MAX) + { + j_.trace << "message key too long"; + return telBAD_PUBLIC_KEY; + } + + if (messageKey.empty ()) + { + j_.debug << "set message key"; + mTxnAccount->makeFieldAbsent (sfMessageKey); + } + else + { + j_.debug << "set message key"; + mTxnAccount->setFieldVL (sfMessageKey, messageKey); + } + } + + // + // Domain + // + if (mTxn.isFieldPresent (sfDomain)) + { + Blob const domain = mTxn.getFieldVL (sfDomain); + + if (domain.size () > DOMAIN_BYTES_MAX) + { + j_.trace << "domain too long"; + return telBAD_DOMAIN; + } + + if (domain.empty ()) + { + j_.trace << "unset domain"; + mTxnAccount->makeFieldAbsent (sfDomain); + } + else + { + j_.trace << "set domain"; + mTxnAccount->setFieldVL (sfDomain, domain); + } + } + + // + // TransferRate + // + if (mTxn.isFieldPresent (sfTransferRate)) + { + std::uint32_t uRate = mTxn.getFieldU32 (sfTransferRate); + + if (uRate == 0 || uRate == QUALITY_ONE) + { + j_.trace << "unset transfer rate"; + mTxnAccount->makeFieldAbsent (sfTransferRate); + } + else if (uRate > QUALITY_ONE) + { + j_.trace << "set transfer rate"; + mTxnAccount->setFieldU32 (sfTransferRate, uRate); + } + } + + if (uFlagsIn != uFlagsOut) + mTxnAccount->setFieldU32 (sfFlags, uFlagsOut); + + return tesSUCCESS; } } diff --git a/src/ripple/app/tx/impl/SetAccount.h b/src/ripple/app/tx/impl/SetAccount.h new file mode 100644 index 000000000..e90e9dd6d --- /dev/null +++ b/src/ripple/app/tx/impl/SetAccount.h @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_SETACCOUNT_H_INCLUDED +#define RIPPLE_TX_SETACCOUNT_H_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace ripple { + +class SetAccount + : public Transactor +{ + static std::size_t const DOMAIN_BYTES_MAX = 256; + static std::size_t const PUBLIC_BYTES_MAX = 33; + +public: + template + SetAccount (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER preCheck () override; + TER doApply () override; +}; + +} // ripple + +#endif diff --git a/src/ripple/app/tx/impl/SetRegularKey.cpp b/src/ripple/app/tx/impl/SetRegularKey.cpp index fa78bf7cc..1c744b981 100644 --- a/src/ripple/app/tx/impl/SetRegularKey.cpp +++ b/src/ripple/app/tx/impl/SetRegularKey.cpp @@ -18,86 +18,62 @@ //============================================================================== #include -#include +#include #include #include #include namespace ripple { -class SetRegularKey - : public Transactor +std::uint64_t +SetRegularKey::calculateBaseFee () { - std::uint64_t calculateBaseFee () override + if ( mTxnAccount + && (! (mTxnAccount->getFlags () & lsfPasswordSpent)) + && (calcAccountID(mSigningPubKey) == mTxnAccountID)) { - if ( mTxnAccount - && (! (mTxnAccount->getFlags () & lsfPasswordSpent)) - && (calcAccountID(mSigningPubKey) == mTxnAccountID)) - { - // flag is armed and they signed with the right account - return 0; - } - - return Transactor::calculateBaseFee (); + // flag is armed and they signed with the right account + return 0; } -public: - SetRegularKey ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("SetRegularKey")) - { - - } - - TER preCheck () override - { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - if (uTxFlags & tfUniversalMask) - { - if (m_journal.trace) m_journal.trace << - "Malformed transaction: Invalid flags set."; - - return temINVALID_FLAG; - } - - return Transactor::preCheck (); - } - - TER doApply () override - { - if (mFeeDue == zero) - mTxnAccount->setFlag (lsfPasswordSpent); - - if (mTxn.isFieldPresent (sfRegularKey)) - { - mTxnAccount->setAccountID (sfRegularKey, - mTxn.getAccountID (sfRegularKey)); - } - else - { - if (mTxnAccount->isFlag (lsfDisableMaster)) - return tecMASTER_DISABLED; - mTxnAccount->makeFieldAbsent (sfRegularKey); - } - - return tesSUCCESS; - } -}; + return Transactor::calculateBaseFee (); +} TER -transact_SetRegularKey ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) +SetRegularKey::preCheck () { - return SetRegularKey(txn, params, engine).apply (); + std::uint32_t const uTxFlags = mTxn.getFlags (); + + if (uTxFlags & tfUniversalMask) + { + if (j_.trace) j_.trace << + "Malformed transaction: Invalid flags set."; + + return temINVALID_FLAG; + } + + return Transactor::preCheck (); +} + +TER +SetRegularKey::doApply () +{ + if (mFeeDue == zero) + mTxnAccount->setFlag (lsfPasswordSpent); + + if (mTxn.isFieldPresent (sfRegularKey)) + { + mTxnAccount->setAccountID (sfRegularKey, + mTxn.getAccountID (sfRegularKey)); + } + else + { + if (mTxnAccount->isFlag (lsfDisableMaster)) + return tecMASTER_DISABLED; + mTxnAccount->makeFieldAbsent (sfRegularKey); + } + + return tesSUCCESS; } } diff --git a/src/ripple/app/tx/impl/SetRegularKey.h b/src/ripple/app/tx/impl/SetRegularKey.h new file mode 100644 index 000000000..7d70f2980 --- /dev/null +++ b/src/ripple/app/tx/impl/SetRegularKey.h @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_SETREGULARKEY_H_INCLUDED +#define RIPPLE_TX_SETREGULARKEY_H_INCLUDED + +#include +#include +#include +#include + +namespace ripple { + +class SetRegularKey + : public Transactor +{ + std::uint64_t calculateBaseFee () override; + +public: + template + SetRegularKey (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER preCheck () override; + TER doApply () override; +}; + +} // ripple + +#endif + diff --git a/src/ripple/app/tx/impl/SetSignerList.cpp b/src/ripple/app/tx/impl/SetSignerList.cpp index 2ad9d1600..b19f92237 100644 --- a/src/ripple/app/tx/impl/SetSignerList.cpp +++ b/src/ripple/app/tx/impl/SetSignerList.cpp @@ -18,7 +18,8 @@ //============================================================================== #include -#include +#include +#include #include #include #include @@ -31,62 +32,6 @@ namespace ripple { -/** -See the README.md for an overview of the SetSignerList transaction that -this class implements. -*/ -class SetSignerList final - : public Transactor -{ -private: - // Values determined during preCheck for use later. - enum Operation {unknown, set, destroy}; - Operation do_ {unknown}; - std::uint32_t quorum_ {0}; - std::vector signers_; - -public: - SetSignerList ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("SetSignerList")) - { - - } - - /** - Applies the transaction if it is well formed and the ledger state permits. - */ - TER doApply () override; - -protected: - /** - Check anything that can be checked without the ledger. - */ - TER preCheck () override; - -private: - // signers are not const because method (intentionally) sorts vector. - TER validateQuorumAndSignerEntries ( - std::uint32_t quorum, - std::vector& signers) const; - - // Methods called by doApply() - TER replaceSignerList (uint256 const& index); - TER destroySignerList (uint256 const& index); - - void writeSignersToLedger (SLE::pointer ledgerEntry); - - static std::size_t ownerCountDelta (std::size_t entryCount); -}; - -//------------------------------------------------------------------------------ - TER SetSignerList::doApply () { @@ -116,6 +61,11 @@ SetSignerList::doApply () TER SetSignerList::preCheck() { +#if ! RIPPLE_ENABLE_MULTI_SIGN + if (! (view().flags() & tapENABLE_TESTING)) + return temDISABLED; +#endif + // We need the account ID later, so do this check first. preCheckAccount (); @@ -127,7 +77,7 @@ SetSignerList::preCheck() if (quorum_ && hasSignerEntries) { SignerEntries::Decoded signers ( - SignerEntries::deserialize (mTxn, m_journal, "transaction")); + SignerEntries::deserialize (mTxn, j_, "transaction")); if (signers.ter != tesSUCCESS) return signers.ter; @@ -150,7 +100,7 @@ SetSignerList::preCheck() else { // Neither a set nor a destroy. Malformed. - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Malformed transaction: Invalid signer set list format."; return temMALFORMED; } @@ -169,7 +119,7 @@ SetSignerList::validateQuorumAndSignerEntries ( if ((signerCount < SignerEntries::minEntries) || (signerCount > SignerEntries::maxEntries)) { - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Too many or too few signers in signer list."; return temMALFORMED; } @@ -180,7 +130,7 @@ SetSignerList::validateQuorumAndSignerEntries ( if (std::adjacent_find ( signers.begin (), signers.end ()) != signers.end ()) { - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Duplicate signers in signer list"; return temBAD_SIGNER; } @@ -194,7 +144,7 @@ SetSignerList::validateQuorumAndSignerEntries ( if (signer.account == mTxnAccountID) { - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "A signer may not self reference account."; return temBAD_SIGNER; } @@ -204,7 +154,7 @@ SetSignerList::validateQuorumAndSignerEntries ( } if ((quorum <= 0) || (allSignersWeight < quorum)) { - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Quorum is unreachable"; return temBAD_QUORUM; } @@ -225,7 +175,7 @@ SetSignerList::replaceSignerList (uint256 const& index) std::uint32_t const addedOwnerCount = ownerCountDelta (signers_.size ()); auto const newReserve = - mEngine->view().fees().accountReserve( + view().fees().accountReserve( oldOwnerCount + addedOwnerCount); // We check the reserve against the starting balance because we want to @@ -236,7 +186,7 @@ SetSignerList::replaceSignerList (uint256 const& index) // Everything's ducky. Add the ltSIGNER_LIST to the ledger. auto signerList = std::make_shared(ltSIGNER_LIST, index); - mEngine->view().insert (signerList); + view().insert (signerList); writeSignersToLedger (signerList); // Lambda for call to dirAdd. @@ -247,10 +197,10 @@ SetSignerList::replaceSignerList (uint256 const& index) // Add the signer list to the account's directory. std::uint64_t hint; - TER result = dirAdd(mEngine->view (), + TER result = dirAdd(ctx_.view (), hint, getOwnerDirIndex (mTxnAccountID), index, describer); - if (m_journal.trace) m_journal.trace << + if (j_.trace) j_.trace << "Create signer list for account " << mTxnAccountID << ": " << transHuman (result); @@ -260,7 +210,7 @@ SetSignerList::replaceSignerList (uint256 const& index) signerList->setFieldU64 (sfOwnerNode, hint); // If we succeeded, the new entry counts against the creator's reserve. - adjustOwnerCount(mEngine->view(), + adjustOwnerCount(view(), mTxnAccount, addedOwnerCount); return result; @@ -271,7 +221,7 @@ SetSignerList::destroySignerList (uint256 const& index) { // See if there's an ltSIGNER_LIST for this account. SLE::pointer signerList = - mEngine->view().peek (keylet::signers(index)); + view().peek (keylet::signers(index)); // If the signer list doesn't exist we've already succeeded in deleting it. if (!signerList) @@ -282,7 +232,7 @@ SetSignerList::destroySignerList (uint256 const& index) std::uint32_t removeFromOwnerCount = 0; auto const k = keylet::signers(mTxnAccountID); SLE::pointer accountSignersList = - mEngine->view().peek (k); + view().peek (k); if (accountSignersList) { STArray const& actualList = @@ -293,14 +243,14 @@ SetSignerList::destroySignerList (uint256 const& index) // Remove the node from the account directory. std::uint64_t const hint (signerList->getFieldU64 (sfOwnerNode)); - TER const result = dirDelete(mEngine->view (), false, hint, + TER const result = dirDelete(ctx_.view (), false, hint, getOwnerDirIndex (mTxnAccountID), index, false, (hint == 0)); if (result == tesSUCCESS) - adjustOwnerCount(mEngine->view(), + adjustOwnerCount(view(), mTxnAccount, removeFromOwnerCount); - mEngine->view ().erase (signerList); + ctx_.view ().erase (signerList); return result; } @@ -357,17 +307,4 @@ SetSignerList::ownerCountDelta (std::size_t entryCount) return 2 + entryCount; } -TER -transact_SetSignerList ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) -{ -#if ! RIPPLE_ENABLE_MULTI_SIGN - if (! (engine->view().flags() & tapENABLE_TESTING)) - return temDISABLED; -#endif - return SetSignerList (txn, params, engine).apply (); -} - } // namespace ripple diff --git a/src/ripple/app/tx/impl/SetSignerList.h b/src/ripple/app/tx/impl/SetSignerList.h new file mode 100644 index 000000000..933fe5acb --- /dev/null +++ b/src/ripple/app/tx/impl/SetSignerList.h @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2014 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_SETSIGNERLIST_H_INCLUDED +#define RIPPLE_TX_SETSIGNERLIST_H_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +/** +See the README.md for an overview of the SetSignerList transaction that +this class implements. +*/ +class SetSignerList : public Transactor +{ +private: + // Values determined during preCheck for use later. + enum Operation {unknown, set, destroy}; + Operation do_ {unknown}; + std::uint32_t quorum_ {0}; + std::vector signers_; + +public: + template + SetSignerList (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER doApply () override; + TER preCheck () override; + +private: + // `signers` is sorted on return + TER validateQuorumAndSignerEntries ( + std::uint32_t quorum, + std::vector& signers) const; + + TER replaceSignerList (uint256 const& index); + TER destroySignerList (uint256 const& index); + + void writeSignersToLedger (SLE::pointer ledgerEntry); + + static std::size_t ownerCountDelta (std::size_t entryCount); +}; + +} // ripple + +#endif diff --git a/src/ripple/app/tx/impl/SetTrust.cpp b/src/ripple/app/tx/impl/SetTrust.cpp index 83ee89243..3cd767d8e 100644 --- a/src/ripple/app/tx/impl/SetTrust.cpp +++ b/src/ripple/app/tx/impl/SetTrust.cpp @@ -19,442 +19,418 @@ #include #include -#include +#include #include #include #include namespace ripple { -class SetTrust - : public Transactor +TER +SetTrust::preCheck () { -public: - SetTrust ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) - : Transactor ( - txn, - params, - engine, - deprecatedLogs().journal("SetTrust")) + std::uint32_t const uTxFlags = mTxn.getFlags (); + + if (uTxFlags & tfTrustSetMask) { + j_.trace << + "Malformed transaction: Invalid flags set."; + return temINVALID_FLAG; } - TER preCheck () override + STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount)); + + if (!isLegalNet (saLimitAmount)) + return temBAD_AMOUNT; + + if (saLimitAmount.native ()) { - std::uint32_t const uTxFlags = mTxn.getFlags (); - - if (uTxFlags & tfTrustSetMask) - { - m_journal.trace << - "Malformed transaction: Invalid flags set."; - return temINVALID_FLAG; - } - - STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount)); - - if (!isLegalNet (saLimitAmount)) - return temBAD_AMOUNT; - - if (saLimitAmount.native ()) - { - if (m_journal.trace) m_journal.trace << - "Malformed transaction: specifies native limit " << - saLimitAmount.getFullText (); - return temBAD_LIMIT; - } - - if (badCurrency() == saLimitAmount.getCurrency ()) - { - if (m_journal.trace) m_journal.trace << - "Malformed transaction: specifies XRP as IOU"; - return temBAD_CURRENCY; - } - - if (saLimitAmount < zero) - { - if (m_journal.trace) m_journal.trace << - "Malformed transaction: Negative credit limit."; - return temBAD_LIMIT; - } - - // Check if destination makes sense. - auto const& issuer = saLimitAmount.getIssuer (); - - if (!issuer || issuer == noAccount()) - { - if (m_journal.trace) m_journal.trace << - "Malformed transaction: no destination account."; - return temDST_NEEDED; - } - - return Transactor::preCheck (); + if (j_.trace) j_.trace << + "Malformed transaction: specifies native limit " << + saLimitAmount.getFullText (); + return temBAD_LIMIT; } - TER doApply () override + if (badCurrency() == saLimitAmount.getCurrency ()) { - TER terResult = tesSUCCESS; + if (j_.trace) j_.trace << + "Malformed transaction: specifies XRP as IOU"; + return temBAD_CURRENCY; + } - STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount)); - bool const bQualityIn (mTxn.isFieldPresent (sfQualityIn)); - bool const bQualityOut (mTxn.isFieldPresent (sfQualityOut)); + if (saLimitAmount < zero) + { + if (j_.trace) j_.trace << + "Malformed transaction: Negative credit limit."; + return temBAD_LIMIT; + } - Currency const currency (saLimitAmount.getCurrency ()); - AccountID uDstAccountID (saLimitAmount.getIssuer ()); + // Check if destination makes sense. + auto const& issuer = saLimitAmount.getIssuer (); - // true, iff current is high account. - bool const bHigh = mTxnAccountID > uDstAccountID; + if (!issuer || issuer == noAccount()) + { + if (j_.trace) j_.trace << + "Malformed transaction: no destination account."; + return temDST_NEEDED; + } - std::uint32_t const uOwnerCount (mTxnAccount->getFieldU32 (sfOwnerCount)); + return Transactor::preCheck (); +} - // The reserve required to create the line. Note that we allow up to - // two trust lines without requiring a reserve because being able to - // exchange currencies is a powerful Ripple feature. - // - // This is also a security feature: if you're a gateway and you want to - // be able to let someone use your services, you would otherwise have to - // give them enough XRP to cover the incremental reserve for their trust - // line. If they had no intention of using your services, they could use - // the XRP for their own purposes. So we make it possible for gateways - // to fund accounts in a way where there's no incentive to trick them - // into creating an account you have no intention of using. +TER +SetTrust::doApply () +{ + TER terResult = tesSUCCESS; - STAmount const reserveCreate ((uOwnerCount < 2) - ? 0 - : mEngine->view().fees().accountReserve(uOwnerCount + 1)); + STAmount const saLimitAmount (mTxn.getFieldAmount (sfLimitAmount)); + bool const bQualityIn (mTxn.isFieldPresent (sfQualityIn)); + bool const bQualityOut (mTxn.isFieldPresent (sfQualityOut)); - std::uint32_t uQualityIn (bQualityIn ? mTxn.getFieldU32 (sfQualityIn) : 0); - std::uint32_t uQualityOut (bQualityOut ? mTxn.getFieldU32 (sfQualityOut) : 0); + Currency const currency (saLimitAmount.getCurrency ()); + AccountID uDstAccountID (saLimitAmount.getIssuer ()); - if (bQualityOut && QUALITY_ONE == uQualityOut) - uQualityOut = 0; + // true, iff current is high account. + bool const bHigh = mTxnAccountID > uDstAccountID; - std::uint32_t const uTxFlags = mTxn.getFlags (); + std::uint32_t const uOwnerCount (mTxnAccount->getFieldU32 (sfOwnerCount)); - bool const bSetAuth = (uTxFlags & tfSetfAuth); - bool const bSetNoRipple = (uTxFlags & tfSetNoRipple); - bool const bClearNoRipple = (uTxFlags & tfClearNoRipple); - bool const bSetFreeze = (uTxFlags & tfSetFreeze); - bool const bClearFreeze = (uTxFlags & tfClearFreeze); + // The reserve required to create the line. Note that we allow up to + // two trust lines without requiring a reserve because being able to + // exchange currencies is a powerful Ripple feature. + // + // This is also a security feature: if you're a gateway and you want to + // be able to let someone use your services, you would otherwise have to + // give them enough XRP to cover the incremental reserve for their trust + // line. If they had no intention of using your services, they could use + // the XRP for their own purposes. So we make it possible for gateways + // to fund accounts in a way where there's no incentive to trick them + // into creating an account you have no intention of using. - if (bSetAuth && !(mTxnAccount->getFieldU32 (sfFlags) & lsfRequireAuth)) - { - m_journal.trace << - "Retry: Auth not required."; - return tefNO_AUTH_REQUIRED; - } + STAmount const reserveCreate ((uOwnerCount < 2) + ? 0 + : view().fees().accountReserve(uOwnerCount + 1)); - if (mTxnAccountID == uDstAccountID) - { - // The only purpose here is to allow a mistakenly created - // trust line to oneself to be deleted. If no such trust - // lines exist now, why not remove this code and simply - // return an error? - SLE::pointer sleDelete = mEngine->view().peek ( - keylet::line(mTxnAccountID, uDstAccountID, currency)); + std::uint32_t uQualityIn (bQualityIn ? mTxn.getFieldU32 (sfQualityIn) : 0); + std::uint32_t uQualityOut (bQualityOut ? mTxn.getFieldU32 (sfQualityOut) : 0); - if (sleDelete) - { - m_journal.warning << - "Clearing redundant line."; + if (bQualityOut && QUALITY_ONE == uQualityOut) + uQualityOut = 0; - return trustDelete (mEngine->view(), - sleDelete, mTxnAccountID, uDstAccountID); - } - else - { - m_journal.trace << - "Malformed transaction: Can not extend credit to self."; - return temDST_IS_SRC; - } - } + std::uint32_t const uTxFlags = mTxn.getFlags (); - SLE::pointer sleDst = - mEngine->view().peek (keylet::account(uDstAccountID)); + bool const bSetAuth = (uTxFlags & tfSetfAuth); + bool const bSetNoRipple = (uTxFlags & tfSetNoRipple); + bool const bClearNoRipple = (uTxFlags & tfClearNoRipple); + bool const bSetFreeze = (uTxFlags & tfSetFreeze); + bool const bClearFreeze = (uTxFlags & tfClearFreeze); - if (!sleDst) - { - m_journal.trace << - "Delay transaction: Destination account does not exist."; - return tecNO_DST; - } + if (bSetAuth && !(mTxnAccount->getFieldU32 (sfFlags) & lsfRequireAuth)) + { + j_.trace << + "Retry: Auth not required."; + return tefNO_AUTH_REQUIRED; + } - STAmount saLimitAllow = saLimitAmount; - saLimitAllow.setIssuer (mTxnAccountID); - - SLE::pointer sleRippleState = mEngine->view().peek ( + if (mTxnAccountID == uDstAccountID) + { + // The only purpose here is to allow a mistakenly created + // trust line to oneself to be deleted. If no such trust + // lines exist now, why not remove this code and simply + // return an error? + SLE::pointer sleDelete = view().peek ( keylet::line(mTxnAccountID, uDstAccountID, currency)); - if (sleRippleState) + if (sleDelete) { - STAmount saLowBalance; - STAmount saLowLimit; - STAmount saHighBalance; - STAmount saHighLimit; - std::uint32_t uLowQualityIn; - std::uint32_t uLowQualityOut; - std::uint32_t uHighQualityIn; - std::uint32_t uHighQualityOut; - auto const& uLowAccountID = !bHigh ? mTxnAccountID : uDstAccountID; - auto const& uHighAccountID = bHigh ? mTxnAccountID : uDstAccountID; - SLE::ref sleLowAccount = !bHigh ? mTxnAccount : sleDst; - SLE::ref sleHighAccount = bHigh ? mTxnAccount : sleDst; + j_.warning << + "Clearing redundant line."; - // - // 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); - } - - std::uint32_t const uFlagsIn (sleRippleState->getFieldU32 (sfFlags)); - std::uint32_t uFlagsOut (uFlagsIn); - - if (bSetNoRipple && !bClearNoRipple && (bHigh ? saHighBalance : saLowBalance) >= zero) - { - uFlagsOut |= (bHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - else if (bClearNoRipple && !bSetNoRipple) - { - uFlagsOut &= ~(bHigh ? lsfHighNoRipple : lsfLowNoRipple); - } - - if (bSetFreeze && !bClearFreeze && !mTxnAccount->isFlag (lsfNoFreeze)) - { - uFlagsOut |= (bHigh ? lsfHighFreeze : lsfLowFreeze); - } - else if (bClearFreeze && !bSetFreeze) - { - uFlagsOut &= ~(bHigh ? lsfHighFreeze : lsfLowFreeze); - } - - if (QUALITY_ONE == uLowQualityOut) uLowQualityOut = 0; - - if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0; - - bool const bLowDefRipple = sleLowAccount->getFlags() & lsfDefaultRipple; - bool const bHighDefRipple = sleHighAccount->getFlags() & lsfDefaultRipple; - - bool const bLowReserveSet = uLowQualityIn || uLowQualityOut || - ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || - (uFlagsOut & lsfLowFreeze) || - saLowLimit || saLowBalance > zero; - bool const bLowReserveClear = !bLowReserveSet; - - bool const bHighReserveSet = uHighQualityIn || uHighQualityOut || - ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || - (uFlagsOut & lsfHighFreeze) || - saHighLimit || saHighBalance > zero; - bool const bHighReserveClear = !bHighReserveSet; - - bool const bDefault = bLowReserveClear && bHighReserveClear; - - bool const bLowReserved = (uFlagsIn & lsfLowReserve); - bool const bHighReserved = (uFlagsIn & lsfHighReserve); - - bool bReserveIncrease = false; - - if (bSetAuth) - { - uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth); - } - - if (bLowReserveSet && !bLowReserved) - { - // Set reserve for low account. - adjustOwnerCount(mEngine->view(), - sleLowAccount, 1); - uFlagsOut |= lsfLowReserve; - - if (!bHigh) - bReserveIncrease = true; - } - - if (bLowReserveClear && bLowReserved) - { - // Clear reserve for low account. - adjustOwnerCount(mEngine->view(), - sleLowAccount, -1); - uFlagsOut &= ~lsfLowReserve; - } - - if (bHighReserveSet && !bHighReserved) - { - // Set reserve for high account. - adjustOwnerCount(mEngine->view(), - sleHighAccount, 1); - uFlagsOut |= lsfHighReserve; - - if (bHigh) - bReserveIncrease = true; - } - - if (bHighReserveClear && bHighReserved) - { - // Clear reserve for high account. - adjustOwnerCount(mEngine->view(), - sleHighAccount, -1); - uFlagsOut &= ~lsfHighReserve; - } - - if (uFlagsIn != uFlagsOut) - sleRippleState->setFieldU32 (sfFlags, uFlagsOut); - - if (bDefault || badCurrency() == currency) - { - // Delete. - - terResult = trustDelete (mEngine->view(), - sleRippleState, uLowAccountID, uHighAccountID); - } - // Reserve is not scaled by load. - else if (bReserveIncrease && mPriorBalance < reserveCreate) - { - m_journal.trace << - "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->view().update (sleRippleState); - - m_journal.trace << "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. - { - m_journal.trace << - "Redundant: Setting non-existent ripple line to defaults."; - return tecNO_LINE_REDUNDANT; - } - else if (mPriorBalance < reserveCreate) // Reserve is not scaled by load. - { - m_journal.trace << - "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; + return trustDelete (view(), + sleDelete, mTxnAccountID, uDstAccountID); } else { - // Zero balance in currency. - STAmount saBalance ({currency, noAccount()}); + j_.trace << + "Malformed transaction: Can not extend credit to self."; + return temDST_IS_SRC; + } + } - uint256 index (getRippleStateIndex ( - mTxnAccountID, uDstAccountID, currency)); + SLE::pointer sleDst = + view().peek (keylet::account(uDstAccountID)); - m_journal.trace << - "doTrustSet: Creating ripple line: " << - to_string (index); + if (!sleDst) + { + j_.trace << + "Delay transaction: Destination account does not exist."; + return tecNO_DST; + } - // Create a new ripple line. - terResult = trustCreate (mEngine->view(), - bHigh, - mTxnAccountID, - uDstAccountID, - index, - mTxnAccount, - bSetAuth, - bSetNoRipple && !bClearNoRipple, - bSetFreeze && !bClearFreeze, - saBalance, - saLimitAllow, // Limit for who is being charged. - uQualityIn, - uQualityOut); + STAmount saLimitAllow = saLimitAmount; + saLimitAllow.setIssuer (mTxnAccountID); + + SLE::pointer sleRippleState = view().peek ( + keylet::line(mTxnAccountID, uDstAccountID, currency)); + + if (sleRippleState) + { + STAmount saLowBalance; + STAmount saLowLimit; + STAmount saHighBalance; + STAmount saHighLimit; + std::uint32_t uLowQualityIn; + std::uint32_t uLowQualityOut; + std::uint32_t uHighQualityIn; + std::uint32_t uHighQualityOut; + auto const& uLowAccountID = !bHigh ? mTxnAccountID : uDstAccountID; + auto const& 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); } - return terResult; + 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); + } + + std::uint32_t const uFlagsIn (sleRippleState->getFieldU32 (sfFlags)); + std::uint32_t uFlagsOut (uFlagsIn); + + if (bSetNoRipple && !bClearNoRipple && (bHigh ? saHighBalance : saLowBalance) >= zero) + { + uFlagsOut |= (bHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + else if (bClearNoRipple && !bSetNoRipple) + { + uFlagsOut &= ~(bHigh ? lsfHighNoRipple : lsfLowNoRipple); + } + + if (bSetFreeze && !bClearFreeze && !mTxnAccount->isFlag (lsfNoFreeze)) + { + uFlagsOut |= (bHigh ? lsfHighFreeze : lsfLowFreeze); + } + else if (bClearFreeze && !bSetFreeze) + { + uFlagsOut &= ~(bHigh ? lsfHighFreeze : lsfLowFreeze); + } + + if (QUALITY_ONE == uLowQualityOut) uLowQualityOut = 0; + + if (QUALITY_ONE == uHighQualityOut) uHighQualityOut = 0; + + bool const bLowDefRipple = sleLowAccount->getFlags() & lsfDefaultRipple; + bool const bHighDefRipple = sleHighAccount->getFlags() & lsfDefaultRipple; + + bool const bLowReserveSet = uLowQualityIn || uLowQualityOut || + ((uFlagsOut & lsfLowNoRipple) == 0) != bLowDefRipple || + (uFlagsOut & lsfLowFreeze) || + saLowLimit || saLowBalance > zero; + bool const bLowReserveClear = !bLowReserveSet; + + bool const bHighReserveSet = uHighQualityIn || uHighQualityOut || + ((uFlagsOut & lsfHighNoRipple) == 0) != bHighDefRipple || + (uFlagsOut & lsfHighFreeze) || + saHighLimit || saHighBalance > zero; + bool const bHighReserveClear = !bHighReserveSet; + + bool const bDefault = bLowReserveClear && bHighReserveClear; + + bool const bLowReserved = (uFlagsIn & lsfLowReserve); + bool const bHighReserved = (uFlagsIn & lsfHighReserve); + + bool bReserveIncrease = false; + + if (bSetAuth) + { + uFlagsOut |= (bHigh ? lsfHighAuth : lsfLowAuth); + } + + if (bLowReserveSet && !bLowReserved) + { + // Set reserve for low account. + adjustOwnerCount(view(), + sleLowAccount, 1); + uFlagsOut |= lsfLowReserve; + + if (!bHigh) + bReserveIncrease = true; + } + + if (bLowReserveClear && bLowReserved) + { + // Clear reserve for low account. + adjustOwnerCount(view(), + sleLowAccount, -1); + uFlagsOut &= ~lsfLowReserve; + } + + if (bHighReserveSet && !bHighReserved) + { + // Set reserve for high account. + adjustOwnerCount(view(), + sleHighAccount, 1); + uFlagsOut |= lsfHighReserve; + + if (bHigh) + bReserveIncrease = true; + } + + if (bHighReserveClear && bHighReserved) + { + // Clear reserve for high account. + adjustOwnerCount(view(), + sleHighAccount, -1); + uFlagsOut &= ~lsfHighReserve; + } + + if (uFlagsIn != uFlagsOut) + sleRippleState->setFieldU32 (sfFlags, uFlagsOut); + + if (bDefault || badCurrency() == currency) + { + // Delete. + + terResult = trustDelete (view(), + sleRippleState, uLowAccountID, uHighAccountID); + } + // Reserve is not scaled by load. + else if (bReserveIncrease && mPriorBalance < reserveCreate) + { + j_.trace << + "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 + { + view().update (sleRippleState); + + j_.trace << "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. + { + j_.trace << + "Redundant: Setting non-existent ripple line to defaults."; + return tecNO_LINE_REDUNDANT; + } + else if (mPriorBalance < reserveCreate) // Reserve is not scaled by load. + { + j_.trace << + "Delay transaction: Line does not exist. Insufficent reserve to create line."; -TER -transact_SetTrust ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) -{ - return SetTrust (txn, params, engine).apply (); + // Another transaction could create the account and then this transaction would succeed. + terResult = tecNO_LINE_INSUF_RESERVE; + } + else + { + // Zero balance in currency. + STAmount saBalance ({currency, noAccount()}); + + uint256 index (getRippleStateIndex ( + mTxnAccountID, uDstAccountID, currency)); + + j_.trace << + "doTrustSet: Creating ripple line: " << + to_string (index); + + // Create a new ripple line. + terResult = trustCreate (view(), + bHigh, + mTxnAccountID, + uDstAccountID, + index, + mTxnAccount, + bSetAuth, + bSetNoRipple && !bClearNoRipple, + bSetFreeze && !bClearFreeze, + saBalance, + saLimitAllow, // Limit for who is being charged. + uQualityIn, + uQualityOut); + } + + return terResult; } } diff --git a/src/ripple/app/tx/impl/SetTrust.h b/src/ripple/app/tx/impl/SetTrust.h new file mode 100644 index 000000000..265117dce --- /dev/null +++ b/src/ripple/app/tx/impl/SetTrust.h @@ -0,0 +1,49 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_TX_SETTRUST_H_INCLUDED +#define RIPPLE_TX_SETTRUST_H_INCLUDED + +#include +#include +#include +#include +#include + +namespace ripple { + +class SetTrust + : public Transactor +{ +public: + template + SetTrust (Args&&... args) + : Transactor(std::forward< + Args>(args)...) + { + } + + TER preCheck () override; + TER doApply () override; +}; + +} // ripple + +#endif + diff --git a/src/ripple/app/tx/impl/Taker.cpp b/src/ripple/app/tx/impl/Taker.cpp index 0697d8a6c..4a9322231 100644 --- a/src/ripple/app/tx/impl/Taker.cpp +++ b/src/ripple/app/tx/impl/Taker.cpp @@ -515,23 +515,15 @@ BasicTaker::do_cross ( //============================================================================== -std::uint32_t -Taker::calculateRate ( - View const& view, - AccountID const& issuer, - AccountID const& account) -{ - return isXRP (issuer) || (account == issuer) - ? QUALITY_ONE - : rippleTransferRate (view, issuer); -} - -Taker::Taker (CrossType cross_type, View& view, AccountID const& account, - Amounts const& offer, std::uint32_t flags, beast::Journal journal) +Taker::Taker (CrossType cross_type, View& view, + AccountID const& account, Amounts const& offer, + std::uint32_t flags, Config const& config, + beast::Journal journal) : BasicTaker (cross_type, account, offer, Quality(offer), flags, calculateRate(view, offer.in.getIssuer(), account), calculateRate(view, offer.out.getIssuer(), account), journal) , view_ (view) + , config_ (config) , xrp_flow_ (0) , direct_crossings_ (0) , bridge_crossings_ (0) @@ -587,7 +579,7 @@ STAmount Taker::get_funds (AccountID const& account, STAmount const& amount) const { return accountFunds(view_, account, amount, fhZERO_IF_FROZEN, - getConfig()); + config_); } TER Taker::transferXRP ( @@ -779,4 +771,16 @@ Taker::cross (Offer const& leg1, Offer const& leg2) return fill (ret.first, leg1, ret.second, leg2); } +std::uint32_t +Taker::calculateRate ( + View const& view, + AccountID const& issuer, + AccountID const& account) +{ + return isXRP (issuer) || (account == issuer) + ? QUALITY_ONE + : rippleTransferRate (view, issuer); } + +} // ripple + diff --git a/src/ripple/app/tx/impl/Taker.h b/src/ripple/app/tx/impl/Taker.h index bc4ff7303..4a3282054 100644 --- a/src/ripple/app/tx/impl/Taker.h +++ b/src/ripple/app/tx/impl/Taker.h @@ -21,6 +21,7 @@ #define RIPPLE_APP_BOOK_TAKER_H_INCLUDED #include +#include #include #include #include @@ -239,48 +240,14 @@ public: class Taker : public BasicTaker { -private: - static - std::uint32_t - calculateRate (View const& view, - AccountID const& issuer, - AccountID const& account); - - // The underlying ledger entry we are dealing with - View& view_; - - // The amount of XRP that flowed if we were autobridging - STAmount xrp_flow_; - - // The number direct crossings that we performed - std::uint32_t direct_crossings_; - - // The number autobridged crossings that we performed - std::uint32_t bridge_crossings_; - - TER - fill (BasicTaker::Flow const& flow, Offer const& offer); - - TER - fill ( - BasicTaker::Flow const& flow1, Offer const& leg1, - BasicTaker::Flow const& flow2, Offer const& leg2); - - TER - transferXRP (AccountID const& from, AccountID const& to, STAmount const& amount); - - TER - redeemIOU (AccountID const& account, STAmount const& amount, Issue const& issue); - - TER - issueIOU (AccountID const& account, STAmount const& amount, Issue const& issue); - public: Taker () = delete; Taker (Taker const&) = delete; - Taker (CrossType cross_type, View& view, AccountID const& account, - Amounts const& offer, std::uint32_t flags, beast::Journal journal); + Taker (CrossType cross_type, View& view, + AccountID const& account, Amounts const& offer, + std::uint32_t flags, Config const& config, + beast::Journal journal); ~Taker () = default; void @@ -318,6 +285,45 @@ public: TER cross (Offer const& leg1, Offer const& leg2); /** @} */ + +private: + static + std::uint32_t + calculateRate (View const& view, + AccountID const& issuer, + AccountID const& account); + + TER + fill (BasicTaker::Flow const& flow, Offer const& offer); + + TER + fill ( + BasicTaker::Flow const& flow1, Offer const& leg1, + BasicTaker::Flow const& flow2, Offer const& leg2); + + TER + transferXRP (AccountID const& from, AccountID const& to, STAmount const& amount); + + TER + redeemIOU (AccountID const& account, STAmount const& amount, Issue const& issue); + + TER + issueIOU (AccountID const& account, STAmount const& amount, Issue const& issue); + +private: + // The underlying ledger entry we are dealing with + View& view_; + + Config const& config_; + + // The amount of XRP that flowed if we were autobridging + STAmount xrp_flow_; + + // The number direct crossings that we performed + std::uint32_t direct_crossings_; + + // The number autobridged crossings that we performed + std::uint32_t bridge_crossings_; }; } diff --git a/src/ripple/app/tx/impl/TransactionEngine.cpp b/src/ripple/app/tx/impl/TransactionEngine.cpp deleted file mode 100644 index 7ae86e295..000000000 --- a/src/ripple/app/tx/impl/TransactionEngine.cpp +++ /dev/null @@ -1,254 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include -#include -#include -#include -#include - -namespace ripple { - -// -// XXX Make sure all fields are recognized in transactions. -// - -std::pair -TransactionEngine::applyTransaction ( - STTx const& txn, - ViewFlags flags) -{ - TER terResult = tefINTERNAL; - bool didApply = false; - - try - { - assert (mLedger); - - WriteLog (lsTRACE, TransactionEngine) << "applyTransaction>"; - - uint256 const& txID = txn.getTransactionID (); - - if (!txID) - { - WriteLog (lsWARNING, TransactionEngine) << - "applyTransaction: invalid transaction id"; - return std::make_pair(temINVALID_FLAG, false); - } - - mNodes.emplace(mLedger.get(), flags, txID); - - #ifdef BEAST_DEBUG - if (1) - { - Serializer ser; - txn.add (ser); - SerialIter sit(ser.slice()); - STTx s2 (sit); - - if (!s2.isEquivalent (txn)) - { - WriteLog (lsFATAL, TransactionEngine) << - "Transaction serdes mismatch"; - WriteLog (lsINFO, TransactionEngine) << txn.getJson (0); - WriteLog (lsFATAL, TransactionEngine) << s2.getJson (0); - assert (false); - } - } - #endif - - terResult = Transactor::transact (txn, flags, this); - - if (terResult == temUNKNOWN) - { - WriteLog (lsWARNING, TransactionEngine) << - "applyTransaction: Invalid transaction: unknown transaction type"; - return std::make_pair(temUNKNOWN, false); - } - - if (ShouldLog (lsDEBUG, TransactionEngine)) - { - std::string strToken; - std::string strHuman; - - transResultInfo (terResult, strToken, strHuman); - - WriteLog (lsDEBUG, TransactionEngine) << - "applyTransaction: terResult=" << strToken << - " : " << terResult << - " : " << strHuman; - } - - didApply = isTesSuccess (terResult); - - if (isTecClaim (terResult) && !(flags & tapRETRY)) - { - // only claim the transaction fee - WriteLog (lsDEBUG, TransactionEngine) << - "Reprocessing tx " << txID << " to only claim fee"; - mNodes.emplace(mLedger.get(), flags, txID); - - SLE::pointer txnAcct = view().peek( - keylet::account(txn.getAccountID(sfAccount))); - - if (!txnAcct) - terResult = terNO_ACCOUNT; - else - { - std::uint32_t t_seq = txn.getSequence (); - std::uint32_t a_seq = txnAcct->getFieldU32 (sfSequence); - - if (a_seq < t_seq) - terResult = terPRE_SEQ; - else if (a_seq > t_seq) - terResult = tefPAST_SEQ; - else - { - STAmount fee = txn.getTransactionFee (); - STAmount balance = txnAcct->getFieldAmount (sfBalance); - - // We retry/reject the transaction if the account - // balance is zero or we're applying against an open - // ledger and the balance is less than the fee - if ((balance == zero) || - ((flags & tapOPEN_LEDGER) && (balance < fee))) - { - // Account has no funds or ledger is open - terResult = terINSUF_FEE_B; - } - else - { - if (fee > balance) - fee = balance; - txnAcct->setFieldAmount (sfBalance, balance - fee); - txnAcct->setFieldU32 (sfSequence, t_seq + 1); - view().update (txnAcct); - didApply = true; - } - } - } - } - else if (!didApply) - { - WriteLog (lsDEBUG, TransactionEngine) << "Not applying transaction " << txID; - } - - if (didApply && !checkInvariants (terResult, txn, flags)) - { - WriteLog (lsFATAL, TransactionEngine) << - "Transaction violates invariants"; - WriteLog (lsFATAL, TransactionEngine) << - txn.getJson (0); - WriteLog (lsFATAL, TransactionEngine) << - transToken (terResult) << ": " << transHuman (terResult); - WriteLog (lsFATAL, TransactionEngine) << - mNodes->getJson (0); - didApply = false; - terResult = tefINTERNAL; - } - - if (didApply) - { - // Transaction succeeded fully or (retries are not allowed and the - // transaction could claim a fee) - Serializer m; - mNodes->calcRawMeta (m, terResult, mTxnSeq++); - - mNodes->apply(*mLedger); - - Serializer s; - txn.add (s); - - if (flags & tapOPEN_LEDGER) - { - if (! mLedger->txInsert(txID, - std::make_shared< - Serializer const>(std::move(s)), - nullptr)) - { - WriteLog (lsFATAL, TransactionEngine) << - "Duplicate transaction applied"; - assert (false); - throw std::runtime_error ("Duplicate transaction applied"); - } - } - else - { - if (! mLedger->txInsert(txID, - std::make_shared(std::move(s)), - std::make_shared(std::move(m)))) - { - WriteLog (lsFATAL, TransactionEngine) << - "Duplicate transaction applied to closed ledger"; - assert (false); - throw std::runtime_error ("Duplicate transaction applied to closed ledger"); - } - - // Charge whatever fee they specified. We break the encapsulation of - // STAmount here and use "special knowledge" - namely that a native - // amount is stored fully in the mantissa: - auto const fee = txn.getTransactionFee (); - - // The transactor guarantees these will never trigger - if (!fee.native () || fee.negative ()) - throw std::runtime_error ("amount is negative!"); - - if (fee != zero) - mLedger->destroyCoins (fee.mantissa ()); - } - } - - mNodes = boost::none; - - if (!(flags & tapOPEN_LEDGER) && isTemMalformed (terResult)) - { - // XXX Malformed or failed transaction in closed ledger must bow out. - } - } - catch(std::exception const& e) - { - WriteLog (lsFATAL, TransactionEngine) << - "Caught exception: " << e.what(); - return { tefEXCEPTION, false }; - } - catch(...) - { - WriteLog (lsFATAL, TransactionEngine) << - "Caught unknown exception"; - return { tefEXCEPTION, false }; - } - - return { terResult, didApply }; -} - -bool -TransactionEngine::checkInvariants ( - TER result, - STTx const& txn, - ViewFlags flags) -{ - // VFALCO I deleted a bunch of code that was wrapped in #if 0. - // If you need it, check the commit log. - - return true; -} - -} // ripple diff --git a/src/ripple/app/tx/impl/Transactor.cpp b/src/ripple/app/tx/impl/Transactor.cpp index 77643fb0a..cd40d33bd 100644 --- a/src/ripple/app/tx/impl/Transactor.cpp +++ b/src/ripple/app/tx/impl/Transactor.cpp @@ -21,94 +21,36 @@ #include #include #include +#include #include #include +#include #include #include namespace ripple { -TER transact_Payment (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_SetAccount (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_SetRegularKey (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_SetTrust (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_CreateOffer (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_CancelOffer (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_Change (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_CreateTicket (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_CancelTicket (STTx const& txn, ViewFlags params, TransactionEngine* engine); -TER transact_SetSignerList (STTx const& txn, ViewFlags params, TransactionEngine* engine); - -TER -Transactor::transact ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine) -{ - switch (txn.getTxnType ()) - { - case ttPAYMENT: - return transact_Payment (txn, params, engine); - - case ttACCOUNT_SET: - return transact_SetAccount (txn, params, engine); - - case ttREGULAR_KEY_SET: - return transact_SetRegularKey (txn, params, engine); - - case ttTRUST_SET: - return transact_SetTrust (txn, params, engine); - - case ttOFFER_CREATE: - return transact_CreateOffer (txn, params, engine); - - case ttOFFER_CANCEL: - return transact_CancelOffer (txn, params, engine); - - case ttAMENDMENT: - case ttFEE: - return transact_Change (txn, params, engine); - - case ttTICKET_CREATE: - return transact_CreateTicket (txn, params, engine); - - case ttTICKET_CANCEL: - return transact_CancelTicket (txn, params, engine); - - case ttSIGNER_LIST_SET: - return transact_SetSignerList (txn, params, engine); - - default: - return temUNKNOWN; - } -} - -Transactor::Transactor ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine, - beast::Journal journal) - : mTxn (txn) - , mEngine (engine) - , mParams (mEngine->view().flags()) +Transactor::Transactor( + ApplyContext& ctx) + : mTxn (ctx.tx) + , ctx_ (ctx) + , j_ (ctx.journal) , mHasAuthKey (false) , mSigMaster (false) - , m_journal (journal) { - assert(mEngine->view().flags() == params); } void Transactor::calculateFee () { mFeeDue = STAmount (getApp().getFeeTrack().scaleFeeLoad( - calculateBaseFee(), mEngine->view().fees().base, - mEngine->view().fees().units, mParams & tapADMIN)); + calculateBaseFee(), view().fees().base, + view().fees().units, view().flags() & tapADMIN)); } std::uint64_t Transactor::calculateBaseFee () { // Returns the fee in fee units - return getConfig ().TRANSACTION_FEE_BASE; + return ctx_.config.TRANSACTION_FEE_BASE; } TER Transactor::payFee () @@ -119,9 +61,9 @@ TER Transactor::payFee () return temBAD_AMOUNT; // Only check fee is sufficient when the ledger is open. - if ((mParams & tapOPEN_LEDGER) && saPaid < mFeeDue) + if (view().open() && saPaid < mFeeDue) { - m_journal.trace << "Insufficient fee paid: " << + j_.trace << "Insufficient fee paid: " << saPaid.getText () << "/" << mFeeDue.getText (); return telINSUF_FEE_P; @@ -135,11 +77,11 @@ TER Transactor::payFee () if (mSourceBalance < saPaid) { - m_journal.trace << "Insufficient balance:" << + j_.trace << "Insufficient balance:" << " balance=" << mSourceBalance.getText () << " paid=" << saPaid.getText (); - if ((mSourceBalance > zero) && (!(mParams & tapOPEN_LEDGER))) + if ((mSourceBalance > zero) && ! view().open()) { // Closed ledger, non-zero balance, less than fee mSourceBalance.clear (); @@ -156,6 +98,8 @@ TER Transactor::payFee () mSourceBalance -= saPaid; mTxnAccount->setFieldAmount (sfBalance, mSourceBalance); + // VFALCO Should we call view().destroyCoins() here as well? + return tesSUCCESS; } @@ -168,16 +112,16 @@ TER Transactor::checkSeq () { if (a_seq < t_seq) { - m_journal.trace << + j_.trace << "applyTransaction: has future sequence number " << "a_seq=" << a_seq << " t_seq=" << t_seq; return terPRE_SEQ; } - if (mEngine->view().txExists(mTxn.getTransactionID ())) + if (view().txExists(mTxn.getTransactionID ())) return tefALREADY; - m_journal.trace << "applyTransaction: has past sequence number " << + j_.trace << "applyTransaction: has past sequence number " << "a_seq=" << a_seq << " t_seq=" << t_seq; return tefPAST_SEQ; } @@ -187,7 +131,7 @@ TER Transactor::checkSeq () return tefWRONG_PRIOR; if (mTxn.isFieldPresent (sfLastLedgerSequence) && - (mEngine->getLedger()->getLedgerSeq() > mTxn.getFieldU32 (sfLastLedgerSequence))) + (view().seq() > mTxn.getFieldU32 (sfLastLedgerSequence))) return tefMAX_LEDGER; mTxnAccount->setFieldU32 (sfSequence, t_seq + 1); @@ -214,7 +158,7 @@ TER Transactor::preCheckAccount () if (!mTxnAccountID) { - m_journal.warning << "applyTransaction: bad transaction source id"; + j_.warning << "applyTransaction: bad transaction source id"; return temBAD_SRC_ACCOUNT; } return tesSUCCESS; @@ -235,17 +179,17 @@ TER Transactor::preCheckSigningKey () if (!mTxn.isKnownGood ()) { if (mTxn.isKnownBad () || - (!(mParams & tapNO_CHECK_SIGN) && !mTxn.checkSign( + (!(view().flags() & tapNO_CHECK_SIGN) && !mTxn.checkSign( ( #if RIPPLE_ENABLE_MULTI_SIGN true #else - mEngine->view().flags() & tapENABLE_TESTING + view().flags() & tapENABLE_TESTING #endif )))) { mTxn.setBad (); - m_journal.debug << "apply: Invalid transaction (bad signature)"; + j_.debug << "apply: Invalid transaction (bad signature)"; return temINVALID; } @@ -269,7 +213,7 @@ TER Transactor::apply () return terResult; // Find source account - mTxnAccount = mEngine->view().peek (keylet::account(mTxnAccountID)); + mTxnAccount = view().peek (keylet::account(mTxnAccountID)); calculateFee (); @@ -279,7 +223,7 @@ TER Transactor::apply () { if (mustHaveValidAccount ()) { - m_journal.trace << + j_.trace << "applyTransaction: delay: source account does not exist " << toBase58(mTxn.getAccountID(sfAccount)); return terNO_ACCOUNT; @@ -305,7 +249,7 @@ TER Transactor::apply () if (terResult != tesSUCCESS) return (terResult); if (mTxnAccount) - mEngine->view().update (mTxnAccount); + view().update (mTxnAccount); return doApply (); } @@ -314,7 +258,7 @@ TER Transactor::checkSign () { #if RIPPLE_ENABLE_MULTI_SIGN #else - if(mEngine->view().flags() & tapENABLE_TESTING) + if(view().flags() & tapENABLE_TESTING) #endif { // If the mSigningPubKey is empty, then we must be multi-signing. @@ -347,13 +291,13 @@ TER Transactor::checkSingleSign () } else if (mHasAuthKey) { - m_journal.trace << + j_.trace << "applyTransaction: Delay: Not authorized to use account."; return tefBAD_AUTH; } else { - m_journal.trace << + j_.trace << "applyTransaction: Invalid: Not authorized to use account."; return tefBAD_AUTH_MASTER; } @@ -372,15 +316,16 @@ struct GetSignerListResult }; // We need the SignerList for every SigningFor while multi-signing. +static GetSignerListResult -getSignerList ( - AccountID signingForAcctID, TransactionEngine* engine, beast::Journal journal) +getSignerList (AccountID signingForAcctID, + BasicView const& view, beast::Journal journal) { GetSignerListResult ret; auto const k = keylet::signers(signingForAcctID); - SLE::pointer accountSignersList = - engine->view().peek (k); + auto const accountSignersList = + view.read (k); // If the signer list doesn't exist the account is not multi-signing. if (!accountSignersList) @@ -414,7 +359,7 @@ CheckSigningAccountsResult checkSigningAccounts ( std::vector signerEntries, STArray const& signingAccounts, - TransactionEngine* engine, + ApplyContext& ctx, beast::Journal journal) { CheckSigningAccountsResult ret; @@ -489,8 +434,8 @@ checkSigningAccounts ( // In any of these cases we need to know whether the account is in // the ledger. Determine that now. - SLE::pointer signersAccountRoot = - engine->view().peek (keylet::account(signingAcctID)); + auto signersAccountRoot = + ctx.view().read (keylet::account(signingAcctID)); if (signingAcctIDFromPubKey == signingAcctID) { @@ -553,7 +498,7 @@ TER Transactor::checkMultiSign () // Get mTxnAccountID's SignerList and Quorum. using namespace TransactorDetail; GetSignerListResult const outer = - getSignerList (mTxnAccountID, mEngine, m_journal); + getSignerList (mTxnAccountID, view(), j_); if (outer.ter != tesSUCCESS) return outer.ter; @@ -587,7 +532,7 @@ TER Transactor::checkMultiSign () // from these signers directly effect the quorum. CheckSigningAccountsResult const outerSigningAccountsResult = checkSigningAccounts ( - outer.signerEntries, signingAccounts, mEngine, m_journal); + outer.signerEntries, signingAccounts, ctx_, j_); if (outerSigningAccountsResult.ter != tesSUCCESS) return outerSigningAccountsResult.ter; @@ -602,7 +547,7 @@ TER Transactor::checkMultiSign () { if (++signerEntriesItr == outer.signerEntries.end ()) { - m_journal.trace << + j_.trace << "applyTransaction: Invalid SigningFor.Account."; return tefBAD_SIGNATURE; } @@ -610,21 +555,21 @@ TER Transactor::checkMultiSign () if (signerEntriesItr->account != signingForID) { // The signingForID is not in the SignerEntries. - m_journal.trace << + j_.trace << "applyTransaction: Invalid SigningFor.Account."; return tefBAD_SIGNATURE; } if (signerEntriesItr->weight <= 0) { // The SigningFor entry needs a weight greater than zero. - m_journal.trace << + j_.trace << "applyTransaction: SigningFor.Account needs weight > 0."; return tefBAD_SIGNATURE; } // See if the signingForID has a SignerList. GetSignerListResult const inner = - getSignerList (signingForID, mEngine, m_journal); + getSignerList (signingForID, view(), j_); if (inner.ter != tesSUCCESS) return inner.ter; @@ -632,7 +577,7 @@ TER Transactor::checkMultiSign () // Results from these signers indirectly effect the quorum. CheckSigningAccountsResult const innerSigningAccountsResult = checkSigningAccounts ( - inner.signerEntries, signingAccounts, mEngine, m_journal); + inner.signerEntries, signingAccounts, ctx_, j_); if (innerSigningAccountsResult.ter != tesSUCCESS) return innerSigningAccountsResult.ter; @@ -652,7 +597,7 @@ TER Transactor::checkMultiSign () // -- January 2015 if (innerSigningAccountsResult.weightSum < inner.quorum) { - m_journal.trace << + j_.trace << "applyTransaction: Level-2 SigningFor did not make quorum."; return tefBAD_QUORUM; } @@ -664,7 +609,7 @@ TER Transactor::checkMultiSign () // Cannot perform transaction if quorum is not met. if (weightSum < outer.quorum) { - m_journal.trace << + j_.trace << "applyTransaction: MultiSignature failed to meet quorum."; return tefBAD_QUORUM; } @@ -673,4 +618,173 @@ TER Transactor::checkMultiSign () return tesSUCCESS; } +//------------------------------------------------------------------------------ + +static +void +log (std::pair< + TER, bool> const& result, + beast::Journal j) +{ + if(j.trace) j.trace << + "apply: { " << transToken(result.first) << + ", " << (result.second ? "true" : "false") << " }"; +} + +std::pair +Transactor::operator()() +{ + JLOG(j_.trace) << + "applyTransaction>"; + + uint256 const& txID = mTxn.getTransactionID (); + + if (!txID) + { + JLOG(j_.warning) << + "applyTransaction: transaction id may not be zero"; + auto const result = + std::make_pair(temINVALID_FLAG, false); + log(result, j_); + return result; + } + +#ifdef BEAST_DEBUG + { + Serializer ser; + mTxn.add (ser); + SerialIter sit(ser.slice()); + STTx s2 (sit); + + if (! s2.isEquivalent(mTxn)) + { + JLOG(j_.fatal) << + "Transaction serdes mismatch"; + JLOG(j_.info) << to_string(mTxn.getJson (0)); + JLOG(j_.fatal) << s2.getJson (0); + assert (false); + } + } +#endif + + TER terResult = apply(); + + if (terResult == temUNKNOWN) + { + JLOG(j_.warning) << + "applyTransaction: Invalid transaction: unknown transaction type"; + auto const result = + std::make_pair(temUNKNOWN, false); + log(result, j_); + return result; + } + + if (j_.debug) + { + std::string strToken; + std::string strHuman; + + transResultInfo (terResult, strToken, strHuman); + + j_.debug << + "applyTransaction: terResult=" << strToken << + " : " << terResult << + " : " << strHuman; + } + + bool didApply = isTesSuccess (terResult); + + if (isTecClaim (terResult) && !(view().flags() & tapRETRY)) + { + // only claim the transaction fee + JLOG(j_.debug) << + "Reprocessing tx " << txID << " to only claim fee"; + + ctx_.discard(); + + auto const txnAcct = view().peek( + keylet::account(mTxn.getAccountID(sfAccount))); + + if (txnAcct) + { + std::uint32_t t_seq = mTxn.getSequence (); + std::uint32_t a_seq = txnAcct->getFieldU32 (sfSequence); + + if (a_seq < t_seq) + terResult = terPRE_SEQ; + else if (a_seq > t_seq) + terResult = tefPAST_SEQ; + else + { + STAmount fee = mTxn.getTransactionFee (); + STAmount balance = txnAcct->getFieldAmount (sfBalance); + + // We retry/reject the transaction if the account + // balance is zero or we're applying against an open + // ledger and the balance is less than the fee + if ((balance == zero) || + (view().open() && (balance < fee))) + { + // Account has no funds or ledger is open + terResult = terINSUF_FEE_B; + } + else + { + if (fee > balance) + fee = balance; + txnAcct->setFieldAmount (sfBalance, balance - fee); + txnAcct->setFieldU32 (sfSequence, t_seq + 1); + view().update (txnAcct); + didApply = true; + } + } + } + else + { + terResult = terNO_ACCOUNT; + } + } + else if (!didApply) + { + JLOG(j_.debug) << "Not applying transaction " << txID; + } + + if (didApply) + { + // Transaction succeeded fully or (retries are + // not allowed and the transaction could claim a fee) + + if(view().closed()) + { + // VFALCO Fix this nonsense with Amount + // Charge whatever fee they specified. We break the + // encapsulation of STAmount here and use "special + // knowledge" - namely that a native amount is + // stored fully in the mantissa: + auto const fee = mTxn.getTransactionFee (); + + // The transactor guarantees these will never trigger + if (!fee.native () || fee.negative ()) + { + // VFALCO Log to journal here + // JLOG(journal.fatal) << "invalid fee"; + throw std::logic_error( + "amount is negative!"); + } + + if (fee != zero) + view().destroyCoins (fee.mantissa ()); + } + + ctx_.apply(terResult); + // VFALCO NOTE since we called apply(), it is not + // okay to look at view() past this point. + } + + auto const result = + std::make_pair(terResult, didApply); + log(result, j_); + return result; +} + } diff --git a/src/ripple/app/tx/impl/Transactor.h b/src/ripple/app/tx/impl/Transactor.h index 972dac7a4..1bbe8b015 100644 --- a/src/ripple/app/tx/impl/Transactor.h +++ b/src/ripple/app/tx/impl/Transactor.h @@ -20,67 +20,74 @@ #ifndef RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED #define RIPPLE_APP_TX_TRANSACTOR_H_INCLUDED -#include +#include +#include namespace ripple { class Transactor { -public: - static - TER - transact ( - STTx const& txn, - ViewFlags params, - TransactionEngine* engine); +protected: + STTx const& mTxn; + ApplyContext& ctx_; + beast::Journal j_; - TER - apply (); + AccountID mTxnAccountID; + STAmount mFeeDue; + STAmount mPriorBalance; // Balance before fees. + STAmount mSourceBalance; // Balance after fees. + SLE::pointer mTxnAccount; + bool mHasAuthKey; + bool mSigMaster; + RippleAddress mSigningPubKey; + +public: + /** Process the transaction. */ + std::pair + operator()(); + + View& + view() + { + return ctx_.view(); + } + + View const& + view() const + { + return ctx_.view(); + } protected: - STTx const& mTxn; - TransactionEngine* mEngine; - ViewFlags const mParams; + TER + apply(); - AccountID mTxnAccountID; - STAmount mFeeDue; - STAmount mPriorBalance; // Balance before fees. - STAmount mSourceBalance; // Balance after fees. - SLE::pointer mTxnAccount; - bool mHasAuthKey; - bool mSigMaster; - RippleAddress mSigningPubKey; + explicit + Transactor (ApplyContext& ctx); - beast::Journal m_journal; - - virtual TER preCheck (); - - // Non-virtual components of preCheck() TER preCheckAccount (); TER preCheckSigningKey (); - - virtual TER checkSeq (); - virtual TER payFee (); - void calculateFee (); + // VFALCO This is the equivalent of dynamic_cast + // to discover the type of the derived class, + // and therefore bad. + virtual + bool + mustHaveValidAccount() + { + return true; + } + // Returns the fee, not scaled for load (Should be in fee units. FIXME) virtual std::uint64_t calculateBaseFee (); + virtual TER preCheck (); + virtual TER checkSeq (); + virtual TER payFee (); virtual TER checkSign (); virtual TER doApply () = 0; - Transactor ( - const STTx& txn, - ViewFlags params, - TransactionEngine* engine, - beast::Journal journal = beast::Journal ()); - - virtual bool mustHaveValidAccount () - { - return true; - } - private: TER checkSingleSign (); TER checkMultiSign (); diff --git a/src/ripple/app/tx/impl/apply.cpp b/src/ripple/app/tx/impl/apply.cpp new file mode 100644 index 000000000..0725a3615 --- /dev/null +++ b/src/ripple/app/tx/impl/apply.cpp @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ripple { + +template +static +std::pair +do_apply (Args&&... args) +{ + ApplyContext ctx ( + std::forward(args)...); + Processor p(ctx); + return p(); +} + +template +static +std::pair +invoke (TxType type, + Args&&... args) +{ + switch(type) + { + case ttACCOUNT_SET: return do_apply< SetAccount >(std::forward(args)...); + case ttOFFER_CANCEL: return do_apply< CancelOffer >(std::forward(args)...); + case ttOFFER_CREATE: return do_apply< CreateOffer >(std::forward(args)...); + case ttPAYMENT: return do_apply< Payment >(std::forward(args)...); + case ttREGULAR_KEY_SET: return do_apply< SetRegularKey >(std::forward(args)...); + case ttSIGNER_LIST_SET: return do_apply< SetSignerList >(std::forward(args)...); + case ttTICKET_CANCEL: return do_apply< CancelTicket >(std::forward(args)...); + case ttTICKET_CREATE: return do_apply< CreateTicket >(std::forward(args)...); + case ttTRUST_SET: return do_apply< SetTrust >(std::forward(args)...); + + // VFALCO These are both the same? + case ttAMENDMENT: + case ttFEE: return do_apply< Change >(std::forward(args)...); + default: + break; + } + return { temUNKNOWN, false }; +} + +std::pair +apply (BasicView& view, + STTx const& tx, ViewFlags flags, + Config const& config, + beast::Journal journal) +{ + return invoke (tx.getTxnType(), + view, tx, flags, config, journal); +} + +} // ripple diff --git a/src/ripple/app/tx/tests/Offer.test.cpp b/src/ripple/app/tx/tests/Offer.test.cpp index 003226d19..335ff2152 100644 --- a/src/ripple/app/tx/tests/Offer.test.cpp +++ b/src/ripple/app/tx/tests/Offer.test.cpp @@ -55,7 +55,7 @@ public: void testCanceledOffer () { using namespace jtx; - Env env = *this; + Env env (*this); auto const gw = Account ("gateway"); auto const USD = gw["USD"]; diff --git a/src/ripple/app/tx/tests/common_transactor.cpp b/src/ripple/app/tx/tests/common_transactor.cpp index 343246deb..c5cacaeea 100644 --- a/src/ripple/app/tx/tests/common_transactor.cpp +++ b/src/ripple/app/tx/tests/common_transactor.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -90,12 +91,13 @@ TestLedger::TestLedger ( createGenesisLedger(startAmountDrops, masterAcct); } -std::pair TestLedger::applyTransaction (STTx const& tx, bool check) +std::pair +TestLedger::applyTransaction (STTx const& tx, bool check) { // Apply the transaction to the open ledger. - TransactionEngine engine(openLedger_); - auto r = engine.applyTransaction ( - tx, tapOPEN_LEDGER | (check ? tapNONE : tapNO_CHECK_SIGN)); + auto const r = apply( + *openLedger_, tx, check ? tapNONE : tapNO_CHECK_SIGN, + getConfig(), beast::Journal{}); // Close the open ledger to see if the transaction was real committed. // diff --git a/src/ripple/ledger/View.h b/src/ripple/ledger/View.h index 2d2a96635..77a88564a 100644 --- a/src/ripple/ledger/View.h +++ b/src/ripple/ledger/View.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -65,11 +66,40 @@ struct Fees //------------------------------------------------------------------------------ -/** A view into a ledger's state items. +/** Information about the notional ledger backing the view. */ +struct ViewInfo +{ + // Fields for all ledgers + bool open = true; + LedgerIndex seq = 0; + std::uint32_t parentCloseTime = 0; - The interface provides raw access for state item - modification operations. There is no checkpointing + // Fields for closed ledgers + // Closed means "tx set already determined" + //uint256 hash; + //uint256 txHash; + //uint256 stateHash; + //uint256 parentHash; + //std::uint64_t coins = 0; + //bool validated = false; + //int closeTimeRes = 0; + + // For closed ledgers, the time the ledger + // closed. For open ledgers, the time the ledger + // will close if there's no transactions. + // + std::uint32_t closeTime = 0; +}; + +//------------------------------------------------------------------------------ + +/** A view into a ledger. + + This interface provides read access to state + and transaction items. There is no checkpointing or calculation of metadata. + + A raw interace is provided for mutable ledgers. */ class BasicView { @@ -80,24 +110,44 @@ public: virtual ~BasicView() = default; + /** Returns information about the ledger. */ + virtual + ViewInfo const& + info() const = 0; + + /** Returns true if this reflects an open ledger. */ + bool + open() const + { + return info().open; + } + + /** Returns true if this reflects a closed ledger. */ + bool + closed() const + { + return ! info().open; + } + + /** Returns the close time of the previous ledger. */ + std::uint32_t + parentCloseTime() const + { + return info().parentCloseTime; + } + + /** Returns the sequence number of the base ledger. */ + LedgerIndex + seq() const + { + return info().seq; + } + /** Returns the fees for the base ledger. */ virtual Fees const& fees() const = 0; - /** Returns the sequence number of the base ledger. */ - virtual - LedgerIndex - seq() const = 0; - - /** Return the last known close time. - - The epoch is based on the Ripple network clock. - */ - virtual - std::uint32_t - time() const = 0; - /** Determine if a state item exists. @note This can be more efficient than calling read. @@ -204,6 +254,18 @@ public: void destroyCoins (std::uint64_t feeDrops) = 0; + /** Returns the number of newly inserted transactions. + + This will always be zero for closed ledgers, there + is no efficient way to count the number of tx in + the map. For views representing open ledgers this + starts out as one and gets incremented for each + transaction that is applied. + */ + virtual + std::size_t + txCount() const = 0; + /** Returns `true` if a tx exists in the tx map. */ virtual bool @@ -214,10 +276,11 @@ public: @param metaData Optional metadata (may be nullptr) */ virtual - bool + void txInsert (uint256 const& key, - std::shared_ptr const& txn, - std::shared_ptr const& metaData) = 0; + std::shared_ptr const& txn, std::shared_ptr< + Serializer const> const& metaData) = 0; // DEBUG ROUTINE // Return a list of transaction keys in the tx map. @@ -255,11 +318,6 @@ enum ViewFlags // tapENABLE_TESTING = 0x02, - // Transaction is running against an open ledger - // true = failures are not forwarded, check transaction fee - // false = debit ledger for consumed funds - tapOPEN_LEDGER = 0x10, - // This is not the transaction's last pass // Transaction can be retried, soft failures allowed tapRETRY = 0x20, @@ -268,6 +326,27 @@ enum ViewFlags tapADMIN = 0x400, }; +inline +ViewFlags operator|( + ViewFlags const& lhs, + ViewFlags const& rhs) +{ + return static_cast( + static_cast(lhs) | + static_cast(rhs)); +} + +inline +ViewFlags +operator&( + ViewFlags const& lhs, + ViewFlags const& rhs) +{ + return static_cast( + static_cast(lhs) & + static_cast(rhs)); +} + /** A contextual view into a ledger's state items. This refinement of BasicView provides an interface where @@ -422,24 +501,18 @@ public: { } + ViewInfo const& + info() const + { + return view_.info(); + } + Fees const& fees() const override { return view_.fees(); } - LedgerIndex - seq() const override - { - return view_.seq(); - } - - std::uint32_t - time() const override - { - return view_.time(); - } - bool exists (Keylet const& k) const override { @@ -488,20 +561,25 @@ public: return view_.destroyCoins(feeDrops); } + std::size_t + txCount() const override + { + return view_.txCount(); + } + bool txExists (uint256 const& key) const override { return view_.txExists(key); } - bool + void txInsert (uint256 const& key, std::shared_ptr const& txn, std::shared_ptr< Serializer const> const& metaData) override { - return view_.txInsert( - key, txn, metaData); + view_.txInsert(key, txn, metaData); } std::vector @@ -528,24 +606,18 @@ public: { } + ViewInfo const& + info() const + { + return view_.info(); + } + Fees const& fees() const override { return view_.fees(); } - LedgerIndex - seq() const override - { - return view_.seq(); - } - - std::uint32_t - time() const override - { - return view_.time(); - } - bool exists (Keylet const& k) const override { @@ -594,20 +666,25 @@ public: return view_.destroyCoins(feeDrops); } + std::size_t + txCount() const override + { + return view_.txCount(); + } + bool txExists (uint256 const& key) const override { return view_.txExists(key); } - bool + void txInsert (uint256 const& key, std::shared_ptr const& txn, std::shared_ptr< Serializer const> const& metaData) override { - return view_.txInsert( - key, txn, metaData); + view_.txInsert(key, txn, metaData); } std::vector diff --git a/src/ripple/ledger/impl/View.cpp b/src/ripple/ledger/impl/View.cpp index f34fcb461..9a426c618 100644 --- a/src/ripple/ledger/impl/View.cpp +++ b/src/ripple/ledger/impl/View.cpp @@ -1240,7 +1240,7 @@ accountSend (View& view, { // VFALCO Its laborious to have to mutate the // TER based on params everywhere - terResult = (view.flags() & tapOPEN_LEDGER) + terResult = view.open() ? telFAILED_PROCESSING : tecFAILED_PROCESSING; } @@ -1497,7 +1497,7 @@ transferXRP (View& view, // VFALCO Its unfortunate we have to keep // mutating these TER everywhere // FIXME: this logic should be moved to callers maybe? - return (view.flags() & tapOPEN_LEDGER) + return view.open() ? telFAILED_PROCESSING : tecFAILED_PROCESSING; } diff --git a/src/ripple/protocol/TER.h b/src/ripple/protocol/TER.h index dc7f4183f..e0c6594e5 100644 --- a/src/ripple/protocol/TER.h +++ b/src/ripple/protocol/TER.h @@ -25,10 +25,11 @@ namespace ripple { // See https://ripple.com/wiki/Transaction_errors - -// VFALCO TODO consider renaming TER to TxErr or TxResult for clarity. // -enum TER // aka TransactionEngineResult +// "Transaction Engine Result" +// or Transaction ERror. +// +enum TER { // Note: Range is stable. Exact numbers are currently unstable. Use tokens. diff --git a/src/ripple/rpc/handlers/RipplePathFind.cpp b/src/ripple/rpc/handlers/RipplePathFind.cpp index ef4362c80..6fa16bf8c 100644 --- a/src/ripple/rpc/handlers/RipplePathFind.cpp +++ b/src/ripple/rpc/handlers/RipplePathFind.cpp @@ -304,7 +304,8 @@ ripplePathFind (RippleLineCache::pointer const& cache, saMaxAmount.negate(); boost::optional sandbox; - sandbox.emplace(lpLedger.get(), tapOPEN_LEDGER); + sandbox.emplace(*lpLedger, tapNONE); + assert(sandbox->open()); auto rc = path::RippleCalc::rippleCalculate( *sandbox, @@ -329,7 +330,8 @@ ripplePathFind (RippleLineCache::pointer const& cache, << "Trying with an extra path element"; spsComputed.push_back(fullLiquidityPath); - sandbox.emplace(lpLedger.get(), tapOPEN_LEDGER); + sandbox.emplace(*lpLedger, tapNONE); + assert(sandbox->open()); rc = path::RippleCalc::rippleCalculate( *sandbox, saMaxAmount, // --> Amount to send is unlimited diff --git a/src/ripple/rpc/handlers/TransactionEntry.cpp b/src/ripple/rpc/handlers/TransactionEntry.cpp index d4883efca..65659a58a 100644 --- a/src/ripple/rpc/handlers/TransactionEntry.cpp +++ b/src/ripple/rpc/handlers/TransactionEntry.cpp @@ -65,7 +65,7 @@ Json::Value doTransactionEntry (RPC::Context& context) else { Transaction::pointer tpTrans; - TransactionMetaSet::pointer tmTrans; + TxMeta::pointer tmTrans; if (!getTransaction (*lpLedger, uTransID, tpTrans, tmTrans, getApp().getMasterTransaction())) diff --git a/src/ripple/rpc/handlers/Tx.cpp b/src/ripple/rpc/handlers/Tx.cpp index 28331d84d..fd810e675 100644 --- a/src/ripple/rpc/handlers/Tx.cpp +++ b/src/ripple/rpc/handlers/Tx.cpp @@ -83,7 +83,7 @@ Json::Value doTx (RPC::Context& context) } else { - TransactionMetaSet::pointer txMeta; + TxMeta::pointer txMeta; if (getTransactionMeta (*lgr, txn->getID (), txMeta)) { diff --git a/src/ripple/rpc/impl/Utilities.cpp b/src/ripple/rpc/impl/Utilities.cpp index 62dbb94ea..14e93c5ca 100644 --- a/src/ripple/rpc/impl/Utilities.cpp +++ b/src/ripple/rpc/impl/Utilities.cpp @@ -27,7 +27,7 @@ addPaymentDeliveredAmount ( Json::Value& meta, RPC::Context& context, Transaction::pointer transaction, - TransactionMetaSet::pointer transactionMeta) + TxMeta::pointer transactionMeta) { // We only want to add a "delivered_amount" field if the transaction // succeeded - otherwise nothing could have been delivered. diff --git a/src/ripple/rpc/impl/Utilities.h b/src/ripple/rpc/impl/Utilities.h index 8bc197f00..9ea0d4302 100644 --- a/src/ripple/rpc/impl/Utilities.h +++ b/src/ripple/rpc/impl/Utilities.h @@ -28,7 +28,7 @@ addPaymentDeliveredAmount ( Json::Value&, RPC::Context&, Transaction::pointer, - TransactionMetaSet::pointer); + TxMeta::pointer); } // RPC } // ripple diff --git a/src/ripple/test/jtx/Env.h b/src/ripple/test/jtx/Env.h index 007a49a07..342151481 100644 --- a/src/ripple/test/jtx/Env.h +++ b/src/ripple/test/jtx/Env.h @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -117,6 +118,10 @@ class Env public: beast::unit_test::suite& test; + /** Configuration used. */ + // VFALCO NOTE Some code still calls getConfig() + Config const config; + /** The master account. */ Account const master; @@ -125,7 +130,7 @@ public: public: Env (beast::unit_test::suite& test_); - + /** Turn on JSON tracing. With no arguments, trace all */ diff --git a/src/ripple/test/jtx/impl/Env.cpp b/src/ripple/test/jtx/impl/Env.cpp index 7d34f7588..e5685bf42 100644 --- a/src/ripple/test/jtx/impl/Env.cpp +++ b/src/ripple/test/jtx/impl/Env.cpp @@ -28,8 +28,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -198,11 +198,12 @@ Env::submit (JTx const& jt) if (stx) { ViewFlags flags = tapNONE; - flags = flags | tapOPEN_LEDGER; flags = flags | tapENABLE_TESTING; - TransactionEngine txe (ledger); + // VFALCO Could wrap the log in a Journal here std::tie(ter, didApply) = - txe.applyTransaction(*stx, flags); + ripple::apply( + *ledger, *stx, flags, config, + beast::Journal{}); } else { diff --git a/src/ripple/unity/app_ledger.cpp b/src/ripple/unity/app_ledger.cpp index b3eec1cd3..3c0e66ad1 100644 --- a/src/ripple/unity/app_ledger.cpp +++ b/src/ripple/unity/app_ledger.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include diff --git a/src/ripple/unity/app_tx.cpp b/src/ripple/unity/app_tx.cpp index 01ddf4bd9..5af83af0b 100644 --- a/src/ripple/unity/app_tx.cpp +++ b/src/ripple/unity/app_tx.cpp @@ -19,6 +19,7 @@ #include +#include #include #include #include @@ -37,8 +38,7 @@ #include #include #include -#include -#include +#include #include #include