/Users/eugenesiegel/btc/bitcoin/src/node/mempool_persist.cpp
| Line | Count | Source (jump to first uncovered line) | 
| 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 <node/mempool_persist.h> | 
| 6 |  |  | 
| 7 |  | #include <clientversion.h> | 
| 8 |  | #include <consensus/amount.h> | 
| 9 |  | #include <logging.h> | 
| 10 |  | #include <primitives/transaction.h> | 
| 11 |  | #include <random.h> | 
| 12 |  | #include <serialize.h> | 
| 13 |  | #include <streams.h> | 
| 14 |  | #include <sync.h> | 
| 15 |  | #include <txmempool.h> | 
| 16 |  | #include <uint256.h> | 
| 17 |  | #include <util/fs.h> | 
| 18 |  | #include <util/fs_helpers.h> | 
| 19 |  | #include <util/obfuscation.h> | 
| 20 |  | #include <util/signalinterrupt.h> | 
| 21 |  | #include <util/syserror.h> | 
| 22 |  | #include <util/time.h> | 
| 23 |  | #include <validation.h> | 
| 24 |  |  | 
| 25 |  | #include <cstdint> | 
| 26 |  | #include <cstdio> | 
| 27 |  | #include <exception> | 
| 28 |  | #include <functional> | 
| 29 |  | #include <map> | 
| 30 |  | #include <memory> | 
| 31 |  | #include <set> | 
| 32 |  | #include <stdexcept> | 
| 33 |  | #include <utility> | 
| 34 |  | #include <vector> | 
| 35 |  |  | 
| 36 |  | using fsbridge::FopenFn; | 
| 37 |  |  | 
| 38 |  | namespace node { | 
| 39 |  |  | 
| 40 |  | static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY{1}; | 
| 41 |  | static const uint64_t MEMPOOL_DUMP_VERSION{2}; | 
| 42 |  |  | 
| 43 |  | bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts) | 
| 44 | 0 | { | 
| 45 | 0 |     if (load_path.empty()) return false; | 
| 46 |  |  | 
| 47 | 0 |     AutoFile file{opts.mockable_fopen_function(load_path, "rb")}; | 
| 48 | 0 |     if (file.IsNull()) { | 
| 49 | 0 |         LogInfo("Failed to open mempool file. Continuing anyway.\n");| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 50 | 0 |         return false; | 
| 51 | 0 |     } | 
| 52 |  |  | 
| 53 | 0 |     int64_t count = 0; | 
| 54 | 0 |     int64_t expired = 0; | 
| 55 | 0 |     int64_t failed = 0; | 
| 56 | 0 |     int64_t already_there = 0; | 
| 57 | 0 |     int64_t unbroadcast = 0; | 
| 58 | 0 |     const auto now{NodeClock::now()}; | 
| 59 |  | 
 | 
| 60 | 0 |     try { | 
| 61 | 0 |         uint64_t version; | 
| 62 | 0 |         file >> version; | 
| 63 |  | 
 | 
| 64 | 0 |         if (version == MEMPOOL_DUMP_VERSION_NO_XOR_KEY) { | 
| 65 | 0 |             file.SetObfuscation({}); | 
| 66 | 0 |         } else if (version == MEMPOOL_DUMP_VERSION) { | 
| 67 | 0 |             Obfuscation obfuscation; | 
| 68 | 0 |             file >> obfuscation; | 
| 69 | 0 |             file.SetObfuscation(obfuscation); | 
| 70 | 0 |         } else { | 
| 71 | 0 |             return false; | 
| 72 | 0 |         } | 
| 73 |  |  | 
| 74 | 0 |         uint64_t total_txns_to_load; | 
| 75 | 0 |         file >> total_txns_to_load; | 
| 76 | 0 |         uint64_t txns_tried = 0; | 
| 77 | 0 |         LogInfo("Loading %u mempool transactions from file...\n", total_txns_to_load);| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 78 | 0 |         int next_tenth_to_report = 0; | 
| 79 | 0 |         while (txns_tried < total_txns_to_load) { | 
| 80 | 0 |             const int percentage_done(100.0 * txns_tried / total_txns_to_load); | 
| 81 | 0 |             if (next_tenth_to_report < percentage_done / 10) { | 
| 82 | 0 |                 LogInfo("Progress loading mempool transactions from file: %d%% (tried %u, %u remaining)\n",| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 83 | 0 |                         percentage_done, txns_tried, total_txns_to_load - txns_tried); | 
| 84 | 0 |                 next_tenth_to_report = percentage_done / 10; | 
| 85 | 0 |             } | 
| 86 | 0 |             ++txns_tried; | 
| 87 |  | 
 | 
| 88 | 0 |             CTransactionRef tx; | 
| 89 | 0 |             int64_t nTime; | 
| 90 | 0 |             int64_t nFeeDelta; | 
| 91 | 0 |             file >> TX_WITH_WITNESS(tx); | 
| 92 | 0 |             file >> nTime; | 
| 93 | 0 |             file >> nFeeDelta; | 
| 94 |  | 
 | 
| 95 | 0 |             if (opts.use_current_time) { | 
| 96 | 0 |                 nTime = TicksSinceEpoch<std::chrono::seconds>(now); | 
| 97 | 0 |             } | 
| 98 |  | 
 | 
| 99 | 0 |             CAmount amountdelta = nFeeDelta; | 
| 100 | 0 |             if (amountdelta && opts.apply_fee_delta_priority) { | 
| 101 | 0 |                 pool.PrioritiseTransaction(tx->GetHash(), amountdelta); | 
| 102 | 0 |             } | 
| 103 | 0 |             if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.expiry)) { | 
| 104 | 0 |                 LOCK(cs_main); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 105 | 0 |                 const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false); | 
| 106 | 0 |                 if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) { | 
| 107 | 0 |                     ++count; | 
| 108 | 0 |                 } else { | 
| 109 |  |                     // mempool may contain the transaction already, e.g. from | 
| 110 |  |                     // wallet(s) having loaded it while we were processing | 
| 111 |  |                     // mempool transactions; consider these as valid, instead of | 
| 112 |  |                     // failed, but mark them as 'already there' | 
| 113 | 0 |                     if (pool.exists(tx->GetHash())) { | 
| 114 | 0 |                         ++already_there; | 
| 115 | 0 |                     } else { | 
| 116 | 0 |                         ++failed; | 
| 117 | 0 |                     } | 
| 118 | 0 |                 } | 
| 119 | 0 |             } else { | 
| 120 | 0 |                 ++expired; | 
| 121 | 0 |             } | 
| 122 | 0 |             if (active_chainstate.m_chainman.m_interrupt) | 
| 123 | 0 |                 return false; | 
| 124 | 0 |         } | 
| 125 | 0 |         std::map<Txid, CAmount> mapDeltas; | 
| 126 | 0 |         file >> mapDeltas; | 
| 127 |  | 
 | 
