/* This file is part of cpp-ethereum. cpp-ethereum is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. cpp-ethereum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ /** @file TransactionQueue.h * @author Gav Wood * @date 2014 */ #pragma once #include #include #include #include #include #include #include #include #include "Transaction.h" namespace dev { namespace eth { class BlockChain; struct TransactionQueueChannel: public LogChannel { static const char* name(); static const int verbosity = 4; }; struct TransactionQueueTraceChannel: public LogChannel { static const char* name(); static const int verbosity = 7; }; #define ctxq dev::LogOutputStream() /// Import transaction policy enum class IfDropped { Ignore, ///< Don't import transaction that was previously dropped. Retry ///< Import transaction even if it was dropped before. }; /** * @brief A queue of Transactions, each stored as RLP. * Maintains a transaction queue sorted by nonce diff and gas price. * @threadsafe */ class TransactionQueue { public: /// @brief TransactionQueue /// @param _limit Maximum number of pending transactions in the queue. /// @param _futureLimit Maximum number of future nonce transactions. TransactionQueue(unsigned _limit = 1024, unsigned _futureLimit = 1024); ~TransactionQueue(); /// Add transaction to the queue to be verified and imported. /// @param _data RLP encoded transaction data. /// @param _nodeId Optional network identified of a node transaction comes from. void enqueue(RLP const& _data, h512 const& _nodeId); /// Verify and add transaction to the queue synchronously. /// @param _tx RLP encoded transaction data. /// @param _ik Set to Retry to force re-addinga transaction that was previously dropped. /// @returns Import result code. ImportResult import(bytes const& _tx, IfDropped _ik = IfDropped::Ignore) { return import(&_tx, _ik); } /// Verify and add transaction to the queue synchronously. /// @param _tx Trasnaction data. /// @param _ik Set to Retry to force re-addinga transaction that was previously dropped. /// @returns Import result code. ImportResult import(Transaction const& _tx, IfDropped _ik = IfDropped::Ignore); /// Remove transaction from the queue /// @param _txHash Trasnaction hash void drop(h256 const& _txHash); /// Get number of pending transactions for account. /// @returns Pending transaction count. unsigned waiting(Address const& _a) const; /// Get top transactions from the queue. Returned transactions are not removed from the queue automatically. /// @param _limit Max number of transactions to return. /// @returns up to _limit transactions ordered by nonce and gas price. Transactions topTransactions(unsigned _limit) const; /// Get a hash set of transactions in the queue /// @returns A hash set of all transactions in the queue h256Hash knownTransactions() const; /// Get max nonce for an account /// @returns Max transaction nonce for account in the queue u256 maxNonce(Address const& _a) const; /// Mark transaction as future. It wont be retured in topTransactions list until a transaction with a preceeding nonce is imported or marked with dropGood /// @param _t Transaction hash void setFuture(h256 const& _t); /// Drop a trasnaction from the list if exists and move following future trasnactions to current (if any) /// @param _t Transaction hash void dropGood(Transaction const& _t); /// Clear the queue void clear(); /// Register a handler that will be called once there is a new transaction imported template Handler<> onReady(T const& _t) { return m_onReady.add(_t); } /// Register a handler that will be called once asynchronous verification is comeplte an transaction has been imported template Handler onImport(T const& _t) { return m_onImport.add(_t); } /// Register a handler that will be called once asynchronous verification is comeplte an transaction has been imported template Handler onReplaced(T const& _t) { return m_onReplaced.add(_t); } private: /// Verified and imported transaction struct VerifiedTransaction { VerifiedTransaction(Transaction const& _t): transaction(_t) {} VerifiedTransaction(VerifiedTransaction&& _t): transaction(std::move(_t.transaction)) {} VerifiedTransaction(VerifiedTransaction const&) = delete; VerifiedTransaction& operator=(VerifiedTransaction const&) = delete; Transaction transaction; ///< Transaction data }; /// Trasnaction pending verification struct UnverifiedTransaction { UnverifiedTransaction() {} UnverifiedTransaction(bytesConstRef const& _t, h512 const& _nodeId): transaction(_t.toBytes()), nodeId(_nodeId) {} UnverifiedTransaction(UnverifiedTransaction&& _t): transaction(std::move(_t.transaction)) {} UnverifiedTransaction& operator=(UnverifiedTransaction&& _other) { transaction = std::move(_other.transaction); nodeId = std::move(_other.nodeId); return *this; } UnverifiedTransaction(UnverifiedTransaction const&) = delete; UnverifiedTransaction& operator=(UnverifiedTransaction const&) = delete; bytes transaction; ///< RLP encoded transaction data h512 nodeId; ///< Network Id of the peer transaction comes from }; struct PriorityCompare { TransactionQueue& queue; /// Compare transaction by nonce height and gas price. bool operator()(VerifiedTransaction const& _first, VerifiedTransaction const& _second) const { u256 const& height1 = _first.transaction.nonce() - queue.m_currentByAddressAndNonce[_first.transaction.sender()].begin()->first; u256 const& height2 = _second.transaction.nonce() - queue.m_currentByAddressAndNonce[_second.transaction.sender()].begin()->first; return height1 < height2 || (height1 == height2 && _first.transaction.gasPrice() > _second.transaction.gasPrice()); } }; // Use a set with dynamic comparator for minmax priority queue. The comparator takes into account min account nonce. Updating it does not affect the order. using PriorityQueue = std::multiset; ImportResult import(bytesConstRef _tx, IfDropped _ik = IfDropped::Ignore); ImportResult check_WITH_LOCK(h256 const& _h, IfDropped _ik); ImportResult manageImport_WITH_LOCK(h256 const& _h, Transaction const& _transaction); void insertCurrent_WITH_LOCK(std::pair const& _p); void makeCurrent_WITH_LOCK(Transaction const& _t); bool remove_WITH_LOCK(h256 const& _txHash); u256 maxNonce_WITH_LOCK(Address const& _a) const; void verifierBody(); mutable SharedMutex m_lock; ///< General lock. h256Hash m_known; ///< Hashes of transactions in both sets. std::unordered_map> m_callbacks; ///< Called once. h256Hash m_dropped; ///< Transactions that have previously been dropped PriorityQueue m_current; std::unordered_map m_currentByHash; ///< Transaction hash to set ref std::unordered_map> m_currentByAddressAndNonce; ///< Transactions grouped by account and nonce std::unordered_map> m_future; /// Future transactions Signal<> m_onReady; ///< Called when a subsequent call to import transactions will return a non-empty container. Be nice and exit fast. Signal m_onImport; ///< Called for each import attempt. Arguments are result, transaction id an node id. Be nice and exit fast. Signal m_onReplaced; ///< Called whan transction is dropped during a call to import() to make room for another transaction. unsigned m_limit; ///< Max number of pending transactions unsigned m_futureLimit; ///< Max number of future transactions unsigned m_futureSize = 0; ///< Current number of future transactions std::condition_variable m_queueReady; ///< Signaled when m_unverified has a new entry. std::vector m_verifiers; std::deque m_unverified; ///< Pending verification queue mutable Mutex x_queue; ///< Verification queue mutex bool m_aborting = false; ///< Exit condition for verifier. }; } }