Line data Source code
1 : // Copyright (c) 2022 The Bitcoin Core developers 2 : // Distributed under the MIT software license, see the accompanying 3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : 5 : #include <kernel/mempool_persist.h> 6 : 7 : #include <clientversion.h> 8 : #include <consensus/amount.h> 9 : #include <logging.h> 10 : #include <primitives/transaction.h> 11 : #include <serialize.h> 12 : #include <streams.h> 13 : #include <sync.h> 14 : #include <txmempool.h> 15 : #include <uint256.h> 16 : #include <util/fs.h> 17 173 : #include <util/fs_helpers.h> 18 173 : #include <util/signalinterrupt.h> 19 : #include <util/time.h> 20 : #include <validation.h> 21 : 22 : #include <cstdint> 23 : #include <cstdio> 24 : #include <exception> 25 : #include <functional> 26 : #include <map> 27 : #include <memory> 28 : #include <set> 29 : #include <stdexcept> 30 : #include <utility> 31 : #include <vector> 32 : 33 : using fsbridge::FopenFn; 34 : 35 : namespace kernel { 36 : 37 : static const uint64_t MEMPOOL_DUMP_VERSION = 1; 38 : 39 0 : bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts) 40 : { 41 0 : if (load_path.empty()) return false; 42 : 43 0 : FILE* filestr{opts.mockable_fopen_function(load_path, "rb")}; 44 0 : CAutoFile file{filestr, CLIENT_VERSION}; 45 0 : if (file.IsNull()) { 46 0 : LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); 47 0 : return false; 48 : } 49 : 50 0 : int64_t count = 0; 51 0 : int64_t expired = 0; 52 0 : int64_t failed = 0; 53 0 : int64_t already_there = 0; 54 0 : int64_t unbroadcast = 0; 55 0 : const auto now{NodeClock::now()}; 56 : 57 : try { 58 : uint64_t version; 59 0 : file >> version; 60 0 : if (version != MEMPOOL_DUMP_VERSION) { 61 0 : return false; 62 : } 63 : uint64_t num; 64 0 : file >> num; 65 0 : while (num) { 66 0 : --num; 67 0 : CTransactionRef tx; 68 : int64_t nTime; 69 : int64_t nFeeDelta; 70 0 : file >> tx; 71 0 : file >> nTime; 72 0 : file >> nFeeDelta; 73 : 74 173 : if (opts.use_current_time) { 75 0 : nTime = TicksSinceEpoch<std::chrono::seconds>(now); 76 0 : } 77 : 78 0 : CAmount amountdelta = nFeeDelta; 79 0 : if (amountdelta && opts.apply_fee_delta_priority) { 80 0 : pool.PrioritiseTransaction(tx->GetHash(), amountdelta); 81 0 : } 82 0 : if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_expiry)) { 83 0 : LOCK(cs_main); 84 0 : const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false); 85 0 : if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) { 86 0 : ++count; 87 0 : } else { 88 : // mempool may contain the transaction already, e.g. from 89 : // wallet(s) having loaded it while we were processing 90 : // mempool transactions; consider these as valid, instead of 91 : // failed, but mark them as 'already there' 92 0 : if (pool.exists(GenTxid::Txid(tx->GetHash()))) { 93 0 : ++already_there; 94 0 : } else { 95 0 : ++failed; 96 : } 97 : } 98 0 : } else { 99 0 : ++expired; 100 : } 101 0 : if (active_chainstate.m_chainman.m_interrupt) 102 0 : return false; 103 0 : } 104 0 : std::map<uint256, CAmount> mapDeltas; 105 0 : file >> mapDeltas; 106 : 107 0 : if (opts.apply_fee_delta_priority) { 108 0 : for (const auto& i : mapDeltas) { 109 0 : pool.PrioritiseTransaction(i.first, i.second); 110 : } 111 0 : } 112 : 113 0 : std::set<uint256> unbroadcast_txids; 114 0 : file >> unbroadcast_txids; 115 0 : if (opts.apply_unbroadcast_set) { 116 0 : unbroadcast = unbroadcast_txids.size(); 117 0 : for (const auto& txid : unbroadcast_txids) { 118 : // Ensure transactions were accepted to mempool then add to 119 : // unbroadcast set. 120 0 : if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid); 121 : } 122 0 : } 123 0 : } catch (const std::exception& e) { 124 0 : LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what()); 125 0 : return false; 126 0 : } 127 : 128 0 : LogPrintf("Imported mempool transactions from disk: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast); 129 0 : return true; 130 0 : } 131 : 132 0 : bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit) 133 : { 134 0 : auto start = SteadyClock::now(); 135 : 136 0 : std::map<uint256, CAmount> mapDeltas; 137 0 : std::vector<TxMempoolInfo> vinfo; 138 0 : std::set<uint256> unbroadcast_txids; 139 : 140 0 : static Mutex dump_mutex; 141 0 : LOCK(dump_mutex); 142 : 143 : { 144 0 : LOCK(pool.cs); 145 0 : for (const auto &i : pool.mapDeltas) { 146 0 : mapDeltas[i.first] = i.second; 147 : } 148 0 : vinfo = pool.infoAll(); 149 0 : unbroadcast_txids = pool.GetUnbroadcastTxs(); 150 0 : } 151 : 152 0 : auto mid = SteadyClock::now(); 153 : 154 : try { 155 0 : FILE* filestr{mockable_fopen_function(dump_path + ".new", "wb")}; 156 0 : if (!filestr) { 157 0 : return false; 158 : } 159 : 160 0 : CAutoFile file{filestr, CLIENT_VERSION}; 161 : 162 0 : uint64_t version = MEMPOOL_DUMP_VERSION; 163 0 : file << version; 164 : 165 0 : file << (uint64_t)vinfo.size(); 166 0 : for (const auto& i : vinfo) { 167 0 : file << *(i.tx); 168 0 : file << int64_t{count_seconds(i.m_time)}; 169 0 : file << int64_t{i.nFeeDelta}; 170 0 : mapDeltas.erase(i.tx->GetHash()); 171 : } 172 : 173 0 : file << mapDeltas; 174 : 175 0 : LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size()); 176 0 : file << unbroadcast_txids; 177 : 178 0 : if (!skip_file_commit && !FileCommit(file.Get())) 179 0 : throw std::runtime_error("FileCommit failed"); 180 0 : file.fclose(); 181 0 : if (!RenameOver(dump_path + ".new", dump_path)) { 182 0 : throw std::runtime_error("Rename failed"); 183 : } 184 0 : auto last = SteadyClock::now(); 185 : 186 0 : LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", 187 : Ticks<SecondsDouble>(mid - start), 188 : Ticks<SecondsDouble>(last - mid)); 189 0 : } catch (const std::exception& e) { 190 0 : LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); 191 0 : return false; 192 0 : } 193 0 : return true; 194 0 : } 195 : 196 : } // namespace kernel