diff options
-rw-r--r-- | src/net.h | 2 | ||||
-rw-r--r-- | src/net_processing.cpp | 6 | ||||
-rw-r--r-- | src/rpc/blockchain.cpp | 2 | ||||
-rw-r--r-- | src/txmempool.cpp | 3 | ||||
-rw-r--r-- | src/txmempool.h | 6 | ||||
-rw-r--r-- | src/util/time.h | 8 | ||||
-rw-r--r-- | src/validation.cpp | 12 | ||||
-rwxr-xr-x | test/functional/mempool_persist.py | 24 |
8 files changed, 40 insertions, 23 deletions
@@ -761,7 +761,7 @@ public: // Used for BIP35 mempool sending bool fSendMempool GUARDED_BY(cs_tx_inventory){false}; // Last time a "MEMPOOL" request was serviced. - std::atomic<int64_t> timeLastMempoolReq{0}; + std::atomic<std::chrono::seconds> m_last_mempool_req{std::chrono::seconds{0}}; int64_t nNextInvSend{0}; CCriticalSection cs_feeFilter; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 34d349e8e9..e345af604c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1541,11 +1541,11 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm if (mi != mapRelay.end()) { connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *mi->second)); push = true; - } else if (pfrom->m_tx_relay->timeLastMempoolReq) { + } else if (pfrom->m_tx_relay->m_last_mempool_req.load().count()) { auto txinfo = mempool.info(inv.hash); // To protect privacy, do not answer getdata using the mempool when // that TX couldn't have been INVed in reply to a MEMPOOL request. - if (txinfo.tx && txinfo.nTime <= pfrom->m_tx_relay->timeLastMempoolReq) { + if (txinfo.tx && txinfo.m_time <= pfrom->m_tx_relay->m_last_mempool_req.load()) { connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::TX, *txinfo.tx)); push = true; } @@ -3873,7 +3873,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) vInv.clear(); } } - pto->m_tx_relay->timeLastMempoolReq = GetTime(); + pto->m_tx_relay->m_last_mempool_req = GetTime<std::chrono::seconds>(); } // Determine transactions to relay diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 02717fa80f..3463145f75 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -418,7 +418,7 @@ static void entryToJSON(const CTxMemPool& pool, UniValue& info, const CTxMemPool info.pushKV("weight", (int)e.GetTxWeight()); info.pushKV("fee", ValueFromAmount(e.GetFee())); info.pushKV("modifiedfee", ValueFromAmount(e.GetModifiedFee())); - info.pushKV("time", e.GetTime()); + info.pushKV("time", count_seconds(e.GetTime())); info.pushKV("height", (int)e.GetHeight()); info.pushKV("descendantcount", e.GetCountWithDescendants()); info.pushKV("descendantsize", e.GetSizeWithDescendants()); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 9257cff718..835b8d63bd 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -917,7 +917,8 @@ void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPool } } -int CTxMemPool::Expire(int64_t time) { +int CTxMemPool::Expire(std::chrono::seconds time) +{ AssertLockHeld(cs); indexed_transaction_set::index<entry_time>::type::iterator it = mapTx.get<entry_time>().begin(); setEntries toremove; diff --git a/src/txmempool.h b/src/txmempool.h index 6e5ba445d3..f2fc1c8310 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -102,7 +102,7 @@ public: const CAmount& GetFee() const { return nFee; } size_t GetTxSize() const; size_t GetTxWeight() const { return nTxWeight; } - int64_t GetTime() const { return nTime; } + std::chrono::seconds GetTime() const { return std::chrono::seconds{nTime}; } unsigned int GetHeight() const { return entryHeight; } int64_t GetSigOpCost() const { return sigOpCost; } int64_t GetModifiedFee() const { return nFee + feeDelta; } @@ -332,7 +332,7 @@ struct TxMempoolInfo CTransactionRef tx; /** Time the transaction entered the mempool. */ - int64_t nTime; + std::chrono::seconds m_time; /** Feerate of the transaction. */ CFeeRate feeRate; @@ -657,7 +657,7 @@ public: void TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ - int Expire(int64_t time) EXCLUSIVE_LOCKS_REQUIRED(cs); + int Expire(std::chrono::seconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); /** * Calculate the ancestor and descendant count for the given transaction. diff --git a/src/util/time.h b/src/util/time.h index e4f9996777..c0470a2136 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -11,6 +11,14 @@ #include <chrono> /** + * Helper to count the seconds of a duration. + * + * All durations should be using std::chrono and calling this should generally be avoided in code. Though, it is still + * preferred to an inline t.count() to protect against a reliance on the exact type of t. + */ +inline int64_t count_seconds(std::chrono::seconds t) { return t.count(); } + +/** * DEPRECATED * Use either GetSystemTimeInSeconds (not mockable) or GetTime<T> (mockable) */ diff --git a/src/validation.cpp b/src/validation.cpp index ac98bd61c7..726f251c5a 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -314,10 +314,10 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag // Returns the script flags which should be checked for a given block static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams); -static void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) +static void LimitMempoolSize(CTxMemPool& pool, size_t limit, std::chrono::seconds age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main) { - int expired = pool.Expire(GetTime() - age); + int expired = pool.Expire(GetTime<std::chrono::seconds>() - age); if (expired != 0) { LogPrint(BCLog::MEMPOOL, "Expired %i transactions from the memory pool\n", expired); } @@ -389,7 +389,7 @@ static void UpdateMempoolForReorg(DisconnectedBlockTransactions& disconnectpool, // We also need to remove any now-immature transactions mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); // Re-limit mempool size, in case we added any transactions - LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); } // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool @@ -1011,7 +1011,7 @@ bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws) // trim mempool and check if tx was trimmed if (!bypass_limits) { - LimitMempoolSize(m_pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60); + LimitMempoolSize(m_pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); if (!m_pool.exists(hash)) return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full"); } @@ -5041,8 +5041,8 @@ bool DumpMempool(const CTxMemPool& pool) file << (uint64_t)vinfo.size(); for (const auto& i : vinfo) { file << *(i.tx); - file << (int64_t)i.nTime; - file << (int64_t)i.nFeeDelta; + file << int64_t{count_seconds(i.m_time)}; + file << int64_t{i.nFeeDelta}; mapDeltas.erase(i.tx->GetHash()); } diff --git a/test/functional/mempool_persist.py b/test/functional/mempool_persist.py index bb0169ee52..398fdeb326 100755 --- a/test/functional/mempool_persist.py +++ b/test/functional/mempool_persist.py @@ -37,9 +37,15 @@ Test is as follows: """ from decimal import Decimal import os +import time from test_framework.test_framework import BitcoinTestFramework -from test_framework.util import assert_equal, assert_raises_rpc_error, wait_until +from test_framework.util import ( + assert_equal, + assert_greater_than_or_equal, + assert_raises_rpc_error, + wait_until, +) class MempoolPersistTest(BitcoinTestFramework): @@ -51,18 +57,13 @@ class MempoolPersistTest(BitcoinTestFramework): self.skip_if_no_wallet() def run_test(self): - chain_height = self.nodes[0].getblockcount() - assert_equal(chain_height, 200) - - self.log.debug("Mine a single block to get out of IBD") - self.nodes[0].generate(1) - self.sync_all() - self.log.debug("Send 5 transactions from node2 (to its own address)") + tx_creation_time_lower = int(time.time()) for i in range(5): last_txid = self.nodes[2].sendtoaddress(self.nodes[2].getnewaddress(), Decimal("10")) node2_balance = self.nodes[2].getbalance() self.sync_all() + tx_creation_time_higher = int(time.time()) self.log.debug("Verify that node0 and node1 have 5 transactions in their mempools") assert_equal(len(self.nodes[0].getrawmempool()), 5) @@ -75,6 +76,10 @@ class MempoolPersistTest(BitcoinTestFramework): fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees'] assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified']) + tx_creation_time = self.nodes[0].getmempoolentry(txid=last_txid)['time'] + assert_greater_than_or_equal(tx_creation_time, tx_creation_time_lower) + assert_greater_than_or_equal(tx_creation_time_higher, tx_creation_time) + self.log.debug("Stop-start the nodes. Verify that node0 has the transactions in its mempool and node1 does not. Verify that node2 calculates its balance correctly after loading wallet transactions.") self.stop_nodes() # Give this node a head-start, so we can be "extra-sure" that it didn't load anything later @@ -93,6 +98,9 @@ class MempoolPersistTest(BitcoinTestFramework): fees = self.nodes[0].getmempoolentry(txid=last_txid)['fees'] assert_equal(fees['base'] + Decimal('0.00001000'), fees['modified']) + self.log.debug('Verify time is loaded correctly') + assert_equal(tx_creation_time, self.nodes[0].getmempoolentry(txid=last_txid)['time']) + # Verify accounting of mempool transactions after restart is correct self.nodes[2].syncwithvalidationinterfacequeue() # Flush mempool to wallet assert_equal(node2_balance, self.nodes[2].getbalance()) |