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: