diff --git a/src/LedgerEntrySet.cpp b/src/LedgerEntrySet.cpp new file mode 100644 index 0000000000..803d244b28 --- /dev/null +++ b/src/LedgerEntrySet.cpp @@ -0,0 +1,106 @@ +#include "LedgerEntrySet.h" + +#include + +LedgerEntrySet LedgerEntrySet::duplicate() +{ + return LedgerEntrySet(mEntries, mSeq + 1); +} + +void LedgerEntrySet::setTo(LedgerEntrySet& e) +{ + mEntries = e.mEntries; + mSeq = e.mSeq; +} + +void LedgerEntrySet::swapWith(LedgerEntrySet& e) +{ + std::swap(mSeq, e.mSeq); + mEntries.swap(e.mEntries); +} + +SLE::pointer LedgerEntrySet::getEntry(const uint256& index, LedgerEntryAction& action) +{ + boost::unordered_map::iterator it = mEntries.find(index); + if (it == mEntries.end()) + { + action = taaNONE; + return SLE::pointer(); + } + if (it->second.mSeq != mSeq) + { + it->second.mEntry = boost::make_shared(*it->second.mEntry); + it->second.mSeq = mSeq; + } + action = it->second.mAction; + return it->second.mEntry; +} + +LedgerEntryAction LedgerEntrySet::hasEntry(const uint256& index) const +{ + boost::unordered_map::const_iterator it = mEntries.find(index); + if (it == mEntries.end()) + return taaNONE; + return it->second.mAction; +} + +void LedgerEntrySet::entryCache(SLE::pointer sle) +{ + boost::unordered_map::iterator it = mEntries.find(sle->getIndex()); + if (it == mEntries.end()) + mEntries.insert(std::make_pair(sle->getIndex(), LedgerEntrySetEntry(sle, taaCACHED, mSeq))); + else if (it->second.mAction == taaCACHED) + { + it->second.mSeq = mSeq; + it->second.mEntry = sle; + } + else + throw std::runtime_error("Cache after modify/delete"); +} + +void LedgerEntrySet::entryCreate(SLE::pointer sle) +{ + boost::unordered_map::iterator it = mEntries.find(sle->getIndex()); + if (it == mEntries.end()) + mEntries.insert(std::make_pair(sle->getIndex(), LedgerEntrySetEntry(sle, taaDELETE, mSeq))); + else if (it->second.mAction == taaDELETE) + throw std::runtime_error("Create after delete"); // We could make this a modify + else if (it->second.mAction == taaMODIFY) + throw std::runtime_error("Create after modify"); + else + { + it->second.mSeq = mSeq; + it->second.mEntry = sle; + it->second.mAction = taaDELETE; + } +} + +void LedgerEntrySet::entryModify(SLE::pointer sle) +{ + boost::unordered_map::iterator it = mEntries.find(sle->getIndex()); + if (it == mEntries.end()) + mEntries.insert(std::make_pair(sle->getIndex(), LedgerEntrySetEntry(sle, taaMODIFY, mSeq))); + else if (it->second.mAction == taaDELETE) + throw std::runtime_error("Modify after delete"); + else + { + it->second.mSeq = mSeq; + it->second.mEntry = sle; + it->second.mAction = (it->second.mAction == taaCREATE) ? taaCREATE : taaDELETE; + } +} + +void LedgerEntrySet::entryDelete(SLE::pointer sle) +{ + boost::unordered_map::iterator it = mEntries.find(sle->getIndex()); + if (it == mEntries.end()) + mEntries.insert(std::make_pair(sle->getIndex(), LedgerEntrySetEntry(sle, taaDELETE, mSeq))); + else if (it->second.mAction == taaCREATE) // We support delete after create + mEntries.erase(it); + else + { + it->second.mSeq = mSeq; + it->second.mEntry = sle; + it->second.mAction = taaDELETE; + } +} diff --git a/src/LedgerEntrySet.h b/src/LedgerEntrySet.h index d946386246..cbc320f291 100644 --- a/src/LedgerEntrySet.h +++ b/src/LedgerEntrySet.h @@ -1,6 +1,8 @@ #ifndef __LEDGERENTRYSET__ #define __LEDGERENTRYSET__ +#include + #include "SerializedLedger.h" enum LedgerEntryAction @@ -10,7 +12,8 @@ enum LedgerEntryAction taaMODIFY, // Modifed, must have previously been taaCACHED. taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY. taaCREATE, // Newly created. -} +}; + class LedgerEntrySetEntry { @@ -19,14 +22,18 @@ public: LedgerEntryAction mAction; int mSeq; + LedgerEntrySetEntry(SLE::pointer e, LedgerEntryAction a, int s) : mEntry(e), mAction(a), mSeq(s) { ; } }; + class LedgerEntrySet { protected: boost::unordered_map mEntries; int mSeq; + LedgerEntrySet(const boost::unordered_map &e, int m) : mEntries(e), mSeq(m) { ; } + public: LedgerEntrySet() : mSeq(0) { ; } @@ -35,6 +42,10 @@ public: void setTo(LedgerEntrySet&); // Set this set to have the same contents as another void swapWith(LedgerEntrySet&); // Swap the contents of two sets + int getSeq() const { return mSeq; } + void bumpSeq() { ++mSeq; } + void clear() { mEntries.empty(); mSeq = 0; } + // basic entry functions SLE::pointer getEntry(const uint256& index, LedgerEntryAction&); LedgerEntryAction hasEntry(const uint256& index) const; @@ -44,11 +55,11 @@ public: void entryModify(SLE::pointer); // This entry will be modified // iterator functions - bool isEmpty() const; - boost::unordered_map::const_iterator begin() const; - boost::unordered_map::const_iterator end() const; - boost::unordered_map::iterator begin(); - boost::unordered_map::iterator end(); + bool isEmpty() const { return mEntries.empty(); } + boost::unordered_map::const_iterator begin() const { return mEntries.begin(); } + boost::unordered_map::const_iterator end() const { return mEntries.end(); } + boost::unordered_map::iterator begin() { return mEntries.begin(); } + boost::unordered_map::iterator end() { return mEntries.end(); } }; #endif diff --git a/src/SHAMap.h b/src/SHAMap.h index cbd1017180..0130a624d7 100644 --- a/src/SHAMap.h +++ b/src/SHAMap.h @@ -77,8 +77,6 @@ public: }; extern std::size_t hash_value(const SHAMapNode& mn); -extern std::size_t hash_value(const uint256& u); -extern std::size_t hash_value(const uint160& u); class SHAMapItem { // an item stored in a SHAMap diff --git a/src/TransactionEngine.cpp b/src/TransactionEngine.cpp index 72b852f23a..3fce70bdd7 100644 --- a/src/TransactionEngine.cpp +++ b/src/TransactionEngine.cpp @@ -699,26 +699,16 @@ SLE::pointer TransactionEngine::entryCache(LedgerEntryType letType, const uint25 if (!uIndex.isZero()) { - entryMap::const_iterator it = mEntries.find(uIndex); - - switch (it == mEntries.end() ? taaNONE : it->second.second) + LedgerEntryAction action; + sleEntry = mNodes.getEntry(uIndex, action); + if (!sleEntry) { - case taaNONE: - sleEntry = mLedger->getSLE(uIndex); - if (sleEntry) - mEntries[uIndex] = std::make_pair(sleEntry, taaCACHED); // Add to cache. - break; - - case taaCREATE: - case taaCACHED: - case taaMODIFY: - sleEntry = it->second.first; // Get from cache. - break; - - case taaDELETE: - assert(false); // Unexpected case. - break; + sleEntry = mLedger->getSLE(uIndex); + if (sleEntry) + mNodes.entryCache(sleEntry); } + else if(action == taaDELETE) + assert(false); } return sleEntry; @@ -729,10 +719,8 @@ SLE::pointer TransactionEngine::entryCreate(LedgerEntryType letType, const uint2 assert(!uIndex.isZero()); SLE::pointer sleNew = boost::make_shared(letType); - sleNew->setIndex(uIndex); - - mEntries[uIndex] = std::make_pair(sleNew, taaCREATE); + mNodes.entryCreate(sleNew); return sleNew; } @@ -740,60 +728,23 @@ SLE::pointer TransactionEngine::entryCreate(LedgerEntryType letType, const uint2 void TransactionEngine::entryDelete(SLE::pointer sleEntry) { - assert(sleEntry); - const uint256& uIndex = sleEntry->getIndex(); - entryMap::const_iterator it = mEntries.find(uIndex); - - switch (it == mEntries.end() ? taaNONE : it->second.second) - { - case taaCREATE: - assert(false); // Unexpected case. - break; - - case taaCACHED: - case taaMODIFY: - case taaNONE: - mEntries[uIndex] = std::make_pair(sleEntry, taaDELETE); // Upgrade. - break; - - case taaDELETE: - nothing(); // No change. - break; - } + mNodes.entryDelete(sleEntry); } void TransactionEngine::entryModify(SLE::pointer sleEntry) { - assert(sleEntry); - const uint256& uIndex = sleEntry->getIndex(); - entryMap::const_iterator it = mEntries.find(uIndex); - - switch (it == mEntries.end() ? taaNONE : it->second.second) - { - case taaDELETE: - assert(false); // Unexpected case. - break; - - case taaCACHED: - case taaNONE: - mEntries[uIndex] = std::make_pair(sleEntry, taaMODIFY); // Upgrade. - break; - - case taaCREATE: - case taaMODIFY: - nothing(); // No change. - break; - } + mNodes.entryModify(sleEntry); } void TransactionEngine::txnWrite() { // Write back the account states and add the transaction to the ledger - BOOST_FOREACH(entryMap_value_type it, mEntries) + for (boost::unordered_map::iterator it = mNodes.begin(), end = mNodes.end(); + it != end; ++it) { - const SLE::pointer& sleEntry = it.second.first; + const SLE::pointer& sleEntry = it->second.mEntry; - switch (it.second.second) + switch (it->second.mAction) { case taaNONE: assert(false); @@ -824,7 +775,7 @@ void TransactionEngine::txnWrite() { Log(lsINFO) << "applyTransaction: taaDELETE: " << sleEntry->getText(); - if (!mLedger->peekAccountStateMap()->delItem(it.first)) + if (!mLedger->peekAccountStateMap()->delItem(it->first)) assert(false); } break; @@ -834,19 +785,9 @@ void TransactionEngine::txnWrite() // This is for when a transaction fails from the issuer's point of view and the current changes need to be cleared so other // actions can be applied to the ledger. -void TransactionEngine::entryReset(const SerializedTransaction& txn) +void TransactionEngine::entryReset() { - mEntries.clear(); // Lose old SLE modifications. - mTxnAccount = entryCache(ltACCOUNT_ROOT, Ledger::getAccountRootIndex(mTxnAccountID)); // Get new SLE. - - entryModify(mTxnAccount); - - STAmount saPaid = txn.getTransactionFee(); - STAmount saSrcBalance = mTxnAccount->getIValueFieldAmount(sfBalance); - - mTxnAccount->setIFieldAmount(sfBalance, saSrcBalance - saPaid); - - // XXX Bump sequence too. + mNodes.setTo(mOrigNodes); } TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTransaction& txn, @@ -855,6 +796,7 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran Log(lsTRACE) << "applyTransaction>"; assert(mLedger); mLedgerParentCloseTime = mLedger->getParentCloseTimeNC(); + mNodes.clear(); #ifdef DEBUG if (1) @@ -1017,7 +959,7 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran bHaveAuthKey = mTxnAccount->getIFieldPresent(sfAuthorizedKey); } - // Check if account cliamed. + // Check if account claimed. if (terSUCCESS == terResult) { switch (txn.getTxnType()) @@ -1168,6 +1110,7 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran if (terSUCCESS == terResult) { entryModify(mTxnAccount); + mOrigNodes = mNodes.duplicate(); switch (txn.getTxnType()) { @@ -1251,7 +1194,8 @@ TransactionEngineResult TransactionEngine::applyTransaction(const SerializedTran } mTxnAccount = SLE::pointer(); - mEntries.clear(); + mNodes.clear(); + mOrigNodes.clear(); mUnfunded.clear(); return terResult; diff --git a/src/TransactionEngine.h b/src/TransactionEngine.h index a02d5009f5..f06038f576 100644 --- a/src/TransactionEngine.h +++ b/src/TransactionEngine.h @@ -7,6 +7,7 @@ #include "Ledger.h" #include "SerializedTransaction.h" #include "SerializedLedger.h" +#include "LedgerEntrySet.h" // A TransactionEngine applies serialized transactions to a ledger // It can also, verify signatures, verify fees, and give rejection reasons @@ -97,17 +98,6 @@ enum TransactionEngineParams tepMETADATA = 5, // put metadata in tree, not transaction }; -enum TransactionAccountAction -{ - taaNONE, - taaCACHED, // Unmodified. - taaMODIFY, // Modifed, must have previously been taaCACHED. - taaDELETE, // Delete, must have previously been taaDELETE or taaMODIFY. - taaCREATE, // Newly created. -}; - -typedef std::pair AffectedAccount; - // Hold a path state under incremental application. class PathState { @@ -129,10 +119,7 @@ public: class TransactionEngine { private: - typedef boost::unordered_map > entryMap; - typedef entryMap::iterator entryMap_iterator; - typedef entryMap::const_iterator entryMap_const_iterator; - typedef entryMap::iterator::value_type entryMap_value_type; + LedgerEntrySet mNodes, mOrigNodes; TransactionEngineResult dirAdd( uint64& uNodeDir, // Node of entry. @@ -194,7 +181,6 @@ protected: uint160 mTxnAccountID; SLE::pointer mTxnAccount; - entryMap mEntries; boost::unordered_set mUnfunded; // Indexes that were found unfunded. SLE::pointer entryCreate(LedgerEntryType letType, const uint256& uIndex); @@ -202,7 +188,7 @@ protected: void entryDelete(SLE::pointer sleEntry); void entryModify(SLE::pointer sleEntry); - void entryReset(const SerializedTransaction& txn); + void entryReset(); STAmount rippleHolds(const uint160& uAccountID, const uint160& uCurrency, const uint160& uIssuerID); STAmount rippleTransit(const uint160& uSenderID, const uint160& uReceiverID, const uint160& uIssuerID, const STAmount& saAmount); diff --git a/src/TransactionMeta.cpp b/src/TransactionMeta.cpp index 40af484b2b..fac9798f26 100644 --- a/src/TransactionMeta.cpp +++ b/src/TransactionMeta.cpp @@ -89,6 +89,22 @@ Json::Value TMNEBalance::getJson(int p) const return ret; } +void TMNEUnfunded::addRaw(Serializer& sit) const +{ + sit.add8(mType); +} + +Json::Value TMNEUnfunded::getJson(int) const +{ + return Json::Value("delete_unfunded"); +} + +int TMNEUnfunded::compare(const TransactionMetaNodeEntry&) const +{ + assert(false); // Can't be two deletes for same node + return 0; +} + TransactionMetaNode::TransactionMetaNode(const uint256& node, SerializerIterator& sit) : mNode(node) { mNode = sit.get256(); @@ -100,6 +116,8 @@ TransactionMetaNode::TransactionMetaNode(const uint256& node, SerializerIterator type = sit.get8(); if (type == TransactionMetaNodeEntry::TMNChangedBalance) mEntries.insert(boost::shared_ptr(new TMNEBalance(sit))); + if (type == TransactionMetaNodeEntry::TMNDeleteUnfunded) + mEntries.insert(boost::shared_ptr(new TMNEUnfunded())); else if (type != TransactionMetaNodeEntry::TMNEndOfMetadata) throw std::runtime_error("Unparseable metadata"); } while (type != TransactionMetaNodeEntry::TMNEndOfMetadata); diff --git a/src/TransactionMeta.h b/src/TransactionMeta.h index 0866036c83..db84452d87 100644 --- a/src/TransactionMeta.h +++ b/src/TransactionMeta.h @@ -21,6 +21,7 @@ public: static const int TMNEndOfMetadata = 0; static const int TMNChangedBalance = 1; + static const int TMNDeleteUnfunded = 2; int mType; TransactionMetaNodeEntry(int type) : mType(type) { ; } @@ -68,6 +69,15 @@ public: virtual int compare(const TransactionMetaNodeEntry&) const; }; +class TMNEUnfunded : public TransactionMetaNodeEntry +{ // node was deleted because it was unfunded +public: + TMNEUnfunded() : TransactionMetaNodeEntry(TMNDeleteUnfunded) { ; } + virtual void addRaw(Serializer&) const; + virtual Json::Value getJson(int) const; + virtual int compare(const TransactionMetaNodeEntry&) const; +}; + class TransactionMetaNode { // a node that has been affected by a transaction public: @@ -117,10 +127,10 @@ public: void addRaw(Serializer&) const; void threadNode(const uint256& node, const uint256& previousTransaction, uint32 previousLedger); - bool signedBy(const uint256& node); + bool signedBy(const uint256& node, const STAmount& fee); + bool deleteUnfunded(const uint256& node); bool adjustBalance(const uint256& node, unsigned flags, const STAmount &amount); bool adjustBalances(const uint256& node, unsigned flags, const STAmount &firstAmt, const STAmount &secondAmt); - }; #endif diff --git a/src/uint256.h b/src/uint256.h index a40c22c6fa..a5cb8c456a 100644 --- a/src/uint256.h +++ b/src/uint256.h @@ -469,6 +469,8 @@ inline const uint160 operator^(const uint160& a, const uint160& b) { return (b inline const uint160 operator&(const uint160& a, const uint160& b) { return (base_uint160)a & (base_uint160)b; } inline const uint160 operator|(const uint160& a, const uint160& b) { return (base_uint160)a | (base_uint160)b; } +extern std::size_t hash_value(const uint160&); + inline const std::string strHex(const uint160& ui) { return strHex(ui.begin(), ui.size()); @@ -555,6 +557,7 @@ inline bool operator!=(const uint256& a, const uint256& b) { return (base_ui inline const uint256 operator^(const uint256& a, const uint256& b) { return (base_uint256)a ^ (base_uint256)b; } inline const uint256 operator&(const uint256& a, const uint256& b) { return (base_uint256)a & (base_uint256)b; } inline const uint256 operator|(const uint256& a, const uint256& b) { return (base_uint256)a | (base_uint256)b; } +extern std::size_t hash_value(const uint256&); inline int Testuint256AdHoc(std::vector vArg) {