From 58cdb5c2e835f219429351fe447ae6eeae3a96d8 Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 28 May 2026 09:34:12 +0100 Subject: [PATCH 01/21] Squashed 'src/leveldb/' changes from ab6c84e6f3..a7f9bdc611 a7f9bdc611 Merge bitcoin-core/leveldb-subtree#52: Revert "Increase maximum read-only mmap()s used from 1000 to 4096 on 64-bit systems" a2f531d2d0 Merge bitcoin-core/leveldb-subtree#60: util: use [[fallthrough]] directly 1a166221cf Merge bitcoin-core/leveldb-subtree#61: Disable seek compaction 6bfdb6093b Disable seek compaction 42a5f29aa9 util: use [[fallthrough]] directly c274b50867 Merge bitcoin-core/leveldb-subtree#53: refactor: Delete unused `ScopedHandle:operator=(ScopedHandle&&)` 68740f586f Merge bitcoin-core/leveldb-subtree#59: Fix Clang `-Wconditional-uninitialized` warning 70142e186b Merge bitcoin-core/leveldb-subtree#55: build: Require C++17 d123cf5a83 Effectively, this change 31361bf339 Allow different C/C++ standards when this is used as a subproject. 0711e6d082 Fix Clang `-Wconditional-uninitialized` warning 85665f9547 refactor: Delete unused `ScopedHandle:operator=(ScopedHandle&&)` fd8f69657e Revert "Increase maximum read-only mmap()s used from 1000 to 4096 on 64-bit systems" git-subtree-dir: src/leveldb git-subtree-split: a7f9bdc6114fe6eeb848fda2980fe61d86ff2045 --- CMakeLists.txt | 26 ++++++++++++++++---------- db/autocompact_test.cc | 18 ++++++++---------- db/db_test.cc | 16 +++++++++------- db/version_set.cc | 19 ++++++++----------- util/env_posix.cc | 4 ++-- util/env_windows.cc | 5 +---- util/hash.cc | 13 ++----------- 7 files changed, 46 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cfd4faa32528..78efde95de1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,19 +2,25 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. See the AUTHORS file for names of contributors. -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.22) # Keep the version below in sync with the one in db.h project(leveldb VERSION 1.22.0 LANGUAGES C CXX) -# This project can use C11, but will gracefully decay down to C89. -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED OFF) -set(CMAKE_C_EXTENSIONS OFF) - -# This project requires C++11. -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +# C standard can be overridden when this is used as a sub-project. +if(NOT CMAKE_C_STANDARD) + # This project can use C11, but will gracefully decay down to C89. + set(CMAKE_C_STANDARD 11) + set(CMAKE_C_STANDARD_REQUIRED OFF) + set(CMAKE_C_EXTENSIONS OFF) +endif(NOT CMAKE_C_STANDARD) + +# C++ standard can be overridden when this is used as a sub-project. +if(NOT CMAKE_CXX_STANDARD) + # This project requires C++17. + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) +endif(NOT CMAKE_CXX_STANDARD) if (WIN32) set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_WINDOWS) diff --git a/db/autocompact_test.cc b/db/autocompact_test.cc index e6c97a05a6b7..f6e714c65240 100644 --- a/db/autocompact_test.cc +++ b/db/autocompact_test.cc @@ -54,8 +54,8 @@ static const int kValueSize = 200 * 1024; static const int kTotalSize = 100 * 1024 * 1024; static const int kCount = kTotalSize / kValueSize; -// Read through the first n keys repeatedly and check that they get -// compacted (verified by checking the size of the key space). +// Read through the first n keys repeatedly and check that reads do NOT +// trigger compaction (seek compaction is disabled in this fork). void AutoCompactTest::DoReads(int n) { std::string value(kValueSize, 'x'); DBImpl* dbi = reinterpret_cast(db_); @@ -76,25 +76,23 @@ void AutoCompactTest::DoReads(int n) { const int64_t initial_size = Size(Key(0), Key(n)); const int64_t initial_other_size = Size(Key(n), Key(kCount)); - // Read until size drops significantly. + // Read repeatedly. The size of the read range must NOT shrink: with + // seek compaction disabled, reads never schedule a compaction. std::string limit_key = Key(n); - for (int read = 0; true; read++) { - ASSERT_LT(read, 100) << "Taking too long to compact"; + for (int read = 0; read < 100; read++) { Iterator* iter = db_->NewIterator(ReadOptions()); for (iter->SeekToFirst(); iter->Valid() && iter->key().ToString() < limit_key; iter->Next()) { // Drop data } delete iter; - // Wait a little bit to allow any triggered compactions to complete. - Env::Default()->SleepForMicroseconds(1000000); uint64_t size = Size(Key(0), Key(n)); fprintf(stderr, "iter %3d => %7.3f MB [other %7.3f MB]\n", read + 1, size / 1048576.0, Size(Key(n), Key(kCount)) / 1048576.0); - if (size <= initial_size / 10) { - break; - } } + // Give any background work a chance to run, even though none should. + Env::Default()->SleepForMicroseconds(1000000); + ASSERT_EQ(Size(Key(0), Key(n)), static_cast(initial_size)); // Verify that the size of the key space not touched by the reads // is pretty much unchanged. diff --git a/db/db_test.cc b/db/db_test.cc index 3c9f89428ff6..16e8ecb9a33f 100644 --- a/db/db_test.cc +++ b/db/db_test.cc @@ -735,15 +735,14 @@ TEST(DBTest, GetPicksCorrectFile) { } while (ChangeOptions()); } -TEST(DBTest, GetEncountersEmptyLevel) { +TEST(DBTest, GetDoesNotTriggerSeekCompaction) { do { // Arrange for the following to happen: // * sstable A in level 0 // * nothing in level 1 // * sstable B in level 2 - // Then do enough Get() calls to arrange for an automatic compaction - // of sstable A. A bug would cause the compaction to be marked as - // occurring at level 1 (instead of the correct level 0). + // Seek compaction is disabled in this fork, so repeated reads must + // not change the level layout. A manual compaction must still work. // Step 1: First place sstables in levels 0 and 2 int compaction_count = 0; @@ -761,14 +760,17 @@ TEST(DBTest, GetEncountersEmptyLevel) { ASSERT_EQ(NumTableFilesAtLevel(1), 0); ASSERT_EQ(NumTableFilesAtLevel(2), 1); - // Step 3: read a bunch of times + // Step 3: many read misses must not schedule any compaction. for (int i = 0; i < 1000; i++) { ASSERT_EQ("NOT_FOUND", Get("missing")); } - - // Step 4: Wait for compaction to finish DelayMilliseconds(1000); + ASSERT_EQ(NumTableFilesAtLevel(0), 1); + ASSERT_EQ(NumTableFilesAtLevel(1), 0); + ASSERT_EQ(NumTableFilesAtLevel(2), 1); + // Step 4: a manual compaction still moves the L0 file down. + dbfull()->TEST_CompactRange(0, nullptr, nullptr); ASSERT_EQ(NumTableFilesAtLevel(0), 0); } while (ChangeOptions()); } diff --git a/db/version_set.cc b/db/version_set.cc index cd07346ea8a0..2b56bbcd6986 100644 --- a/db/version_set.cc +++ b/db/version_set.cc @@ -400,16 +400,11 @@ Status Version::Get(const ReadOptions& options, const LookupKey& k, return state.found ? state.s : Status::NotFound(Slice()); } -bool Version::UpdateStats(const GetStats& stats) { - FileMetaData* f = stats.seek_file; - if (f != nullptr) { - f->allowed_seeks--; - if (f->allowed_seeks <= 0 && file_to_compact_ == nullptr) { - file_to_compact_ = f; - file_to_compact_level_ = stats.seek_file_level; - return true; - } - } +bool Version::UpdateStats(const GetStats& /*stats*/) { + // Disable automatic compactions triggered by read seek counters. + // The heuristic was tuned for expensive random seeks and can create + // severe write amplification on large random-key databases. + // Size and manual compactions still run. return false; } @@ -661,6 +656,8 @@ class VersionSet::Builder { // same as the compaction of 40KB of data. We are a little // conservative and allow approximately one seek for every 16KB // of data before triggering a compaction. + // + // Note: seek compactions are disabled. See Version::UpdateStats. f->allowed_seeks = static_cast((f->file_size / 16384U)); if (f->allowed_seeks < 100) f->allowed_seeks = 100; @@ -994,7 +991,7 @@ bool VersionSet::ReuseManifest(const std::string& dscname, } FileType manifest_type; uint64_t manifest_number; - uint64_t manifest_size; + uint64_t manifest_size = 0; if (!ParseFileName(dscbase, &manifest_number, &manifest_type) || manifest_type != kDescriptorFile || !env_->GetFileSize(dscname, &manifest_size).ok() || diff --git a/util/env_posix.cc b/util/env_posix.cc index 8a33534ed5dc..00bfd5986dba 100644 --- a/util/env_posix.cc +++ b/util/env_posix.cc @@ -42,8 +42,8 @@ namespace { // Set by EnvPosixTestHelper::SetReadOnlyMMapLimit() and MaxOpenFiles(). int g_open_read_only_file_limit = -1; -// Up to 4096 mmap regions for 64-bit binaries; none for 32-bit. -constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 4096 : 0; +// Up to 1000 mmap regions for 64-bit binaries; none for 32-bit. +constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0; // Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit(). int g_mmap_limit = kDefaultMmapLimit; diff --git a/util/env_windows.cc b/util/env_windows.cc index 8897e2aa26b9..6ef27e456ddf 100644 --- a/util/env_windows.cc +++ b/util/env_windows.cc @@ -81,10 +81,7 @@ class ScopedHandle { ScopedHandle& operator=(const ScopedHandle&) = delete; - ScopedHandle& operator=(ScopedHandle&& rhs) noexcept { - if (this != &rhs) handle_ = rhs.Release(); - return *this; - } + ScopedHandle& operator=(ScopedHandle&& rhs) = delete; bool Close() { if (!is_valid()) { diff --git a/util/hash.cc b/util/hash.cc index 5432b6180dde..6b2773066600 100644 --- a/util/hash.cc +++ b/util/hash.cc @@ -8,15 +8,6 @@ #include "util/coding.h" -// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through -// between switch labels. The real definition should be provided externally. -// This one is a fallback version for unsupported compilers. -#ifndef FALLTHROUGH_INTENDED -#define FALLTHROUGH_INTENDED \ - do { \ - } while (0) -#endif - namespace leveldb { uint32_t Hash(const char* data, size_t n, uint32_t seed) { @@ -39,10 +30,10 @@ uint32_t Hash(const char* data, size_t n, uint32_t seed) { switch (limit - data) { case 3: h += static_cast(data[2]) << 16; - FALLTHROUGH_INTENDED; + [[fallthrough]]; case 2: h += static_cast(data[1]) << 8; - FALLTHROUGH_INTENDED; + [[fallthrough]]; case 1: h += static_cast(data[0]); h *= m; From e958ffd5a743020ca323827ea8ef1e4bb5628d89 Mon Sep 17 00:00:00 2001 From: fanquake Date: Mon, 18 May 2026 13:13:47 +0100 Subject: [PATCH 02/21] build: remove -Wno-conditional-uninitialized from leveldb build --- cmake/leveldb.cmake | 3 --- 1 file changed, 3 deletions(-) diff --git a/cmake/leveldb.cmake b/cmake/leveldb.cmake index cff8c61f96ca..f69e386e660a 100644 --- a/cmake/leveldb.cmake +++ b/cmake/leveldb.cmake @@ -84,9 +84,6 @@ if(MSVC) _CRT_NONSTDC_NO_WARNINGS ) else() - try_append_cxx_flags("-Wconditional-uninitialized" TARGET nowarn_leveldb_interface SKIP_LINK - IF_CHECK_PASSED "-Wno-conditional-uninitialized" - ) try_append_cxx_flags("-Wcovered-switch-default" TARGET nowarn_leveldb_interface SKIP_LINK IF_CHECK_PASSED "-Wno-covered-switch-default" ) From 1d2687757cb6d0126393607bb88fb4f864ebd1b7 Mon Sep 17 00:00:00 2001 From: fanquake Date: Thu, 28 May 2026 09:35:12 +0100 Subject: [PATCH 03/21] build: remove FALLTHROUGH_INTENDED from leveldb.cmake No-longer needed after https://github.com/bitcoin-core/leveldb-subtree/pull/60. --- cmake/leveldb.cmake | 1 - 1 file changed, 1 deletion(-) diff --git a/cmake/leveldb.cmake b/cmake/leveldb.cmake index f69e386e660a..b9246cf81030 100644 --- a/cmake/leveldb.cmake +++ b/cmake/leveldb.cmake @@ -56,7 +56,6 @@ target_compile_definitions(leveldb HAVE_FDATASYNC=$ HAVE_FULLFSYNC=$ HAVE_O_CLOEXEC=$ - FALLTHROUGH_INTENDED=[[fallthrough]] $<$>:LEVELDB_PLATFORM_POSIX> $<$:LEVELDB_PLATFORM_WINDOWS> $<$:_UNICODE;UNICODE> From c0b0c09a2006778d3796b3ea7ddbbaba8f623741 Mon Sep 17 00:00:00 2001 From: merge-script Date: Thu, 28 May 2026 23:04:29 +0200 Subject: [PATCH 04/21] Merge bitcoin/bitcoin#34953: crypto: disable ASan instrumentation of SSE4 SHA256 for GCC (matching Clang) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fedeff7f201df0206eefb744ad125e44a63a3ea0 crypto: disable ASan instrumentation of SSE4 SHA256 for GCC (deadmanoz) Pull request description: Fix the runtime crash described in #34881. Upstream already disables ASan instrumentation for `sha256_sse4::Transform()` under Clang. This extends the same workaround to GCC by adding an `#elif` branch for `__GNUC__` / `__SANITIZE_ADDRESS__` that applies the same `no_sanitize("address")` attribute. Testing: - reproduced the crash before the fix with GCC 13, 14, and 15 on Haswell-class machines / guests without SHA-NI (including by forcing the SSE4 implementation on GitHub CI) - SEGV in debug builds regardless of optimization level (tested `-O0`, `-O1`, `-O2`, `-O3`) - verified that the GCC + ASan debug configurations that previously crashed pass with this change Issue #34881 has more details about the issue. Note: the original Clang code placed the `__attribute__` between the function declarator and the opening brace. GCC's [Attribute Syntax](https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html) documentation notes that this position in a function definition "may, in future, be permitted," so it is not currently supported. Placing the attribute at the start of the function definition is valid form for both GCC and Clang. ACKs for top commit: maflcko: lgtm ACK fedeff7f201df0206eefb744ad125e44a63a3ea0 🏒 sedited: tACK fedeff7f201df0206eefb744ad125e44a63a3ea0 Tree-SHA512: d8adda0df140b6c93d18f5ecd096b12012332bb640e678075e668122e596baddcb2182cbaeafe7908ada90b4b5cc776a59dcdfb488229ab6640058ccbbd7ea93 (cherry picked from commit 1ea532e590cdc16b86436a2bc4f92d74082307f9) --- src/crypto/sha256_sse4.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/crypto/sha256_sse4.cpp b/src/crypto/sha256_sse4.cpp index 2d37d124d1e8..4464ec92439e 100644 --- a/src/crypto/sha256_sse4.cpp +++ b/src/crypto/sha256_sse4.cpp @@ -12,18 +12,23 @@ namespace sha256_sse4 { -void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks) -#if defined(__clang__) - /* - clang is unable to compile this with -O0 and -fsanitize=address. - See upstream bug: https://github.com/llvm/llvm-project/issues/92182. - This also fails to compile with -O2, -fcf-protection & -fsanitize=address. - See https://github.com/bitcoin/bitcoin/issues/31913. - */ -#if __has_feature(address_sanitizer) +/* +Both Clang and GCC fail with ASan on this inline assembly: +- Clang: compile failure with -O0 or -O2 + -fcf-protection under ASan. + See https://github.com/llvm/llvm-project/issues/92182 + and https://github.com/bitcoin/bitcoin/issues/31913. +- GCC: runtime SEGV during SHA256AutoDetect()'s self-test under ASan, + regardless of optimization level. + See https://github.com/bitcoin/bitcoin/issues/34881. +*/ +#if defined(__SANITIZE_ADDRESS__) + __attribute__((no_sanitize("address"))) +#elif defined(__clang__) +#if __has_feature(address_sanitizer) // fallback can be removed once support for Clang 21 is dropped __attribute__((no_sanitize("address"))) #endif #endif +void Transform(uint32_t* s, const unsigned char* chunk, size_t blocks) { static const uint32_t K256 alignas(16) [] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, From 4dd34044f0687926d8c496b9481ab5a46714d9fc Mon Sep 17 00:00:00 2001 From: merge-script Date: Sat, 30 May 2026 10:25:25 +0100 Subject: [PATCH 05/21] Merge bitcoin/bitcoin#35131: guix, refactor: Minor script cleanups and improvements 53388773af78f8f1b8f3e705a5400cff6bee6de0 guix: Remove redundant ShellCheck `source` directives (Hennadii Stepanov) 62cf7bc53f238849cfa872972fc384dd25175fba guix, refactor: Add `BASE` argument to `*_for_host` functions (Hennadii Stepanov) 5d46429e322aa4840e0e7a714caa87672aee5faa guix, refactor: Move `distsrc_for_host()` to `prelude.bash` (Hennadii Stepanov) cab65ea9c6963bfffe73524b1485378b6305c398 guix, refactor: Move duplicated `profiledir_for_host()` to `prelude.bash` (Hennadii Stepanov) faa9d4345ff584eb2a7c09f4087c487c381e6b42 guix, refactor: Move duplicated `outdir_for_host()` to `prelude.bash` (Hennadii Stepanov) 6b59fd6b8cfd21bb008d3b71d4f1b24cf562854b guix, refactor: Remove `contains()` function (Hennadii Stepanov) d4c69a72246ed6a290c5aca812d5b896904b4382 guix, refactor: Remove unused `out_name()` function (Hennadii Stepanov) Pull request description: While working on https://github.com/bitcoin/bitcoin/pull/35098, I reviewed my notes regarding a few minor Guix script flaws and decided to address them here. This PR: 1. Removes unused code. 2. Reduces code duplication. 3. Improves consistency across function usage. 4. Minimizes ShellCheck directive usage. ACKs for top commit: fanquake: ACK 53388773af78f8f1b8f3e705a5400cff6bee6de0 - the move-only deduplication, and dead code removal seem fine. The other (refactoring) changes seem less well motived Tree-SHA512: d565e31ba49300f3001b04d3143721aced305f41bca6d04c33dc479d568efb2c06d91758619378199f174a40f1306c9418ae80d5478013a65ad96341bea122b1 (cherry picked from commit fbe628756cc417dd4b6ccd9d3a709ca8e2f6023c) --- contrib/guix/guix-attest | 8 ----- contrib/guix/guix-build | 56 ++++++------------------------- contrib/guix/guix-codesign | 47 +++++++------------------- contrib/guix/libexec/prelude.bash | 31 +++++++++++++++-- 4 files changed, 51 insertions(+), 91 deletions(-) diff --git a/contrib/guix/guix-attest b/contrib/guix/guix-attest index b0ef28dc3f92..3d70731cbdb4 100755 --- a/contrib/guix/guix-attest +++ b/contrib/guix/guix-attest @@ -139,14 +139,6 @@ fi ## Attest ## ############## -# Usage: out_name $outdir -# -# HOST: The output directory being attested -# -out_name() { - basename "$(dirname "$1")" -} - shasum_already_exists() { cat < "${VAR_BASE}/precious_dirs" @@ -305,22 +286,6 @@ for host in $HOSTS; do make -C "${PWD}/depends" -j"$JOBS" download-"$(host_to_commonname "$host")" ${V:+V=1} ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} done -# Usage: outdir_for_host HOST SUFFIX -# -# HOST: The current platform triple we're building for -# -outdir_for_host() { - echo "${OUTDIR_BASE}/${1}${2:+-${2}}" -} - -# Usage: profiledir_for_host HOST SUFFIX -# -# HOST: The current platform triple we're building for -# -profiledir_for_host() { - echo "${PROFILES_BASE}/${1}${2:+-${2}}" -} - ######### # BUILD # @@ -356,7 +321,6 @@ for host in $HOSTS; do # for the particular $HOST we're building for export HOST="$host" - # shellcheck disable=SC2030 cat << EOF INFO: Building ${VERSION:?not set} for platform triple ${HOST:?not set}: ...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set} @@ -364,9 +328,9 @@ INFO: Building ${VERSION:?not set} for platform triple ${HOST:?not set}: ...from worktree directory: '${PWD}' ...bind-mounted in container to: '/bitcoin' ...in build directory: '$(distsrc_for_host "$HOST")' - ...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")' + ...bind-mounted in container to: '$(distsrc_for_host "$HOST" "" /distsrc-base)' ...outputting in: '$(outdir_for_host "$HOST")' - ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")' + ...bind-mounted in container to: '$(outdir_for_host "$HOST" "" /outdir-base)' ADDITIONAL FLAGS (if set) ADDITIONAL_GUIX_COMMON_FLAGS: ${ADDITIONAL_GUIX_COMMON_FLAGS} ADDITIONAL_GUIX_ENVIRONMENT_FLAGS: ${ADDITIONAL_GUIX_ENVIRONMENT_FLAGS} @@ -440,7 +404,7 @@ EOF # Please read the README.md in the same directory as this file for # more information. # - # shellcheck disable=SC2086,SC2031 + # shellcheck disable=SC2086 time-machine shell --manifest="${PWD}/contrib/guix/manifest_build.scm" \ --container \ --writable-root \ @@ -468,8 +432,8 @@ EOF ${SOURCES_PATH:+SOURCES_PATH="$SOURCES_PATH"} \ ${BASE_CACHE:+BASE_CACHE="$BASE_CACHE"} \ ${SDK_PATH:+SDK_PATH="$SDK_PATH"} \ - DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \ - OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST")" \ + DISTSRC="$(distsrc_for_host "$HOST" "" /distsrc-base)" \ + OUTDIR="$(outdir_for_host "$HOST" "" /outdir-base)" \ DIST_ARCHIVE_BASE=/outdir-base/dist-archive \ bash -c "cd /bitcoin && bash contrib/guix/libexec/build.sh" ) diff --git a/contrib/guix/guix-codesign b/contrib/guix/guix-codesign index 8c8682ddd0af..8cc6993b824d 100755 --- a/contrib/guix/guix-codesign +++ b/contrib/guix/guix-codesign @@ -99,18 +99,10 @@ fi # Default to building for all supported HOSTs (overridable by environment) export HOSTS="${HOSTS:-x86_64-w64-mingw32 x86_64-apple-darwin arm64-apple-darwin}" -# Usage: distsrc_for_host HOST -# -# HOST: The current platform triple we're building for -# -distsrc_for_host() { - echo "${DISTSRC_BASE}/distsrc-${VERSION}-${1}-codesigned" -} - # Accumulate a list of build directories that already exist... hosts_distsrc_exists="" for host in $HOSTS; do - if [ -e "$(distsrc_for_host "$host")" ]; then + if [ -e "$(distsrc_for_host "$host" codesigned)" ]; then hosts_distsrc_exists+=" ${host}" fi done @@ -134,7 +126,7 @@ packages cache, the garbage collector roots for Guix environments, and the output directory. EOF for host in $hosts_distsrc_exists; do - echo " ${host} '$(distsrc_for_host "$host")'" + echo " ${host} '$(distsrc_for_host "$host" codesigned)'" done exit 1 else @@ -146,22 +138,18 @@ fi # Codesigning tarballs SHOULD exist ################ -# Usage: outdir_for_host HOST SUFFIX +# Usage: codesigning_tarball_for_host HOST [BASE] # # HOST: The current platform triple we're building for +# BASE: Optional. If provided, replaces ${OUTDIR_BASE} # -outdir_for_host() { - echo "${OUTDIR_BASE}/${1}${2:+-${2}}" -} - - codesigning_tarball_for_host() { case "$1" in *mingw*) - echo "$(outdir_for_host "$1")/${DISTNAME}-win64-codesigning.tar.gz" + echo "$(outdir_for_host "$1" "" "$2")/${DISTNAME}-win64-codesigning.tar.gz" ;; *darwin*) - echo "$(outdir_for_host "$1")/${DISTNAME}-${1}-codesigning.tar.gz" + echo "$(outdir_for_host "$1" "" "$2")/${DISTNAME}-${1}-codesigning.tar.gz" ;; *) exit 1 @@ -232,14 +220,6 @@ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(git -c log.showSignature=false log --f OUTDIR_BASE="${OUTDIR_BASE:-${VERSION_BASE}/output}" mkdir -p "$OUTDIR_BASE" -# Usage: profiledir_for_host HOST SUFFIX -# -# HOST: The current platform triple we're building for -# -profiledir_for_host() { - echo "${PROFILES_BASE}/${1}${2:+-${2}}" -} - ######### # BUILD # ######### @@ -274,16 +254,15 @@ for host in $HOSTS; do # for the particular $HOST we're building for export HOST="$host" - # shellcheck disable=SC2030 cat << EOF INFO: Codesigning ${VERSION:?not set} for platform triple ${HOST:?not set}: ...using reference timestamp: ${SOURCE_DATE_EPOCH:?not set} ...from worktree directory: '${PWD}' ...bind-mounted in container to: '/bitcoin' - ...in build directory: '$(distsrc_for_host "$HOST")' - ...bind-mounted in container to: '$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")' + ...in build directory: '$(distsrc_for_host "$HOST" codesigned)' + ...bind-mounted in container to: '$(distsrc_for_host "$HOST" codesigned /distsrc-base)' ...outputting in: '$(outdir_for_host "$HOST" codesigned)' - ...bind-mounted in container to: '$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)' + ...bind-mounted in container to: '$(outdir_for_host "$HOST" codesigned /outdir-base)' ...using detached signatures in: '${DETACHED_SIGS_REPO:?not set}' ...bind-mounted in container to: '/detached-sigs' EOF @@ -340,7 +319,7 @@ EOF # Please read the README.md in the same directory as this file for # more information. # - # shellcheck disable=SC2086,SC2031 + # shellcheck disable=SC2086 time-machine shell --manifest="${PWD}/contrib/guix/manifest_codesign.scm" \ --container \ --writable-root \ @@ -364,11 +343,11 @@ EOF JOBS="$JOBS" \ SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:?unable to determine value}" \ ${V:+V=1} \ - DISTSRC="$(DISTSRC_BASE=/distsrc-base && distsrc_for_host "$HOST")" \ - OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)" \ + DISTSRC="$(distsrc_for_host "$HOST" codesigned /distsrc-base)" \ + OUTDIR="$(outdir_for_host "$HOST" codesigned /outdir-base)" \ DIST_ARCHIVE_BASE=/outdir-base/dist-archive \ DETACHED_SIGS_REPO=/detached-sigs \ - CODESIGNING_TARBALL="$(OUTDIR_BASE=/outdir-base && codesigning_tarball_for_host "$HOST")" \ + CODESIGNING_TARBALL="$(codesigning_tarball_for_host "$HOST" /outdir-base)" \ bash -c "cd /bitcoin && bash contrib/guix/libexec/codesign.sh" ) diff --git a/contrib/guix/libexec/prelude.bash b/contrib/guix/libexec/prelude.bash index 166675e8bf09..238527678979 100644 --- a/contrib/guix/libexec/prelude.bash +++ b/contrib/guix/libexec/prelude.bash @@ -2,10 +2,7 @@ export LC_ALL=C set -e -o pipefail -# shellcheck source=contrib/shell/realpath.bash source contrib/shell/realpath.bash - -# shellcheck source=contrib/shell/git-utils.bash source contrib/shell/git-utils.bash ################ @@ -80,6 +77,34 @@ time-machine() { -- "$@" } +# Usage: distsrc_for_host HOST [SUFFIX] [BASE] +# +# HOST: The current platform triple we're building for +# SUFFIX: Optional. If provided, appended to the directory name as "-SUFFIX" +# BASE: Optional. If provided, replaces ${DISTSRC_BASE} +# +distsrc_for_host() { + echo "${3:-${DISTSRC_BASE}}/distsrc-${VERSION}-${1}${2:+-${2}}" +} + +# Usage: outdir_for_host HOST [SUFFIX] [BASE] +# +# HOST: The current platform triple we're building for +# SUFFIX: Optional. If provided, appended to the directory name as "-SUFFIX" +# BASE: Optional. If provided, replaces ${OUTDIR_BASE} +# +outdir_for_host() { + echo "${3:-${OUTDIR_BASE}}/${1}${2:+-${2}}" +} + +# Usage: profiledir_for_host HOST [SUFFIX] +# +# HOST: The current platform triple we're building for +# SUFFIX: Optional. If provided, appended to the directory name as "-SUFFIX" +# +profiledir_for_host() { + echo "${PROFILES_BASE}/${1}${2:+-${2}}" +} ################ # Set common variables From 97e3e53c4d2769238c85bf8e277f4d1db40c7570 Mon Sep 17 00:00:00 2001 From: merge-script Date: Mon, 1 Jun 2026 21:47:38 +0200 Subject: [PATCH 06/21] Merge bitcoin/bitcoin#35312: kernel: assert invalid buffer preconditions in `btck_*_create` functions 570a6276400401f56ac5bb3a81c43e9553a3ee8a kernel: assert invalid buffer preconditions in `btck_*_create` functions (stringintech) Pull request description: The kernel API appears to use `nullptr` returns to report failures that callers may reasonably want to recover from: malformed serialized input, object construction failures, chainstate/load failures, and similar runtime conditions. The raw create-function buffer checks seem to be a different case. A failure of `ptr == nullptr && len > 0` does not indicate malformed input data or a failure encountered while deserializing or constructing the requested object. Returning `nullptr` for these checks widens the recoverable error surface with cases that are better treated as programmer errors, similar to other asserted preconditions in this API such as invalid indices and impossible enum/flag states. This change switches those buffer argument checks from `nullptr` returns to assertions in `btck_transaction_create`, `btck_script_pubkey_create`, `btck_block_create`, `btck_block_header_create`, and `btck_chainstate_manager_options_create`. `btck_block_header_create` additionally asserts the pre-existing documented length contract (must be 80 bytes). These functions still return `nullptr` when the provided bytes cannot be parsed or when object creation fails during processing. I ended up looking at this while working on the `kernel-bindings-tests` spec/schema for `btck_script_pubkey_create`, where treating this path as a regular error did not seem like the right contract: https://github.com/stringintech/kernel-bindings-tests/pull/14#discussion_r3240859568. ACKs for top commit: stickies-v: ACK 570a6276400401f56ac5bb3a81c43e9553a3ee8a janb84: ACK 570a6276400401f56ac5bb3a81c43e9553a3ee8a w0xlt: ACK 570a6276400401f56ac5bb3a81c43e9553a3ee8a sedited: ACK 570a6276400401f56ac5bb3a81c43e9553a3ee8a Tree-SHA512: 064d834abe0c27245a144e5290bbeeb510daf9e4d50bb3a8e50bd8a0bf897b3dcf6ad5acfcabf1d8110da120e5e014ee3aea0241c0f181a21c6f3c14dc452ade (cherry picked from commit 19e45334bcf9c1169ae576c911655758bd8a80b0) --- src/kernel/bitcoinkernel.cpp | 20 +++++++------------- src/kernel/bitcoinkernel.h | 13 ++++++------- src/test/kernel/test_kernel.cpp | 4 ---- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index d8d97f197bd3..6d38c3b3dbca 100644 --- a/src/kernel/bitcoinkernel.cpp +++ b/src/kernel/bitcoinkernel.cpp @@ -507,9 +507,7 @@ struct btck_ConsensusParams: Handle {}; btck_Transaction* btck_transaction_create(const void* raw_transaction, size_t raw_transaction_len) { - if (raw_transaction == nullptr && raw_transaction_len != 0) { - return nullptr; - } + assert(raw_transaction != nullptr || raw_transaction_len == 0); try { SpanReader stream{std::span{reinterpret_cast(raw_transaction), raw_transaction_len}}; return btck_Transaction::create(std::make_shared(deserialize, TX_WITH_WITNESS, stream)); @@ -574,9 +572,7 @@ void btck_transaction_destroy(btck_Transaction* transaction) btck_ScriptPubkey* btck_script_pubkey_create(const void* script_pubkey, size_t script_pubkey_len) { - if (script_pubkey == nullptr && script_pubkey_len != 0) { - return nullptr; - } + assert(script_pubkey != nullptr || script_pubkey_len == 0); auto data = std::span{reinterpret_cast(script_pubkey), script_pubkey_len}; return btck_ScriptPubkey::create(data.begin(), data.end()); } @@ -966,7 +962,9 @@ btck_BlockValidationResult btck_block_validation_state_get_block_validation_resu btck_ChainstateManagerOptions* btck_chainstate_manager_options_create(const btck_Context* context, const char* data_dir, size_t data_dir_len, const char* blocks_dir, size_t blocks_dir_len) { - if (data_dir == nullptr || data_dir_len == 0 || blocks_dir == nullptr || blocks_dir_len == 0) { + assert(data_dir != nullptr || data_dir_len == 0); + assert(blocks_dir != nullptr || blocks_dir_len == 0); + if (data_dir_len == 0 || blocks_dir_len == 0) { LogError("Failed to create chainstate manager options: dir must be non-null and non-empty"); return nullptr; } @@ -1116,9 +1114,7 @@ int btck_chainstate_manager_import_blocks(btck_ChainstateManager* chainman, cons btck_Block* btck_block_create(const void* raw_block, size_t raw_block_length) { - if (raw_block == nullptr && raw_block_length != 0) { - return nullptr; - } + assert(raw_block != nullptr || raw_block_length == 0); auto block{std::make_shared()}; SpanReader stream{std::span{reinterpret_cast(raw_block), raw_block_length}}; @@ -1381,9 +1377,7 @@ int btck_chain_contains(const btck_Chain* chain, const btck_BlockTreeEntry* entr btck_BlockHeader* btck_block_header_create(const void* raw_block_header, size_t raw_block_header_len) { - if (raw_block_header == nullptr && raw_block_header_len != 0) { - return nullptr; - } + assert(raw_block_header != nullptr && raw_block_header_len == 80); auto header{std::make_unique()}; SpanReader stream{std::span{reinterpret_cast(raw_block_header), raw_block_header_len}}; diff --git a/src/kernel/bitcoinkernel.h b/src/kernel/bitcoinkernel.h index 6ce65a04378d..fc0eb9e82d3f 100644 --- a/src/kernel/bitcoinkernel.h +++ b/src/kernel/bitcoinkernel.h @@ -1151,12 +1151,11 @@ BITCOINKERNEL_API const btck_BlockTreeEntry* btck_block_tree_entry_get_ancestor( * * @param[in] context Non-null, the created options and through it the chainstate manager will * associate with this kernel context for the duration of their lifetimes. - * @param[in] data_directory Non-null, non-empty path string of the directory containing the - * chainstate data. If the directory does not exist yet, it will be - * created. - * @param[in] blocks_directory Non-null, non-empty path string of the directory containing the block - * data. If the directory does not exist yet, it will be created. - * @return The allocated chainstate manager options, or null on error. + * @param[in] data_directory Path string of the directory containing the chainstate data. If the directory + * does not exist yet, it will be created. + * @param[in] blocks_directory Path string of the directory containing the block data. If the directory + * does not exist yet, it will be created. + * @return The allocated chainstate manager options, or null on error (e.g. if a path is invalid). */ BITCOINKERNEL_API btck_ChainstateManagerOptions* BITCOINKERNEL_WARN_UNUSED_RESULT btck_chainstate_manager_options_create( const btck_Context* context, @@ -1877,7 +1876,7 @@ BITCOINKERNEL_API void btck_block_hash_destroy(btck_BlockHash* block_hash); * @return btck_BlockHeader, or null on error. */ BITCOINKERNEL_API btck_BlockHeader* BITCOINKERNEL_WARN_UNUSED_RESULT btck_block_header_create( - const void* raw_block_header, size_t raw_block_header_len); + const void* raw_block_header, size_t raw_block_header_len) BITCOINKERNEL_ARG_NONNULL(1); /** * @brief Copy a btck_BlockHeader. diff --git a/src/test/kernel/test_kernel.cpp b/src/test/kernel/test_kernel.cpp index e6d215dc5784..9050719c372c 100644 --- a/src/test/kernel/test_kernel.cpp +++ b/src/test/kernel/test_kernel.cpp @@ -683,10 +683,6 @@ BOOST_AUTO_TEST_CASE(btck_block_header_tests) BlockHeader header_1{hex_string_to_byte_vec("00c00020e7cb7b4de21d26d55bd384017b8bb9333ac3b2b55bed00000000000000000000d91b4484f801b99f03d36b9d26cfa83420b67f81da12d7e6c1e7f364e743c5ba9946e268b4dd011799c8533d")}; CheckHandle(header_0, header_1); - // Test error handling for invalid data - BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("00")}, std::runtime_error); - BOOST_CHECK_THROW(BlockHeader{hex_string_to_byte_vec("")}, std::runtime_error); - // Test all header field accessors using mainnet block 1 auto mainnet_block_1_header = hex_string_to_byte_vec("010000006fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000982051fd1e4ba744bbbe680e1fee14677ba1a3c3540bf7b1cdb606e857233e0e61bc6649ffff001d01e36299"); BlockHeader header{mainnet_block_1_header}; From 912901fd0937f477a2b39fef75be133f3fc29f40 Mon Sep 17 00:00:00 2001 From: merge-script Date: Mon, 1 Jun 2026 21:53:58 +0200 Subject: [PATCH 07/21] Merge bitcoin/bitcoin#33661: test: Add test on skip heights in CBlockIndex 131fa570b9551c1bfa6a4844b15dc8ee3e443201 test: Add test for BuildSkip() and skip heights (optout) Pull request description: The skip height values computed by the (internal) function `GetSkipHeight()`, and the `CBlockIndex::BuildSkip()` method are not tested directly, and the skip logic is not well documented. To improve test coverage, a new test is added, to verify `CBlockIndex::BuildSkip()` and the skip height bit-manipulation logic. Note: the original version contained a test for the complexity of the `GetAncestor()` algorithm, whose performance is greatly determined by the skip height logic. This test was out-scoped. (see: https://github.com/bitcoin/bitcoin/pull/33661#issuecomment-4486353485) The motivation is to document the skip value computation through a test. (The issued was noticed while reviewing #33515.) ACKs for top commit: l0rinc: ACK 131fa570b9551c1bfa6a4844b15dc8ee3e443201 sipa: ACK 131fa570b9551c1bfa6a4844b15dc8ee3e443201 Tree-SHA512: 468070946419d9d2891e43ed014b040348fc9d3b35dc21522487ac28e06b6d5d556ac02ebc4fa7761e202fc80f9e9ab7a7ec47c6e05ae55e33950ff5f8f3596f (cherry picked from commit 654a5223af532e965998bb1d6988be421fba186f) --- src/test/skiplist_tests.cpp | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src/test/skiplist_tests.cpp b/src/test/skiplist_tests.cpp index ce44b3e3003a..4fc08b0cadce 100644 --- a/src/test/skiplist_tests.cpp +++ b/src/test/skiplist_tests.cpp @@ -6,7 +6,9 @@ #include #include +#include #include +#include #include @@ -188,4 +190,101 @@ BOOST_AUTO_TEST_CASE(findearliestatleast_edge_test) BOOST_CHECK(ret2->nTimeMax >= 200 && ret2->nHeight == 4); } +BOOST_AUTO_TEST_CASE(build_skip_height_test) +{ + // clang-format off + const std::pair TEST_DATA[]{ + // EVEN values: the rightmost set bit is zeroed + // Various even values with at least 2 bits set + { 0b00010010 , + 0b00010000 }, + { 0b00100010 , + 0b00100000 }, + { 0b01000010 , + 0b01000000 }, + { 0b00010100 , + 0b00010000 }, + { 0b00011000 , + 0b00010000 }, + { 0b10101010 , + 0b10101000 }, + // ODD values: the 2nd and 3rd set bits are zeroed + // Various odd values with at least 4 bits set + { 0b10010011 , + 0b10000001 }, + { 0b10100011 , + 0b10000001 }, + { 0b11000011 , + 0b10000001 }, + { 0b10010101 , + 0b10000001 }, + { 0b10011001 , + 0b10000001 }, + { 0b10101011 , + 0b10100001 }, + // Some longer random values (even and odd) + { 0b0001011101101000 , + 0b0001011101100000 }, + { 0b0001011101101001 , + 0b0001011101000001 }, + { 0b0110101101011000 , + 0b0110101101010000 }, + { 0b0110101101011001 , + 0b0110101101000001 }, + // All values 1-20 + { 1, 0 }, + { 2, 0 }, + { 3, 1 }, + { 4, 0 }, + { 5, 1 }, + { 6, 4 }, + { 7, 1 }, + { 8, 0 }, + { 9, 1 }, + { 10, 8 }, + { 11, 1 }, + { 12, 8 }, + { 13, 1 }, + { 14, 12 }, + { 15, 9 }, + { 16, 0 }, + { 17, 1 }, + { 18, 16 }, + { 19, 1 }, + { 20, 16 }, + }; + // clang-format on + + // Test `CBlockIndex::BuildSkip()` and that the skip height conforms to expected logic. + // It tests that: + // - `pprev` field is set (to an earlier block index), + // - the skip is to the index as dictated by the `GetSkipHeight()` bit-manipulation logic, + // - `GetAncestor()` works as expected (indirectly). + + // Build a chain (up to the highest test input value) + const auto max_test_input{std::ranges::max_element(TEST_DATA, [](auto& a, auto& b) { return a.first < b.first; })}; + const int chain_size{max_test_input->first + 1}; + std::vector block_index(chain_size); + for (auto i{0}; i < chain_size; ++i) { + // pprev and nHeight are used by BuildSkip() + block_index[i].pprev = i == 0 ? nullptr : &block_index[i - 1]; + block_index[i].nHeight = i; + block_index[i].BuildSkip(); + BOOST_CHECK(block_index[i].pskip || i == 0); + } + + for (auto& [input, expected] : TEST_DATA) { + BOOST_REQUIRE_LT(input, chain_size); + BOOST_REQUIRE_GT(input, 0); + BOOST_REQUIRE_LT(expected, input); + BOOST_REQUIRE(block_index[input].pskip); + + const int skip_height{block_index[input].pskip->nHeight}; + BOOST_CHECK_EQUAL(skip_height, expected); + } + + // Special value: height 0 (genesis) has no skip + BOOST_CHECK(!block_index[0].pskip); +} + BOOST_AUTO_TEST_SUITE_END() From 6b5f5d0209b0dc10c3bc61e481ee9ffd53b2b171 Mon Sep 17 00:00:00 2001 From: merge-script Date: Wed, 3 Jun 2026 10:02:30 +0100 Subject: [PATCH 08/21] Merge bitcoin/bitcoin#34866: fuzz: target concurrent leveldb reads 8cb8653a2236ee4e2e683216b31919c83df43920 fuzz: target concurrent leveldb reads (Andrew Toth) 6609088fe67995a92d6dbbd47fa66af501bc9f3f fuzz: extract ConsumeDBParams helper (Andrew Toth) Pull request description: Inspired by https://github.com/bitcoin/bitcoin/pull/31132#issuecomment-4054461591. We currently do concurrent leveldb reads when accessing our indexes. 1. `txindex` - we call `FindTx()` from multiple RPC threads. 2. `blockfilterindex` - we call `LookupFilter/Header()` concurrently from `msghand` thread for p2p requests as well as RPC threads. 3. `coinstatsindex` - we call `LookUpStats()` from multiple RPC threads. 4. `txospenderindex` - we call `FindSpender()` from multiple RPC threads. We also read from our chainstate and blocks index while background compactions are writing. While OSS-Fuzz does cover leveldb (https://github.com/google/oss-fuzz/blob/master/projects/leveldb/fuzz_db.cc), it doesn't cover multi threaded access. Without a deterministic hypervisor this fuzz harness won't be deterministic, but we can at least run it with TSan to get a higher confidence that the synchronization code in leveldb is correct. Hopefully other reviewers find this useful. This harness creates a threadpool with 16 threads, and then creates an in-memory levelDB which it seeds with deterministically random values. It chooses a random set of keys to query. It first performs all queries on the db on a single thread to get a baseline, then synchronizes all threads on a latch so they hit the db at the same time. Each thread performs the same queries, and afterwards are all checked against the baseline. It uses a `DeterministicEnv` to capture background compaction work when seeding the db, which is also run immediately after the latch is released. This causes a race between compaction and reading, ensuring we exercise many thread synchronization code paths in leveldb. I ran both TSan and ASan/UBSan overnight with no issues. ACKs for top commit: fjahr: Code review ACK 8cb8653a2236ee4e2e683216b31919c83df43920 l0rinc: ACK 8cb8653a2236ee4e2e683216b31919c83df43920 Tree-SHA512: 2ca31a824715b92e258c84ecf0c762f43ee2a528e3a3192f94d8aaeddf6e99f820a0297ce9efcc95bc32c7ec74489f240a25bab856d724d768117a7d95a33974 (cherry picked from commit b28cf409a13b893787c4c95b1ba975ac5f5b6254) --- src/test/fuzz/dbwrapper.cpp | 162 ++++++++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 17 deletions(-) diff --git a/src/test/fuzz/dbwrapper.cpp b/src/test/fuzz/dbwrapper.cpp index 13fff136aeef..784b696d8166 100644 --- a/src/test/fuzz/dbwrapper.cpp +++ b/src/test/fuzz/dbwrapper.cpp @@ -4,12 +4,16 @@ #include #include +#include +#include #include #include #include #include #include #include +#include +#include #include #include @@ -18,12 +22,16 @@ #include #include #include +#include +#include +#include #include #include #include #include #include #include +#include #include namespace { @@ -57,29 +65,35 @@ class DeterministicEnv final : public leveldb::EnvWrapper void* arg; }; - std::deque m_queue; + Mutex m_mutex; + std::deque m_queue GUARDED_BY(m_mutex); public: explicit DeterministicEnv(leveldb::Env* base) : EnvWrapper(base) {} - void Schedule(WorkFunction function, void* arg) override + void Schedule(WorkFunction function, void* arg) override EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { + LOCK(m_mutex); m_queue.push_back({function, arg}); } /** Execute one pending background task. The task may schedule a * successor which is left pending for a later call. */ - bool RunOne() + bool RunOne() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { - if (m_queue.empty()) return false; - const Work work{m_queue.front()}; - m_queue.pop_front(); + Work work; + { + LOCK(m_mutex); + if (m_queue.empty()) return false; + work = m_queue.front(); + m_queue.pop_front(); + } work.function(work.arg); return true; } /** Execute pending background tasks until none remain. */ - void DrainWork() { while (RunOne()) {} } + void DrainWork() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex) { while (RunOne()) {} } }; constexpr size_t MAX_VALUE_LEN{4096}; @@ -152,6 +166,58 @@ void VerifyIterator(CDBWrapper& dbw, const Oracle& oracle, assert(oracle_it == oracle.end()); } +/** Maximum number of concurrent reader threads in dbwrapper_concurrent_reads. */ +constexpr size_t MAX_READ_WORKERS{16}; + +/** Build randomized DBParams from the fuzz input, shared by all targets. */ +DBParams ConsumeDBParams(FuzzedDataProvider& provider, leveldb::Env* testing_env, + bool obfuscate, DBOptions options = {}) +{ + return DBParams{ + .path = "dbwrapper_fuzz", + .cache_bytes = provider.ConsumeIntegralInRange(64 << 10, 1_MiB), + .obfuscate = obfuscate, + .options = options, + .testing_env = testing_env, + .max_file_size = provider.ConsumeBool() + ? DBWRAPPER_MAX_FILE_SIZE + : provider.ConsumeIntegralInRange(1_MiB, 4_MiB), + }; +} + +/** A single read-only operation run concurrently in dbwrapper_concurrent_reads. */ +enum class ReadOp { Read, Exists, IteratorSeek }; +using ReadQuery = std::tuple; +using Results = std::vector>; + +Results RunReadQueries(CDBWrapper& db, const std::vector& queries, FastRandomContext& rng) +{ + std::vector order(queries.size()); + std::iota(order.begin(), order.end(), size_t{0}); + std::shuffle(order.begin(), order.end(), rng); + + Results results(queries.size()); + for (const auto i : order) { + const auto& [op, key] = queries[i]; + std::string v; + switch (op) { + case ReadOp::Read: + if (db.Read(key, v)) results[i] = std::move(v); + break; + case ReadOp::Exists: + if (db.Exists(key)) results[i] = std::move(v); + break; + case ReadOp::IteratorSeek: { + const std::unique_ptr it{db.NewIterator()}; + it->Seek(key); + if (it->Valid() && it->GetValue(v)) results[i] = std::move(v); + break; + } + } + } + return results; +} + template void TestDbWrapper(FuzzedDataProvider& provider, leveldb::Env* testing_env, @@ -164,16 +230,7 @@ void TestDbWrapper(FuzzedDataProvider& provider, const bool obfuscate{provider.ConsumeBool()}; const auto make_db{[&](DBOptions options = {}) { - return std::make_unique(DBParams{ - .path = "dbwrapper_fuzz", - .cache_bytes = provider.ConsumeIntegralInRange(64 << 10, 1_MiB), - .obfuscate = obfuscate, - .options = options, - .testing_env = testing_env, - .max_file_size = provider.ConsumeBool() - ? DBWRAPPER_MAX_FILE_SIZE - : provider.ConsumeIntegralInRange(1_MiB, 4_MiB), - }); + return std::make_unique(ConsumeDBParams(provider, testing_env, obfuscate, options)); }}; std::unique_ptr dbw{make_db()}; @@ -323,3 +380,74 @@ FUZZ_TARGET(dbwrapper_threaded, .init = [] { static auto setup{MakeNoLogFileCont /*run_one=*/[] { return false; }, /*allow_force_compact=*/true); } + +FUZZ_TARGET(dbwrapper_concurrent_reads, .init = [] { static auto setup{MakeNoLogFileContext<>()}; }) +{ + SeedRandomStateForTest(SeedRand::ZEROS); + + FuzzedDataProvider provider{buffer.data(), buffer.size()}; + + const auto memenv{std::unique_ptr{leveldb::NewMemEnv(leveldb::Env::Default())}}; + DeterministicEnv det_env{memenv.get()}; + + CDBWrapper db{ConsumeDBParams(provider, &det_env, /*obfuscate=*/provider.ConsumeBool())}; + + // Seed the DB. Drain work after small batches so we don't deadlock on a + // scheduled compaction. + const size_t num_entries{provider.ConsumeIntegralInRange(100, 5'000)}; + std::vector keys; + keys.reserve(num_entries); + constexpr size_t SEED_BATCH_SIZE{400}; + for (size_t start{0}; start < num_entries; start += SEED_BATCH_SIZE) { + CDBBatch batch{db}; + const size_t end{std::min(start + SEED_BATCH_SIZE, num_entries)}; + for (size_t i{start}; i < end; ++i) { + const auto k{ConsumeKey(provider)}; + batch.Write(k, MakeValue(k, ConsumeValueSize(provider))); + keys.push_back(k); + } + det_env.DrainWork(); + db.WriteBatch(batch, /*fSync=*/true); + } + + while (provider.ConsumeBool() && det_env.RunOne()) {} + + // Build query list from seeded and random keys. + const size_t num_queries{provider.ConsumeIntegralInRange(1, 2'000)}; + std::vector queries; + queries.reserve(num_queries); + for (size_t i{0}; i < num_queries; ++i) { + const auto op{static_cast(provider.ConsumeIntegralInRange(0, 2))}; + const uint16_t key{provider.ConsumeBool() + ? keys[provider.ConsumeIntegralInRange(0, keys.size() - 1)] + : ConsumeKey(provider)}; + queries.emplace_back(op, key); + } + + // Baseline read on a single thread + FastRandomContext rng{ConsumeUInt256(provider)}; + const Results baseline{RunReadQueries(db, queries, rng)}; + + ThreadPool pool{"dbfuzz"}; + pool.Start(MAX_READ_WORKERS); + + // Workers + main thread synchronize on the latch so all reads start together. + std::latch start_latch{static_cast(MAX_READ_WORKERS + 1)}; + std::vector> tasks(MAX_READ_WORKERS); + std::generate(tasks.begin(), tasks.end(), [&] { + return [&, seed = rng.rand256()]() -> Results { + FastRandomContext thread_rng{seed}; + start_latch.arrive_and_wait(); + return RunReadQueries(db, queries, thread_rng); + }; + }); + auto futures{*Assert(pool.Submit(std::move(tasks)))}; + + // Release the workers and immediately run the queued compaction on this + // thread, so compaction races against the concurrent reads. + start_latch.arrive_and_wait(); + det_env.DrainWork(); + + for (auto& fut : futures) assert(fut.get() == baseline); + det_env.DrainWork(); +} From 203d70e10edbbbe5fc3a1a543b0317fd58040084 Mon Sep 17 00:00:00 2001 From: Ava Chow Date: Wed, 3 Jun 2026 13:53:24 -0700 Subject: [PATCH 09/21] Merge bitcoin/bitcoin#35335: Make deployment configuration available outside of regtest in unit tests 801e3bfe38e82ca8e99e6628e59dde1f1fe9a060 chainparams: add overloads for RegTest and SigNet with no options (Antoine Poinsot) 4995c00a9cc06b1123a4bbf3f4e8ea73769dc871 chainparams: make deployment configuration available on all test networks (Antoine Poinsot) df7ed5f3554e2737950117118e65ca91f6979bbe chainparams: encapsulate deployment configuration logic (Antoine Poinsot) Pull request description: It's sometimes useful to test a deployment on other networks than regtest. This may be e.g. because regtest lacks a property relevant for the test, or simply because the test aims to be portable while regtest is Bitcoin Core specific. This PR makes it possible to set the `-vbparams` and `-testactivationheight` options on any network in unit tests, and on any **test** network as a startup option. This is preparatory work for a BIP 54 implementation, but may be useful separately. ACKs for top commit: edilmedeiros: utACK 801e3bfe38e82ca8e99e6628e59dde1f1fe9a060 achow101: ACK 801e3bfe38e82ca8e99e6628e59dde1f1fe9a060 sedited: ACK 801e3bfe38e82ca8e99e6628e59dde1f1fe9a060 instagibbs: ACK 801e3bfe38e82ca8e99e6628e59dde1f1fe9a060 Tree-SHA512: 10649dc9bbc70a830bb0c4b1c965de5bf2e6be1a6c2832bdf11e9248dacb4a1f60421b410d0284213e88de6c54218252ae5de423b4c6e659d10bab1cbe7e7e87 (cherry picked from commit 082bb1a1047e9699605060aa93f17bb55110e062) --- src/chainparams.cpp | 81 ++++++++++++++++++++++------------ src/chainparamsbase.cpp | 4 +- src/init.cpp | 10 +++++ src/kernel/bitcoinkernel.cpp | 4 +- src/kernel/chainparams.cpp | 85 +++++++++++++++++++++--------------- src/kernel/chainparams.h | 46 +++++++++++++------ 6 files changed, 149 insertions(+), 81 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 32c3bff73482..cba8fd27894d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -23,29 +23,8 @@ using util::SplitString; -void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options) -{ - if (!args.GetArgs("-signetseednode").empty()) { - options.seeds.emplace(args.GetArgs("-signetseednode")); - } - if (!args.GetArgs("-signetchallenge").empty()) { - const auto signet_challenge = args.GetArgs("-signetchallenge"); - if (signet_challenge.size() != 1) { - throw std::runtime_error("-signetchallenge cannot be multiple values."); - } - const auto val{TryParseHex(signet_challenge[0])}; - if (!val) { - throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0])); - } - options.challenge.emplace(*val); - } -} - -void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options) +static void HandleDeploymentArgs(const ArgsManager& args, CChainParams::DeploymentOptions& options) { - if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value; - if (HasTestOption(args, "bip94")) options.enforce_bip94 = true; - for (const std::string& arg : args.GetArgs("-testactivationheight")) { const auto found{arg.find('@')}; if (found == std::string::npos) { @@ -107,6 +86,43 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } } +void ReadMainNetArgs(const ArgsManager& args, CChainParams::MainNetOptions& options) +{ + HandleDeploymentArgs(args, options.dep_opts); +} + +void ReadTestNetArgs(const ArgsManager& args, CChainParams::TestNetOptions& options) +{ + HandleDeploymentArgs(args, options.dep_opts); +} + +void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options) +{ + if (!args.GetArgs("-signetseednode").empty()) { + options.seeds.emplace(args.GetArgs("-signetseednode")); + } + if (!args.GetArgs("-signetchallenge").empty()) { + const auto signet_challenge = args.GetArgs("-signetchallenge"); + if (signet_challenge.size() != 1) { + throw std::runtime_error("-signetchallenge cannot be multiple values."); + } + const auto val{TryParseHex(signet_challenge[0])}; + if (!val) { + throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0])); + } + options.challenge.emplace(*val); + } + HandleDeploymentArgs(args, options.dep_opts); +} + +void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options) +{ + if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value; + if (HasTestOption(args, "bip94")) options.enforce_bip94 = true; + + HandleDeploymentArgs(args, options.dep_opts); +} + static std::unique_ptr globalChainParams; const CChainParams &Params() { @@ -117,12 +133,21 @@ const CChainParams &Params() { std::unique_ptr CreateChainParams(const ArgsManager& args, const ChainType chain) { switch (chain) { - case ChainType::MAIN: - return CChainParams::Main(); - case ChainType::TESTNET: - return CChainParams::TestNet(); - case ChainType::TESTNET4: - return CChainParams::TestNet4(); + case ChainType::MAIN: { + auto opts = CChainParams::MainNetOptions{}; + ReadMainNetArgs(args, opts); + return CChainParams::Main(opts); + } + case ChainType::TESTNET: { + auto opts = CChainParams::TestNetOptions{}; + ReadTestNetArgs(args, opts); + return CChainParams::TestNet(opts); + } + case ChainType::TESTNET4: { + auto opts = CChainParams::TestNetOptions{}; + ReadTestNetArgs(args, opts); + return CChainParams::TestNet4(opts); + } case ChainType::SIGNET: { auto opts = CChainParams::SigNetOptions{}; ReadSigNetArgs(args, opts); diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index d816d1af91c3..f62e455fac45 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -16,10 +16,10 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: " LIST_CHAIN_NAMES, ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " "This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (test-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-testnet", "Use the testnet3 chain. Equivalent to -chain=test. Support for testnet3 is deprecated and will be removed in an upcoming release. Consider moving to testnet4 now by using -testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-testnet4", "Use the testnet4 chain. Equivalent to -chain=testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (test-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS); diff --git a/src/init.cpp b/src/init.cpp index 694f24777c63..19cbc46c53ef 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -972,6 +972,16 @@ bool AppInitParameterInteraction(const ArgsManager& args) } } + // Prevent setting deployment parameters on mainnet. + if (chainparams.GetChainType() == ChainType::MAIN) { + if (args.IsArgSet("-testactivationheight")) { + return InitError(_("The -testactivationheight option may not be used on mainnet.")); + } + if (args.IsArgSet("-vbparams")) { + return InitError(_("The -vbparams option may not be used on mainnet.")); + } + } + // Also report errors from parsing before daemonization { kernel::Notifications notifications{}; diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp index 6d38c3b3dbca..4f0a7785a5a2 100644 --- a/src/kernel/bitcoinkernel.cpp +++ b/src/kernel/bitcoinkernel.cpp @@ -813,10 +813,10 @@ btck_ChainParameters* btck_chain_parameters_create(const btck_ChainType chain_ty return btck_ChainParameters::ref(const_cast(CChainParams::TestNet4().release())); } case btck_ChainType_SIGNET: { - return btck_ChainParameters::ref(const_cast(CChainParams::SigNet({}).release())); + return btck_ChainParameters::ref(const_cast(CChainParams::SigNet().release())); } case btck_ChainType_REGTEST: { - return btck_ChainParameters::ref(const_cast(CChainParams::RegTest({}).release())); + return btck_ChainParameters::ref(const_cast(CChainParams::RegTest().release())); } } assert(false); diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index b63ce1119e7a..2cb6b00665b5 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -72,12 +72,41 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } +void CChainParams::ApplyDeploymentOptions(const DeploymentOptions& opts) +{ + for (const auto& [dep, height] : opts.activation_heights) { + switch (dep) { + case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: + consensus.SegwitHeight = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: + consensus.BIP34Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: + consensus.BIP66Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: + consensus.BIP65Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CSV: + consensus.CSVHeight = int{height}; + break; + } + } + + for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { + consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; + consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; + consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; + } +} + /** * Main network on which people trade goods and services. */ class CMainParams : public CChainParams { public: - CMainParams() { + CMainParams(const MainNetOptions& opts) { m_chain_type = ChainType::MAIN; consensus.signet_blocks = false; consensus.signet_challenge.clear(); @@ -106,6 +135,8 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1815; // 90% consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016; + ApplyDeploymentOptions(opts.dep_opts); + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000001128750f82f4c366153a3a030"}; consensus.defaultAssumeValid = uint256{"00000000000000000000ccebd6d74d9194d8dcdc1d177c478e094bfad51ba5ac"}; // 938343 @@ -176,7 +207,7 @@ class CMainParams : public CChainParams { */ class CTestNetParams : public CChainParams { public: - CTestNetParams() { + CTestNetParams(const TestNetOptions& opts) { m_chain_type = ChainType::TESTNET; consensus.signet_blocks = false; consensus.signet_challenge.clear(); @@ -203,6 +234,8 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1512; // 75% consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016; + ApplyDeploymentOptions(opts.dep_opts); + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000017dde1c649f3708d14b6"}; consensus.defaultAssumeValid = uint256{"000000007a61e4230b28ac5cb6b5e5a0130de37ac1faf2f8987d2fa6505b67f4"}; // 4842348 @@ -263,7 +296,7 @@ class CTestNetParams : public CChainParams { */ class CTestNet4Params : public CChainParams { public: - CTestNet4Params() { + CTestNet4Params(const TestNetOptions& opts) { m_chain_type = ChainType::TESTNET4; consensus.signet_blocks = false; consensus.signet_challenge.clear(); @@ -289,6 +322,8 @@ class CTestNet4Params : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1512; // 75% consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016; + ApplyDeploymentOptions(opts.dep_opts); + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000009a0fe15d0177d086304"}; consensus.defaultAssumeValid = uint256{"0000000002368b1e4ee27e2e85676ae6f9f9e69579b29093e9a82c170bf7cf8a"}; // 123613 @@ -418,6 +453,8 @@ class SigNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1815; // 90% consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016; + ApplyDeploymentOptions(options.dep_opts); + // message start is defined as the first 4 bytes of the sha256d of the block script HashWriter h{}; h << consensus.signet_challenge; @@ -498,31 +535,7 @@ class CRegTestParams : public CChainParams m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; - for (const auto& [dep, height] : opts.activation_heights) { - switch (dep) { - case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: - consensus.SegwitHeight = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: - consensus.BIP34Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: - consensus.BIP66Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: - consensus.BIP65Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_CSV: - consensus.CSVHeight = int{height}; - break; - } - } - - for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { - consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; - consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; - consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; - } + ApplyDeploymentOptions(opts.dep_opts); genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); @@ -570,19 +583,19 @@ std::unique_ptr CChainParams::RegTest(const RegTestOptions& return std::make_unique(options); } -std::unique_ptr CChainParams::Main() +std::unique_ptr CChainParams::Main(const MainNetOptions& options) { - return std::make_unique(); + return std::make_unique(options); } -std::unique_ptr CChainParams::TestNet() +std::unique_ptr CChainParams::TestNet(const TestNetOptions& options) { - return std::make_unique(); + return std::make_unique(options); } -std::unique_ptr CChainParams::TestNet4() +std::unique_ptr CChainParams::TestNet4(const TestNetOptions& options) { - return std::make_unique(); + return std::make_unique(options); } std::optional GetNetworkForMagic(const MessageStartChars& message) @@ -590,8 +603,8 @@ std::optional GetNetworkForMagic(const MessageStartChars& message) const auto mainnet_msg = CChainParams::Main()->MessageStart(); const auto testnet_msg = CChainParams::TestNet()->MessageStart(); const auto testnet4_msg = CChainParams::TestNet4()->MessageStart(); - const auto regtest_msg = CChainParams::RegTest({})->MessageStart(); - const auto signet_msg = CChainParams::SigNet({})->MessageStart(); + const auto regtest_msg = CChainParams::RegTest()->MessageStart(); + const auto signet_msg = CChainParams::SigNet()->MessageStart(); if (std::ranges::equal(message, mainnet_msg)) { return ChainType::MAIN; diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index e3d08d8217ed..197946c1a6cd 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -88,14 +88,6 @@ class CChainParams const ChainTxData& TxData() const { return chainTxData; } - /** - * SigNetOptions holds configurations for creating a signet CChainParams. - */ - struct SigNetOptions { - std::optional> challenge{}; - std::optional> seeds{}; - }; - /** * VersionBitsParameters holds activation parameters */ @@ -105,21 +97,47 @@ class CChainParams int min_activation_height; }; + struct DeploymentOptions { + std::unordered_map version_bits_parameters{}; + std::unordered_map activation_heights{}; + }; + + /** + * SigNetOptions holds configurations for creating a signet CChainParams. + */ + struct SigNetOptions { + DeploymentOptions dep_opts{}; + std::optional> challenge{}; + std::optional> seeds{}; + }; + /** * RegTestOptions holds configurations for creating a regtest CChainParams. */ struct RegTestOptions { - std::unordered_map version_bits_parameters{}; - std::unordered_map activation_heights{}; + DeploymentOptions dep_opts{}; bool fastprune{false}; bool enforce_bip94{false}; }; + struct MainNetOptions { + DeploymentOptions dep_opts{}; + }; + + struct TestNetOptions { + DeploymentOptions dep_opts{}; + }; + static std::unique_ptr RegTest(const RegTestOptions& options); + static std::unique_ptr RegTest() { const RegTestOptions opts{}; return RegTest(opts); } static std::unique_ptr SigNet(const SigNetOptions& options); - static std::unique_ptr Main(); - static std::unique_ptr TestNet(); - static std::unique_ptr TestNet4(); + static std::unique_ptr SigNet() { const SigNetOptions opts{}; return SigNet(opts); } + static std::unique_ptr Main(const MainNetOptions& options); + static std::unique_ptr Main() { const MainNetOptions opts{}; return Main(opts); } + static std::unique_ptr TestNet(const TestNetOptions& options); + static std::unique_ptr TestNet() { const TestNetOptions opts{}; return TestNet(opts); } + static std::unique_ptr TestNet4(const TestNetOptions& options); + static std::unique_ptr TestNet4() { const TestNetOptions opts{}; return TestNet4(opts); } protected: CChainParams() = default; @@ -140,6 +158,8 @@ class CChainParams bool m_is_mockable_chain; ChainTxData chainTxData; HeadersSyncParams m_headers_sync_params; + + void ApplyDeploymentOptions(const DeploymentOptions& opts); }; std::optional GetNetworkForMagic(const MessageStartChars& pchMessageStart); From e92fd53b75d83ee9d601085025badd58c1a50d22 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 28 May 2026 07:26:02 -0400 Subject: [PATCH 10/21] Merge bitcoin/bitcoin#34644: mining: add submitBlock to IPC Mining interface 3962138cc036578926d901e6ff4ff807243bbb4e test: add IPC submitBlock functional test (woltx) 5b60f69e40ac1ec2b2f4276e2529914813cd22e5 mining: add submitBlock IPC method to Mining interface (woltx) 813b4a80d7f8c2d7cc6cf7b09fb74039b7e28b55 refactor: introduce SubmitBlock helper (w0xlt) Pull request description: This PR adds a `submitBlock` method to the IPC Mining interface, equivalent to the `submitblock` RPC. It accepts a serialized block over IPC, validates/processes it via the normal block-processing path. The method uses the same result shape as `checkBlock`: `bool` + `reason/debug out-params`. It reports duplicate, inconclusive, and invalid-block rejection details, and initializes reason/debug on every call. Closes #34626 ACKs for top commit: Sjors: ACK 3962138cc036578926d901e6ff4ff807243bbb4e optout21: reACK 3962138cc036578926d901e6ff4ff807243bbb4e ryanofsky: Code review ACK 3962138cc036578926d901e6ff4ff807243bbb4e. Just rebased since and made suggested changes since last review. Tree-SHA512: 705cbb89972a80b6ff0ab75a78f686983d6077c97f1758795efe5b8968f01065ebef664ac850eae2bc86af8964efa2a68e8dfc677209c312856650f9387ed006 --- src/interfaces/mining.h | 27 ++++++++++++++++-- src/ipc/capnp/mining.capnp | 1 + src/node/interfaces.cpp | 15 +++++++++- src/node/miner.cpp | 57 ++++++++++++++++++++++++++++++++++++++ src/node/miner.h | 4 +++ src/test/miner_tests.cpp | 18 ++++++++---- 6 files changed, 114 insertions(+), 8 deletions(-) diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index a95bc220185c..ff4f87109f92 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -60,8 +60,10 @@ class BlockTemplate * @param[in] nonce nonce block header field * @param[in] coinbase complete coinbase transaction (including witness) * - * @note unlike the submitblock RPC, this method does NOT add the - * coinbase witness automatically. + * @note Unlike the submitblock RPC, this method does not call + * UpdateUncommittedBlockStructures to add a missing coinbase witness + * reserved value. Callers must provide a complete coinbase transaction, + * including the witness when a witness commitment is present. * * @note for heights <= 16, the BIP34 height push in getCoinbaseTx().script_sig_prefix * is only one byte long, so the coinbase scriptSig needs at least @@ -157,6 +159,27 @@ class Mining */ virtual bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) = 0; + /** + * Process a fully assembled block. + * + * Similar to the submitblock RPC. Accepts a complete block, validates + * it, and if accepted as new, processes it into chainstate. Accepted + * blocks may then be announced to peers through normal validation signals. + * + * @param[in] block the complete block to submit + * @param[out] reason failure reason (BIP22) + * @param[out] debug more detailed rejection reason + * @returns true if the block was accepted as a new block. Returns + * false and sets reason if the block is a duplicate or + * the validation result is inconclusive. + * + * @note Unlike the submitblock RPC, this method does not call + * UpdateUncommittedBlockStructures to add a missing coinbase witness + * reserved value. Callers must submit a fully formed block, including + * the coinbase witness when a witness commitment is present. + */ + virtual bool submitBlock(const CBlock& block, std::string& reason, std::string& debug) = 0; + //! Get internal node context. Useful for RPC and testing, //! but not accessible across processes. virtual const node::NodeContext* context() { return nullptr; } diff --git a/src/ipc/capnp/mining.capnp b/src/ipc/capnp/mining.capnp index 64cad4d49f28..a6dd8d71f315 100644 --- a/src/ipc/capnp/mining.capnp +++ b/src/ipc/capnp/mining.capnp @@ -25,6 +25,7 @@ interface Mining $Proxy.wrap("interfaces::Mining") { createNewBlock @4 (context :Proxy.Context, options: BlockCreateOptions, cooldown: Bool = true) -> (result: BlockTemplate); checkBlock @5 (context :Proxy.Context, block: Data, options: BlockCheckOptions) -> (reason: Text, debug: Text, result: Bool); interrupt @6 () -> (); + submitBlock @7 (context :Proxy.Context, block: Data) -> (reason: Text, debug: Text, result: Bool); } interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") { diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 50b70fafcb0f..611db6bb2fdd 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -798,7 +798,9 @@ class BlockTemplateImpl : public BlockTemplate bool submitSolution(uint32_t version, uint32_t timestamp, uint32_t nonce, CTransactionRef coinbase) override { AddMerkleRootAndCoinbase(m_block_template->block, std::move(coinbase), version, timestamp, nonce); - return ProcessNewBlock(chainman(), std::make_shared(m_block_template->block), /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/nullptr); + std::string reason; + std::string debug; + return SubmitBlock(chainman(), std::make_shared(m_block_template->block), /*new_block=*/nullptr, reason, debug); } std::unique_ptr waitNext(BlockWaitOptions options) override @@ -899,6 +901,17 @@ class MinerImpl : public Mining return state.IsValid(); } + bool submitBlock(const CBlock& block_in, std::string& reason, std::string& debug) override + { + auto block = std::make_shared(block_in); + bool new_block; + const bool accepted = SubmitBlock(chainman(), block, &new_block, reason, debug); + // ProcessNewBlock() can accept and store a block before it is checked + // for validity. Treat duplicates as errors for mining clients, and only + // return success when validation completed without setting a reason. + return accepted && new_block && reason.empty(); + } + const NodeContext* context() override { return &m_node; } ChainstateManager& chainman() { return *Assert(m_node.chainman); } KernelNotifications& notifications() { return *Assert(m_node.notifications); } diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 5069ac12f9de..882378d78a2e 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -356,6 +357,62 @@ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t block.fChecked = false; } +namespace { +class SubmitBlockStateCatcher final : public CValidationInterface +{ +public: + uint256 m_hash; + bool m_found{false}; + BlockValidationState m_state; + + explicit SubmitBlockStateCatcher(const uint256& hash) : m_hash{hash} {} + +protected: + void BlockChecked(const std::shared_ptr& block, const BlockValidationState& state) override + { + if (block->GetHash() != m_hash) return; + // ProcessNewBlock emits BlockChecked synchronously while holding cs_main, + // so SubmitBlock can read these fields after ProcessNewBlock returns + // without extra synchronization. + m_found = true; + m_state = state; + } +}; +} // namespace + +bool SubmitBlock(ChainstateManager& chainman, const std::shared_ptr& block, bool* new_block, std::string& reason, std::string& debug) +{ + reason.clear(); + debug.clear(); + + // This follows the submitblock RPC's validation-state capture pattern, but + // is intentionally kept separate from the RPC implementation. The RPC entry + // point decodes hex, formats BIP22/JSONRPC results, and calls + // UpdateUncommittedBlockStructures() for legacy witness handling. IPC + // callers submit already-formed blocks and need bool + reason/debug + // results, while submitSolution() preserves its duplicate-as-success + // behavior. + auto sc = std::make_shared(block->GetHash()); + CHECK_NONFATAL(chainman.m_options.signals)->RegisterSharedValidationInterface(sc); + bool accepted = ProcessNewBlock(chainman, block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/new_block); + CHECK_NONFATAL(chainman.m_options.signals)->UnregisterSharedValidationInterface(sc); + + if (new_block && !*new_block && accepted) { + reason = "duplicate"; + } else if (!sc->m_found) { + // A block can be accepted and stored without being connected, for + // example if it does not have more work than the current tip. In that + // case no BlockChecked callback is emitted, so the validation result is + // inconclusive. Mining::submitBlock treats this as an error for mining + // clients, but it does not mean the block is invalid. + reason = "inconclusive"; + } else if (!sc->m_state.IsValid()) { + reason = sc->m_state.GetRejectReason(); + debug = sc->m_state.GetDebugMessage(); + } + return accepted; +} + void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait) { LOCK(kernel_notifications.m_tip_block_mutex); diff --git a/src/node/miner.h b/src/node/miner.h index 14780833c02b..af3273073295 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -18,6 +18,7 @@ #include #include #include +#include #include class CBlockIndex; @@ -129,6 +130,9 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman); /* Compute the block's merkle root, insert or replace the coinbase transaction and the merkle root into the block */ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce); +//! Submit a block and capture the validation state via the BlockChecked callback. +//! Returns whether ProcessNewBlock accepted the block. +bool SubmitBlock(ChainstateManager& chainman, const std::shared_ptr& block, bool* new_block, std::string& reason, std::string& debug); /* Interrupt a blocking call. */ void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 724be18d1c39..31d90541b9a0 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -857,12 +857,20 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) block.hashMerkleRoot = BlockMerkleRoot(block); block.nNonce = bi.nonce; } - std::shared_ptr shared_pblock = std::make_shared(block); - // Alternate calls between Chainman's ProcessNewBlock and submitSolution - // via the Mining interface. The former is used by net_processing as well - // as the submitblock RPC. + // Alternate calls between submitBlock and submitSolution via the + // Mining interface. if (current_height % 2 == 0) { - BOOST_REQUIRE(ProcessNewBlock(*Assert(m_node.chainman), shared_pblock, /*force_processing=*/true, /*min_pow_checked=*/true, nullptr)); + std::string reason{"stale reason"}; + std::string debug{"stale debug"}; + BOOST_REQUIRE(mining->submitBlock(block, reason, debug)); + BOOST_REQUIRE_EQUAL(reason, ""); + BOOST_REQUIRE_EQUAL(debug, ""); + + reason = "stale reason"; + debug = "stale debug"; + BOOST_REQUIRE(!mining->submitBlock(block, reason, debug)); + BOOST_REQUIRE_EQUAL(reason, "duplicate"); + BOOST_REQUIRE_EQUAL(debug, ""); } else { BOOST_REQUIRE(block_template->submitSolution(block.nVersion, block.nTime, block.nNonce, MakeTransactionRef(txCoinbase))); } From d4d4a68b37bd0681998774334926da6a81c573ab Mon Sep 17 00:00:00 2001 From: merge-script Date: Thu, 28 May 2026 16:12:55 +0100 Subject: [PATCH 11/21] Merge bitcoin/bitcoin#35400: doc: Remove good_first_issue.yml, Reword "Getting started" section fa51f37f1800d3886172fbc4c418685530011e2d doc: Reword the Getting-Started section (MarcoFalke) fab5733f5d6c025b524cd7cd7af7b505f83e3624 doc: Remove good_first_issue.yml (MarcoFalke) Pull request description: Fixes https://github.com/bitcoin/bitcoin/issues/35399 IIUC, the good-first-issue label and template were meant to make it easier for completely new contributors to get started with something simple. However, I don't think the label and issue template are applicable anymore: * There are currently no issues with this label, and directing people toward an empty list seems pointless. * Historically the issue and label has been used rarely. 2026: once, 2025 twice, 2024 thrice. Source: https://github.com/bitcoin/bitcoin/issues?q=state%3Aclosed%20is%3Aissue%20label%3A%22good%20first%20issue%22 * The template has been mis-used, according to https://github.com/bitcoin/bitcoin/issues/35399 Fix all issues by removing it, since it is clear that it is no longer actively used, nor applicable and possibly a net-negative overall. Of course, regular devs are still free to open issues of this kind as a normal issue, if they wish. However, having the template for this in this repo and tracking it via a label doesn't seem useful. Since removing the template and label requires rewriting the "Getting Started" section in `CONTRIBUTING.md`, I went ahead and also removed the mention of `Up for grabs` as good things for newcomers to work on. I don't recall the last new contributor that picked something up successfully. Also, `Up for grabs` is usually stuff that people lost interest in, or is no longer relevant. Instead I've added a sentence to encourage new contributors to help with critical and broad review, which will naturally guide them to good first follow-up issues to work on. Meta: I know this topic can be subjective and offer bike-shed potential, but I am happy to iterate a bit on this for a few days. ACKs for top commit: stickies-v: ACK fa51f37f1800d3886172fbc4c418685530011e2d danielabrozzoni: ACK fa51f37f1800d3886172fbc4c418685530011e2d sedited: ACK fa51f37f1800d3886172fbc4c418685530011e2d darosior: ACK fa51f37f1800d3886172fbc4c418685530011e2d furszy: ACK fa51f37f1800d3886172fbc4c418685530011e2d winterrdog: ACK fa51f37f1800d3886172fbc4c418685530011e2d Tree-SHA512: 9e6d7fe86262bee2df1e0af33ecfb5f77036da2d5d1832bb6afb08f4107d9313eec06d8b769966bf8ecaf8a4c574da5ff99509cc4b7fd9c53ea86788da29721c --- .github/ISSUE_TEMPLATE/good_first_issue.yml | 44 --------------------- CONTRIBUTING.md | 30 +++----------- 2 files changed, 5 insertions(+), 69 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/good_first_issue.yml diff --git a/.github/ISSUE_TEMPLATE/good_first_issue.yml b/.github/ISSUE_TEMPLATE/good_first_issue.yml deleted file mode 100644 index 44c1994763b5..000000000000 --- a/.github/ISSUE_TEMPLATE/good_first_issue.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: Good First Issue -description: (Regular devs only) Suggest a new good first issue -labels: [good first issue] -body: - - type: markdown - attributes: - value: | - Please add the label "good first issue" manually before or after opening - - A good first issue is an uncontroversial issue, that has a relatively unique and obvious solution - - Motivate the issue and explain the solution briefly - - type: textarea - id: motivation - attributes: - label: Motivation - description: Motivate the issue - validations: - required: true - - type: textarea - id: solution - attributes: - label: Possible solution - description: Describe a possible solution - validations: - required: false - - type: textarea - id: useful-skills - attributes: - label: Useful Skills - description: For example, “`std::thread`”, “async design” or “basic understanding of Bitcoin mining and the Bitcoin Core RPC interface”. - value: | - * Compiling Bitcoin Core from source - * Running the C++ unit tests and the Python functional tests - * ... - - type: textarea - attributes: - label: Guidance for new contributors - description: Please leave this to automatically add the footer for new contributors - value: | - Want to work on this issue? - - For guidance on contributing, please read [CONTRIBUTING.md](https://github.com/bitcoin/bitcoin/blob/master/CONTRIBUTING.md) before opening your pull request. - diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c8892cc21891..14e9e91d907f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,38 +19,18 @@ Getting Started New contributors are very welcome and needed. -Reviewing and testing is highly valued and the most effective way you can contribute -as a new contributor. It also will teach you much more about the code and -process than opening pull requests. Please refer to the [peer review](#peer-review) -section below. +In-depth reviewing and testing are the bottleneck of the project, and are the +most effective way anyone can start to contribute. It will teach you much more +about the code and process than opening pull requests, and may help you uncover +related issues and follow-ups to contribute code for. Please refer to the [peer +review](#peer-review) section below. Before you start contributing, familiarize yourself with the Bitcoin Core build system and tests. Refer to the documentation in the repository on how to build Bitcoin Core and how to run the unit tests, functional tests, and fuzz tests. -There are many open issues of varying difficulty waiting to be fixed. -If you're looking for somewhere to start contributing, check out the -[good first issue](https://github.com/bitcoin/bitcoin/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) -list or changes that are -[up for grabs](https://github.com/bitcoin/bitcoin/issues?utf8=%E2%9C%93&q=label%3A%22Up+for+grabs%22). -Some of them might no longer be applicable. So if you are interested, but -unsure, you might want to leave a comment on the issue first. - You may also participate in the [Bitcoin Core PR Review Club](https://bitcoincore.reviews/). -### Good First Issue Label - -The purpose of the `good first issue` label is to highlight which issues are -suitable for a new contributor without a deep understanding of the codebase. - -However, good first issues can be solved by anyone. If they remain unsolved -for a longer time, a frequent contributor might address them. - -You do not need to request permission to start working on an issue. However, -you are encouraged to leave a comment if you are planning to work on it. This -will help other contributors monitor which issues are actively being addressed -and is also an effective way to request assistance if and when you need it. - Communication Channels ---------------------- From af01ea0356712f7969a8d31d3acc5c4488f18cb7 Mon Sep 17 00:00:00 2001 From: merge-script Date: Thu, 28 May 2026 17:10:21 +0100 Subject: [PATCH 12/21] Merge bitcoin/bitcoin#35378: ci: switch to warp runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4bdd46ace37f02da062a53a2943caeddca4ed8f9 ci: switch runners from cirrus to warpbuild (will) Pull request description: As cirrus is closing down, switch to warpbuild runners. Switch runner and provider names over. We now use GHA cache, so we don't need to switch that over here. ACKs for top commit: m3dwards: ACK 4bdd46ace37f02da062a53a2943caeddca4ed8f9 maflcko: review ACK 4bdd46ace37f02da062a53a2943caeddca4ed8f9 🤾 hebasto: ACK 4bdd46ace37f02da062a53a2943caeddca4ed8f9. Tree-SHA512: 47ed28a6cb7ab10a973af6aa24f4f7a632f59ed17e189ae4f658de37069d763c92cc0e32769693568db6d0e5d2543abcb77bb0977f0b3f296d80a254d6bb3833 --- .github/workflows/ci.yml | 42 ++++++++++++++++++++-------------------- ci/README.md | 8 ++++---- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 920c1f46047d..2a8626e767bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ concurrency: env: CI_FAILFAST_TEST_LEAVE_DANGLING: 1 # GHA does not care about dangling processes and setting this variable avoids killing the CI script itself on error - REPO_USE_CIRRUS_RUNNERS: 'bitcoin/bitcoin' # Use cirrus runners for this repo, instead of falling back to the slow GHA runners + REPO_USE_WARP_RUNNERS: 'bitcoin/bitcoin' # Use warp runners for this repo, instead of falling back to the slow GHA runners defaults: run: @@ -46,9 +46,9 @@ jobs: fi - id: runners run: | - if [[ "${REPO_USE_CIRRUS_RUNNERS}" == "${{ github.repository }}" ]]; then - echo "provider=cirrus" >> "$GITHUB_OUTPUT" - echo "::notice title=Runner Selection::Using Cirrus Runners" + if [[ "${REPO_USE_WARP_RUNNERS}" == "${{ github.repository }}" ]]; then + echo "provider=warp" >> "$GITHUB_OUTPUT" + echo "::notice title=Runner Selection::Using Warp Runners" else echo "provider=gha" >> "$GITHUB_OUTPUT" echo "::notice title=Runner Selection::Using GitHub-hosted runners" @@ -57,7 +57,7 @@ jobs: test-each-commit: name: 'test ancestor commits' needs: runners - runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' || 'ubuntu-24.04' }} + runs-on: ${{ needs.runners.outputs.provider == 'warp' && 'warp-ubuntu-latest-x64-8x' || 'ubuntu-24.04' }} env: TEST_RUNNER_PORT_MIN: "14000" # Use a larger port range to avoid colliding with other CI services. if: github.event_name == 'pull_request' && github.event.pull_request.commits != 1 @@ -215,7 +215,7 @@ jobs: ci-matrix: name: ${{ matrix.name }} needs: runners - runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && matrix.cirrus-runner || matrix.fallback-runner }} + runs-on: ${{ needs.runners.outputs.provider == 'warp' && matrix.warp-runner || matrix.fallback-runner }} if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }} timeout-minutes: ${{ matrix.timeout-minutes }} @@ -228,86 +228,86 @@ jobs: matrix: include: - name: 'iwyu' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_iwyu.sh' - name: '32 bit ARM' - cirrus-runner: 'ubuntu-24.04-arm' # Cirrus' Arm runners are Apple (with virtual Linux aarch64), which doesn't support 32-bit mode + warp-runner: 'ubuntu-24.04-arm' # Warp's Arm runners don't support 32-bit mode currently fallback-runner: 'ubuntu-24.04-arm' timeout-minutes: 120 file-env: './ci/test/00_setup_env_arm.sh' provider: 'gha' - name: 'ASan + LSan + UBSan + integer' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' # has to match container in ci/test/00_setup_env_native_asan.sh for tracing tools + warp-runner: 'warp-ubuntu-2404-x64-8x' # has to match container in ci/test/00_setup_env_native_asan.sh for tracing tools fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_asan.sh' - name: 'macOS-cross to arm64' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-sm' + warp-runner: 'warp-ubuntu-latest-x64-4x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_mac_cross.sh' - name: 'macOS-cross to x86_64' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-sm' + warp-runner: 'warp-ubuntu-latest-x64-4x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_mac_cross_intel.sh' - name: 'FreeBSD Cross' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_freebsd_cross.sh' - name: 'i686' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_i686.sh' - name: 'fuzzer,address,undefined,integer' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-lg' + warp-runner: 'warp-ubuntu-latest-x64-16x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 240 file-env: './ci/test/00_setup_env_native_fuzz.sh' - name: 'previous releases' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_previous_releases.sh' - name: 'Alpine (musl)' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_alpine_musl.sh' - name: 'tidy' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_tidy.sh' - name: 'TSan' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_tsan.sh' - name: 'MSan, fuzz' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-md' + warp-runner: 'warp-ubuntu-latest-x64-8x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 150 file-env: './ci/test/00_setup_env_native_fuzz_with_msan.sh' - name: 'MSan' - cirrus-runner: 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-lg' + warp-runner: 'warp-ubuntu-latest-x64-16x' fallback-runner: 'ubuntu-24.04' timeout-minutes: 120 file-env: './ci/test/00_setup_env_native_msan.sh' @@ -351,7 +351,7 @@ jobs: lint: name: 'lint' needs: runners - runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-xs' || 'ubuntu-24.04' }} + runs-on: ${{ needs.runners.outputs.provider == 'warp' && 'warp-ubuntu-latest-x64-2x' || 'ubuntu-24.04' }} if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }} timeout-minutes: 20 env: diff --git a/ci/README.md b/ci/README.md index 0f43b4f1f630..314d336b76cf 100644 --- a/ci/README.md +++ b/ci/README.md @@ -80,8 +80,8 @@ trigger cache-invalidation and rebuilds as necessary. To configure the primary repository, follow these steps: -1. Register with [Cirrus Runners](https://cirrus-runners.app/) and purchase runners. -2. Install the Cirrus Runners GitHub app against the GitHub organization. +1. Register with [WarpBuild](https://www.warpbuild.com/) and purchase runners. +2. Install the WarpBuild GitHub app against the GitHub organization. 3. Enable organisation-level runners to be used in public repositories: 1. `Org settings -> Actions -> Runner Groups -> Default -> Allow public repos` 4. Permit the following actions to run: @@ -95,5 +95,5 @@ To configure the primary repository, follow these steps: When used in a fork the CI will run on GitHub's free hosted runners by default. In this case, GitHub's cache size limitations may cause caches to be frequently evicted and missed, but the workflows will run (slowly). -It is also possible to use your own Cirrus Runners in your own fork with an appropriate patch to the `REPO_USE_CIRRUS_RUNNERS` variable in ../.github/workflows/ci.yml -NB that Cirrus Runners only work at an organisation level, therefore in order to use your own Cirrus Runners, *the fork must be within your own organisation*. +It is also possible to use your own WarpBuild Runners in your own fork with an appropriate patch to the `REPO_USE_WARP_RUNNERS` variable in ../.github/workflows/ci.yml +NB that WarpBuild Runners only work at an organisation level, therefore in order to use your own WarpBuild Runners, *the fork must be within your own organisation*. From 63c2514dceebf0fa2834a46b01ab561cec3dd113 Mon Sep 17 00:00:00 2001 From: merge-script Date: Fri, 29 May 2026 10:30:30 +0100 Subject: [PATCH 13/21] Merge bitcoin/bitcoin#35011: iwyu: Fix warnings in `src/script` and treat them as errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 6183942513789fb3dc94fa02a097744b149bb69a ci, iwyu: Fix warnings in src/scripts and treat them as error (Brandon Odiwuor) Pull request description: This PR [continues](https://github.com/bitcoin/bitcoin/pull/33725#issuecomment-3466897433) the ongoing effort to enforce IWYU warnings. See [Developer Notes](https://github.com/bitcoin/bitcoin/blob/master/doc/developer-notes.md#using-iwyu). ACKs for top commit: maflcko: review ACK 6183942513789fb3dc94fa02a097744b149bb69a 🚖 hebasto: ACK 6183942513789fb3dc94fa02a097744b149bb69a. Tree-SHA512: 75f36189953cae82e03a7a69c285969e3fbf896656f60c77ab44b80264f88a03cf78af5352b719567f80abc0d7c818b7e4ffff0c83cfbc6d08c2d5fcc2fb0c5b --- ci/test/03_test_script.sh | 2 +- src/bench/verify_script.cpp | 1 + src/core_io.cpp | 1 + src/kernel/bitcoinkernel.cpp | 1 + src/kernel/chainparams.cpp | 1 + src/script/descriptor.cpp | 24 ++++++++++++++++++++---- src/script/descriptor.h | 16 +++++++++++++--- src/script/interpreter.cpp | 10 ++++++++++ src/script/interpreter.h | 13 +++++++------ src/script/miniscript.cpp | 8 ++++---- src/script/miniscript.h | 29 ++++++++++++++++------------- src/script/parsing.cpp | 2 -- src/script/parsing.h | 3 +-- src/script/script.cpp | 1 + src/script/script.h | 5 +++-- src/script/sigcache.cpp | 2 +- src/script/sigcache.h | 6 ++++-- src/script/sign.cpp | 16 +++++++++++++++- src/script/sign.h | 20 +++++++++++++------- src/script/signingprovider.cpp | 11 ++++++++--- src/script/signingprovider.h | 14 +++++++++++++- src/script/solver.cpp | 6 +++--- src/script/solver.h | 4 ++-- src/signet.cpp | 1 + src/test/script_tests.cpp | 17 ++++++++--------- 25 files changed, 148 insertions(+), 66 deletions(-) diff --git a/ci/test/03_test_script.sh b/ci/test/03_test_script.sh index 8729b7c7cb18..837e661a9cc9 100755 --- a/ci/test/03_test_script.sh +++ b/ci/test/03_test_script.sh @@ -197,7 +197,7 @@ fi if [[ "${RUN_IWYU}" == true ]]; then # TODO: Consider enforcing IWYU across the entire codebase. - FILES_WITH_ENFORCED_IWYU="/src/(((crypto|index|kernel|primitives|univalue/(lib|test)|util)/.*|bench/(block_assemble|connectblock)|common/license_info|node/(blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp)" + FILES_WITH_ENFORCED_IWYU="/src/(((crypto|index|kernel|primitives|script|univalue/(lib|test)|util)/.*|bench/(block_assemble|connectblock)|common/license_info|node/(blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp)" jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns)))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_errors.json" jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns) | not))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_warnings.json" diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 8f07fd34fd17..63a9544ad981 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include diff --git a/src/core_io.cpp b/src/core_io.cpp index 584f823bae23..3650d70810a9 100644 --- a/src/core_io.cpp +++ b/src/core_io.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include