Merge bitcoin/bitcoin#35450: [29.x] 29.4rc1

88cd3a7711 doc: update manual pages for v29.4rc1 (fanquake)
116565bc0a build: bump version to v29.4rc1 (fanquake)
d82afa961b doc: update release notes for v29.4rc1 (fanquake)
ea8fafd2bf fuzz: Remove unused g_setup pointers (MarcoFalke)
1907b3f56c coins: compact chainstate in background (Lőrinc)
9dd8e32853 validation: randomly compact chainstate (Lőrinc)
e0ab4cffaa coins: test chainstate flush baseline (Lőrinc)
53a5c7f1c9 lint: disable leveldb subtree check (fanquake)

Pull request description:

  Backports:
  * #35465

  Final changes for v`29.4rc1`.

  Backports since 29.3:
  * #34680
  * #34855
  * #35212
  * #35234

ACKs for top commit:
  sedited:
    ACK 88cd3a7711

Tree-SHA512: 57d647f0baf124128d84072c3865cca72ca4a266e95f88b8e1313230aa70a25d76d372e042b095312325b57983805943179d46bfa898522303e90adb69c74347
This commit is contained in:
merge-script 2026-06-23 20:20:19 +02:00
commit 01b4861961
No known key found for this signature in database
GPG key ID: 9B79B45691DB4173
17 changed files with 147 additions and 38 deletions

View file

@ -28,9 +28,9 @@ get_directory_property(precious_variables CACHE_VARIABLES)
#=============================
set(CLIENT_NAME "Bitcoin Core")
set(CLIENT_VERSION_MAJOR 29)
set(CLIENT_VERSION_MINOR 3)
set(CLIENT_VERSION_MINOR 4)
set(CLIENT_VERSION_BUILD 0)
set(CLIENT_VERSION_RC 0)
set(CLIENT_VERSION_RC 1)
set(CLIENT_VERSION_IS_RELEASE "true")
set(COPYRIGHT_YEAR "2025")

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-CLI "1" "February 2026" "bitcoin-cli v29.3.0" "User Commands"
.TH BITCOIN-CLI "1" "June 2026" "bitcoin-cli v29.4.0rc1" "User Commands"
.SH NAME
bitcoin-cli \- manual page for bitcoin-cli v29.3.0
bitcoin-cli \- manual page for bitcoin-cli v29.4.0rc1
.SH SYNOPSIS
.B bitcoin-cli
[\fI\,options\/\fR] \fI\,<command> \/\fR[\fI\,params\/\fR]
@ -15,7 +15,7 @@ bitcoin-cli \- manual page for bitcoin-cli v29.3.0
.B bitcoin-cli
[\fI\,options\/\fR] \fI\,help <command>\/\fR
.SH DESCRIPTION
Bitcoin Core RPC client version v29.3.0
Bitcoin Core RPC client version v29.4.0rc1
.PP
The bitcoin\-cli utility provides a command line interface to interact with a Bitcoin Core RPC server.
.PP

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-QT "1" "February 2026" "bitcoin-qt v29.3.0" "User Commands"
.TH BITCOIN-QT "1" "June 2026" "bitcoin-qt v29.4.0rc1" "User Commands"
.SH NAME
bitcoin-qt \- manual page for bitcoin-qt v29.3.0
bitcoin-qt \- manual page for bitcoin-qt v29.4.0rc1
.SH SYNOPSIS
.B bitcoin-qt
[\fI\,options\/\fR] [\fI\,URI\/\fR]
.SH DESCRIPTION
Bitcoin Core version v29.3.0
Bitcoin Core version v29.4.0rc1
.PP
The bitcoin\-qt application provides a graphical interface for interacting with Bitcoin Core.
.PP

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-TX "1" "February 2026" "bitcoin-tx v29.3.0" "User Commands"
.TH BITCOIN-TX "1" "June 2026" "bitcoin-tx v29.4.0rc1" "User Commands"
.SH NAME
bitcoin-tx \- manual page for bitcoin-tx v29.3.0
bitcoin-tx \- manual page for bitcoin-tx v29.4.0rc1
.SH SYNOPSIS
.B bitcoin-tx
[\fI\,options\/\fR] \fI\,<hex-tx> \/\fR[\fI\,commands\/\fR]
@ -9,7 +9,7 @@ bitcoin-tx \- manual page for bitcoin-tx v29.3.0
.B bitcoin-tx
[\fI\,options\/\fR] \fI\,-create \/\fR[\fI\,commands\/\fR]
.SH DESCRIPTION
Bitcoin Core bitcoin\-tx utility version v29.3.0
Bitcoin Core bitcoin\-tx utility version v29.4.0rc1
.PP
The bitcoin\-tx tool is used for creating and modifying bitcoin transactions.
.PP

