diff --git a/.cirrus.yml b/.cirrus.yml
index 3f52adde8a..3669d168c1 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -43,7 +43,7 @@ env: # Global defaults
# The following specific types should exist, with the following requirements:
# - small: For an x86_64 machine, recommended to have 2 CPUs and 8 GB of memory.
# - medium: For an x86_64 machine, recommended to have 4 CPUs and 16 GB of memory.
-# - mantic: For a machine running the Linux kernel shipped with exaclty Ubuntu Mantic 23.10. The machine is recommended to have 4 CPUs and 16 GB of memory.
+# - noble: For a machine running the Linux kernel shipped with exaclty Ubuntu Noble 24.04. The machine is recommended to have 4 CPUs and 16 GB of memory.
# - arm64: For an aarch64 machine, recommended to have 2 CPUs and 8 GB of memory.
# https://cirrus-ci.org/guide/tips-and-tricks/#sharing-configuration-between-tasks
@@ -165,7 +165,7 @@ task:
<< : *GLOBAL_TASK_TEMPLATE
persistent_worker:
labels:
- type: mantic # Must use this specific worker (needed for USDT functional tests)
+ type: noble # Must use this specific worker (needed for USDT functional tests)
env:
FILE_ENV: "./ci/test/00_setup_env_native_asan.sh"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e8564c3a28..07a88b2969 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -92,6 +92,8 @@ jobs:
run: clang --version
- name: Install Homebrew packages
+ env:
+ HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
run: brew install automake libtool pkg-config gnu-getopt ccache boost libevent miniupnpc libnatpmp zeromq qt@5 qrencode
- name: Set Ccache directory
diff --git a/ci/test/00_setup_env_native_asan.sh b/ci/test/00_setup_env_native_asan.sh
index 93d84bf17e..60486f8f61 100755
--- a/ci/test/00_setup_env_native_asan.sh
+++ b/ci/test/00_setup_env_native_asan.sh
@@ -6,6 +6,7 @@
export LC_ALL=C.UTF-8
+export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
# Only install BCC tracing packages in Cirrus CI.
if [[ "${CIRRUS_CI}" == "true" ]]; then
BPFCC_PACKAGE="bpfcc-tools linux-headers-$(uname --kernel-release)"
@@ -17,7 +18,6 @@ fi
export CONTAINER_NAME=ci_native_asan
export PACKAGES="systemtap-sdt-dev clang-17 llvm-17 libclang-rt-17-dev python3-zmq qtbase5-dev qttools5-dev-tools libevent-dev libboost-dev libdb5.3++-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev libqrencode-dev libsqlite3-dev ${BPFCC_PACKAGE}"
-export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.10" # This version will reach EOL in Jul 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version).
export NO_DEPENDS=1
export GOAL="install"
export BITCOIN_CONFIG="--enable-c++20 --enable-usdt --enable-zmq --with-incompatible-bdb --with-gui=qt5 \
diff --git a/ci/test/00_setup_env_native_fuzz.sh b/ci/test/00_setup_env_native_fuzz.sh
index 122f044b58..c13ee0cbe9 100755
--- a/ci/test/00_setup_env_native_fuzz.sh
+++ b/ci/test/00_setup_env_native_fuzz.sh
@@ -6,7 +6,7 @@
export LC_ALL=C.UTF-8
-export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.10" # This version will reach EOL in Jul 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version).
+export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
export CONTAINER_NAME=ci_native_fuzz
export PACKAGES="clang-17 llvm-17 libclang-rt-17-dev libevent-dev libboost-dev libsqlite3-dev"
export NO_DEPENDS=1
diff --git a/ci/test/00_setup_env_native_tidy.sh b/ci/test/00_setup_env_native_tidy.sh
index 6b0c708f19..c03392af06 100755
--- a/ci/test/00_setup_env_native_tidy.sh
+++ b/ci/test/00_setup_env_native_tidy.sh
@@ -6,7 +6,7 @@
export LC_ALL=C.UTF-8
-export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.10" # This version will reach EOL in Jul 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version).
+export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
export CONTAINER_NAME=ci_native_tidy
export TIDY_LLVM_V="17"
export PACKAGES="clang-${TIDY_LLVM_V} libclang-${TIDY_LLVM_V}-dev llvm-${TIDY_LLVM_V}-dev libomp-${TIDY_LLVM_V}-dev clang-tidy-${TIDY_LLVM_V} jq bear cmake libevent-dev libboost-dev libminiupnpc-dev libnatpmp-dev libzmq3-dev systemtap-sdt-dev libqt5gui5 libqt5core5a libqt5dbus5 qttools5-dev qttools5-dev-tools libqrencode-dev libsqlite3-dev libdb++-dev"
diff --git a/ci/test/00_setup_env_native_tsan.sh b/ci/test/00_setup_env_native_tsan.sh
index 67c29d4bc8..aa23bad809 100755
--- a/ci/test/00_setup_env_native_tsan.sh
+++ b/ci/test/00_setup_env_native_tsan.sh
@@ -7,7 +7,7 @@
export LC_ALL=C.UTF-8
export CONTAINER_NAME=ci_native_tsan
-export CI_IMAGE_NAME_TAG="docker.io/ubuntu:23.10" # This version will reach EOL in Jul 2024, and can be replaced by "ubuntu:24.04" (or anything else that ships the wanted clang version).
+export CI_IMAGE_NAME_TAG="docker.io/ubuntu:24.04"
export PACKAGES="clang-17 llvm-17 libclang-rt-17-dev libc++abi-17-dev libc++-17-dev python3-zmq"
export DEP_OPTS="CC=clang-17 CXX='clang++-17 -stdlib=libc++'"
export GOAL="install"
diff --git a/doc/release-notes.md b/doc/release-notes.md
index b30dadf62b..f55c72ab9a 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,9 +1,9 @@
-26.0 Release Notes
+26.x Release Notes
==================
-Bitcoin Core version 26.0 is now available from:
+Bitcoin Core version 26.x is now available from:
-
+
This release includes new features, various bug fixes and performance
improvements, as well as updated translations.
@@ -40,296 +40,32 @@ unsupported systems.
Notable changes
===============
-P2P and network changes
------------------------
+### Wallet
-- Experimental support for the v2 transport protocol defined in
- [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki) was added.
- It is off by default, but when enabled using `-v2transport` it will be negotiated
- on a per-connection basis with other peers that support it too. The existing
- v1 transport protocol remains fully supported.
+- #28994 wallet: skip BnB when SFFO is enabled
+- #28920 wallet: birth time update during tx scanning
+- #29176 wallet: Fix use-after-free in WalletBatch::EraseRecords
-- Nodes with multiple reachable networks will actively try to have at least one
- outbound connection to each network. This improves individual resistance to
- eclipse attacks and network level resistance to partition attacks. Users no
- longer need to perform active measures to ensure being connected to multiple
- enabled networks. (#27213)
+### RPC
-Pruning
--------
+- #29003 rpc: fix getrawtransaction segfault
-- When using assumeutxo with `-prune`, the prune budget may be exceeded if it is set
- lower than 1100MB (i.e. `MIN_DISK_SPACE_FOR_BLOCK_FILES * 2`). Prune budget is normally
- split evenly across each chainstate, unless the resulting prune budget per chainstate
- is beneath `MIN_DISK_SPACE_FOR_BLOCK_FILES` in which case that value will be used. (#27596)
+### CI
-Updated RPCs
-------------
-
-- Setting `-rpcserialversion=0` is deprecated and will be removed in
- a future release. It can currently still be used by also adding
- the `-deprecatedrpc=serialversion` option. (#28448)
-
-- The `hash_serialized_2` value has been removed from `gettxoutsetinfo` since the value it
- calculated contained a bug and did not take all data into account. It is superseded by
- `hash_serialized_3` which provides the same functionality but serves the correctly calculated hash. (#28685)
-
-- New fields `transport_protocol_type` and `session_id` were added to the `getpeerinfo` RPC to indicate
- whether the v2 transport protocol is in use, and if so, what the session id is.
-
-- A new argument `v2transport` was added to the `addnode` RPC to indicate whether a v2 transaction connection
- is to be attempted with the peer.
-
-- [Miniscript](https://bitcoin.sipa.be/miniscript/) expressions can now be used in Taproot descriptors for all RPCs working with descriptors. (#27255)
-
-- `finalizepsbt` is now able to finalize a PSBT with inputs spending [Miniscript](https://bitcoin.sipa.be/miniscript/)-compatible Taproot leaves. (#27255)
-
-Changes to wallet related RPCs can be found in the Wallet section below.
-
-New RPCs
---------
-
-- `loadtxoutset` has been added, which allows loading a UTXO snapshot of the format
- generated by `dumptxoutset`. Once this snapshot is loaded, its contents will be
- deserialized into a second chainstate data structure, which is then used to sync to
- the network's tip.
-
- Meanwhile, the original chainstate will complete the initial block download process in
- the background, eventually validating up to the block that the snapshot is based upon.
-
- The result is a usable bitcoind instance that is current with the network tip in a
- matter of minutes rather than hours. UTXO snapshot are typically obtained via
- third-party sources (HTTP, torrent, etc.) which is reasonable since their contents
- are always checked by hash.
-
- You can find more information on this process in the `assumeutxo` design
- document ().
-
- `getchainstates` has been added to aid in monitoring the assumeutxo sync process.
-
-- A new `getprioritisedtransactions` RPC has been added. It returns a map of all fee deltas created by the
- user with prioritisetransaction, indexed by txid. The map also indicates whether each transaction is
- present in the mempool. (#27501)
-
-- A new RPC, `submitpackage`, has been added. It can be used to submit a list of raw hex
-transactions to the mempool to be evaluated as a package using consensus and mempool policy rules.
-These policies include package CPFP, allowing a child with high fees to bump a parent below the
-mempool minimum feerate (but not minimum relay feerate). (#27609)
-
- - Warning: successful submission does not mean the transactions will propagate throughout the
- network, as package relay is not supported.
-
- - Not all features are available. The package is limited to a child with all of its
- unconfirmed parents, and no parent may spend the output of another parent. Also, package
- RBF is not supported. Refer to doc/policy/packages.md for more details on package policies
- and limitations.
-
- - This RPC is experimental. Its interface may change.
-
-- A new RPC `getaddrmaninfo` has been added to view the distribution of addresses in the new and tried table of the
- node's address manager across different networks(ipv4, ipv6, onion, i2p, cjdns). The RPC returns count of addresses
- in new and tried table as well as their sum for all networks. (#27511)
-
-- A new `importmempool` RPC has been added. It loads a valid `mempool.dat` file and attempts to
- add its contents to the mempool. This can be useful to import mempool data from another node
- without having to modify the datadir contents and without having to restart the node. (#27460)
- - Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.
- - If you want to apply fee deltas, it is recommended to use the `getprioritisedtransactions` and
- `prioritisetransaction` RPCs instead of the `apply_fee_delta_priority` option to avoid
- double-prioritising any already-prioritised transactions in the mempool.
-
-Updated settings
-----------------
-
-- `bitcoind` and `bitcoin-qt` will now raise an error on startup
- if a datadir that is being used contains a bitcoin.conf file that
- will be ignored, which can happen when a datadir= line is used in
- a bitcoin.conf file. The error message is just a diagnostic intended
- to prevent accidental misconfiguration, and it can be disabled to
- restore the previous behavior of using the datadir while ignoring
- the bitcoin.conf contained in it. (#27302)
-
-- Passing an invalid `-debug`, `-debugexclude`, or `-loglevel` logging configuration
- option now raises an error, rather than logging an easily missed warning. (#27632)
-
-Changes to GUI or wallet related settings can be found in the GUI or Wallet section below.
-
-New settings
-------------
-
-Tools and Utilities
--------------------
-
-- A new `bitcoinconsensus_verify_script_with_spent_outputs` function is available in libconsensus which optionally accepts the spent outputs of the transaction being verified.
-- A new `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT` flag is available in libconsensus that will verify scripts with the Taproot spending rules.
-
-Wallet
-------
-
-- Wallet loading has changed in this release. Wallets with some corrupted records that could be
- previously loaded (with warnings) may no longer load. For example, wallets with corrupted
- address book entries may no longer load. If this happens, it is recommended
- load the wallet in a previous version of Bitcoin Core and import the data into a new wallet.
- Please also report an issue to help improve the software and make wallet loading more robust
- in these cases. (#24914)
-
-- The `gettransaction`, `listtransactions`, `listsinceblock` RPCs now return
- the `abandoned` field for all transactions. Previously, the "abandoned" field
- was only returned for sent transactions. (#25158)
-
-- The `listdescriptors`, `decodepsbt` and similar RPC methods now show `h` rather than apostrophe (`'`) to indicate
- hardened derivation. This does not apply when using the `private` parameter, which
- matches the marker used when descriptor was generated or imported. Newly created
- wallets use `h`. This change makes it easier to handle descriptor strings manually.
- E.g. the `importdescriptors` RPC call is easiest to use `h` as the marker: `'["desc": ".../0h/..."]'`.
- With this change `listdescriptors` will use `h`, so you can copy-paste the result,
- without having to add escape characters or switch `'` to 'h' manually.
- Note that this changes the descriptor checksum.
- For legacy wallets the `hdkeypath` field in `getaddressinfo` is unchanged,
- nor is the serialization format of wallet dumps. (#26076)
-
-- The `getbalances` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
- hash and height at the time the balances were calculated. This result shouldn't be cached because importing new keys could invalidate it. (#26094)
-
-- The `gettransaction` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
- hash and height at the time the transaction information was generated. (#26094)
-
-- The `getwalletinfo` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
- hash and height at the time the wallet information was generated. (#26094)
-
-- Coin selection and transaction building now accounts for unconfirmed low-feerate ancestor transactions. When it is necessary to spend unconfirmed outputs, the wallet will add fees to ensure that the new transaction with its ancestors will achieve a mining score equal to the feerate requested by the user. (#26152)
-
-- For RPC methods which accept `options` parameters ((`importmulti`, `listunspent`,
- `fundrawtransaction`, `bumpfee`, `send`, `sendall`, `walletcreatefundedpsbt`,
- `simulaterawtransaction`), it is now possible to pass the options as named
- parameters without the need for a nested object. (#26485)
-
-This means it is possible make calls like:
-
-```sh
-src/bitcoin-cli -named bumpfee txid fee_rate=100
-```
-
-instead of
-
-```sh
-src/bitcoin-cli -named bumpfee txid options='{"fee_rate": 100}'
-```
-
-- The `deprecatedrpc=walletwarningfield` configuration option has been removed.
- The `createwallet`, `loadwallet`, `restorewallet` and `unloadwallet` RPCs no
- longer return the "warning" string field. The same information is provided
- through the "warnings" field added in v25.0, which returns a JSON array of
- strings. The "warning" string field was deprecated also in v25.0. (#27757)
-
-- The `signrawtransactionwithkey`, `signrawtransactionwithwallet`,
- `walletprocesspsbt` and `descriptorprocesspsbt` calls now return the more
- specific RPC_INVALID_PARAMETER error instead of RPC_MISC_ERROR if their
- sighashtype argument is malformed. (#28113)
-
-- RPC `walletprocesspsbt`, and `descriptorprocesspsbt` return
- object now includes field `hex` (if the transaction
- is complete) containing the serialized transaction
- suitable for RPC `sendrawtransaction`. (#28414)
-
-- It's now possible to use [Miniscript](https://bitcoin.sipa.be/miniscript/) inside Taproot leaves for descriptor wallets. (#27255)
-
-GUI changes
------------
-
-- The transaction list in the GUI no longer provides a special category for "payment to yourself". Now transactions that have both inputs and outputs that affect the wallet are displayed on separate lines for spending and receiving. (gui#119)
-
-- A new menu option allows migrating a legacy wallet based on keys and implied output script types stored in BerkeleyDB (BDB) to a modern wallet that uses descriptors stored in SQLite. (gui#738)
-
-- The PSBT operations dialog marks outputs paying your own wallet with "own address". (gui#740)
-
-- The ability to create legacy wallets is being removed. (gui#764)
-
-Low-level changes
-=================
-
-Tests
------
-
-- Non-standard transactions are now disabled by default on testnet
- for relay and mempool acceptance. The previous behaviour can be
- re-enabled by setting `-acceptnonstdtxn=1`. (#28354)
+- #28992 ci: Use Ubuntu 24.04 Noble for asan,tsan,tidy,fuzz
+- #29080 ci: Set HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK to avoid unrelated failures
Credits
=======
Thanks to everyone who directly contributed to this release:
-- 0xb10c
-- Amiti Uttarwar
- Andrew Chow
-- Andrew Toth
-- Anthony Towns
-- Antoine Poinsot
-- Antoine Riard
-- Ari
-- Aurèle Oulès
-- Ayush Singh
-- Ben Woosley
-- Brandon Odiwuor
-- Brotcrunsher
-- brunoerg
-- Bufo
-- Carl Dong
-- Casey Carter
-- Cory Fields
-- David Álvarez Rosa
-- dergoegge
-- dhruv
-- dimitaracev
-- Erik Arvstedt
-- Erik McKelvey
-- Fabian Jahr
- furszy
-- glozow
-- Greg Sanders
-- Harris
- Hennadii Stepanov
-- Hernan Marino
-- ishaanam
-- ismaelsadeeq
-- Jake Rawsthorne
-- James O'Beirne
-- John Moffett
-- Jon Atack
-- josibake
-- kevkevin
-- Kiminuo
-- Larry Ruane
-- Luke Dashjr
- MarcoFalke
-- Marnix
-- Martin Leitner-Ankerl
- Martin Zumsande
-- Matthew Zipkin
-- Michael Ford
-- Michael Tidwell
-- mruddy
- Murch
-- ns-xvrn
-- pablomartin4btc
-- Pieter Wuille
-- Reese Russell
-- Rhythm Garg
-- Ryan Ofsky
-- Sebastian Falbesoner
-- Sjors Provoost
-- stickies-v
-- stratospher
-- Suhas Daftuar
-- TheCharlatan
-- Tim Neubauer
-- Tim Ruffing
-- Vasil Dimov
-- virtu
-- vuittont60
-- willcl-ark
-- Yusuf Sahin HAMZA
As well as to everyone that helped with translations on
[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/doc/release-notes/release-notes-26.0.md b/doc/release-notes/release-notes-26.0.md
new file mode 100644
index 0000000000..b7c7c35f65
--- /dev/null
+++ b/doc/release-notes/release-notes-26.0.md
@@ -0,0 +1,357 @@
+26.0 Release Notes
+==================
+
+Bitcoin Core version 26.0 is now available from:
+
+
+
+This release includes new features, various bug fixes and performance
+improvements, as well as updated translations.
+
+Please report bugs using the issue tracker at GitHub:
+
+
+
+To receive security and update notifications, please subscribe to:
+
+
+
+How to Upgrade
+==============
+
+If you are running an older version, shut it down. Wait until it has completely
+shut down (which might take a few minutes in some cases), then run the
+installer (on Windows) or just copy over `/Applications/Bitcoin-Qt` (on macOS)
+or `bitcoind`/`bitcoin-qt` (on Linux).
+
+Upgrading directly from a version of Bitcoin Core that has reached its EOL is
+possible, but it might take some time if the data directory needs to be migrated. Old
+wallet versions of Bitcoin Core are generally supported.
+
+Compatibility
+==============
+
+Bitcoin Core is supported and extensively tested on operating systems
+using the Linux kernel, macOS 11.0+, and Windows 7 and newer. Bitcoin
+Core should also work on most other Unix-like systems but is not as
+frequently tested on them. It is not recommended to use Bitcoin Core on
+unsupported systems.
+
+Notable changes
+===============
+
+P2P and network changes
+-----------------------
+
+- Experimental support for the v2 transport protocol defined in
+ [BIP324](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki) was added.
+ It is off by default, but when enabled using `-v2transport` it will be negotiated
+ on a per-connection basis with other peers that support it too. The existing
+ v1 transport protocol remains fully supported.
+
+- Nodes with multiple reachable networks will actively try to have at least one
+ outbound connection to each network. This improves individual resistance to
+ eclipse attacks and network level resistance to partition attacks. Users no
+ longer need to perform active measures to ensure being connected to multiple
+ enabled networks. (#27213)
+
+Pruning
+-------
+
+- When using assumeutxo with `-prune`, the prune budget may be exceeded if it is set
+ lower than 1100MB (i.e. `MIN_DISK_SPACE_FOR_BLOCK_FILES * 2`). Prune budget is normally
+ split evenly across each chainstate, unless the resulting prune budget per chainstate
+ is beneath `MIN_DISK_SPACE_FOR_BLOCK_FILES` in which case that value will be used. (#27596)
+
+Updated RPCs
+------------
+
+- Setting `-rpcserialversion=0` is deprecated and will be removed in
+ a future release. It can currently still be used by also adding
+ the `-deprecatedrpc=serialversion` option. (#28448)
+
+- The `hash_serialized_2` value has been removed from `gettxoutsetinfo` since the value it
+ calculated contained a bug and did not take all data into account. It is superseded by
+ `hash_serialized_3` which provides the same functionality but serves the correctly calculated hash. (#28685)
+
+- New fields `transport_protocol_type` and `session_id` were added to the `getpeerinfo` RPC to indicate
+ whether the v2 transport protocol is in use, and if so, what the session id is.
+
+- A new argument `v2transport` was added to the `addnode` RPC to indicate whether a v2 transaction connection
+ is to be attempted with the peer.
+
+- [Miniscript](https://bitcoin.sipa.be/miniscript/) expressions can now be used in Taproot descriptors for all RPCs working with descriptors. (#27255)
+
+- `finalizepsbt` is now able to finalize a PSBT with inputs spending [Miniscript](https://bitcoin.sipa.be/miniscript/)-compatible Taproot leaves. (#27255)
+
+Changes to wallet related RPCs can be found in the Wallet section below.
+
+New RPCs
+--------
+
+- `loadtxoutset` has been added, which allows loading a UTXO snapshot of the format
+ generated by `dumptxoutset`. Once this snapshot is loaded, its contents will be
+ deserialized into a second chainstate data structure, which is then used to sync to
+ the network's tip.
+
+ Meanwhile, the original chainstate will complete the initial block download process in
+ the background, eventually validating up to the block that the snapshot is based upon.
+
+ The result is a usable bitcoind instance that is current with the network tip in a
+ matter of minutes rather than hours. UTXO snapshot are typically obtained via
+ third-party sources (HTTP, torrent, etc.) which is reasonable since their contents
+ are always checked by hash.
+
+ You can find more information on this process in the `assumeutxo` design
+ document ().
+
+ `getchainstates` has been added to aid in monitoring the assumeutxo sync process.
+
+- A new `getprioritisedtransactions` RPC has been added. It returns a map of all fee deltas created by the
+ user with prioritisetransaction, indexed by txid. The map also indicates whether each transaction is
+ present in the mempool. (#27501)
+
+- A new RPC, `submitpackage`, has been added. It can be used to submit a list of raw hex
+transactions to the mempool to be evaluated as a package using consensus and mempool policy rules.
+These policies include package CPFP, allowing a child with high fees to bump a parent below the
+mempool minimum feerate (but not minimum relay feerate). (#27609)
+
+ - Warning: successful submission does not mean the transactions will propagate throughout the
+ network, as package relay is not supported.
+
+ - Not all features are available. The package is limited to a child with all of its
+ unconfirmed parents, and no parent may spend the output of another parent. Also, package
+ RBF is not supported. Refer to doc/policy/packages.md for more details on package policies
+ and limitations.
+
+ - This RPC is experimental. Its interface may change.
+
+- A new RPC `getaddrmaninfo` has been added to view the distribution of addresses in the new and tried table of the
+ node's address manager across different networks(ipv4, ipv6, onion, i2p, cjdns). The RPC returns count of addresses
+ in new and tried table as well as their sum for all networks. (#27511)
+
+- A new `importmempool` RPC has been added. It loads a valid `mempool.dat` file and attempts to
+ add its contents to the mempool. This can be useful to import mempool data from another node
+ without having to modify the datadir contents and without having to restart the node. (#27460)
+ - Warning: Importing untrusted files is dangerous, especially if metadata from the file is taken over.
+ - If you want to apply fee deltas, it is recommended to use the `getprioritisedtransactions` and
+ `prioritisetransaction` RPCs instead of the `apply_fee_delta_priority` option to avoid
+ double-prioritising any already-prioritised transactions in the mempool.
+
+Updated settings
+----------------
+
+- `bitcoind` and `bitcoin-qt` will now raise an error on startup
+ if a datadir that is being used contains a bitcoin.conf file that
+ will be ignored, which can happen when a datadir= line is used in
+ a bitcoin.conf file. The error message is just a diagnostic intended
+ to prevent accidental misconfiguration, and it can be disabled to
+ restore the previous behavior of using the datadir while ignoring
+ the bitcoin.conf contained in it. (#27302)
+
+- Passing an invalid `-debug`, `-debugexclude`, or `-loglevel` logging configuration
+ option now raises an error, rather than logging an easily missed warning. (#27632)
+
+Changes to GUI or wallet related settings can be found in the GUI or Wallet section below.
+
+New settings
+------------
+
+Tools and Utilities
+-------------------
+
+- A new `bitcoinconsensus_verify_script_with_spent_outputs` function is available in libconsensus which optionally accepts the spent outputs of the transaction being verified.
+- A new `bitcoinconsensus_SCRIPT_FLAGS_VERIFY_TAPROOT` flag is available in libconsensus that will verify scripts with the Taproot spending rules.
+
+Wallet
+------
+
+- Wallet loading has changed in this release. Wallets with some corrupted records that could be
+ previously loaded (with warnings) may no longer load. For example, wallets with corrupted
+ address book entries may no longer load. If this happens, it is recommended
+ load the wallet in a previous version of Bitcoin Core and import the data into a new wallet.
+ Please also report an issue to help improve the software and make wallet loading more robust
+ in these cases. (#24914)
+
+- The `createwallet` RPC will no longer create legacy (BDB) wallets when
+ setting `descriptors=false` without also providing the
+ `-deprecatedrpc=create_bdb` option. This is because the legacy wallet is
+ being deprecated in a future release. (#28597)
+
+- The `gettransaction`, `listtransactions`, `listsinceblock` RPCs now return
+ the `abandoned` field for all transactions. Previously, the "abandoned" field
+ was only returned for sent transactions. (#25158)
+
+- The `listdescriptors`, `decodepsbt` and similar RPC methods now show `h` rather than apostrophe (`'`) to indicate
+ hardened derivation. This does not apply when using the `private` parameter, which
+ matches the marker used when descriptor was generated or imported. Newly created
+ wallets use `h`. This change makes it easier to handle descriptor strings manually.
+ E.g. the `importdescriptors` RPC call is easiest to use `h` as the marker: `'["desc": ".../0h/..."]'`.
+ With this change `listdescriptors` will use `h`, so you can copy-paste the result,
+ without having to add escape characters or switch `'` to 'h' manually.
+ Note that this changes the descriptor checksum.
+ For legacy wallets the `hdkeypath` field in `getaddressinfo` is unchanged,
+ nor is the serialization format of wallet dumps. (#26076)
+
+- The `getbalances` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
+ hash and height at the time the balances were calculated. This result shouldn't be cached because importing new keys could invalidate it. (#26094)
+
+- The `gettransaction` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
+ hash and height at the time the transaction information was generated. (#26094)
+
+- The `getwalletinfo` RPC now returns a `lastprocessedblock` JSON object which contains the wallet's last processed block
+ hash and height at the time the wallet information was generated. (#26094)
+
+- Coin selection and transaction building now accounts for unconfirmed low-feerate ancestor transactions. When it is necessary to spend unconfirmed outputs, the wallet will add fees to ensure that the new transaction with its ancestors will achieve a mining score equal to the feerate requested by the user. (#26152)
+
+- For RPC methods which accept `options` parameters ((`importmulti`, `listunspent`,
+ `fundrawtransaction`, `bumpfee`, `send`, `sendall`, `walletcreatefundedpsbt`,
+ `simulaterawtransaction`), it is now possible to pass the options as named
+ parameters without the need for a nested object. (#26485)
+
+This means it is possible make calls like:
+
+```sh
+src/bitcoin-cli -named bumpfee txid fee_rate=100
+```
+
+instead of
+
+```sh
+src/bitcoin-cli -named bumpfee txid options='{"fee_rate": 100}'
+```
+
+- The `deprecatedrpc=walletwarningfield` configuration option has been removed.
+ The `createwallet`, `loadwallet`, `restorewallet` and `unloadwallet` RPCs no
+ longer return the "warning" string field. The same information is provided
+ through the "warnings" field added in v25.0, which returns a JSON array of
+ strings. The "warning" string field was deprecated also in v25.0. (#27757)
+
+- The `signrawtransactionwithkey`, `signrawtransactionwithwallet`,
+ `walletprocesspsbt` and `descriptorprocesspsbt` calls now return the more
+ specific RPC_INVALID_PARAMETER error instead of RPC_MISC_ERROR if their
+ sighashtype argument is malformed. (#28113)
+
+- RPC `walletprocesspsbt`, and `descriptorprocesspsbt` return
+ object now includes field `hex` (if the transaction
+ is complete) containing the serialized transaction
+ suitable for RPC `sendrawtransaction`. (#28414)
+
+- It's now possible to use [Miniscript](https://bitcoin.sipa.be/miniscript/) inside Taproot leaves for descriptor wallets. (#27255)
+
+Descriptors
+-----------
+
+- The usage of hybrid public keys in output descriptors has been removed. Hybrid
+ public keys are an exotic public key encoding not supported by output descriptors
+ (as specified in BIP380 and documented in doc/descriptors.md). Bitcoin Core would
+ previously incorrectly accept descriptors containing such hybrid keys. (#28587)
+
+GUI changes
+-----------
+
+- The transaction list in the GUI no longer provides a special category for "payment to yourself". Now transactions that have both inputs and outputs that affect the wallet are displayed on separate lines for spending and receiving. (gui#119)
+
+- A new menu option allows migrating a legacy wallet based on keys and implied output script types stored in BerkeleyDB (BDB) to a modern wallet that uses descriptors stored in SQLite. (gui#738)
+
+- The PSBT operations dialog marks outputs paying your own wallet with "own address". (gui#740)
+
+- The ability to create legacy wallets is being removed. (gui#764)
+
+Contrib
+-------
+
+- Bash completion files have been renamed from `bitcoin*.bash-completion` to
+ `bitcoin*.bash`. This means completions can be automatically loaded on demand
+ based on invoked commands' names when they are put into the completion
+ directory (found with `pkg-config --variable=completionsdir
+ bash-completion`) without requiring renaming. (#28507)
+
+Low-level changes
+=================
+
+Tests
+-----
+
+- Non-standard transactions are now disabled by default on testnet
+ for relay and mempool acceptance. The previous behaviour can be
+ re-enabled by setting `-acceptnonstdtxn=1`. (#28354)
+
+Credits
+=======
+
+Thanks to everyone who directly contributed to this release:
+
+- 0xb10c
+- Amiti Uttarwar
+- Andrew Chow
+- Andrew Toth
+- Anthony Towns
+- Antoine Poinsot
+- Antoine Riard
+- Ari
+- Aurèle Oulès
+- Ayush Singh
+- Ben Woosley
+- Brandon Odiwuor
+- Brotcrunsher
+- brunoerg
+- Bufo
+- Carl Dong
+- Casey Carter
+- Cory Fields
+- David Álvarez Rosa
+- dergoegge
+- dhruv
+- dimitaracev
+- Erik Arvstedt
+- Erik McKelvey
+- Fabian Jahr
+- furszy
+- glozow
+- Greg Sanders
+- Harris
+- Hennadii Stepanov
+- Hernan Marino
+- ishaanam
+- ismaelsadeeq
+- Jake Rawsthorne
+- James O'Beirne
+- John Moffett
+- Jon Atack
+- josibake
+- kevkevin
+- Kiminuo
+- Larry Ruane
+- Luke Dashjr
+- MarcoFalke
+- Marnix
+- Martin Leitner-Ankerl
+- Martin Zumsande
+- Matthew Zipkin
+- Michael Ford
+- Michael Tidwell
+- mruddy
+- Murch
+- ns-xvrn
+- pablomartin4btc
+- Pieter Wuille
+- Reese Russell
+- Rhythm Garg
+- Ryan Ofsky
+- Sebastian Falbesoner
+- Sjors Provoost
+- stickies-v
+- stratospher
+- Suhas Daftuar
+- TheCharlatan
+- Tim Neubauer
+- Tim Ruffing
+- Vasil Dimov
+- virtu
+- vuittont60
+- willcl-ark
+- Yusuf Sahin HAMZA
+
+As well as to everyone that helped with translations on
+[Transifex](https://www.transifex.com/bitcoin/bitcoin/).
diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp
index 8747373fbf..09976964ff 100644
--- a/src/wallet/rpc/wallet.cpp
+++ b/src/wallet/rpc/wallet.cpp
@@ -70,6 +70,7 @@ static RPCHelpMan getwalletinfo()
{RPCResult::Type::BOOL, "descriptors", "whether this wallet uses descriptors for scriptPubKey management"},
{RPCResult::Type::BOOL, "external_signer", "whether this wallet is configured to use an external signer such as a hardware wallet"},
{RPCResult::Type::BOOL, "blank", "Whether this wallet intentionally does not contain any keys, scripts, or descriptors"},
+ {RPCResult::Type::NUM_TIME, "birthtime", /*optional=*/true, "The start time for blocks scanning. It could be modified by (re)importing any descriptor with an earlier timestamp."},
RESULT_LAST_PROCESSED_BLOCK,
}},
},
@@ -134,6 +135,9 @@ static RPCHelpMan getwalletinfo()
obj.pushKV("descriptors", pwallet->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS));
obj.pushKV("external_signer", pwallet->IsWalletFlagSet(WALLET_FLAG_EXTERNAL_SIGNER));
obj.pushKV("blank", pwallet->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET));
+ if (int64_t birthtime = pwallet->GetBirthTime(); birthtime != UNKNOWN_TIME) {
+ obj.pushKV("birthtime", birthtime);
+ }
AppendLastProcessedBlock(obj, *pwallet);
return obj;
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 5c4bde6e08..1ee4753bdf 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -727,7 +727,7 @@ void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime)
// Cannot determine birthday information, so set the wallet birthday to
// the beginning of time.
nTimeFirstKey = 1;
- } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {
+ } else if (nTimeFirstKey == UNKNOWN_TIME || nCreateTime < nTimeFirstKey) {
nTimeFirstKey = nCreateTime;
}
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 951708fab1..04aabe9534 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -51,6 +51,9 @@ class WalletStorage
virtual bool IsLocked() const = 0;
};
+//! Constant representing an unknown spkm creation time
+static constexpr int64_t UNKNOWN_TIME = std::numeric_limits::max();
+
//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
@@ -288,7 +291,8 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
WatchOnlySet setWatchOnly GUARDED_BY(cs_KeyStore);
WatchKeyMap mapWatchKeys GUARDED_BY(cs_KeyStore);
- int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = 0;
+ // By default, do not scan any block until keys/scripts are generated/imported
+ int64_t nTimeFirstKey GUARDED_BY(cs_KeyStore) = UNKNOWN_TIME;
//! Number of pre-generated keys/scripts (part of the look-ahead process, used to detect payments)
int64_t m_keypool_size GUARDED_BY(cs_KeyStore){DEFAULT_KEYPOOL_SIZE};
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 6aa2c655e1..6fbbfbace4 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -655,9 +655,12 @@ util::Result ChooseSelectionResult(interfaces::Chain& chain, co
// Maximum allowed weight
int max_inputs_weight = MAX_STANDARD_TX_WEIGHT - (coin_selection_params.tx_noinputs_size * WITNESS_SCALE_FACTOR);
- if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_inputs_weight)}) {
- results.push_back(*bnb_result);
- } else append_error(bnb_result);
+ // SFFO frequently causes issues in the context of changeless input sets: skip BnB when SFFO is active
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ if (auto bnb_result{SelectCoinsBnB(groups.positive_group, nTargetValue, coin_selection_params.m_cost_of_change, max_inputs_weight)}) {
+ results.push_back(*bnb_result);
+ } else append_error(bnb_result);
+ }
// As Knapsack and SRD can create change, also deduce change weight.
max_inputs_weight -= (coin_selection_params.change_output_size * WITNESS_SCALE_FACTOR);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index 90700eadf4..8f35f70c33 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -316,7 +316,6 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
coin_selection_params_bnb.m_change_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(coin_selection_params_bnb.change_output_size);
coin_selection_params_bnb.m_cost_of_change = coin_selection_params_bnb.m_effective_feerate.GetFee(coin_selection_params_bnb.change_spend_size) + coin_selection_params_bnb.m_change_fee;
coin_selection_params_bnb.min_viable_change = coin_selection_params_bnb.m_effective_feerate.GetFee(coin_selection_params_bnb.change_spend_size);
- coin_selection_params_bnb.m_subtract_fee_outputs = true;
{
std::unique_ptr wallet = NewWallet(m_node);
@@ -341,6 +340,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
CoinsResult available_coins;
+ coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
add_coin(available_coins, *wallet, 5 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
add_coin(available_coins, *wallet, 3 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
add_coin(available_coins, *wallet, 2 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
@@ -351,7 +351,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
PreSelectedInputs selected_input;
selected_input.Insert(select_coin, coin_selection_params_bnb.m_subtract_fee_outputs);
available_coins.Erase({available_coins.coins[OutputType::BECH32].begin()->outpoint});
- coin_selection_params_bnb.m_effective_feerate = CFeeRate(0);
+
LOCK(wallet->cs_wallet);
const auto result10 = SelectCoins(*wallet, available_coins, selected_input, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(result10);
@@ -365,12 +365,14 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// single coin should be selected when effective fee > long term fee
coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
- add_coin(available_coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
- add_coin(available_coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
- add_coin(available_coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ // Add selectable outputs, increasing their raw amounts by their input fee to make the effective value equal to the raw amount
+ CAmount input_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(/*num_bytes=*/68); // bech32 input size (default test output type)
+ add_coin(available_coins, *wallet, 10 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 9 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 1 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
expected_result.Clear();
- add_coin(10 * CENT, 2, expected_result);
+ add_coin(10 * CENT + input_fee, 2, expected_result);
CCoinControl coin_control;
const auto result11 = SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/{}, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(EquivalentResult(expected_result, *result11));
@@ -379,13 +381,15 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// more coins should be selected when effective fee < long term fee
coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000);
- add_coin(available_coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
- add_coin(available_coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
- add_coin(available_coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ // Add selectable outputs, increasing their raw amounts by their input fee to make the effective value equal to the raw amount
+ input_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(/*num_bytes=*/68); // bech32 input size (default test output type)
+ add_coin(available_coins, *wallet, 10 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 9 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 1 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
expected_result.Clear();
- add_coin(9 * CENT, 2, expected_result);
- add_coin(1 * CENT, 2, expected_result);
+ add_coin(9 * CENT + input_fee, 2, expected_result);
+ add_coin(1 * CENT + input_fee, 2, expected_result);
const auto result12 = SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/{}, 10 * CENT, coin_control, coin_selection_params_bnb);
BOOST_CHECK(EquivalentResult(expected_result, *result12));
available_coins.Clear();
@@ -393,13 +397,15 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
// pre selected coin should be selected even if disadvantageous
coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
- add_coin(available_coins, *wallet, 10 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
- add_coin(available_coins, *wallet, 9 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
- add_coin(available_coins, *wallet, 1 * CENT, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ // Add selectable outputs, increasing their raw amounts by their input fee to make the effective value equal to the raw amount
+ input_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(/*num_bytes=*/68); // bech32 input size (default test output type)
+ add_coin(available_coins, *wallet, 10 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 9 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
+ add_coin(available_coins, *wallet, 1 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
expected_result.Clear();
- add_coin(9 * CENT, 2, expected_result);
- add_coin(1 * CENT, 2, expected_result);
+ add_coin(9 * CENT + input_fee, 2, expected_result);
+ add_coin(1 * CENT + input_fee, 2, expected_result);
coin_control.m_allow_other_inputs = true;
COutput select_coin = available_coins.All().at(1); // pre select 9 coin
coin_control.Select(select_coin.outpoint);
@@ -442,6 +448,42 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
}
}
+BOOST_AUTO_TEST_CASE(bnb_sffo_restriction)
+{
+ // Verify the coin selection process does not produce a BnB solution when SFFO is enabled.
+ // This is currently problematic because it could require a change output. And BnB is specialized on changeless solutions.
+ std::unique_ptr wallet = NewWallet(m_node);
+ WITH_LOCK(wallet->cs_wallet, wallet->SetLastBlockProcessed(300, uint256{})); // set a high block so internal UTXOs are selectable
+
+ FastRandomContext rand{};
+ CoinSelectionParams params{
+ rand,
+ /*change_output_size=*/ 31, // unused value, p2wpkh output size (wallet default change type)
+ /*change_spend_size=*/ 68, // unused value, p2wpkh input size (high-r signature)
+ /*effective_feerate=*/ CFeeRate(3000),
+ /*long_term_feerate=*/ CFeeRate(1000),
+ /*tx_noinputs_size=*/ 0,
+ /*avoid_partial=*/ false,
+ };
+ params.m_subtract_fee_outputs = true;
+ params.m_change_fee = params.m_effective_feerate.GetFee(params.change_output_size);
+ params.m_cost_of_change = params.m_discard_feerate.GetFee(params.change_spend_size) + params.m_change_fee;
+ params.m_min_change_target = params.m_cost_of_change + 1;
+ // Add spendable coin at the BnB selection upper bound
+ CoinsResult available_coins;
+ add_coin(available_coins, *wallet, COIN + params.m_cost_of_change, /*feerate=*/params.m_effective_feerate, /*nAge=*/6, /*fIsFromMe=*/true, /*nInput=*/0, /*spendable=*/true);
+ add_coin(available_coins, *wallet, 0.5 * COIN + params.m_cost_of_change, /*feerate=*/params.m_effective_feerate, /*nAge=*/6, /*fIsFromMe=*/true, /*nInput=*/0, /*spendable=*/true);
+ add_coin(available_coins, *wallet, 0.5 * COIN, /*feerate=*/params.m_effective_feerate, /*nAge=*/6, /*fIsFromMe=*/true, /*nInput=*/0, /*spendable=*/true);
+ // Knapsack will only find a changeless solution on an exact match to the satoshi, SRD doesn’t look for changeless
+ // If BnB were run, it would produce a single input solution with the best waste score
+ auto result = WITH_LOCK(wallet->cs_wallet, return SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/{}, COIN, /*coin_control=*/{}, params));
+ BOOST_CHECK(result.has_value());
+ BOOST_CHECK_NE(result->GetAlgo(), SelectionAlgorithm::BNB);
+ BOOST_CHECK(result->GetInputSet().size() == 2);
+ // We have only considered BnB, SRD, and Knapsack. Test needs to be reevaluated if new algo is added
+ BOOST_CHECK(result->GetAlgo() == SelectionAlgorithm::SRD || result->GetAlgo() == SelectionAlgorithm::KNAPSACK);
+}
+
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
{
FastRandomContext rand{};
diff --git a/src/wallet/test/fuzz/coinselection.cpp b/src/wallet/test/fuzz/coinselection.cpp
index 5c24c14f73..cc65991fc5 100644
--- a/src/wallet/test/fuzz/coinselection.cpp
+++ b/src/wallet/test/fuzz/coinselection.cpp
@@ -115,7 +115,8 @@ FUZZ_TARGET(coinselection)
}
// Run coinselection algorithms
- auto result_bnb = SelectCoinsBnB(group_pos, target, coin_params.m_cost_of_change, MAX_STANDARD_TX_WEIGHT);
+ auto result_bnb = coin_params.m_subtract_fee_outputs ? util::Error{Untranslated("BnB disabled when SFFO is enabled")} :
+ SelectCoinsBnB(group_pos, target, coin_params.m_cost_of_change, MAX_STANDARD_TX_WEIGHT);
if (result_bnb) {
assert(result_bnb->GetChange(coin_params.m_cost_of_change, CAmount{0}) == 0);
assert(result_bnb->GetSelectedValue() >= target);
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 2e7c48092b..cbf21ea1f8 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1050,6 +1050,9 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block);
AddToSpends(wtx, &batch);
+
+ // Update birth time when tx time is older than it.
+ MaybeUpdateBirthTime(wtx.GetTxTime());
}
if (!fInsertedNew)
@@ -1185,6 +1188,10 @@ bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx
}
}
}
+
+ // Update birth time when tx time is older than it.
+ MaybeUpdateBirthTime(wtx.GetTxTime());
+
return true;
}
@@ -1740,11 +1747,11 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set CWallet::Create(WalletContext& context, const std::stri
int64_t time = spk_man->GetTimeFirstKey();
if (!time_first_key || time < *time_first_key) time_first_key = time;
}
- if (time_first_key) walletInstance->m_birth_time = *time_first_key;
+ if (time_first_key) walletInstance->MaybeUpdateBirthTime(*time_first_key);
if (chain && !AttachChain(walletInstance, *chain, rescan_required, error, warnings)) {
return nullptr;
@@ -3516,10 +3523,12 @@ LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan()
void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr spkm_man)
{
+ // Add spkm_man to m_spk_managers before calling any method
+ // that might access it.
const auto& spkm = m_spk_managers[id] = std::move(spkm_man);
// Update birth time if needed
- FirstKeyTimeChanged(spkm.get(), spkm->GetTimeFirstKey());
+ MaybeUpdateBirthTime(spkm->GetTimeFirstKey());
}
void CWallet::SetupLegacyScriptPubKeyMan()
@@ -3552,7 +3561,7 @@ void CWallet::ConnectScriptPubKeyManNotifiers()
for (const auto& spk_man : GetActiveScriptPubKeyMans()) {
spk_man->NotifyWatchonlyChanged.connect(NotifyWatchonlyChanged);
spk_man->NotifyCanGetAddressesChanged.connect(NotifyCanGetAddressesChanged);
- spk_man->NotifyFirstKeyTimeChanged.connect(std::bind(&CWallet::FirstKeyTimeChanged, this, std::placeholders::_1, std::placeholders::_2));
+ spk_man->NotifyFirstKeyTimeChanged.connect(std::bind(&CWallet::MaybeUpdateBirthTime, this, std::placeholders::_2));
}
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 758fbe2174..67e22cc4bd 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -687,8 +687,8 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
bool ImportPubKeys(const std::vector& ordered_pubkeys, const std::map& pubkey_map, const std::map>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportScriptPubKeys(const std::string& label, const std::set& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /** Updates wallet birth time if 'new_birth_time' is below it */
- void FirstKeyTimeChanged(const ScriptPubKeyMan* spkm, int64_t new_birth_time);
+ /** Updates wallet birth time if 'time' is below it */
+ void MaybeUpdateBirthTime(int64_t time);
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
@@ -878,6 +878,9 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
/* Returns true if the wallet can give out new addresses. This means it has keys in the keypool or can generate new keys */
bool CanGetAddresses(bool internal = false) const;
+ /* Returns the time of the first created key or, in case of an import, it could be the time of the first received transaction */
+ int64_t GetBirthTime() const { return m_birth_time; }
+
/* Function that will remove potentially abandoned coinstakes, returning the input to the wallet. */
void AbandonOrphanedCoinstakes();
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 92eca46f05..3a40b7a34f 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1401,13 +1401,13 @@ bool WalletBatch::EraseRecords(const std::unordered_set& types)
}
// Make a copy of key to avoid data being deleted by the following read of the type
- Span key_data{key};
+ const SerializeData key_data{key.begin(), key.end()};
std::string type;
key >> type;
if (types.count(type) > 0) {
- if (!m_batch->Erase(key_data)) {
+ if (!m_batch->Erase(Span{key_data})) {
cursor.reset(nullptr);
m_batch->TxnAbort();
return false; // erase failed
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 1be0e3ee6c..c80f5f2f4e 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -199,6 +199,8 @@
'wallet_createwallet.py --descriptors',
'wallet_watchonly.py --legacy-wallet',
'wallet_watchonly.py --usecli --legacy-wallet',
+ 'wallet_reindex.py --legacy-wallet',
+ 'wallet_reindex.py --descriptors',
'wallet_reorgsrestore.py',
'wallet_conflicts.py --legacy-wallet',
'wallet_conflicts.py --descriptors',
diff --git a/test/functional/wallet_reindex.py b/test/functional/wallet_reindex.py
new file mode 100755
index 0000000000..5388de4b71
--- /dev/null
+++ b/test/functional/wallet_reindex.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+# Copyright (c) 2023-present The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or https://www.opensource.org/licenses/mit-license.php.
+
+"""Test wallet-reindex interaction"""
+
+import time
+
+from test_framework.blocktools import COINBASE_MATURITY
+from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import (
+ assert_equal,
+)
+BLOCK_TIME = 60 * 10
+
+class WalletReindexTest(BitcoinTestFramework):
+ def add_options(self, parser):
+ self.add_wallet_options(parser)
+
+ def set_test_params(self):
+ self.num_nodes = 1
+ self.setup_clean_chain = True
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def advance_time(self, node, secs):
+ self.node_time += secs
+ node.setmocktime(self.node_time)
+
+ # Verify the wallet updates the birth time accordingly when it detects a transaction
+ # with a time older than the oldest descriptor timestamp.
+ # This could happen when the user blindly imports a descriptor with 'timestamp=now'.
+ def birthtime_test(self, node, miner_wallet):
+ self.log.info("Test birth time update during tx scanning")
+ # Fund address to test
+ wallet_addr = miner_wallet.getnewaddress()
+ tx_id = miner_wallet.sendtoaddress(wallet_addr, 2)
+
+ # Generate 50 blocks, one every 10 min to surpass the 2 hours rescan window the wallet has
+ for _ in range(50):
+ self.generate(node, 1)
+ self.advance_time(node, BLOCK_TIME)
+
+ # Now create a new wallet, and import the descriptor
+ node.createwallet(wallet_name='watch_only', disable_private_keys=True, load_on_startup=True)
+ wallet_watch_only = node.get_wallet_rpc('watch_only')
+ # Blank wallets don't have a birth time
+ assert 'birthtime' not in wallet_watch_only.getwalletinfo()
+
+ # For a descriptors wallet: Import address with timestamp=now.
+ # For legacy wallet: There is no way of importing a script/address with a custom time. The wallet always imports it with birthtime=1.
+ # In both cases, disable rescan to not detect the transaction.
+ wallet_watch_only.importaddress(wallet_addr, rescan=False)
+ assert_equal(len(wallet_watch_only.listtransactions()), 0)
+
+ # Depending on the wallet type, the birth time changes.
+ wallet_birthtime = wallet_watch_only.getwalletinfo()['birthtime']
+ if self.options.descriptors:
+ # As blocks were generated every 10 min, the chain MTP timestamp is node_time - 60 min.
+ assert_equal(self.node_time - BLOCK_TIME * 6, wallet_birthtime)
+ else:
+ # No way of importing scripts/addresses with a custom time on a legacy wallet.
+ # It's always set to the beginning of time.
+ assert_equal(wallet_birthtime, 1)
+
+ # Rescan the wallet to detect the missing transaction
+ wallet_watch_only.rescanblockchain()
+ assert_equal(wallet_watch_only.gettransaction(tx_id)['confirmations'], 50)
+ assert_equal(wallet_watch_only.getbalances()['mine' if self.options.descriptors else 'watchonly']['trusted'], 2)
+
+ # Reindex and wait for it to finish
+ with node.assert_debug_log(expected_msgs=["initload thread exit"]):
+ self.restart_node(0, extra_args=['-reindex=1', f'-mocktime={self.node_time}'])
+ node.syncwithvalidationinterfacequeue()
+
+ # Verify the transaction is still 'confirmed' after reindex
+ wallet_watch_only = node.get_wallet_rpc('watch_only')
+ tx_info = wallet_watch_only.gettransaction(tx_id)
+ assert_equal(tx_info['confirmations'], 50)
+
+ # Depending on the wallet type, the birth time changes.
+ if self.options.descriptors:
+ # For descriptors, verify the wallet updated the birth time to the transaction time
+ assert_equal(tx_info['time'], wallet_watch_only.getwalletinfo()['birthtime'])
+ else:
+ # For legacy, as the birth time was set to the beginning of time, verify it did not change
+ assert_equal(wallet_birthtime, 1)
+
+ wallet_watch_only.unloadwallet()
+
+ def run_test(self):
+ node = self.nodes[0]
+ self.node_time = int(time.time())
+ node.setmocktime(self.node_time)
+
+ # Fund miner
+ node.createwallet(wallet_name='miner', load_on_startup=True)
+ miner_wallet = node.get_wallet_rpc('miner')
+ self.generatetoaddress(node, COINBASE_MATURITY + 10, miner_wallet.getnewaddress())
+
+ # Tests
+ self.birthtime_test(node, miner_wallet)
+
+
+if __name__ == '__main__':
+ WalletReindexTest().main()