| 128 | 0 |         if (opts.apply_fee_delta_priority) { | 
| 129 | 0 |             for (const auto& i : mapDeltas) { | 
| 130 | 0 |                 pool.PrioritiseTransaction(i.first, i.second); | 
| 131 | 0 |             } | 
| 132 | 0 |         } | 
| 133 |  | 
 | 
| 134 | 0 |         std::set<Txid> unbroadcast_txids; | 
| 135 | 0 |         file >> unbroadcast_txids; | 
| 136 | 0 |         if (opts.apply_unbroadcast_set) { | 
| 137 | 0 |             unbroadcast = unbroadcast_txids.size(); | 
| 138 | 0 |             for (const auto& txid : unbroadcast_txids) { | 
| 139 |  |                 // Ensure transactions were accepted to mempool then add to | 
| 140 |  |                 // unbroadcast set. | 
| 141 | 0 |                 if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid); | 
| 142 | 0 |             } | 
| 143 | 0 |         } | 
| 144 | 0 |     } catch (const std::exception& e) { | 
| 145 | 0 |         LogInfo("Failed to deserialize mempool data on file: %s. Continuing anyway.\n", e.what());| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 146 | 0 |         return false; | 
| 147 | 0 |     } | 
| 148 |  |  | 
| 149 | 0 |     LogInfo("Imported mempool transactions from file: %i succeeded, %i failed, %i expired, %i already there, %i waiting for initial broadcast\n", count, failed, expired, already_there, unbroadcast);| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 150 | 0 |     return true; | 
| 151 | 0 | } | 
| 152 |  |  | 
| 153 |  | bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit) | 
| 154 | 0 | { | 
| 155 | 0 |     auto start = SteadyClock::now(); | 
| 156 |  | 
 | 
| 157 | 0 |     std::map<Txid, CAmount> mapDeltas; | 
| 158 | 0 |     std::vector<TxMempoolInfo> vinfo; | 
| 159 | 0 |     std::set<Txid> unbroadcast_txids; | 
| 160 |  | 
 | 
| 161 | 0 |     static Mutex dump_mutex; | 
| 162 | 0 |     LOCK(dump_mutex); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 163 |  | 
 | 
| 164 | 0 |     { | 
| 165 | 0 |         LOCK(pool.cs); | Line | Count | Source |  | 259 | 0 | #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__) | Line | Count | Source |  | 11 | 0 | #define UNIQUE_NAME(name) PASTE2(name, __COUNTER__) | Line | Count | Source |  | 9 | 0 | #define PASTE2(x, y) PASTE(x, y) | Line | Count | Source |  | 8 | 0 | #define PASTE(x, y) x ## y | 
 | 
 | 
 | 
 | 
| 166 | 0 |         for (const auto &i : pool.mapDeltas) { | 
| 167 | 0 |             mapDeltas[i.first] = i.second; | 
| 168 | 0 |         } | 
| 169 | 0 |         vinfo = pool.infoAll(); | 
| 170 | 0 |         unbroadcast_txids = pool.GetUnbroadcastTxs(); | 
| 171 | 0 |     } | 
| 172 |  | 
 | 
| 173 | 0 |     auto mid = SteadyClock::now(); | 
| 174 |  | 
 | 
| 175 | 0 |     const fs::path file_fspath{dump_path + ".new"}; | 
| 176 | 0 |     AutoFile file{mockable_fopen_function(file_fspath, "wb")}; | 
| 177 | 0 |     if (file.IsNull()) { | 
| 178 | 0 |         return false; | 
| 179 | 0 |     } | 
| 180 |  |  | 
| 181 | 0 |     try { | 
| 182 | 0 |         const uint64_t version{pool.m_opts.persist_v1_dat ? MEMPOOL_DUMP_VERSION_NO_XOR_KEY : MEMPOOL_DUMP_VERSION}; | 
| 183 | 0 |         file << version; | 
| 184 |  | 
 | 
| 185 | 0 |         if (!pool.m_opts.persist_v1_dat) { | 
| 186 | 0 |             const Obfuscation obfuscation{FastRandomContext{}.randbytes<Obfuscation::KEY_SIZE>()}; | 
| 187 | 0 |             file << obfuscation; | 
| 188 | 0 |             file.SetObfuscation(obfuscation); | 
| 189 | 0 |         } else { | 
| 190 | 0 |             file.SetObfuscation({}); | 
| 191 | 0 |         } | 
| 192 |  | 
 | 
| 193 | 0 |         uint64_t mempool_transactions_to_write(vinfo.size()); | 
| 194 | 0 |         file << mempool_transactions_to_write; | 
| 195 | 0 |         LogInfo("Writing %u mempool transactions to file...\n", mempool_transactions_to_write);| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 196 | 0 |         for (const auto& i : vinfo) { | 
| 197 | 0 |             file << TX_WITH_WITNESS(*(i.tx)); | 
| 198 | 0 |             file << int64_t{count_seconds(i.m_time)}; | 
| 199 | 0 |             file << int64_t{i.nFeeDelta}; | 
| 200 | 0 |             mapDeltas.erase(i.tx->GetHash()); | 
| 201 | 0 |         } | 
| 202 |  | 
 | 
| 203 | 0 |         file << mapDeltas; | 
| 204 |  | 
 | 
| 205 | 0 |         LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 206 | 0 |         file << unbroadcast_txids; | 
| 207 |  | 
 | 
| 208 | 0 |         if (!skip_file_commit && !file.Commit()) { | 
| 209 | 0 |             (void)file.fclose(); | 
| 210 | 0 |             throw std::runtime_error("Commit failed"); | 
| 211 | 0 |         } | 
| 212 | 0 |         if (file.fclose() != 0) { | 
| 213 | 0 |             throw std::runtime_error( | 
| 214 | 0 |                 strprintf("Error closing %s: %s", fs::PathToString(file_fspath), SysErrorString(errno)));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 215 | 0 |         } | 
| 216 | 0 |         if (!RenameOver(dump_path + ".new", dump_path)) { | 
| 217 | 0 |             throw std::runtime_error("Rename failed"); | 
| 218 | 0 |         } | 
| 219 | 0 |         auto last = SteadyClock::now(); | 
| 220 |  | 
 | 
| 221 | 0 |         LogInfo("Dumped mempool: %.3fs to copy, %.3fs to dump, %d bytes dumped to file\n",| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 222 | 0 |                   Ticks<SecondsDouble>(mid - start), | 
| 223 | 0 |                   Ticks<SecondsDouble>(last - mid), | 
| 224 | 0 |                   fs::file_size(dump_path)); | 
| 225 | 0 |     } catch (const std::exception& e) { | 
| 226 | 0 |         LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());| Line | Count | Source |  | 356 | 0 | #define LogInfo(...) LogPrintLevel_(BCLog::LogFlags::ALL, BCLog::Level::Info, /*should_ratelimit=*/true, __VA_ARGS__) | Line | Count | Source |  | 350 | 0 | #define LogPrintLevel_(category, level, should_ratelimit, ...) LogPrintFormatInternal(std::source_location::current(), category, level, should_ratelimit, __VA_ARGS__) | 
 | 
 | 
| 227 | 0 |         (void)file.fclose(); | 
| 228 | 0 |         return false; | 
| 229 | 0 |     } | 
| 230 | 0 |     return true; | 
| 231 | 0 | } | 
| 232 |  |  | 
| 233 |  | } // namespace node |