18 const TxId &txid = tx->GetId();
19 if (m_pool_txs.count(txid)) {
26 unsigned int sz = tx->GetTotalSize();
29 "ignoring large %s tx (size: %u, hash: %s)\n",
txKind, sz,
34 auto ret = m_pool_txs.emplace(
38 m_txs_list.push_back(ret.first);
39 for (
const CTxIn &txin : tx->vin) {
40 m_outpoint_to_tx_it[txin.prevout].insert(ret.first);
44 "stored %s tx %s, size: %u (mapsz %u outsz %u)\n",
txKind,
45 txid.
ToString(), sz, m_pool_txs.size(),
46 m_outpoint_to_tx_it.size());
57 std::map<TxId, PoolTx>::iterator it = m_pool_txs.find(txid);
58 if (it == m_pool_txs.end()) {
61 for (
const CTxIn &txin : it->second.tx->vin) {
62 auto itPrev = m_outpoint_to_tx_it.find(txin.prevout);
63 if (itPrev == m_outpoint_to_tx_it.end()) {
66 itPrev->second.erase(it);
67 if (itPrev->second.empty()) {
68 m_outpoint_to_tx_it.erase(itPrev);
72 size_t old_pos = it->second.list_pos;
73 assert(m_txs_list[old_pos] == it);
74 if (old_pos + 1 != m_txs_list.size()) {
77 auto it_last = m_txs_list.back();
78 m_txs_list[old_pos] = it_last;
79 it_last->second.list_pos = old_pos;
87 it->second.nTimeExpire));
88 m_txs_list.pop_back();
97 m_peer_work_set.erase(peer);
100 std::map<TxId, PoolTx>::iterator iter = m_pool_txs.begin();
101 while (iter != m_pool_txs.end()) {
103 const auto &[txid, orphan] = *iter++;
104 if (orphan.fromPeer == peer) {
110 "Erased %d %s transaction(s) from peer=%d\n", nErased,
txKind,
118 unsigned int nEvicted = 0;
119 auto nNow{Now<NodeSeconds>()};
120 if (m_next_sweep <= nNow) {
124 std::map<TxId, PoolTx>::iterator iter = m_pool_txs.begin();
125 while (iter != m_pool_txs.end()) {
126 std::map<TxId, PoolTx>::iterator maybeErase = iter++;
127 if (maybeErase->second.nTimeExpire <= nNow) {
131 std::min(maybeErase->second.nTimeExpire, nMinExpTime);
142 while (m_pool_txs.size() > max_txs) {
144 size_t randompos = rng.
randrange(m_txs_list.size());
154 for (
size_t i = 0; i < tx.vout.size(); i++) {
155 const auto it_by_prev =
156 m_outpoint_to_tx_it.find(COutPoint(tx.GetId(), i));
157 if (it_by_prev != m_outpoint_to_tx_it.end()) {
158 for (
const auto &elem : it_by_prev->second) {
161 std::set<TxId> &work_set =
162 m_peer_work_set.try_emplace(elem->second.fromPeer)
165 work_set.insert(elem->first);
167 "added %s tx %s to peer %d workset\n",
txKind,
168 tx.GetId().ToString(), elem->second.fromPeer);
176 return m_pool_txs.count(txid);
182 const auto tx_it = m_pool_txs.find(txid);
183 if (tx_it != m_pool_txs.end()) {
184 return tx_it->second.tx;
190std::vector<CTransactionRef>
194 std::vector<CTransactionRef> conflictingTxs;
195 for (
const auto &txin : tx->vin) {
196 auto itByPrev = m_outpoint_to_tx_it.find(txin.prevout);
197 if (itByPrev == m_outpoint_to_tx_it.end()) {
201 for (
auto mi = itByPrev->second.begin(); mi != itByPrev->second.end();
203 conflictingTxs.push_back((*mi)->second.tx);
206 return conflictingTxs;
212 auto work_set_it = m_peer_work_set.find(peer);
213 if (work_set_it != m_peer_work_set.end()) {
214 auto &work_set = work_set_it->second;
215 while (!work_set.empty()) {
217 work_set.erase(work_set.begin());
219 const auto tx_it = m_pool_txs.find(txid);
220 if (tx_it != m_pool_txs.end()) {
221 return tx_it->second.tx;
231 auto work_set_it = m_peer_work_set.find(peer);
232 if (work_set_it != m_peer_work_set.end()) {
233 auto &work_set = work_set_it->second;
234 return !work_set.empty();
242 std::vector<TxId> vTxErase;
245 const CTransaction &tx = *ptx;
248 for (
const auto &txin : tx.vin) {
249 auto itByPrev = m_outpoint_to_tx_it.find(txin.prevout);
250 if (itByPrev == m_outpoint_to_tx_it.end()) {
254 for (
auto mi = itByPrev->second.begin();
255 mi != itByPrev->second.end(); ++mi) {
256 const CTransaction &orphanTx = *(*mi)->second.tx;
257 const TxId &txid = orphanTx.GetId();
258 vTxErase.push_back(txid);
264 if (vTxErase.size()) {
266 for (
const auto &txid : vTxErase) {
271 "Erased %d %s transaction(s) included or conflicted by block\n",
276std::vector<CTransactionRef>
283 std::vector<PoolTxMap::iterator> iters;
287 for (
unsigned int i = 0; i < parent->vout.size(); i++) {
288 const auto it_by_prev =
289 m_outpoint_to_tx_it.find(COutPoint(parent->GetId(), i));
290 if (it_by_prev != m_outpoint_to_tx_it.end()) {
291 for (
const auto &elem : it_by_prev->second) {
292 if (elem->second.fromPeer == nodeid) {
293 iters.emplace_back(elem);
303 std::sort(iters.begin(), iters.end(), [](
const auto &lhs,
const auto &rhs) {
304 if (lhs->second.nTimeExpire == rhs->second.nTimeExpire) {
305 return &(*lhs) < &(*rhs);
307 return lhs->second.nTimeExpire > rhs->second.nTimeExpire;
310 iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
313 std::vector<CTransactionRef> children_found;
314 children_found.reserve(iters.size());
315 for (
const auto &child_iter : iters) {
316 children_found.emplace_back(child_iter->second.tx);
318 return children_found;
321std::vector<std::pair<CTransactionRef, NodeId>>
328 std::vector<PoolTxMap::iterator> iters;
332 for (
unsigned int i = 0; i < parent->vout.size(); i++) {
333 const auto it_by_prev =
334 m_outpoint_to_tx_it.find(COutPoint(parent->GetId(), i));
335 if (it_by_prev != m_outpoint_to_tx_it.end()) {
336 for (
const auto &elem : it_by_prev->second) {
337 if (elem->second.fromPeer != nodeid) {
338 iters.emplace_back(elem);
346 iters.erase(std::unique(iters.begin(), iters.end()), iters.end());
349 std::vector<std::pair<CTransactionRef, NodeId>> children_found;
350 children_found.reserve(iters.size());
351 for (
const auto &child_iter : iters) {
352 children_found.emplace_back(child_iter->second.tx,
353 child_iter->second.fromPeer);
355 return children_found;
std::vector< CTransactionRef > vtx
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
CTransactionRef GetTx(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
int EraseTx(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase a tx by txid.
const std::chrono::seconds expireInterval
Minimum time between transactions expire time checks.
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase all txs announced by a peer (eg, after that peer disconnects)
std::vector< CTransactionRef > GetChildrenFromSamePeer(const CTransactionRef &parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Get all children that spend from this tx and were received from nodeid.
bool AddTx(const CTransactionRef &tx, NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add a new transaction to the pool.
int EraseTxNoLock(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
Erase a transaction by txid.
unsigned int LimitTxs(unsigned int max_txs, FastRandomContext &rng) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Limit the txs to the given maximum.
void EraseForBlock(const CBlock &block) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase all txs included in or invalidated by a new block.
const std::chrono::seconds expireTime
Expiration time for transactions.
std::vector< CTransactionRef > GetConflictTxs(const CTransactionRef &tx) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
void AddChildrenToWorkSet(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add any tx that list a particular tx as a parent into the from peer's work set.
const std::string txKind
The transaction kind as string, used for logging.
Mutex m_mutex
Guards transactions.
bool HaveTx(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Check if we already have an the transaction.
CTransactionRef GetTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Extract a transaction from a peer's work set.
std::vector< std::pair< CTransactionRef, NodeId > > GetChildrenFromDifferentPeer(const CTransactionRef &parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Get all children that spend from this tx but were not received from nodeid.
bool HaveTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Does this peer have any work to do?
std::string ToString() const
#define LogPrint(category,...)
static constexpr unsigned int MAX_STANDARD_TX_SIZE
The maximum size for transactions we're willing to relay/mine.
std::shared_ptr< const CTransaction > CTransactionRef
static time_point now() noexcept
Return current system time or mocked time, if set.
A TxId is the identifier of a transaction.