aboutsummaryrefslogtreecommitdiff
path: root/src/node
diff options
context:
space:
mode:
Diffstat (limited to 'src/node')
-rw-r--r--src/node/blockmanager_args.cpp1
-rw-r--r--src/node/blockstorage.cpp155
-rw-r--r--src/node/blockstorage.h49
-rw-r--r--src/node/caches.cpp1
-rw-r--r--src/node/chainstatemanager_args.cpp25
-rw-r--r--src/node/context.h2
-rw-r--r--src/node/interface_ui.h3
-rw-r--r--src/node/interfaces.cpp127
-rw-r--r--src/node/kernel_notifications.cpp6
-rw-r--r--src/node/kernel_notifications.h12
-rw-r--r--src/node/mempool_args.cpp3
-rw-r--r--src/node/mempool_persist.cpp221
-rw-r--r--src/node/mempool_persist.h34
-rw-r--r--src/node/miner.cpp33
-rw-r--r--src/node/miner.h36
-rw-r--r--src/node/mini_miner.cpp22
-rw-r--r--src/node/transaction.cpp2
-rw-r--r--src/node/txreconciliation.cpp2
-rw-r--r--src/node/types.h22
-rw-r--r--src/node/utxo_snapshot.cpp6
-rw-r--r--src/node/utxo_snapshot.h13
-rw-r--r--src/node/validation_cache_args.cpp34
-rw-r--r--src/node/validation_cache_args.h17
-rw-r--r--src/node/warnings.cpp3
24 files changed, 619 insertions, 210 deletions
diff --git a/src/node/blockmanager_args.cpp b/src/node/blockmanager_args.cpp
index fa76566652..0fc4e1646a 100644
--- a/src/node/blockmanager_args.cpp
+++ b/src/node/blockmanager_args.cpp
@@ -16,6 +16,7 @@
namespace node {
util::Result<void> ApplyArgsManOptions(const ArgsManager& args, BlockManager::Options& opts)
{
+ if (auto value{args.GetBoolArg("-blocksxor")}) opts.use_xor = *value;
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nPruneArg{args.GetIntArg("-prune", opts.prune_target)};
if (nPruneArg < 0) {
diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp
index fb62e78138..07878a5602 100644
--- a/src/node/blockstorage.cpp
+++ b/src/node/blockstorage.cpp
@@ -19,7 +19,7 @@
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
-#include <reverse_iterator.h>
+#include <random.h>
#include <serialize.h>
#include <signet.h>
#include <span.h>
@@ -37,6 +37,7 @@
#include <validation.h>
#include <map>
+#include <ranges>
#include <unordered_map>
namespace kernel {
@@ -366,7 +367,7 @@ void BlockManager::FindFilesToPrune(
}
}
- LogPrint(BCLog::PRUNE, "[%s] target=%dMiB actual=%dMiB diff=%dMiB min_height=%d max_prune_height=%d removed %d blk/rev pairs\n",
+ LogDebug(BCLog::PRUNE, "[%s] target=%dMiB actual=%dMiB diff=%dMiB min_height=%d max_prune_height=%d removed %d blk/rev pairs\n",
chain.GetRole(), target / 1024 / 1024, nCurrentUsage / 1024 / 1024,
(int64_t(target) - int64_t(nCurrentUsage)) / 1024 / 1024,
min_block_to_prune, last_block_can_prune, count);
@@ -410,11 +411,11 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
m_snapshot_height = au_data.height;
CBlockIndex* base{LookupBlockIndex(*snapshot_blockhash)};
- // Since nChainTx (responsible for estimated progress) isn't persisted
+ // Since m_chain_tx_count (responsible for estimated progress) isn't persisted
// to disk, we must bootstrap the value for assumedvalid chainstates
// from the hardcoded assumeutxo chainparams.
- base->nChainTx = au_data.nChainTx;
- LogPrintf("[snapshot] set nChainTx=%d for %s\n", au_data.nChainTx, snapshot_blockhash->ToString());
+ base->m_chain_tx_count = au_data.m_chain_tx_count;
+ LogPrintf("[snapshot] set m_chain_tx_count=%d for %s\n", au_data.m_chain_tx_count, snapshot_blockhash->ToString());
} else {
// If this isn't called with a snapshot blockhash, make sure the cached snapshot height
// is null. This is relevant during snapshot completion, when the blockman may be loaded
@@ -449,15 +450,15 @@ bool BlockManager::LoadBlockIndex(const std::optional<uint256>& snapshot_blockha
if (m_snapshot_height && pindex->nHeight == *m_snapshot_height &&
pindex->GetBlockHash() == *snapshot_blockhash) {
// Should have been set above; don't disturb it with code below.
- Assert(pindex->nChainTx > 0);
- } else if (pindex->pprev->nChainTx > 0) {
- pindex->nChainTx = pindex->pprev->nChainTx + pindex->nTx;
+ Assert(pindex->m_chain_tx_count > 0);
+ } else if (pindex->pprev->m_chain_tx_count > 0) {
+ pindex->m_chain_tx_count = pindex->pprev->m_chain_tx_count + pindex->nTx;
} else {
- pindex->nChainTx = 0;
+ pindex->m_chain_tx_count = 0;
m_blocks_unlinked.insert(std::make_pair(pindex->pprev, pindex));
}
} else {
- pindex->nChainTx = pindex->nTx;
+ pindex->m_chain_tx_count = pindex->nTx;
}
}
if (!(pindex->nStatus & BLOCK_FAILED_MASK) && pindex->pprev && (pindex->pprev->nStatus & BLOCK_FAILED_MASK)) {
@@ -578,7 +579,7 @@ const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
{
const MapCheckpoints& checkpoints = data.mapCheckpoints;
- for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) {
+ for (const MapCheckpoints::value_type& i : checkpoints | std::views::reverse) {
const uint256& hash = i.second;
const CBlockIndex* pindex = LookupBlockIndex(hash);
if (pindex) {
@@ -588,18 +589,18 @@ const CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data)
return nullptr;
}
-bool BlockManager::IsBlockPruned(const CBlockIndex& block)
+bool BlockManager::IsBlockPruned(const CBlockIndex& block) const
{
AssertLockHeld(::cs_main);
return m_have_pruned && !(block.nStatus & BLOCK_HAVE_DATA) && (block.nTx > 0);
}
-const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& upper_block, const CBlockIndex* lower_block)
+const CBlockIndex* BlockManager::GetFirstBlock(const CBlockIndex& upper_block, uint32_t status_mask, const CBlockIndex* lower_block) const
{
AssertLockHeld(::cs_main);
const CBlockIndex* last_block = &upper_block;
- assert(last_block->nStatus & BLOCK_HAVE_DATA); // 'upper_block' must have data
- while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) {
+ assert((last_block->nStatus & status_mask) == status_mask); // 'upper_block' must satisfy the status mask
+ while (last_block->pprev && ((last_block->pprev->nStatus & status_mask) == status_mask)) {
if (lower_block) {
// Return if we reached the lower_block
if (last_block == lower_block) return lower_block;
@@ -616,7 +617,7 @@ const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& upper_bl
bool BlockManager::CheckBlockDataAvailability(const CBlockIndex& upper_block, const CBlockIndex& lower_block)
{
if (!(upper_block.nStatus & BLOCK_HAVE_DATA)) return false;
- return GetFirstStoredBlock(upper_block, &lower_block) == &lower_block;
+ return GetFirstBlock(upper_block, BLOCK_HAVE_DATA, &lower_block) == &lower_block;
}
// If we're using -prune with -reindex, then delete block files that will be ignored by the
@@ -682,11 +683,7 @@ bool BlockManager::UndoWriteToDisk(const CBlockUndo& blockundo, FlatFilePos& pos
fileout << GetParams().MessageStart() << nSize;
// Write undo data
- long fileOutPos = ftell(fileout.Get());
- if (fileOutPos < 0) {
- LogError("%s: ftell failed\n", __func__);
- return false;
- }
+ long fileOutPos = fileout.tell();
pos.nPos = (unsigned int)fileOutPos;
fileout << blockundo;
@@ -703,15 +700,10 @@ bool BlockManager::UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& in
{
const FlatFilePos pos{WITH_LOCK(::cs_main, return index.GetUndoPos())};
- if (pos.IsNull()) {
- LogError("%s: no undo data available\n", __func__);
- return false;
- }
-
// Open history file to read
AutoFile filein{OpenUndoFile(pos, true)};
if (filein.IsNull()) {
- LogError("%s: OpenUndoFile failed\n", __func__);
+ LogError("%s: OpenUndoFile failed for %s\n", __func__, pos.ToString());
return false;
}
@@ -723,13 +715,13 @@ bool BlockManager::UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& in
verifier >> blockundo;
filein >> hashChecksum;
} catch (const std::exception& e) {
- LogError("%s: Deserialize or I/O error - %s\n", __func__, e.what());
+ LogError("%s: Deserialize or I/O error - %s at %s\n", __func__, e.what(), pos.ToString());
return false;
}
// Verify checksum
if (hashChecksum != verifier.GetHash()) {
- LogError("%s: Checksum mismatch\n", __func__);
+ LogError("%s: Checksum mismatch at %s\n", __func__, pos.ToString());
return false;
}
@@ -739,7 +731,7 @@ bool BlockManager::UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex& in
bool BlockManager::FlushUndoFile(int block_file, bool finalize)
{
FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize);
- if (!UndoFileSeq().Flush(undo_pos_old, finalize)) {
+ if (!m_undo_file_seq.Flush(undo_pos_old, finalize)) {
m_opts.notifications.flushError(_("Flushing undo file to disk failed. This is likely the result of an I/O error."));
return false;
}
@@ -761,7 +753,7 @@ bool BlockManager::FlushBlockFile(int blockfile_num, bool fFinalize, bool finali
assert(static_cast<int>(m_blockfile_info.size()) > blockfile_num);
FlatFilePos block_pos_old(blockfile_num, m_blockfile_info[blockfile_num].nSize);
- if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) {
+ if (!m_block_file_seq.Flush(block_pos_old, fFinalize)) {
m_opts.notifications.flushError(_("Flushing block file to disk failed. This is likely the result of an I/O error."));
success = false;
}
@@ -813,38 +805,28 @@ void BlockManager::UnlinkPrunedFiles(const std::set<int>& setFilesToPrune) const
std::error_code ec;
for (std::set<int>::iterator it = setFilesToPrune.begin(); it != setFilesToPrune.end(); ++it) {
FlatFilePos pos(*it, 0);
- const bool removed_blockfile{fs::remove(BlockFileSeq().FileName(pos), ec)};
- const bool removed_undofile{fs::remove(UndoFileSeq().FileName(pos), ec)};
+ const bool removed_blockfile{fs::remove(m_block_file_seq.FileName(pos), ec)};
+ const bool removed_undofile{fs::remove(m_undo_file_seq.FileName(pos), ec)};
if (removed_blockfile || removed_undofile) {
- LogPrint(BCLog::BLOCKSTORAGE, "Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
+ LogDebug(BCLog::BLOCKSTORAGE, "Prune: %s deleted blk/rev (%05u)\n", __func__, *it);
}
}
}
-FlatFileSeq BlockManager::BlockFileSeq() const
-{
- return FlatFileSeq(m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kb */ : BLOCKFILE_CHUNK_SIZE);
-}
-
-FlatFileSeq BlockManager::UndoFileSeq() const
-{
- return FlatFileSeq(m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE);
-}
-
AutoFile BlockManager::OpenBlockFile(const FlatFilePos& pos, bool fReadOnly) const
{
- return AutoFile{BlockFileSeq().Open(pos, fReadOnly)};
+ return AutoFile{m_block_file_seq.Open(pos, fReadOnly), m_xor_key};
}
/** Open an undo file (rev?????.dat) */
AutoFile BlockManager::OpenUndoFile(const FlatFilePos& pos, bool fReadOnly) const
{
- return AutoFile{UndoFileSeq().Open(pos, fReadOnly)};
+ return AutoFile{m_undo_file_seq.Open(pos, fReadOnly), m_xor_key};
}
fs::path BlockManager::GetBlockPosFilename(const FlatFilePos& pos) const
{
- return BlockFileSeq().FileName(pos);
+ return m_block_file_seq.FileName(pos);
}
FlatFilePos BlockManager::FindNextBlockPos(unsigned int nAddSize, unsigned int nHeight, uint64_t nTime)
@@ -858,7 +840,7 @@ FlatFilePos BlockManager::FindNextBlockPos(unsigned int nAddSize, unsigned int n
assert(chain_type == BlockfileType::ASSUMED);
const auto new_cursor = BlockfileCursor{this->MaxBlockfileNum() + 1};
m_blockfile_cursors[chain_type] = new_cursor;
- LogPrint(BCLog::BLOCKSTORAGE, "[%s] initializing blockfile cursor to %s\n", chain_type, new_cursor);
+ LogDebug(BCLog::BLOCKSTORAGE, "[%s] initializing blockfile cursor to %s\n", chain_type, new_cursor);
}
const int last_blockfile = m_blockfile_cursors[chain_type]->file_num;
@@ -901,7 +883,7 @@ FlatFilePos BlockManager::FindNextBlockPos(unsigned int nAddSize, unsigned int n
pos.nPos = m_blockfile_info[nFile].nSize;
if (nFile != last_blockfile) {
- LogPrint(BCLog::BLOCKSTORAGE, "Leaving block file %i: %s (onto %i) (height %i)\n",
+ LogDebug(BCLog::BLOCKSTORAGE, "Leaving block file %i: %s (onto %i) (height %i)\n",
last_blockfile, m_blockfile_info[last_blockfile].ToString(), nFile, nHeight);
// Do not propagate the return code. The flush concerns a previous block
@@ -924,7 +906,7 @@ FlatFilePos BlockManager::FindNextBlockPos(unsigned int nAddSize, unsigned int n
m_blockfile_info[nFile].nSize += nAddSize;
bool out_of_space;
- size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space);
+ size_t bytes_allocated = m_block_file_seq.Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
m_opts.notifications.fatalError(_("Disk space is too low!"));
return {};
@@ -970,7 +952,7 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP
m_dirty_fileinfo.insert(nFile);
bool out_of_space;
- size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space);
+ size_t bytes_allocated = m_undo_file_seq.Allocate(pos, nAddSize, out_of_space);
if (out_of_space) {
return FatalError(m_opts.notifications, state, _("Disk space is too low!"));
}
@@ -986,7 +968,7 @@ bool BlockManager::WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const
// Open history file to append
AutoFile fileout{OpenBlockFile(pos)};
if (fileout.IsNull()) {
- LogError("WriteBlockToDisk: OpenBlockFile failed\n");
+ LogError("%s: OpenBlockFile failed\n", __func__);
return false;
}
@@ -995,11 +977,7 @@ bool BlockManager::WriteBlockToDisk(const CBlock& block, FlatFilePos& pos) const
fileout << GetParams().MessageStart() << nSize;
// Write block
- long fileOutPos = ftell(fileout.Get());
- if (fileOutPos < 0) {
- LogError("WriteBlockToDisk: ftell failed\n");
- return false;
- }
+ long fileOutPos = fileout.tell();
pos.nPos = (unsigned int)fileOutPos;
fileout << TX_WITH_WITNESS(block);
@@ -1016,7 +994,7 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid
if (block.GetUndoPos().IsNull()) {
FlatFilePos _pos;
if (!FindUndoPos(state, block.nFile, _pos, ::GetSerializeSize(blockundo) + 40)) {
- LogError("ConnectBlock(): FindUndoPos failed\n");
+ LogError("%s: FindUndoPos failed\n", __func__);
return false;
}
if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash())) {
@@ -1055,7 +1033,7 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons
// Open history file to read
AutoFile filein{OpenBlockFile(pos, true)};
if (filein.IsNull()) {
- LogError("ReadBlockFromDisk: OpenBlockFile failed for %s\n", pos.ToString());
+ LogError("%s: OpenBlockFile failed for %s\n", __func__, pos.ToString());
return false;
}
@@ -1069,13 +1047,13 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const FlatFilePos& pos) cons
// Check the header
if (!CheckProofOfWork(block.GetHash(), block.nBits, GetConsensus())) {
- LogError("ReadBlockFromDisk: Errors in block header at %s\n", pos.ToString());
+ LogError("%s: Errors in block header at %s\n", __func__, pos.ToString());
return false;
}
// Signet only: check block solution
if (GetConsensus().signet_blocks && !CheckSignetBlockSolution(block, GetConsensus())) {
- LogError("ReadBlockFromDisk: Errors in block solution at %s\n", pos.ToString());
+ LogError("%s: Errors in block solution at %s\n", __func__, pos.ToString());
return false;
}
@@ -1090,8 +1068,7 @@ bool BlockManager::ReadBlockFromDisk(CBlock& block, const CBlockIndex& index) co
return false;
}
if (block.GetHash() != index.GetBlockHash()) {
- LogError("ReadBlockFromDisk(CBlock&, CBlockIndex*): GetHash() doesn't match index for %s at %s\n",
- index.ToString(), block_pos.ToString());
+ LogError("%s: GetHash() doesn't match index for %s at %s\n", __func__, index.ToString(), block_pos.ToString());
return false;
}
return true;
@@ -1160,6 +1137,54 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight)
return blockPos;
}
+static auto InitBlocksdirXorKey(const BlockManager::Options& opts)
+{
+ // Bytes are serialized without length indicator, so this is also the exact
+ // size of the XOR-key file.
+ std::array<std::byte, 8> xor_key{};
+
+ if (opts.use_xor && fs::is_empty(opts.blocks_dir)) {
+ // Only use random fresh key when the boolean option is set and on the
+ // very first start of the program.
+ FastRandomContext{}.fillrand(xor_key);
+ }
+
+ const fs::path xor_key_path{opts.blocks_dir / "xor.dat"};
+ if (fs::exists(xor_key_path)) {
+ // A pre-existing xor key file has priority.
+ AutoFile xor_key_file{fsbridge::fopen(xor_key_path, "rb")};
+ xor_key_file >> xor_key;
+ } else {
+ // Create initial or missing xor key file
+ AutoFile xor_key_file{fsbridge::fopen(xor_key_path,
+#ifdef __MINGW64__
+ "wb" // Temporary workaround for https://github.com/bitcoin/bitcoin/issues/30210
+#else
+ "wbx"
+#endif
+ )};
+ xor_key_file << xor_key;
+ }
+ // If the user disabled the key, it must be zero.
+ if (!opts.use_xor && xor_key != decltype(xor_key){}) {
+ throw std::runtime_error{
+ strprintf("The blocksdir XOR-key can not be disabled when a random key was already stored! "
+ "Stored key: '%s', stored path: '%s'.",
+ HexStr(xor_key), fs::PathToString(xor_key_path)),
+ };
+ }
+ LogInfo("Using obfuscation key for blocksdir *.dat files (%s): '%s'\n", fs::PathToString(opts.blocks_dir), HexStr(xor_key));
+ return std::vector<std::byte>{xor_key.begin(), xor_key.end()};
+}
+
+BlockManager::BlockManager(const util::SignalInterrupt& interrupt, Options opts)
+ : m_prune_mode{opts.prune_target > 0},
+ m_xor_key{InitBlocksdirXorKey(opts)},
+ m_opts{std::move(opts)},
+ m_block_file_seq{FlatFileSeq{m_opts.blocks_dir, "blk", m_opts.fast_prune ? 0x4000 /* 16kB */ : BLOCKFILE_CHUNK_SIZE}},
+ m_undo_file_seq{FlatFileSeq{m_opts.blocks_dir, "rev", UNDOFILE_CHUNK_SIZE}},
+ m_interrupt{interrupt} {}
+
class ImportingNow
{
std::atomic<bool>& m_importing;
@@ -1177,7 +1202,7 @@ public:
}
};
-void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFiles)
+void ImportBlocks(ChainstateManager& chainman, std::span<const fs::path> import_paths)
{
ImportingNow imp{chainman.m_blockman.m_importing};
@@ -1212,7 +1237,7 @@ void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFile
}
// -loadblock=
- for (const fs::path& path : vImportFiles) {
+ for (const fs::path& path : import_paths) {
AutoFile file{fsbridge::fopen(path, "rb")};
if (!file.IsNull()) {
LogPrintf("Importing blocks file %s...\n", fs::PathToString(path));
diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h
index 108a08a72b..03bc5f4600 100644
--- a/src/node/blockstorage.h
+++ b/src/node/blockstorage.h
@@ -29,6 +29,7 @@
#include <memory>
#include <optional>
#include <set>
+#include <span>
#include <string>
#include <unordered_map>
#include <utility>
@@ -166,9 +167,6 @@ private:
[[nodiscard]] bool FlushChainstateBlockFile(int tip_height);
bool FindUndoPos(BlockValidationState& state, int nFile, FlatFilePos& pos, unsigned int nAddSize);
- FlatFileSeq BlockFileSeq() const;
- FlatFileSeq UndoFileSeq() const;
-
AutoFile OpenUndoFile(const FlatFilePos& pos, bool fReadOnly = false) const;
/**
@@ -243,6 +241,8 @@ private:
const bool m_prune_mode;
+ const std::vector<std::byte> m_xor_key;
+
/** Dirty block index entries. */
std::set<CBlockIndex*> m_dirty_blockindex;
@@ -261,13 +261,13 @@ private:
const kernel::BlockManagerOpts m_opts;
+ const FlatFileSeq m_block_file_seq;
+ const FlatFileSeq m_undo_file_seq;
+
public:
using Options = kernel::BlockManagerOpts;
- explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts)
- : m_prune_mode{opts.prune_target > 0},
- m_opts{std::move(opts)},
- m_interrupt{interrupt} {}
+ explicit BlockManager(const util::SignalInterrupt& interrupt, Options opts);
const util::SignalInterrupt& m_interrupt;
std::atomic<bool> m_importing{false};
@@ -372,16 +372,39 @@ public:
//! (part of the same chain).
bool CheckBlockDataAvailability(const CBlockIndex& upper_block LIFETIMEBOUND, const CBlockIndex& lower_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
- //! Find the first stored ancestor of start_block immediately after the last
- //! pruned ancestor. Return value will never be null. Caller is responsible
- //! for ensuring that start_block has data is not pruned.
- const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND, const CBlockIndex* lower_block=nullptr) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ /**
+ * @brief Returns the earliest block with specified `status_mask` flags set after
+ * the latest block _not_ having those flags.
+ *
+ * This function starts from `upper_block`, which must have all
+ * `status_mask` flags set, and iterates backwards through its ancestors. It
+ * continues as long as each block has all `status_mask` flags set, until
+ * reaching the oldest ancestor or `lower_block`.
+ *
+ * @pre `upper_block` must have all `status_mask` flags set.
+ * @pre `lower_block` must be null or an ancestor of `upper_block`
+ *
+ * @param upper_block The starting block for the search, which must have all
+ * `status_mask` flags set.
+ * @param status_mask Bitmask specifying required status flags.
+ * @param lower_block The earliest possible block to return. If null, the
+ * search can extend to the genesis block.
+ *
+ * @return A non-null pointer to the earliest block between `upper_block`
+ * and `lower_block`, inclusive, such that every block between the
+ * returned block and `upper_block` has `status_mask` flags set.
+ */
+ const CBlockIndex* GetFirstBlock(
+ const CBlockIndex& upper_block LIFETIMEBOUND,
+ uint32_t status_mask,
+ const CBlockIndex* lower_block = nullptr
+ ) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
/** True if any block files have ever been pruned. */
bool m_have_pruned = false;
//! Check whether the block associated with this index entry is pruned or not.
- bool IsBlockPruned(const CBlockIndex& block) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
+ bool IsBlockPruned(const CBlockIndex& block) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
//! Create or update a prune lock identified by its name
void UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
@@ -407,7 +430,7 @@ public:
void CleanupBlockRevFiles() const;
};
-void ImportBlocks(ChainstateManager& chainman, std::vector<fs::path> vImportFiles);
+void ImportBlocks(ChainstateManager& chainman, std::span<const fs::path> import_paths);
} // namespace node
#endif // BITCOIN_NODE_BLOCKSTORAGE_H
diff --git a/src/node/caches.cpp b/src/node/caches.cpp
index 7403f7ddea..dc4d98f592 100644
--- a/src/node/caches.cpp
+++ b/src/node/caches.cpp
@@ -13,7 +13,6 @@ CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
{
int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
- nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
CacheSizes sizes;
sizes.block_tree_db = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
nTotalCache -= sizes.block_tree_db;
diff --git a/src/node/chainstatemanager_args.cpp b/src/node/chainstatemanager_args.cpp
index bc4a815a3e..b86d0b2991 100644
--- a/src/node/chainstatemanager_args.cpp
+++ b/src/node/chainstatemanager_args.cpp
@@ -32,13 +32,20 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
if (auto value{args.GetBoolArg("-checkpoints")}) opts.checkpoints_enabled = *value;
if (auto value{args.GetArg("-minimumchainwork")}) {
- if (!IsHexNumber(*value)) {
- return util::Error{strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), *value)};
+ if (auto min_work{uint256::FromUserHex(*value)}) {
+ opts.minimum_chain_work = UintToArith256(*min_work);
+ } else {
+ return util::Error{strprintf(Untranslated("Invalid minimum work specified (%s), must be up to %d hex digits"), *value, uint256::size() * 2)};
}
- opts.minimum_chain_work = UintToArith256(uint256S(*value));
}
- if (auto value{args.GetArg("-assumevalid")}) opts.assumed_valid_block = uint256S(*value);
+ if (auto value{args.GetArg("-assumevalid")}) {
+ if (auto block_hash{uint256::FromUserHex(*value)}) {
+ opts.assumed_valid_block = *block_hash;
+ } else {
+ return util::Error{strprintf(Untranslated("Invalid assumevalid block hash specified (%s), must be up to %d hex digits (or 0 to disable)"), *value, uint256::size() * 2)};
+ }
+ }
if (auto value{args.GetIntArg("-maxtipage")}) opts.max_tip_age = std::chrono::seconds{*value};
@@ -56,6 +63,16 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
opts.worker_threads_num = std::clamp(script_threads - 1, 0, MAX_SCRIPTCHECK_THREADS);
LogPrintf("Script verification uses %d additional threads\n", opts.worker_threads_num);
+ if (auto max_size = args.GetIntArg("-maxsigcachesize")) {
+ // 1. When supplied with a max_size of 0, both the signature cache and
+ // script execution cache create the minimum possible cache (2
+ // elements). Therefore, we can use 0 as a floor here.
+ // 2. Multiply first, divide after to avoid integer truncation.
+ size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
+ opts.script_execution_cache_bytes = clamped_size_each;
+ opts.signature_cache_bytes = clamped_size_each;
+ }
+
return {};
}
} // namespace node
diff --git a/src/node/context.h b/src/node/context.h
index a664fad80b..398089ff3c 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -9,6 +9,7 @@
#include <cstdlib>
#include <functional>
#include <memory>
+#include <thread>
#include <vector>
class ArgsManager;
@@ -86,6 +87,7 @@ struct NodeContext {
std::atomic<int> exit_status{EXIT_SUCCESS};
//! Manages all the node warnings
std::unique_ptr<node::Warnings> warnings;
+ std::thread background_init_thread;
//! Declare default constructor and destructor that are not inline, so code
//! instantiating the NodeContext struct doesn't need to #include class
diff --git a/src/node/interface_ui.h b/src/node/interface_ui.h
index 22c241cb78..85c34f5834 100644
--- a/src/node/interface_ui.h
+++ b/src/node/interface_ui.h
@@ -6,9 +6,10 @@
#ifndef BITCOIN_NODE_INTERFACE_UI_H
#define BITCOIN_NODE_INTERFACE_UI_H
+#include <cstdint>
#include <functional>
-#include <memory>
#include <string>
+#include <vector>
class CBlockIndex;
enum class SynchronizationState;
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index fa151407fa..febd968313 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -17,6 +17,7 @@
#include <interfaces/handler.h>
#include <interfaces/mining.h>
#include <interfaces/node.h>
+#include <interfaces/types.h>
#include <interfaces/wallet.h>
#include <kernel/chain.h>
#include <kernel/context.h>
@@ -33,6 +34,7 @@
#include <node/interface_ui.h>
#include <node/mini_miner.h>
#include <node/miner.h>
+#include <node/kernel_notifications.h>
#include <node/transaction.h>
#include <node/types.h>
#include <node/warnings.h>
@@ -67,6 +69,8 @@
#include <boost/signals2/signal.hpp>
+using interfaces::BlockRef;
+using interfaces::BlockTemplate;
using interfaces::BlockTip;
using interfaces::Chain;
using interfaces::FoundBlock;
@@ -100,7 +104,7 @@ public:
void initParameterInteraction() override { InitParameterInteraction(args()); }
bilingual_str getWarnings() override { return Join(Assert(m_context->warnings)->GetMessages(), Untranslated("<hr />")); }
int getExitStatus() override { return Assert(m_context)->exit_status.load(); }
- uint32_t getLogCategories() override { return LogInstance().GetCategoryMask(); }
+ BCLog::CategoryMask getLogCategories() override { return LogInstance().GetCategoryMask(); }
bool baseInitialize() override
{
if (!AppInitBasicSetup(args(), Assert(context())->exit_status)) return false;
@@ -136,7 +140,7 @@ public:
// Stop RPC for clean shutdown if any of waitfor* commands is executed.
if (args().GetBoolArg("-server", false)) {
InterruptRPC();
- StopRPC();
+ StopRPC(m_context);
}
}
bool shutdownRequested() override { return ShutdownRequested(*Assert(m_context)); };
@@ -180,7 +184,7 @@ public:
});
args().WriteSettingsFile();
}
- void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
+ void mapPort(bool use_upnp, bool use_pcp) override { StartMapPort(use_upnp, use_pcp); }
bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(ConnectionDirection flags) override
{
@@ -278,6 +282,7 @@ public:
int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; }
size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; }
size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; }
+ size_t getMempoolMaxUsage() override { return m_context->mempool ? m_context->mempool->m_opts.max_size_bytes : 0; }
bool getHeaderTip(int& height, int64_t& block_time) override
{
LOCK(::cs_main);
@@ -289,6 +294,13 @@ public:
}
return false;
}
+ std::map<CNetAddr, LocalServiceInfo> getNetLocalAddresses() override
+ {
+ if (m_context->connman)
+ return m_context->connman->getNetLocalAddresses();
+ else
+ return {};
+ }
int getNumBlocks() override
{
LOCK(::cs_main);
@@ -806,16 +818,34 @@ public:
});
return result;
}
- bool updateRwSetting(const std::string& name, const common::SettingsValue& value, bool write) override
+ bool updateRwSetting(const std::string& name,
+ const interfaces::SettingsUpdate& update_settings_func) override
{
+ std::optional<interfaces::SettingsAction> action;
args().LockSettings([&](common::Settings& settings) {
- if (value.isNull()) {
- settings.rw_settings.erase(name);
+ if (auto* value = common::FindKey(settings.rw_settings, name)) {
+ action = update_settings_func(*value);
+ if (value->isNull()) settings.rw_settings.erase(name);
} else {
- settings.rw_settings[name] = value;
+ UniValue new_value;
+ action = update_settings_func(new_value);
+ if (!new_value.isNull()) settings.rw_settings[name] = std::move(new_value);
}
});
- return !write || args().WriteSettingsFile();
+ if (!action) return false;
+ // Now dump value to disk if requested
+ return *action != interfaces::SettingsAction::WRITE || args().WriteSettingsFile();
+ }
+ bool overwriteRwSetting(const std::string& name, common::SettingsValue value, interfaces::SettingsAction action) override
+ {
+ return updateRwSetting(name, [&](common::SettingsValue& settings) {
+ settings = std::move(value);
+ return action;
+ });
+ }
+ bool deleteRwSettings(const std::string& name, interfaces::SettingsAction action) override
+ {
+ return overwriteRwSetting(name, {}, action);
}
void requestMempoolTransactions(Notifications& notifications) override
{
@@ -837,6 +867,52 @@ public:
NodeContext& m_node;
};
+class BlockTemplateImpl : public BlockTemplate
+{
+public:
+ explicit BlockTemplateImpl(std::unique_ptr<CBlockTemplate> block_template) : m_block_template(std::move(block_template))
+ {
+ assert(m_block_template);
+ }
+
+ CBlockHeader getBlockHeader() override
+ {
+ return m_block_template->block;
+ }
+
+ CBlock getBlock() override
+ {
+ return m_block_template->block;
+ }
+
+ std::vector<CAmount> getTxFees() override
+ {
+ return m_block_template->vTxFees;
+ }
+
+ std::vector<int64_t> getTxSigops() override
+ {
+ return m_block_template->vTxSigOpsCost;
+ }
+
+ CTransactionRef getCoinbaseTx() override
+ {
+ return m_block_template->block.vtx[0];
+ }
+
+ std::vector<unsigned char> getCoinbaseCommitment() override
+ {
+ return m_block_template->vchCoinbaseCommitment;
+ }
+
+ int getWitnessCommitmentIndex() override
+ {
+ return GetWitnessCommitmentIndex(m_block_template->block);
+ }
+
+ const std::unique_ptr<CBlockTemplate> m_block_template;
+};
+
class MinerImpl : public Mining
{
public:
@@ -852,12 +928,33 @@ public:
return chainman().IsInitialBlockDownload();
}
- std::optional<uint256> getTipHash() override
+ std::optional<BlockRef> getTip() override
{
LOCK(::cs_main);
CBlockIndex* tip{chainman().ActiveChain().Tip()};
if (!tip) return {};
- return tip->GetBlockHash();
+ return BlockRef{tip->GetBlockHash(), tip->nHeight};
+ }
+
+ BlockRef waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override
+ {
+ // Interrupt check interval
+ const MillisecondsDouble tick{1000};
+ auto now{std::chrono::steady_clock::now()};
+ auto deadline = now + timeout;
+ // std::chrono does not check against overflow
+ if (deadline < now) deadline = std::chrono::steady_clock::time_point::max();
+ {
+ WAIT_LOCK(notifications().m_tip_block_mutex, lock);
+ while ((notifications().m_tip_block == uint256() || notifications().m_tip_block == current_tip) && !chainman().m_interrupt) {
+ now = std::chrono::steady_clock::now();
+ if (now >= deadline) break;
+ notifications().m_tip_block_cv.wait_until(lock, std::min(deadline, now + tick));
+ }
+ }
+ // Must release m_tip_block_mutex before locking cs_main, to avoid deadlocks.
+ LOCK(::cs_main);
+ return BlockRef{chainman().ActiveChain().Tip()->GetBlockHash(), chainman().ActiveChain().Tip()->nHeight};
}
bool processNewBlock(const std::shared_ptr<const CBlock>& block, bool* new_block) override
@@ -883,16 +980,16 @@ public:
return TestBlockValidity(state, chainman().GetParams(), chainman().ActiveChainstate(), block, tip, /*fCheckPOW=*/false, check_merkle_root);
}
- std::unique_ptr<CBlockTemplate> createNewBlock(const CScript& script_pub_key, bool use_mempool) override
+ std::unique_ptr<BlockTemplate> createNewBlock(const CScript& script_pub_key, const BlockCreateOptions& options) override
{
- BlockAssembler::Options options;
- ApplyArgsManOptions(gArgs, options);
-
- return BlockAssembler{chainman().ActiveChainstate(), use_mempool ? context()->mempool.get() : nullptr, options}.CreateNewBlock(script_pub_key);
+ BlockAssembler::Options assemble_options{options};
+ ApplyArgsManOptions(*Assert(m_node.args), assemble_options);
+ return std::make_unique<BlockTemplateImpl>(BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(script_pub_key));
}
NodeContext* context() override { return &m_node; }
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
+ KernelNotifications& notifications() { return *Assert(m_node.notifications); }
NodeContext& m_node;
};
} // namespace
diff --git a/src/node/kernel_notifications.cpp b/src/node/kernel_notifications.cpp
index 9894052a3a..40d45c8c2b 100644
--- a/src/node/kernel_notifications.cpp
+++ b/src/node/kernel_notifications.cpp
@@ -50,6 +50,12 @@ namespace node {
kernel::InterruptResult KernelNotifications::blockTip(SynchronizationState state, CBlockIndex& index)
{
+ {
+ LOCK(m_tip_block_mutex);
+ m_tip_block = index.GetBlockHash();
+ m_tip_block_cv.notify_all();
+ }
+
uiInterface.NotifyBlockTip(state, &index);
if (m_stop_at_height && index.nHeight >= m_stop_at_height) {
if (!m_shutdown()) {
diff --git a/src/node/kernel_notifications.h b/src/node/kernel_notifications.h
index e37f4d4e1e..9ff980ea56 100644
--- a/src/node/kernel_notifications.h
+++ b/src/node/kernel_notifications.h
@@ -7,6 +7,10 @@
#include <kernel/notifications_interface.h>
+#include <sync.h>
+#include <threadsafety.h>
+#include <uint256.h>
+
#include <atomic>
#include <cstdint>
@@ -34,7 +38,7 @@ public:
KernelNotifications(util::SignalInterrupt& shutdown, std::atomic<int>& exit_status, node::Warnings& warnings)
: m_shutdown(shutdown), m_exit_status{exit_status}, m_warnings{warnings} {}
- [[nodiscard]] kernel::InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) override;
+ [[nodiscard]] kernel::InterruptResult blockTip(SynchronizationState state, CBlockIndex& index) override EXCLUSIVE_LOCKS_REQUIRED(!m_tip_block_mutex);
void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) override;
@@ -52,6 +56,12 @@ public:
int m_stop_at_height{DEFAULT_STOPATHEIGHT};
//! Useful for tests, can be set to false to avoid shutdown on fatal error.
bool m_shutdown_on_fatal_error{true};
+
+ Mutex m_tip_block_mutex;
+ std::condition_variable m_tip_block_cv;
+ //! The block for which the last blockTip notification was received for.
+ uint256 m_tip_block;
+
private:
util::SignalInterrupt& m_shutdown;
std::atomic<int>& m_exit_status;
diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp
index f329affb1d..a488c1b149 100644
--- a/src/node/mempool_args.cpp
+++ b/src/node/mempool_args.cpp
@@ -93,6 +93,9 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
}
mempool_opts.full_rbf = argsman.GetBoolArg("-mempoolfullrbf", mempool_opts.full_rbf);
+ if (!mempool_opts.full_rbf) {
+ LogInfo("Warning: mempoolfullrbf=0 set but deprecated and will be removed in a future release\n");
+ }
mempool_opts.persist_v1_dat = argsman.GetBoolArg("-persistmempoolv1", mempool_opts.persist_v1_dat);
diff --git a/src/node/mempool_persist.cpp b/src/node/mempool_persist.cpp
new file mode 100644
index 0000000000..ff7de8c64a
--- /dev/null
+++ b/src/node/mempool_persist.cpp
@@ -0,0 +1,221 @@
+// 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 <node/mempool_persist.h>
+
+#include <clientversion.h>
+#include <consensus/amount.h>
+#include <logging.h>
+#include <primitives/transaction.h>
+#include <random.h>
+#include <serialize.h>
+#include <streams.h>
+#include <sync.h>
+#include <txmempool.h>
+#include <uint256.h>
+#include <util/fs.h>
+#include <util/fs_helpers.h>
+#include <util/signalinterrupt.h>
+#include <util/time.h>
+#include <validation.h>
+
+#include <cstdint>
+#include <cstdio>
+#include <exception>
+#include <functional>
+#include <map>
+#include <memory>
+#include <set>
+#include <stdexcept>
+#include <utility>
+#include <vector>
+
+using fsbridge::FopenFn;
+
+namespace node {
+
+static const uint64_t MEMPOOL_DUMP_VERSION_NO_XOR_KEY{1};
+static const uint64_t MEMPOOL_DUMP_VERSION{2};
+
+bool LoadMempool(CTxMemPool& pool, const fs::path& load_path, Chainstate& active_chainstate, ImportMempoolOptions&& opts)
+{
+ if (load_path.empty()) return false;
+
+ AutoFile file{opts.mockable_fopen_function(load_path, "rb")};
+ if (file.IsNull()) {
+ LogInfo("Failed to open mempool file. 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;
+ const auto now{NodeClock::now()};
+
+ try {
+ uint64_t version;
+ file >> version;
+ std::vector<std::byte> xor_key;
+ if (version == MEMPOOL_DUMP_VERSION_NO_XOR_KEY) {
+ // Leave XOR-key empty
+ } else if (version == MEMPOOL_DUMP_VERSION) {
+ file >> xor_key;
+ } else {
+ return false;
+ }
+ file.SetXor(xor_key);
+ uint64_t total_txns_to_load;
+ file >> total_txns_to_load;
+ uint64_t txns_tried = 0;
+ LogInfo("Loading %u mempool transactions from file...\n", total_txns_to_load);
+ int next_tenth_to_report = 0;
+ while (txns_tried < total_txns_to_load) {
+ const int percentage_done(100.0 * txns_tried / total_txns_to_load);
+ if (next_tenth_to_report < percentage_done / 10) {
+ LogInfo("Progress loading mempool transactions from file: %d%% (tried %u, %u remaining)\n",
+ percentage_done, txns_tried, total_txns_to_load - txns_tried);
+ next_tenth_to_report = percentage_done / 10;
+ }
+ ++txns_tried;
+
+ CTransactionRef tx;
+ int64_t nTime;
+ int64_t nFeeDelta;
+ file >> TX_WITH_WITNESS(tx);
+ file >> nTime;
+ file >> nFeeDelta;
+
+ if (opts.use_current_time) {
+ nTime = TicksSinceEpoch<std::chrono::seconds>(now);
+ }
+
+ CAmount amountdelta = nFeeDelta;
+ if (amountdelta && opts.apply_fee_delta_priority) {
+ pool.PrioritiseTransaction(tx->GetHash(), amountdelta);
+ }
+ if (nTime > TicksSinceEpoch<std::chrono::seconds>(now - pool.m_opts.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 (active_chainstate.m_chainman.m_interrupt)
+ return false;
+ }
+ std::map<uint256, CAmount> mapDeltas;
+ file >> mapDeltas;
+
+ if (opts.apply_fee_delta_priority) {
+ for (const auto& i : mapDeltas) {
+ pool.PrioritiseTransaction(i.first, i.second);
+ }
+ }
+
+ std::set<uint256> unbroadcast_txids;
+ file >> unbroadcast_txids;
+ if (opts.apply_unbroadcast_set) {
+ 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) {
+ LogInfo("Failed to deserialize mempool data on file: %s. Continuing anyway.\n", e.what());
+ return false;
+ }
+
+ 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);
+ 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<uint256, CAmount> mapDeltas;
+ std::vector<TxMempoolInfo> vinfo;
+ std::set<uint256> 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();
+
+ AutoFile file{mockable_fopen_function(dump_path + ".new", "wb")};
+ if (file.IsNull()) {
+ return false;
+ }
+
+ try {
+ const uint64_t version{pool.m_opts.persist_v1_dat ? MEMPOOL_DUMP_VERSION_NO_XOR_KEY : MEMPOOL_DUMP_VERSION};
+ file << version;
+
+ std::vector<std::byte> xor_key(8);
+ if (!pool.m_opts.persist_v1_dat) {
+ FastRandomContext{}.fillrand(xor_key);
+ file << xor_key;
+ }
+ file.SetXor(xor_key);
+
+ uint64_t mempool_transactions_to_write(vinfo.size());
+ file << mempool_transactions_to_write;
+ LogInfo("Writing %u mempool transactions to file...\n", mempool_transactions_to_write);
+ for (const auto& i : vinfo) {
+ file << TX_WITH_WITNESS(*(i.tx));
+ file << int64_t{count_seconds(i.m_time)};
+ file << int64_t{i.nFeeDelta};
+ mapDeltas.erase(i.tx->GetHash());
+ }
+
+ file << mapDeltas;
+
+ LogInfo("Writing %d unbroadcast transactions to file.\n", unbroadcast_txids.size());
+ file << unbroadcast_txids;
+
+ if (!skip_file_commit && !file.Commit())
+ throw std::runtime_error("Commit failed");
+ file.fclose();
+ if (!RenameOver(dump_path + ".new", dump_path)) {
+ throw std::runtime_error("Rename failed");
+ }
+ auto last = SteadyClock::now();
+
+ LogInfo("Dumped mempool: %.3fs to copy, %.3fs to dump, %d bytes dumped to file\n",
+ Ticks<SecondsDouble>(mid - start),
+ Ticks<SecondsDouble>(last - mid),
+ fs::file_size(dump_path));
+ } catch (const std::exception& e) {
+ LogInfo("Failed to dump mempool: %s. Continuing anyway.\n", e.what());
+ return false;
+ }
+ return true;
+}
+
+} // namespace node
diff --git a/src/node/mempool_persist.h b/src/node/mempool_persist.h
new file mode 100644
index 0000000000..7c5754a90c
--- /dev/null
+++ b/src/node/mempool_persist.h
@@ -0,0 +1,34 @@
+// 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.
+
+#ifndef BITCOIN_NODE_MEMPOOL_PERSIST_H
+#define BITCOIN_NODE_MEMPOOL_PERSIST_H
+
+#include <util/fs.h>
+
+class Chainstate;
+class CTxMemPool;
+
+namespace node {
+
+/** Dump the mempool to a file. */
+bool DumpMempool(const CTxMemPool& pool, const fs::path& dump_path,
+ fsbridge::FopenFn mockable_fopen_function = fsbridge::fopen,
+ bool skip_file_commit = false);
+
+struct ImportMempoolOptions {
+ fsbridge::FopenFn mockable_fopen_function{fsbridge::fopen};
+ bool use_current_time{false};
+ bool apply_fee_delta_priority{true};
+ bool apply_unbroadcast_set{true};
+};
+/** Import the file and attempt to add its contents to the mempool. */
+bool LoadMempool(CTxMemPool& pool, const fs::path& load_path,
+ Chainstate& active_chainstate,
+ ImportMempoolOptions&& opts);
+
+} // namespace node
+
+
+#endif // BITCOIN_NODE_MEMPOOL_PERSIST_H
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index 03c6d74deb..181ae2ef05 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -33,6 +33,14 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
int64_t nOldTime = pblock->nTime;
int64_t nNewTime{std::max<int64_t>(pindexPrev->GetMedianTimePast() + 1, TicksSinceEpoch<std::chrono::seconds>(NodeClock::now()))};
+ if (consensusParams.enforce_BIP94) {
+ // Height of block to be mined.
+ const int height{pindexPrev->nHeight + 1};
+ if (height % consensusParams.DifficultyAdjustmentInterval() == 0) {
+ nNewTime = std::max<int64_t>(nNewTime, pindexPrev->GetBlockTime() - MAX_TIMEWARP);
+ }
+ }
+
if (nOldTime < nNewTime) {
pblock->nTime = nNewTime;
}
@@ -59,14 +67,17 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
static BlockAssembler::Options ClampOptions(BlockAssembler::Options options)
{
- // Limit weight to between 4K and DEFAULT_BLOCK_MAX_WEIGHT for sanity:
- options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, 4000, DEFAULT_BLOCK_MAX_WEIGHT);
+ Assert(options.coinbase_max_additional_weight <= DEFAULT_BLOCK_MAX_WEIGHT);
+ Assert(options.coinbase_output_max_additional_sigops <= MAX_BLOCK_SIGOPS_COST);
+ // Limit weight to between coinbase_max_additional_weight and DEFAULT_BLOCK_MAX_WEIGHT for sanity:
+ // Coinbase (reserved) outputs can safely exceed -blockmaxweight, but the rest of the block template will be empty.
+ options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, options.coinbase_max_additional_weight, DEFAULT_BLOCK_MAX_WEIGHT);
return options;
}
BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options)
: chainparams{chainstate.m_chainman.GetParams()},
- m_mempool{mempool},
+ m_mempool{options.use_mempool ? mempool : nullptr},
m_chainstate{chainstate},
m_options{ClampOptions(options)}
{
@@ -79,6 +90,7 @@ void ApplyArgsManOptions(const ArgsManager& args, BlockAssembler::Options& optio
if (const auto blockmintxfee{args.GetArg("-blockmintxfee")}) {
if (const auto parsed{ParseMoney(*blockmintxfee)}) options.blockMinFeeRate = CFeeRate{*parsed};
}
+ options.print_modified_fee = args.GetBoolArg("-printpriority", options.print_modified_fee);
}
void BlockAssembler::resetBlock()
@@ -86,8 +98,8 @@ void BlockAssembler::resetBlock()
inBlock.clear();
// Reserve space for coinbase tx
- nBlockWeight = 4000;
- nBlockSigOpsCost = 400;
+ nBlockWeight = m_options.coinbase_max_additional_weight;
+ nBlockSigOpsCost = m_options.coinbase_output_max_additional_sigops;
// These counters do not include coinbase tx
nBlockTx = 0;
@@ -101,10 +113,6 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
resetBlock();
pblocktemplate.reset(new CBlockTemplate());
-
- if (!pblocktemplate.get()) {
- return nullptr;
- }
CBlock* const pblock = &pblocktemplate->block; // pointer for convenience
// Add dummy coinbase tx as first transaction
@@ -167,7 +175,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock(const CScript& sc
}
const auto time_2{SteadyClock::now()};
- LogPrint(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n",
+ LogDebug(BCLog::BENCH, "CreateNewBlock() packages: %.2fms (%d packages, %d updated descendants), validity: %.2fms (total %.2fms)\n",
Ticks<MillisecondsDouble>(time_1 - time_start), nPackagesSelected, nDescendantsUpdated,
Ticks<MillisecondsDouble>(time_2 - time_1),
Ticks<MillisecondsDouble>(time_2 - time_start));
@@ -222,8 +230,7 @@ void BlockAssembler::AddToBlock(CTxMemPool::txiter iter)
nFees += iter->GetFee();
inBlock.insert(iter->GetSharedTx()->GetHash());
- bool fPrintPriority = gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
- if (fPrintPriority) {
+ if (m_options.print_modified_fee) {
LogPrintf("fee rate %s txid %s\n",
CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(),
iter->GetTx().GetHash().ToString());
@@ -379,7 +386,7 @@ void BlockAssembler::addPackageTxs(const CTxMemPool& mempool, int& nPackagesSele
++nConsecutiveFailed;
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight >
- m_options.nBlockMaxWeight - 4000) {
+ m_options.nBlockMaxWeight - m_options.coinbase_max_additional_weight) {
// Give up if we're close to full and haven't succeeded in a while
break;
}
diff --git a/src/node/miner.h b/src/node/miner.h
index c3178a7532..1b82943766 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -6,6 +6,7 @@
#ifndef BITCOIN_NODE_MINER_H
#define BITCOIN_NODE_MINER_H
+#include <node/types.h>
#include <policy/policy.h>
#include <primitives/block.h>
#include <txmempool.h>
@@ -30,7 +31,7 @@ class ChainstateManager;
namespace Consensus { struct Params; };
namespace node {
-static const bool DEFAULT_PRINTPRIORITY = false;
+static const bool DEFAULT_PRINT_MODIFIED_FEE = false;
struct CBlockTemplate
{
@@ -96,21 +97,25 @@ struct CompareTxIterByAncestorCount {
}
};
+
+struct CTxMemPoolModifiedEntry_Indices final : boost::multi_index::indexed_by<
+ boost::multi_index::ordered_unique<
+ modifiedentry_iter,
+ CompareCTxMemPoolIter
+ >,
+ // sorted by modified ancestor fee rate
+ boost::multi_index::ordered_non_unique<
+ // Reuse same tag from CTxMemPool's similar index
+ boost::multi_index::tag<ancestor_score>,
+ boost::multi_index::identity<CTxMemPoolModifiedEntry>,
+ CompareTxMemPoolEntryByAncestorFee
+ >
+>
+{};
+
typedef boost::multi_index_container<
CTxMemPoolModifiedEntry,
- boost::multi_index::indexed_by<
- boost::multi_index::ordered_unique<
- modifiedentry_iter,
- CompareCTxMemPoolIter
- >,
- // sorted by modified ancestor fee rate
- boost::multi_index::ordered_non_unique<
- // Reuse same tag from CTxMemPool's similar index
- boost::multi_index::tag<ancestor_score>,
- boost::multi_index::identity<CTxMemPoolModifiedEntry>,
- CompareTxMemPoolEntryByAncestorFee
- >
- >
+ CTxMemPoolModifiedEntry_Indices
> indexed_modified_transaction_set;
typedef indexed_modified_transaction_set::nth_index<0>::type::iterator modtxiter;
@@ -153,12 +158,13 @@ private:
Chainstate& m_chainstate;
public:
- struct Options {
+ struct Options : BlockCreateOptions {
// Configuration parameters for the block size
size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT};
CFeeRate blockMinFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
// Whether to call TestBlockValidity() at the end of CreateNewBlock().
bool test_block_validity{true};
+ bool print_modified_fee{DEFAULT_PRINT_MODIFIED_FEE};
};
explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options);
diff --git a/src/node/mini_miner.cpp b/src/node/mini_miner.cpp
index 58422c4439..d7d15554b3 100644
--- a/src/node/mini_miner.cpp
+++ b/src/node/mini_miner.cpp
@@ -174,7 +174,7 @@ MiniMiner::MiniMiner(const std::vector<MiniMinerMempoolEntry>& manual_entries,
SanityCheck();
}
-// Compare by min(ancestor feerate, individual feerate), then iterator
+// Compare by min(ancestor feerate, individual feerate), then txid
//
// Under the ancestor-based mining approach, high-feerate children can pay for parents, but high-feerate
// parents do not incentive inclusion of their children. Therefore the mining algorithm only considers
@@ -183,21 +183,13 @@ struct AncestorFeerateComparator
{
template<typename I>
bool operator()(const I& a, const I& b) const {
- auto min_feerate = [](const MiniMinerMempoolEntry& e) -> CFeeRate {
- const CAmount ancestor_fee{e.GetModFeesWithAncestors()};
- const int64_t ancestor_size{e.GetSizeWithAncestors()};
- const CAmount tx_fee{e.GetModifiedFee()};
- const int64_t tx_size{e.GetTxSize()};
- // Comparing ancestor feerate with individual feerate:
- // ancestor_fee / ancestor_size <= tx_fee / tx_size
- // Avoid division and possible loss of precision by
- // multiplying both sides by the sizes:
- return ancestor_fee * tx_size < tx_fee * ancestor_size ?
- CFeeRate(ancestor_fee, ancestor_size) :
- CFeeRate(tx_fee, tx_size);
+ auto min_feerate = [](const MiniMinerMempoolEntry& e) -> FeeFrac {
+ FeeFrac self_feerate(e.GetModifiedFee(), e.GetTxSize());
+ FeeFrac ancestor_feerate(e.GetModFeesWithAncestors(), e.GetSizeWithAncestors());
+ return std::min(ancestor_feerate, self_feerate);
};
- CFeeRate a_feerate{min_feerate(a->second)};
- CFeeRate b_feerate{min_feerate(b->second)};
+ FeeFrac a_feerate{min_feerate(a->second)};
+ FeeFrac b_feerate{min_feerate(b->second)};
if (a_feerate != b_feerate) {
return a_feerate > b_feerate;
}
diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp
index 591dcd698d..0f45da45db 100644
--- a/src/node/transaction.cpp
+++ b/src/node/transaction.cpp
@@ -55,7 +55,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t
const Coin& existingCoin = view.AccessCoin(COutPoint(txid, o));
// IsSpent doesn't mean the coin is spent, it means the output doesn't exist.
// So if the output does exist, then this transaction exists in the chain.
- if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_CHAIN;
+ if (!existingCoin.IsSpent()) return TransactionError::ALREADY_IN_UTXO_SET;
}
if (auto mempool_tx = node.mempool->get(txid); mempool_tx) {
diff --git a/src/node/txreconciliation.cpp b/src/node/txreconciliation.cpp
index d62046daaa..e6e19c5756 100644
--- a/src/node/txreconciliation.cpp
+++ b/src/node/txreconciliation.cpp
@@ -85,7 +85,7 @@ public:
LOCK(m_txreconciliation_mutex);
LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Pre-register peer=%d\n", peer_id);
- const uint64_t local_salt{GetRand(UINT64_MAX)};
+ const uint64_t local_salt{FastRandomContext().rand64()};
// We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's
// safe to assume we don't have this record yet.
diff --git a/src/node/types.h b/src/node/types.h
index 0461e85f43..1302f1b127 100644
--- a/src/node/types.h
+++ b/src/node/types.h
@@ -13,17 +13,37 @@
#ifndef BITCOIN_NODE_TYPES_H
#define BITCOIN_NODE_TYPES_H
+#include <cstddef>
+
namespace node {
enum class TransactionError {
OK, //!< No error
MISSING_INPUTS,
- ALREADY_IN_CHAIN,
+ ALREADY_IN_UTXO_SET,
MEMPOOL_REJECTED,
MEMPOOL_ERROR,
MAX_FEE_EXCEEDED,
MAX_BURN_EXCEEDED,
INVALID_PACKAGE,
};
+
+struct BlockCreateOptions {
+ /**
+ * Set false to omit mempool transactions
+ */
+ bool use_mempool{true};
+ /**
+ * The maximum additional weight which the pool will add to the coinbase
+ * scriptSig, witness and outputs. This must include any additional
+ * weight needed for larger CompactSize encoded lengths.
+ */
+ size_t coinbase_max_additional_weight{4000};
+ /**
+ * The maximum additional sigops which the pool will add in coinbase
+ * transaction outputs.
+ */
+ size_t coinbase_output_max_additional_sigops{400};
+};
} // namespace node
#endif // BITCOIN_NODE_TYPES_H
diff --git a/src/node/utxo_snapshot.cpp b/src/node/utxo_snapshot.cpp
index 976421e455..ca5491bdc2 100644
--- a/src/node/utxo_snapshot.cpp
+++ b/src/node/utxo_snapshot.cpp
@@ -73,10 +73,10 @@ std::optional<uint256> ReadSnapshotBaseBlockhash(fs::path chaindir)
}
afile >> base_blockhash;
- if (std::fgetc(afile.Get()) != EOF) {
+ int64_t position = afile.tell();
+ afile.seek(0, SEEK_END);
+ if (position != afile.tell()) {
LogPrintf("[snapshot] warning: unexpected trailing data in %s\n", read_from_str);
- } else if (std::ferror(afile.Get())) {
- LogPrintf("[snapshot] warning: i/o error reading %s\n", read_from_str);
}
return base_blockhash;
}
diff --git a/src/node/utxo_snapshot.h b/src/node/utxo_snapshot.h
index a7c4135787..e4eb6d60ad 100644
--- a/src/node/utxo_snapshot.h
+++ b/src/node/utxo_snapshot.h
@@ -28,16 +28,17 @@ class Chainstate;
namespace node {
//! Metadata describing a serialized version of a UTXO set from which an
//! assumeutxo Chainstate can be constructed.
+//! All metadata fields come from an untrusted file, so must be validated
+//! before being used. Thus, new fields should be added only if needed.
class SnapshotMetadata
{
- const uint16_t m_version{1};
- const std::set<uint16_t> m_supported_versions{1};
+ inline static const uint16_t VERSION{2};
+ const std::set<uint16_t> m_supported_versions{VERSION};
const MessageStartChars m_network_magic;
public:
//! The hash of the block that reflects the tip of the chain for the
//! UTXO set contained in this snapshot.
uint256 m_base_blockhash;
- uint32_t m_base_blockheight;
//! The number of coins in the UTXO set contained in this snapshot. Used
@@ -50,19 +51,16 @@ public:
SnapshotMetadata(
const MessageStartChars network_magic,
const uint256& base_blockhash,
- const int base_blockheight,
uint64_t coins_count) :
m_network_magic(network_magic),
m_base_blockhash(base_blockhash),
- m_base_blockheight(base_blockheight),
m_coins_count(coins_count) { }
template <typename Stream>
inline void Serialize(Stream& s) const {
s << SNAPSHOT_MAGIC_BYTES;
- s << m_version;
+ s << VERSION;
s << m_network_magic;
- s << m_base_blockheight;
s << m_base_blockhash;
s << m_coins_count;
}
@@ -98,7 +96,6 @@ public:
}
}
- s >> m_base_blockheight;
s >> m_base_blockhash;
s >> m_coins_count;
}
diff --git a/src/node/validation_cache_args.cpp b/src/node/validation_cache_args.cpp
deleted file mode 100644
index ddf24f798d..0000000000
--- a/src/node/validation_cache_args.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-// 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 <node/validation_cache_args.h>
-
-#include <kernel/validation_cache_sizes.h>
-
-#include <common/args.h>
-
-#include <algorithm>
-#include <cstddef>
-#include <cstdint>
-#include <memory>
-#include <optional>
-
-using kernel::ValidationCacheSizes;
-
-namespace node {
-void ApplyArgsManOptions(const ArgsManager& argsman, ValidationCacheSizes& cache_sizes)
-{
- if (auto max_size = argsman.GetIntArg("-maxsigcachesize")) {
- // 1. When supplied with a max_size of 0, both InitSignatureCache and
- // InitScriptExecutionCache create the minimum possible cache (2
- // elements). Therefore, we can use 0 as a floor here.
- // 2. Multiply first, divide after to avoid integer truncation.
- size_t clamped_size_each = std::max<int64_t>(*max_size, 0) * (1 << 20) / 2;
- cache_sizes = {
- .signature_cache_bytes = clamped_size_each,
- .script_execution_cache_bytes = clamped_size_each,
- };
- }
-}
-} // namespace node
diff --git a/src/node/validation_cache_args.h b/src/node/validation_cache_args.h
deleted file mode 100644
index f447c13b49..0000000000
--- a/src/node/validation_cache_args.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// 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.
-
-#ifndef BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
-#define BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
-
-class ArgsManager;
-namespace kernel {
-struct ValidationCacheSizes;
-};
-
-namespace node {
-void ApplyArgsManOptions(const ArgsManager& argsman, kernel::ValidationCacheSizes& cache_sizes);
-} // namespace node
-
-#endif // BITCOIN_NODE_VALIDATION_CACHE_ARGS_H
diff --git a/src/node/warnings.cpp b/src/node/warnings.cpp
index b99c845900..87389e472b 100644
--- a/src/node/warnings.cpp
+++ b/src/node/warnings.cpp
@@ -28,8 +28,7 @@ Warnings::Warnings()
}
bool Warnings::Set(warning_type id, bilingual_str message)
{
- LOCK(m_mutex);
- const auto& [_, inserted]{m_warnings.insert({id, std::move(message)})};
+ const auto& [_, inserted]{WITH_LOCK(m_mutex, return m_warnings.insert({id, std::move(message)}))};
if (inserted) uiInterface.NotifyAlertChanged();
return inserted;
}