diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c1355c8cf12..32b2e6d6f20 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -211,11 +211,16 @@ jobs:
steps:
- *CHECKOUT
- - name: Configure Developer Command Prompt for Microsoft Visual C++
- # Using microsoft/setup-msbuild is not enough.
- uses: ilammy/msvc-dev-cmd@v1
- with:
- arch: x64
+ - &SET_UP_VS
+ name: Set up VS Developer Prompt
+ shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
+ run: |
+ $vswherePath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
+ $installationPath = & $vswherePath -latest -property installationPath
+ & "${env:COMSPEC}" /s /c "`"$installationPath\Common7\Tools\vsdevcmd.bat`" -arch=x64 -no_logo && set" | foreach-object {
+ $name, $value = $_ -split '=', 2
+ echo "$name=$value" >> $env:GITHUB_ENV
+ }
- name: Get tool information
shell: pwsh
@@ -235,7 +240,7 @@ jobs:
sed -i '1s/^/set(ENV{CMAKE_POLICY_VERSION_MINIMUM} 3.5)\n/' "${VCPKG_INSTALLATION_ROOT}/scripts/ports.cmake"
- name: vcpkg tools cache
- uses: actions/cache@v4
+ uses: actions/cache@v5
with:
path: C:/vcpkg/downloads/tools
key: ${{ github.job }}-vcpkg-tools
@@ -263,14 +268,26 @@ jobs:
run: |
cmake --build . -j $NUMBER_OF_PROCESSORS --config Release
- - name: Get bitcoind manifest
+ - name: Check executable manifests
if: matrix.job-type == 'standard'
working-directory: build
+ shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
run: |
- mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -out:bitcoind.manifest
- cat bitcoind.manifest
- echo
- mt.exe -nologo -inputresource:bin/Release/bitcoind.exe -validate_manifest
+ mt.exe -nologo -inputresource:bin\Release\bitcoind.exe -out:bitcoind.manifest
+ Get-Content bitcoind.manifest
+
+ Get-ChildItem -Filter "bin\Release\*.exe" | ForEach-Object {
+ $exeName = $_.Name
+
+ # Skip as they currently do not have manifests
+ if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe" -or $exeName -eq "test_bitcoin-qt.exe") {
+ Write-Host "Skipping $exeName (no manifest present)"
+ return
+ }
+
+ Write-Host "Checking $exeName"
+ & mt.exe -nologo -inputresource:$_.FullName -validate_manifest
+ }
- name: Run test suite
if: matrix.job-type == 'standard'
@@ -341,7 +358,7 @@ jobs:
uses: ./.github/actions/save-caches
- name: Upload built executables
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v6
with:
name: x86_64-w64-mingw32-executables-${{ github.run_id }}
path: |
@@ -363,26 +380,33 @@ jobs:
- *CHECKOUT
- name: Download built executables
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v7
with:
name: x86_64-w64-mingw32-executables-${{ github.run_id }}
- name: Run bitcoind.exe
run: ./bin/bitcoind.exe -version
- - name: Find mt.exe tool
- shell: pwsh
- run: |
- $sdk_dir = (Get-ItemProperty 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows Kits\Installed Roots' -Name KitsRoot10).KitsRoot10
- $sdk_latest = (Get-ChildItem "$sdk_dir\bin" -Directory | Where-Object { $_.Name -match '^\d+\.\d+\.\d+\.\d+$' } | Sort-Object Name -Descending | Select-Object -First 1).Name
- "MT_EXE=${sdk_dir}bin\${sdk_latest}\x64\mt.exe" >> $env:GITHUB_ENV
+ - *SET_UP_VS
- - name: Get bitcoind manifest
- shell: pwsh
+ - name: Check executable manifests
+ shell: pwsh -Command "$PSVersionTable; $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; & '{0}'"
run: |
- & $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
+ mt.exe -nologo -inputresource:bin\bitcoind.exe -out:bitcoind.manifest
Get-Content bitcoind.manifest
- & $env:MT_EXE -nologo -inputresource:bin\bitcoind.exe -validate_manifest
+
+ Get-ChildItem -Filter "bin\*.exe" | ForEach-Object {
+ $exeName = $_.Name
+
+ # Skip as they currently do not have manifests
+ if ($exeName -eq "fuzz.exe" -or $exeName -eq "bench_bitcoin.exe") {
+ Write-Host "Skipping $exeName (no manifest present)"
+ return
+ }
+
+ Write-Host "Checking $exeName"
+ & mt.exe -nologo -inputresource:$_.FullName -validate_manifest
+ }
- name: Run unit tests
# Can't use ctest here like other jobs as we don't have a CMake build tree.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4f832625170..5d46df1d3bc 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -479,14 +479,8 @@ configure_file(contrib/filter-lcov.py filter-lcov.py USE_SOURCE_PERMISSIONS COPY
# Don't allow extended (non-ASCII) symbols in identifiers. This is easier for code review.
try_append_cxx_flags("-fno-extended-identifiers" TARGET core_interface SKIP_LINK)
-# Avoiding the `-ffile-prefix-map` compiler option because it implies
-# `-fcoverage-prefix-map` on Clang or `-fprofile-prefix-map` on GCC,
-# which can cause issues with coverage builds, particularly when using
-# Clang in the OSS-Fuzz environment due to its use of other options
-# and a third party script, or with GCC.
-try_append_cxx_flags("-fdebug-prefix-map=A=B" TARGET core_interface SKIP_LINK
- IF_CHECK_PASSED "-fdebug-prefix-map=${PROJECT_SOURCE_DIR}/src=."
-)
+# Set `-fmacro-prefix-map`, so that source file names are expanded without the
+# src prefix.
try_append_cxx_flags("-fmacro-prefix-map=A=B" TARGET core_interface SKIP_LINK
IF_CHECK_PASSED "-fmacro-prefix-map=${PROJECT_SOURCE_DIR}/src=."
)
diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh
index f23133fd584..ebfafb75baa 100755
--- a/contrib/guix/libexec/build.sh
+++ b/contrib/guix/libexec/build.sh
@@ -211,6 +211,7 @@ CONFIGFLAGS="-DREDUCE_EXPORTS=ON -DBUILD_BENCH=OFF -DBUILD_GUI_TESTS=OFF -DBUILD
# CFLAGS
HOST_CFLAGS="-O2 -g"
HOST_CFLAGS+=$(find /gnu/store -maxdepth 1 -mindepth 1 -type d -exec echo -n " -ffile-prefix-map={}=/usr" \;)
+HOST_CFLAGS+=" -fdebug-prefix-map=${DISTSRC}/src=."
case "$HOST" in
*mingw*) HOST_CFLAGS+=" -fno-ident" ;;
*darwin*) unset HOST_CFLAGS ;;
diff --git a/doc/bips.md b/doc/bips.md
index 97645d0eebe..c814717a09f 100644
--- a/doc/bips.md
+++ b/doc/bips.md
@@ -73,3 +73,4 @@ BIPs that are implemented by Bitcoin Core:
* [`BIP 386`](https://github.com/bitcoin/bips/blob/master/bip-0386.mediawiki): tr() Output Script Descriptors are implemented as of **v22.0** ([PR 22051](https://github.com/bitcoin/bitcoin/pull/22051)).
* [`BIP 387`](https://github.com/bitcoin/bips/blob/master/bip-0387.mediawiki): Tapscript Multisig Output Script Descriptors are implemented as of **v24.0** ([PR 24043](https://github.com/bitcoin/bitcoin/pull/24043)).
* [`BIP 431`](https://github.com/bitcoin/bips/blob/master/bip-0431.mediawiki): transactions with nVersion=3 are standard and treated as Topologically Restricted Until Confirmation as of **v28.0** ([PR 29496](https://github.com/bitcoin/bitcoin/pull/29496)).
+* [`BIP 433`](https://github.com/bitcoin/bips/blob/master/bip-0433.mediawiki): Spending of Pay to Anchor (P2A) outputs is standard as of **v28.0** ([PR 30352](https://github.com/bitcoin/bitcoin/pull/30352)).
diff --git a/doc/developer-notes.md b/doc/developer-notes.md
index 220343ab321..558e054089a 100644
--- a/doc/developer-notes.md
+++ b/doc/developer-notes.md
@@ -305,37 +305,6 @@ If you need to build exclusively for debugging, set the `-DCMAKE_BUILD_TYPE`
to `Debug` (i.e. `-DCMAKE_BUILD_TYPE=Debug`). You can always check the cmake
build options of an existing build with `ccmake build`.
-### Show sources in debugging
-
-If you have ccache enabled, absolute paths are stripped from debug information
-with the `-fdebug-prefix-map` and `-fmacro-prefix-map` options (if supported by the
-compiler). This might break source file detection in case you move binaries
-after compilation, debug from the directory other than the project root or use
-an IDE that only supports absolute paths for debugging (e.g. it won't stop at breakpoints).
-
-There are a few possible fixes:
-
-1. Configure source file mapping.
-
-For `gdb` create or append to [`.gdbinit` file](https://sourceware.org/gdb/current/onlinedocs/gdb#gdbinit-man):
-```
-set substitute-path ./src /path/to/project/root/src
-```
-
-For `lldb` create or append to [`.lldbinit` file](https://lldb.llvm.org/man/lldb.html#configuration-files):
-```
-settings set target.source-map ./src /path/to/project/root/src
-```
-
-2. Add a symlink to the `./src` directory:
-```
-ln -s /path/to/project/root/src src
-```
-
-3. Use `debugedit` to modify debug information in the binary.
-
-4. If your IDE has an option for this, change your breakpoints to use the file name only.
-
### debug.log
If the code is behaving strangely, take a look in the `debug.log` file in the data directory;
diff --git a/doc/release-notes.md b/doc/release-notes.md
index 888b3f919c3..e8ce2e59f29 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,9 +1,9 @@
-v30.2 Release Notes
+v30.x Release Notes
===================
-Bitcoin Core version v30.2 is now available from:
+Bitcoin Core version v30.x is now available from:
-
+
This release includes new features, various bug fixes and performance
improvements, as well as updated translations.
@@ -42,50 +42,46 @@ Notable changes
### Wallet
-- #34156 wallet: fix unnamed legacy wallet migration failure
-- #34215 wallettool: fix unnamed createfromdump failure walletsdir deletion
-- #34221 test: migration, avoid backup name mismatch in default_wallet_failure
+- #34358 wallet: fix removeprunedfunds bug with conflicting transactions
-### IPC
+### PSBT
-- #33511 init: Fix Ctrl-C shutdown hangs during wait calls
+- #34272 psbt: Fix PSBTInputSignedAndVerified bounds assert
### Build
-- #33950 guix: reduce allowed exported symbols
-- #34107 build: Update minimum required Boost version
-- #34227 guix: Fix osslsigncode tests
+- #34281 build: Temporarily remove confusing and brittle -fdebug-prefix-map
### Test
-- #34137 test: Avoid hard time.sleep(1) in feature_init.py
-- #34226 wallet: test: Relative wallet failed migration cleanup
-
-### Fuzz
-
-- #34091 fuzz: doc: remove any mention to address_deserialize_v2
+- #34185 test: fix feature_pruning when built without wallet
+- #34282 qa: Fix Windows logging bug
+- #34390 test: allow overriding tar in get_previous_releases.py
### Doc
-- #34182 doc: Update OpenBSD Build Guide
+- #34252 doc: add 433 (Pay to Anchor) to bips.md
+- #34413 doc: Remove outdated -fdebug-prefix-map section in dev notes
-### Misc
+### CI
-- #34174 doc: update copyright year to 2026
+- #32513 ci: remove 3rd party js from windows dll gha job
+- #34344 ci: update GitHub Actions versions
Credits
=======
Thanks to everyone who directly contributed to this release:
-- Ava Chow
- brunoerg
-- davidgumberg
- fanquake
-- furszy
- Hennadii Stepanov
+- Lőrinc
+- m3dwards
- MarcoFalke
-- Ryan Ofsky
+- mzumsande
+- Padraic Slattery
+- Sebastian Falbesoner
As well as to everyone that helped with translations on
[Transifex](https://explore.transifex.com/bitcoin/bitcoin/).
diff --git a/src/psbt.cpp b/src/psbt.cpp
index 28ccdcb1eaa..30c0d88a938 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -301,7 +301,7 @@ bool PSBTInputSigned(const PSBTInput& input)
bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned int input_index, const PrecomputedTransactionData* txdata)
{
CTxOut utxo;
- assert(psbt.inputs.size() >= input_index);
+ assert(input_index < psbt.inputs.size());
const PSBTInput& input = psbt.inputs[input_index];
if (input.non_witness_utxo) {
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 622ee963afa..15ee7a4eedc 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2383,8 +2383,15 @@ util::Result CWallet::RemoveTxs(WalletBatch& batch, std::vector& txs
for (const auto& it : erased_txs) {
const Txid hash{it->first};
wtxOrdered.erase(it->second.m_it_wtxOrdered);
- for (const auto& txin : it->second.tx->vin)
- mapTxSpends.erase(txin.prevout);
+ for (const auto& txin : it->second.tx->vin) {
+ auto range = mapTxSpends.equal_range(txin.prevout);
+ for (auto iter = range.first; iter != range.second; ++iter) {
+ if (iter->second == hash) {
+ mapTxSpends.erase(iter);
+ break;
+ }
+ }
+ }
for (unsigned int i = 0; i < it->second.tx->vout.size(); ++i) {
m_txos.erase(COutPoint(hash, i));
}
diff --git a/test/functional/feature_pruning.py b/test/functional/feature_pruning.py
index 76c79fe0036..d9a4c9c5b7e 100755
--- a/test/functional/feature_pruning.py
+++ b/test/functional/feature_pruning.py
@@ -346,20 +346,22 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Success")
- def wallet_test(self):
+ def test_wallet_rescan(self):
# check that the pruning node's wallet is still in good shape
self.log.info("Stop and start pruning node to trigger wallet rescan")
self.restart_node(2, extra_args=["-prune=550"])
- self.log.info("Success")
+
+ wallet_info = self.nodes[2].getwalletinfo()
+ self.wait_until(lambda: wallet_info["scanning"] == False)
+ self.wait_until(lambda: wallet_info["lastprocessedblock"]["height"] == self.nodes[2].getblockcount())
# check that wallet loads successfully when restarting a pruned node after IBD.
# this was reported to fail in #7494.
- self.log.info("Syncing node 5 to test wallet")
- self.connect_nodes(0, 5)
- nds = [self.nodes[0], self.nodes[5]]
- self.sync_blocks(nds, wait=5, timeout=300)
self.restart_node(5, extra_args=["-prune=550", "-blockfilterindex=1"]) # restart to trigger rescan
- self.log.info("Success")
+
+ wallet_info = self.nodes[5].getwalletinfo()
+ self.wait_until(lambda: wallet_info["scanning"] == False)
+ self.wait_until(lambda: wallet_info["lastprocessedblock"]["height"] == self.nodes[0].getblockcount())
def run_test(self):
self.log.info("Warning! This test requires 4GB of disk space")
@@ -467,9 +469,13 @@ class PruneTest(BitcoinTestFramework):
self.log.info("Test manual pruning with timestamps")
self.manual_test(4, use_timestamp=True)
+ self.log.info("Syncing node 5 to node 0")
+ self.connect_nodes(0, 5)
+ self.sync_blocks([self.nodes[0], self.nodes[5]], wait=5, timeout=300)
+
if self.is_wallet_compiled():
self.log.info("Test wallet re-scan")
- self.wallet_test()
+ self.test_wallet_rescan()
self.log.info("Test it's not possible to rescan beyond pruned data")
self.test_rescan_blockchain()
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 9d556d1cd7b..56522aa6ffd 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -765,7 +765,7 @@ class TestHandler:
status = "Passed"
elif proc.returncode == TEST_EXIT_SKIPPED:
status = "Skipped"
- skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1)
+ skip_reason = re.search(r"Test Skipped: (.*)", stdout).group(1).strip()
else:
status = "Failed"
self.jobs.remove(job)
diff --git a/test/functional/wallet_importprunedfunds.py b/test/functional/wallet_importprunedfunds.py
index d45be4aa188..060549d3b64 100755
--- a/test/functional/wallet_importprunedfunds.py
+++ b/test/functional/wallet_importprunedfunds.py
@@ -14,6 +14,7 @@ from test_framework.messages import (
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
+ assert_not_equal,
assert_raises_rpc_error,
wallet_importprivkey,
)
@@ -129,6 +130,33 @@ class ImportPrunedFundsTest(BitcoinTestFramework):
mb.header.nTime += 1 # modify arbitrary block header field to change block hash
assert_raises_rpc_error(-5, "Block not found in chain", w1.importprunedfunds, rawtxn1, mb.serialize().hex())
+ self.log.info("Test removeprunedfunds with conflicting transactions")
+ node = self.nodes[0]
+
+ # Create a transaction
+ utxo = node.listunspent()[0]
+ addr = node.getnewaddress()
+ tx1_id = node.send(outputs=[{addr: 1}], inputs=[utxo])["txid"]
+ tx1_fee = node.gettransaction(tx1_id)["fee"]
+
+ # Create a conflicting tx with a larger fee (tx1_fee is negative)
+ output_value = utxo["amount"] + tx1_fee - Decimal("0.00001")
+ raw_tx2 = node.createrawtransaction(inputs=[utxo], outputs=[{addr: output_value}])
+ signed_tx2 = node.signrawtransactionwithwallet(raw_tx2)
+ tx2_id = node.sendrawtransaction(signed_tx2["hex"])
+ assert_not_equal(tx2_id, tx1_id)
+
+ # Both txs should be in the wallet, tx2 replaced tx1 in mempool
+ assert tx1_id in [tx["txid"] for tx in node.listtransactions()]
+ assert tx2_id in [tx["txid"] for tx in node.listtransactions()]
+
+ # Remove the replaced tx from wallet
+ node.removeprunedfunds(tx1_id)
+
+ # The UTXO should still be considered spent (by tx2)
+ available_utxos = [u["txid"] for u in node.listunspent(minconf=0)]
+ assert utxo["txid"] not in available_utxos, "UTXO should still be spent by conflicting tx"
+
if __name__ == '__main__':
ImportPrunedFundsTest(__file__).main()
diff --git a/test/get_previous_releases.py b/test/get_previous_releases.py
index ecb585c12d7..d6344a5ca56 100755
--- a/test/get_previous_releases.py
+++ b/test/get_previous_releases.py
@@ -23,6 +23,8 @@ import time
import urllib.request
import zipfile
+TAR = os.getenv('TAR', 'tar')
+
SHA256_SUMS = {
"0e2819135366f150d9906e294b61dff58fd1996ebd26c2f8e979d6c0b7a79580": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-aarch64-linux-gnu.tar.gz"},
"d86fc90824a85c38b25c8488115178d5785dbc975f5ff674f9f5716bc8ad6e65": {"tag": "v0.14.3", "archive": "bitcoin-0.14.3-arm-linux-gnueabihf.tar.gz"},
@@ -202,7 +204,7 @@ def download_binary(tag, args) -> int:
print(f"Zip extraction failed: {e}", file=sys.stderr)
return 1
else:
- ret = subprocess.run(['tar', '-zxf', archive, '-C', tag,
+ ret = subprocess.run([TAR, '-zxf', archive, '-C', tag,
'--strip-components=1',
'bitcoin-{tag}'.format(tag=tag[1:])]).returncode
if ret != 0: