// Copyright (c) 2022 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using fsbridge::FopenFn; namespace kernel { static const uint64_t MEMPOOL_DUMP_VERSION = 1; bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, FopenFn mockable_fopen_function) { if (load_path.empty()) return false; FILE* filestr{mockable_fopen_function(load_path, "rb")}; CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); if (file.IsNull()) { LogPrintf("Failed to open mempool file from disk. Continuing anyway.\n"); return false; } int64_t count = 0; int64_t expired = 0; int64_t failed = 0; int64_t already_there = 0; int64_t unbroadcast = 0; auto now = NodeClock::now(); try { uint64_t version; file >> version; if (version != MEMPOOL_DUMP_VERSION) { return false; } uint64_t num; file >> num; while (num) { --num; CTransactionRef tx; int64_t nTime; int64_t nFeeDelta; file >> tx; file >> nTime; file >> nFeeDelta; CAmount amountdelta = nFeeDelta; if (amountdelta) { pool.PrioritiseTransaction(tx->GetHash(), amountdelta); } if (nTime > TicksSinceEpoch(now - pool.m_expiry)) { LOCK(cs_main); const auto& accepted = AcceptToMemoryPool(active_chainstate, tx, nTime, /*bypass_limits=*/false, /*test_accept=*/false); if (accepted.m_result_type == MempoolAcceptResult::ResultType::VALID) { ++count; } else { // mempool may contain the transaction already, e.g. from // wallet(s) having loaded it while we were processing // mempool transactions; consider these as valid, instead of // failed, but mark them as 'already there' if (pool.exists(GenTxid::Txid(tx->GetHash()))) { ++already_there; } else { ++failed; } } } else { ++expired; } if (ShutdownRequested()) return false; } std::map mapDeltas; file >> mapDeltas; for (const auto& i : mapDeltas) { pool.PrioritiseTransaction(i.first, i.second); } std::set unbroadcast_txids; file >> unbroadcast_txids; unbroadcast = unbroadcast_txids.size(); for (const auto& txid : unbroadcast_txids) { // Ensure transactions were accepted to mempool then add to // unbroadcast set. if (pool.get(txid) != nullptr) pool.AddUnbroadcastTx(txid); } } catch (const std::exception& e) { LogPrintf("Failed to deserialize mempool data on disk: %s. Continuing anyway.\n", e.what()); return false; } 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); return true; } bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path, FopenFn mockable_fopen_function, bool skip_file_commit) { auto start = SteadyClock::now(); std::map mapDeltas; std::vector vinfo; std::set unbroadcast_txids; static Mutex dump_mutex; LOCK(dump_mutex); { LOCK(pool.cs); for (const auto &i : pool.mapDeltas) { mapDeltas[i.first] = i.second; } vinfo = pool.infoAll(); unbroadcast_txids = pool.GetUnbroadcastTxs(); } auto mid = SteadyClock::now(); try { FILE* filestr{mockable_fopen_function(dump_path + ".new", "wb")}; if (!filestr) { return false; } CAutoFile file(filestr, SER_DISK, CLIENT_VERSION); uint64_t version = MEMPOOL_DUMP_VERSION; file << version; file << (uint64_t)vinfo.size(); for (const auto& i : vinfo) { file << *(i.tx); file << int64_t{count_seconds(i.m_time)}; file << int64_t{i.nFeeDelta}; mapDeltas.erase(i.tx->GetHash()); } file << mapDeltas; LogPrintf("Writing %d unbroadcast transactions to disk.\n", unbroadcast_txids.size()); file << unbroadcast_txids; if (!skip_file_commit && !FileCommit(file.Get())) throw std::runtime_error("FileCommit failed"); file.fclose(); if (!RenameOver(dump_path + ".new", dump_path)) { throw std::runtime_error("Rename failed"); } auto last = SteadyClock::now(); LogPrintf("Dumped mempool: %gs to copy, %gs to dump\n", Ticks(mid - start), Ticks(last - mid)); } catch (const std::exception& e) { LogPrintf("Failed to dump mempool: %s. Continuing anyway.\n", e.what()); return false; } return true; } } // namespace kernel