diff options
author | glozow <gloriajzhao@gmail.com> | 2024-01-09 14:34:38 +0000 |
---|---|---|
committer | glozow <gloriajzhao@gmail.com> | 2024-01-09 15:39:19 +0000 |
commit | 04edf9f5862c3ca2d1aed6b7a0f24b8d2914f96c (patch) | |
tree | 79e01917350b320055904918a33f09d41198044f | |
parent | 44d8b13c81e5276eb610c99f227a4d090cc532f6 (diff) | |
parent | 7b79e54474b86864c81148c74824bfe4b732412d (diff) |
Merge bitcoin/bitcoin#29011: [26.x] Backports
7b79e54474b86864c81148c74824bfe4b732412d doc: update release notes for 26.x (fanquake)
ccf00b1e6eb811a3af2c22518a832dd9a51f8aa4 wallet: Fix use-after-free in WalletBatch::EraseRecords (MarcoFalke)
40252e184eb188c4af767adb4918f729015910e8 ci: Set `HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK` to avoid failures (Hennadii Stepanov)
b06b14e68d81f8be091d32db162c8d85b9d29e4c rpc: getwalletinfo, return wallet 'birthtime' (furszy)
12834012c2a31f7c33e3410d67edba1eaec53ae9 test: coverage for wallet birth time interaction with -reindex (furszy)
0fa47e2569a40e74db701f16421b8638a486ebd3 wallet: fix legacy spkm default birth time (furszy)
84f4a6c14587344652ef34dbf944364580417a87 wallet: birth time update during tx scanning (furszy)
074296dd60ef49bf6b28b191472f45ea4f995b4b refactor: rename FirstKeyTimeChanged to MaybeUpdateBirthTime (furszy)
35039ac3cc1c2318762907961aee412a7239ce9b fuzz: disable BnB when SFFO is enabled (furszy)
903b4623d369d86925093fdce08d18019e5266e1 test: add coverage for BnB-SFFO restriction (furszy)
05d0576d3c03ae3797c91ac51bff4e2a18d6a993 wallet: create tx, log resulting coin selection info (furszy)
5493ebbe7428f95e41d5c7a64aea9e7f33190579 wallet: skip BnB when SFFO is active (Murch)
b15e2e2cec596662275e790449fd250b6be75076 test: add regression test for the getrawtransaction segfault (Martin Zumsande)
5097bb3389fe3add504851302918692b3f05db70 rpc: fix getrawtransaction segfault (Martin Zumsande)
81e744a9a6a55cb5c2b88cd6eeb9dd6523f3a438 ci: Use Ubuntu 24.04 Noble for asan (MarcoFalke)
69e53d1e47613efb831d01e446b790d21c35ba4b ci: Use Ubuntu 24.04 Noble for tsan,tidy,fuzz (MarcoFalke)
d2c80b6f52f255c8d9237177bf0bd1b160409b81 doc: Missing additions to 26.0 release notes (fanquake)
8dc2c753ff3b89c3afefdc791fff38ac590488da doc: add historical release notes for 26.0 (fanquake)
Pull request description:
Backports for `26.x`. Currently:
* https://github.com/bitcoin/bitcoin/pull/28920
* https://github.com/bitcoin/bitcoin/pull/28992
* https://github.com/bitcoin/bitcoin/pull/28994
* https://github.com/bitcoin/bitcoin/pull/29003
* https://github.com/bitcoin/bitcoin/pull/29023
* https://github.com/bitcoin/bitcoin/pull/29080
* https://github.com/bitcoin/bitcoin/pull/29176
ACKs for top commit:
TheCharlatan:
ACK 7b79e54474b86864c81148c74824bfe4b732412d
glozow:
ACK 7b79e54474b86864c81148c74824bfe4b732412d, matches mine
Tree-SHA512: 898aec76ed3ad35e0edd0980af5bcc21bd60003bbf69e0b4f473ed2aa38c4e3b360b930bc3747cf798195906a8f9fe66417524f5e5ef40fa68f1c1aaceebdeb0
-rw-r--r-- | .cirrus.yml | 4 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 2 | ||||
-rwxr-xr-x | ci/test/00_setup_env_native_asan.sh | 2 | ||||
-rwxr-xr-x | ci/test/00_setup_env_native_fuzz.sh | 2 | ||||
-rwxr-xr-x | ci/test/00_setup_env_native_tidy.sh | 2 | ||||
-rwxr-xr-x | ci/test/00_setup_env_native_tsan.sh | 2 | ||||
-rw-r--r-- | doc/release-notes.md | 288 | ||||
-rw-r--r-- | doc/release-notes/release-notes-26.0.md | 357 | ||||
-rw-r--r-- | src/rpc/rawtransaction.cpp | 5 | ||||
-rw-r--r-- | src/wallet/rpc/wallet.cpp | 4 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.cpp | 2 | ||||
-rw-r--r-- | src/wallet/scriptpubkeyman.h | 6 | ||||
-rw-r--r-- | src/wallet/spend.cpp | 10 | ||||
-rw-r--r-- | src/wallet/test/coinselector_tests.cpp | 76 | ||||
-rw-r--r-- | src/wallet/test/fuzz/coinselection.cpp | 3 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 21 | ||||
-rw-r--r-- | src/wallet/wallet.h | 7 | ||||
-rw-r--r-- | src/wallet/walletdb.cpp | 4 | ||||
-rwxr-xr-x | test/functional/rpc_rawtransaction.py | 13 | ||||
-rwxr-xr-x | test/functional/test_runner.py | 2 | ||||
-rwxr-xr-x | test/functional/wallet_reindex.py | 108 |
21 files changed, 601 insertions, 319 deletions
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: - <https://bitcoincore.org/bin/bitcoin-core-26.0/> + <https://bitcoincore.org/bin/bitcoin-core-26.x/> 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 (<https://github.com/bitcoin/bitcoin/blob/master/doc/design/assumeutxo.md>). - - `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: + + <https://bitcoincore.org/bin/bitcoin-core-26.0/> + +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: + + <https://github.com/bitcoin/bitcoin/issues> + +To receive security and update notifications, please subscribe to: + + <https://bitcoincore.org/en/list/announcements/join/> + +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 (<https://github.com/bitcoin/bitcoin/blob/master/doc/design/assumeutxo.md>). + + `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/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 16705b3ce2..b30b0bbfeb 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -396,7 +396,7 @@ static RPCHelpMan getrawtransaction() LOCK(cs_main); blockindex = chainman.m_blockman.LookupBlockIndex(hash_block); } - if (verbosity == 1) { + if (verbosity == 1 || !blockindex) { TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate()); return result; } @@ -405,8 +405,7 @@ static RPCHelpMan getrawtransaction() CBlock block; const bool is_block_pruned{WITH_LOCK(cs_main, return chainman.m_blockman.IsBlockPruned(blockindex))}; - if (tx->IsCoinBase() || - !blockindex || is_block_pruned || + if (tx->IsCoinBase() || is_block_pruned || !(chainman.m_blockman.UndoReadFromDisk(blockUndo, *blockindex) && chainman.m_blockman.ReadBlockFromDisk(block, *blockindex))) { TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate()); return result; diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index 164ce9afed..c635093344 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -69,6 +69,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, }}, }, @@ -132,6 +133,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 bc3327cdb2..f5f3a17ae7 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -709,7 +709,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 7c0eca1475..7231486df0 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -51,6 +51,9 @@ public: virtual bool IsLocked() const = 0; }; +//! Constant representing an unknown spkm creation time +static constexpr int64_t UNKNOWN_TIME = std::numeric_limits<int64_t>::max(); + //! Default for -keypool static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; @@ -286,7 +289,8 @@ private: 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 8314a2ddfa..e473c5a093 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -693,9 +693,12 @@ util::Result<SelectionResult> 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); @@ -1260,6 +1263,7 @@ static util::Result<CreatedTransactionResult> CreateTransactionInternal( // accidental re-use. reservedest.KeepDestination(); + wallet.WalletLogPrintf("Coin Selection: Algorithm:%s, Waste Metric Score:%d\n", GetAlgorithmName(result.GetAlgo()), result.GetWaste()); wallet.WalletLogPrintf("Fee Calculation: Fee:%d Bytes:%u Tgt:%d (requested %d) Reason:\"%s\" Decay %.5f: Estimation: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out) Fail: (%g - %g) %.2f%% %.1f/(%.1f %d mem %.1f out)\n", current_fee, nBytes, feeCalc.returnedTarget, feeCalc.desiredTarget, StringForFeeReason(feeCalc.reason), feeCalc.est.decay, feeCalc.est.pass.start, feeCalc.est.pass.end, diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 9569210ba0..2b7ae888d8 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -320,7 +320,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<CWallet> wallet = NewWallet(m_node); @@ -345,6 +344,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); @@ -355,7 +355,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); @@ -370,12 +370,14 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000); coin_selection_params_bnb.m_long_term_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 + 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)); @@ -385,13 +387,15 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000); coin_selection_params_bnb.m_long_term_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); 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(); @@ -400,13 +404,15 @@ BOOST_AUTO_TEST_CASE(bnb_search_test) coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000); coin_selection_params_bnb.m_long_term_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); 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); @@ -449,6 +455,44 @@ 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<CWallet> 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) + /*min_change_target=*/ 0, // dummy, set later + /*effective_feerate=*/ CFeeRate(3000), + /*long_term_feerate=*/ CFeeRate(1000), + /*discard_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 4caf96b18d..ade3ec3f60 100644 --- a/src/wallet/test/fuzz/coinselection.cpp +++ b/src/wallet/test/fuzz/coinselection.cpp @@ -116,7 +116,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 162d7f9ec7..78febb8195 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1080,6 +1080,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) @@ -1215,6 +1218,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; } @@ -1763,11 +1770,11 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri return true; } -void CWallet::FirstKeyTimeChanged(const ScriptPubKeyMan* spkm, int64_t new_birth_time) +void CWallet::MaybeUpdateBirthTime(int64_t time) { int64_t birthtime = m_birth_time.load(); - if (new_birth_time < birthtime) { - m_birth_time = new_birth_time; + if (time < birthtime) { + m_birth_time = time; } } @@ -3103,7 +3110,7 @@ std::shared_ptr<CWallet> 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; @@ -3494,10 +3501,12 @@ LegacyScriptPubKeyMan* CWallet::GetOrCreateLegacyScriptPubKeyMan() void CWallet::AddScriptPubKeyMan(const uint256& id, std::unique_ptr<ScriptPubKeyMan> 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() @@ -3530,7 +3539,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 9333493a6e..c7cd3b3959 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -678,8 +678,8 @@ public: bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& 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<CScript>& 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}; unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET}; @@ -877,6 +877,9 @@ public: /* 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; } + /** * Blocks until the wallet state is up-to-date to /at least/ the current * chain at the time this function is entered 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<std::string>& 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/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py index 2395935620..c12865b5e3 100755 --- a/test/functional/rpc_rawtransaction.py +++ b/test/functional/rpc_rawtransaction.py @@ -32,6 +32,7 @@ from test_framework.script import ( from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_equal, + assert_greater_than, assert_raises_rpc_error, ) from test_framework.wallet import ( @@ -70,7 +71,7 @@ class RawTransactionsTest(BitcoinTestFramework): self.extra_args = [ ["-txindex"], ["-txindex"], - [], + ["-fastprune", "-prune=1"], ] # whitelist all peers to speed up tx relay / mempool sync for args in self.extra_args: @@ -85,7 +86,6 @@ class RawTransactionsTest(BitcoinTestFramework): self.wallet = MiniWallet(self.nodes[0]) self.getrawtransaction_tests() - self.getrawtransaction_verbosity_tests() self.createrawtransaction_tests() self.sendrawtransaction_tests() self.sendrawtransaction_testmempoolaccept_tests() @@ -94,6 +94,8 @@ class RawTransactionsTest(BitcoinTestFramework): if self.is_specified_wallet_compiled() and not self.options.descriptors: self.import_deterministic_coinbase_privkeys() self.raw_multisig_transaction_legacy_tests() + self.getrawtransaction_verbosity_tests() + def getrawtransaction_tests(self): tx = self.wallet.send_self_transfer(from_node=self.nodes[0]) @@ -243,6 +245,13 @@ class RawTransactionsTest(BitcoinTestFramework): coin_base = self.nodes[1].getblock(block1)['tx'][0] gottx = self.nodes[1].getrawtransaction(txid=coin_base, verbosity=2, blockhash=block1) assert 'fee' not in gottx + # check that verbosity 2 for a mempool tx will fallback to verbosity 1 + # Do this with a pruned chain, as a regression test for https://github.com/bitcoin/bitcoin/pull/29003 + self.generate(self.nodes[2], 400) + assert_greater_than(self.nodes[2].pruneblockchain(250), 0) + mempool_tx = self.wallet.send_self_transfer(from_node=self.nodes[2])['txid'] + gottx = self.nodes[2].getrawtransaction(txid=mempool_tx, verbosity=2) + assert 'fee' not in gottx def createrawtransaction_tests(self): self.log.info("Test createrawtransaction") diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index fbf48a0e4d..148902032f 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -203,6 +203,8 @@ BASE_SCRIPTS = [ '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() |