View file

@ -1,7 +1,7 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-UTIL "1" "February 2026" "bitcoin-util v29.3.0" "User Commands"
.TH BITCOIN-UTIL "1" "June 2026" "bitcoin-util v29.4.0rc1" "User Commands"
.SH NAME
bitcoin-util \- manual page for bitcoin-util v29.3.0
bitcoin-util \- manual page for bitcoin-util v29.4.0rc1
.SH SYNOPSIS
.B bitcoin-util
[\fI\,options\/\fR] [\fI\,command\/\fR]
@ -9,7 +9,7 @@ bitcoin-util \- manual page for bitcoin-util v29.3.0
.B bitcoin-util
[\fI\,options\/\fR] \fI\,grind <hex-block-header>\/\fR
.SH DESCRIPTION
Bitcoin Core bitcoin\-util utility version v29.3.0
Bitcoin Core bitcoin\-util utility version v29.4.0rc1
.PP
The bitcoin\-util tool provides bitcoin related functionality that does not rely on the ability to access a running node. Available [commands] are listed below.
.SH OPTIONS

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIN-WALLET "1" "February 2026" "bitcoin-wallet v29.3.0" "User Commands"
.TH BITCOIN-WALLET "1" "June 2026" "bitcoin-wallet v29.4.0rc1" "User Commands"
.SH NAME
bitcoin-wallet \- manual page for bitcoin-wallet v29.3.0
bitcoin-wallet \- manual page for bitcoin-wallet v29.4.0rc1
.SH SYNOPSIS
.B bitcoin-wallet
[\fI\,options\/\fR] \fI\,<command>\/\fR
.SH DESCRIPTION
Bitcoin Core bitcoin\-wallet utility version v29.3.0
Bitcoin Core bitcoin\-wallet utility version v29.4.0rc1
.PP
bitcoin\-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.
.PP

View file

@ -1,12 +1,12 @@
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3.
.TH BITCOIND "1" "February 2026" "bitcoind v29.3.0" "User Commands"
.TH BITCOIND "1" "June 2026" "bitcoind v29.4.0rc1" "User Commands"
.SH NAME
bitcoind \- manual page for bitcoind v29.3.0
bitcoind \- manual page for bitcoind v29.4.0rc1
.SH SYNOPSIS
.B bitcoind
[\fI\,options\/\fR]
.SH DESCRIPTION
Bitcoin Core daemon version v29.3.0
Bitcoin Core daemon version v29.4.0rc1
.PP
The Bitcoin Core daemon (bitcoind) is a headless program that connects to the Bitcoin network to validate and relay transactions and blocks, as well as relaying addresses.
.PP

View file

@ -1,6 +1,6 @@
Bitcoin Core version 29.x is now available from:
Bitcoin Core version 29.4rc1 is now available from:
<https://bitcoincore.org/bin/bitcoin-core-29.x/>
<https://bitcoincore.org/bin/bitcoin-core-29.4/test.rc1/>
This release includes various bug fixes and performance
improvements, as well as updated translations.
@ -37,9 +37,14 @@ unsupported systems.
Notable changes
===============
This release fixes an issue where the chainstate database would repeatedly
rewrite large portions of itself, causing excessive disk reads and writes
during normal operation.
### Validation
- #35209 validation: correct lifetime of precomputed tx data
- #35465 coins: compact chainstate regularly
### Leveldb
@ -58,6 +63,10 @@ Notable changes
- #34228 depends: Unset SOURCE_DATE_EPOCH in gen_id script
- #34848 cmake: Migrate away from deprecated SQLite3 target
### Test
- #34918 fuzz: [refactor] Remove unused g_setup pointers
### Doc
- #34510 doc: fix broken bpftrace installation link

View file

@ -251,7 +251,7 @@ CDBWrapper::CDBWrapper(const DBParams& params)
if (params.options.force_compact) {
LogPrintf("Starting database compaction of %s\n", fs::PathToString(params.path));
DBContext().pdb->CompactRange(nullptr, nullptr);
CompactFull();
LogPrintf("Finished database compaction of %s\n", fs::PathToString(params.path));
}
@ -306,11 +306,18 @@ bool CDBWrapper::WriteBatch(CDBBatch& batch, bool fSync)
return true;
}
std::optional<std::string> CDBWrapper::GetProperty(const std::string& property) const
{
if (std::string value; DBContext().pdb->GetProperty(property, &value)) return value;
return std::nullopt;
}
void CDBWrapper::CompactFull() { DBContext().pdb->CompactRange(nullptr, nullptr); }
size_t CDBWrapper::DynamicMemoryUsage() const
{
std::string memory;
std::optional<size_t> parsed;
if (!DBContext().pdb->GetProperty("leveldb.approximate-memory-usage", &memory) || !(parsed = ToIntegral<size_t>(memory))) {
if (auto memory{GetProperty("leveldb.approximate-memory-usage")}; !memory || !(parsed = ToIntegral<size_t>(*memory))) {
LogDebug(BCLog::LEVELDB, "Failed to get approximate-memory-usage property\n");
return 0;
}

View file

@ -273,6 +273,12 @@ public:
bool WriteBatch(CDBBatch& batch, bool fSync = false);
//! Perform a blocking full compaction of the underlying LevelDB.
void CompactFull();
//! Return a LevelDB property value, if available.
std::optional<std::string> GetProperty(const std::string& property) const;
// Get an estimate of LevelDB memory usage (in bytes).
size_t DynamicMemoryUsage() const;

View file

@ -12,6 +12,7 @@
#include <txdb.h>
#include <uint256.h>
#include <undo.h>
#include <util/check.h>
#include <util/strencodings.h>
#include <map>
@ -1049,6 +1050,30 @@ BOOST_FIXTURE_TEST_CASE(ccoins_flush_behavior, FlushTest)
}
}
BOOST_FIXTURE_TEST_CASE(coins_db_leveldb_layout, FlushTest)
{
auto level2_files{[](CCoinsViewDB& base) {
return *Assert(ToIntegral<int>(*Assert(base.GetDBProperty("leveldb.num-files-at-level2"))));
}};
const COutPoint outpoint{Txid::FromUint256(m_rng.rand256()), 0};
const Coin coin{MakeCoin()};
const uint256 block_hash{m_rng.rand256()};
CCoinsViewDB base{{.path = m_args.GetDataDirBase() / "coins_db_leveldb_layout", .cache_bytes = 1_MiB, .wipe_data = true}, {}};
CCoinsViewCache cache{&base};
cache.EmplaceCoinInternalDANGER(COutPoint{outpoint}, Coin{coin});
cache.SetBestBlock(block_hash);
cache.Sync();
BOOST_CHECK_EQUAL(level2_files(base), 0);
WITH_LOCK(::cs_main, return base.CompactFull()).wait();
BOOST_CHECK_EQUAL(level2_files(base), 1);
BOOST_CHECK(*Assert(base.GetCoin(outpoint)) == coin);
BOOST_CHECK_EQUAL(base.GetBestBlock(), block_hash);
}
BOOST_AUTO_TEST_CASE(coins_resource_is_used)
{
CCoinsMapMemoryResource resource;

View file

@ -36,18 +36,13 @@
using node::SnapshotMetadata;
namespace {
const BasicTestingSetup* g_setup;
} // namespace
void initialize_deserialize()
{
static const auto testing_setup = MakeNoLogFileContext<>();
g_setup = testing_setup.get();
}
#define FUZZ_TARGET_DESERIALIZE(name, code) \
FUZZ_TARGET(name, .init = initialize_deserialize) \
FUZZ_TARGET(name, .init = initialize_deserialize) \
{ \
try { \
code \

View file

@ -12,10 +12,14 @@
#include <random.h>
#include <serialize.h>
#include <uint256.h>
#include <util/threadnames.h>
#include <util/vector.h>
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <exception>
#include <future>
#include <iterator>
#include <utility>
@ -51,11 +55,22 @@ CCoinsViewDB::CCoinsViewDB(DBParams db_params, CoinsViewOptions options) :
m_options{std::move(options)},
m_db{std::make_unique<CDBWrapper>(m_db_params)} { }
CCoinsViewDB::~CCoinsViewDB()
{
if (m_compaction.valid()) {
if (m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) {
LogInfo("Waiting for background chainstate compaction of %s", fs::PathToString(m_db_params.path));
}
m_compaction.wait();
}
}
void CCoinsViewDB::ResizeCache(size_t new_cache_size)
{
// We can't do this operation with an in-memory DB since we'll lose all the coins upon
// reset.
if (!m_db_params.memory_only) {
LOCK(m_db_mutex);
// Have to do a reset first to get the original `m_db` state to release its
// filesystem lock.
m_db.reset();
@ -156,6 +171,30 @@ size_t CCoinsViewDB::EstimateSize() const
return m_db->EstimateSize(DB_COIN, uint8_t(DB_COIN + 1));
}
std::optional<std::string> CCoinsViewDB::GetDBProperty(const std::string& property)
{
return m_db->GetProperty(property);
}
std::shared_future<void> CCoinsViewDB::CompactFull()
{
AssertLockHeld(::cs_main);
if (m_compaction.valid() && m_compaction.wait_for(std::chrono::seconds{0}) != std::future_status::ready) return m_compaction;
m_compaction = std::async(std::launch::async, [this] {
try {
util::ThreadRename("utxocompact");
LOCK(m_db_mutex);
LogDebug(BCLog::COINDB, "Starting chainstate compaction of %s", fs::PathToString(m_db_params.path));
m_db->CompactFull();
LogDebug(BCLog::COINDB, "Finished chainstate compaction of %s", fs::PathToString(m_db_params.path));
} catch (const std::exception& e) {
LogWarning("Failed chainstate compaction (%s)", e.what());
}
}).share();
return m_compaction;
}
/** Specialization of CCoinsViewCursor to iterate over a CCoinsViewDB */
class CCoinsViewDBCursor: public CCoinsViewCursor
{

View file

@ -14,8 +14,10 @@
#include <cstddef>
#include <cstdint>
#include <future>
#include <memory>
#include <optional>
#include <string>
#include <vector>
class COutPoint;
@ -39,9 +41,13 @@ class CCoinsViewDB final : public CCoinsView
protected:
DBParams m_db_params;
CoinsViewOptions m_options;
//! Prevents CompactFull() from using m_db while ResizeCache() replaces it.
Mutex m_db_mutex;
std::unique_ptr<CDBWrapper> m_db;
std::shared_future<void> m_compaction;
public:
explicit CCoinsViewDB(DBParams db_params, CoinsViewOptions options);
~CCoinsViewDB() override;
std::optional<Coin> GetCoin(const COutPoint& outpoint) const override;
bool HaveCoin(const COutPoint &outpoint) const override;
@ -55,10 +61,16 @@ public:
size_t EstimateSize() const override;
//! Dynamically alter the underlying leveldb cache size.
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
void ResizeCache(size_t new_cache_size) EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_db_mutex);
//! @returns filesystem path to on-disk storage or std::nullopt if in memory.
std::optional<fs::path> StoragePath() { return m_db->StoragePath(); }
//! Perform a full compaction of the underlying LevelDB on a one-shot background thread.
std::shared_future<void> CompactFull() EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_db_mutex);
//! Return an underlying LevelDB property value, if available.
std::optional<std::string> GetDBProperty(const std::string& property);
};
#endif // BITCOIN_TXDB_H

View file

@ -111,6 +111,13 @@ const std::vector<std::string> CHECKLEVEL_DOC {
* */
static constexpr int PRUNE_LOCK_BUFFER{10};
// Return whether the completed full flush should compact chainstate
static bool ShouldCompactChainstate(bool in_ibd)
{
static constexpr uint32_t flush_ratio{320}; // Roughly every 2 weeks with hourly flushes
return !in_ibd && FastRandomContext().randrange(flush_ratio) == 0;
}
TRACEPOINT_SEMAPHORE(validation, block_connected);
TRACEPOINT_SEMAPHORE(utxocache, flush);
TRACEPOINT_SEMAPHORE(mempool, replaced);
@ -2938,9 +2945,20 @@ bool Chainstate::FlushStateToDisk(
(bool)fFlushForPrune);
}
}
if (full_flush_completed && m_chainman.m_options.signals) {
// Update best block in wallet (so we can detect restored wallets).
m_chainman.m_options.signals->ChainStateFlushed(this->GetRole(), m_chain.GetLocator());
if (full_flush_completed) {
if (m_chainman.m_options.signals) {
// Update best block in wallet (so we can detect restored wallets).
m_chainman.m_options.signals->ChainStateFlushed(this->GetRole(), m_chain.GetLocator());
}
if (!m_chainman.m_interrupt && ShouldCompactChainstate(m_chainman.IsInitialBlockDownload())) {
try {
CoinsDB().CompactFull();
} catch (const std::exception& e) {
LogWarning("Failed to start chainstate compaction (%s)", e.what());
}
}
}
} catch (const std::runtime_error& e) {
return FatalError(m_chainman.GetNotifications(), state, strprintf(_("System error while flushing: %s"), e.what()));

View file

@ -11,11 +11,9 @@
namespace wallet {
namespace {
const TestingSetup* g_setup;
void initialize_crypter()
{
static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
g_setup = testing_setup.get();
}
FUZZ_TARGET(crypter, .init = initialize_crypter)

View file

@ -203,7 +203,7 @@ fn get_subtrees() -> Vec<&'static str> {
vec![
"src/crc32c",
"src/crypto/ctaes",
"src/leveldb",
//"src/leveldb", No longer a subtree in this release branch, due to direct cherry-picks
"src/minisketch",
"src/secp256k1",
]