diff --git a/.github/actions/configure-docker/action.yml b/.github/actions/configure-docker/action.yml index 09a1e6fd3af..fc014156590 100644 --- a/.github/actions/configure-docker/action.yml +++ b/.github/actions/configure-docker/action.yml @@ -4,12 +4,21 @@ inputs: cache-provider: description: 'gha or cirrus cache provider' required: true - options: - - gh - - cirrus runs: using: 'composite' steps: + - name: Check inputs + shell: bash + run: | + # We expect only gha or cirrus as inputs to cache-provider + case "${{ inputs.cache-provider }}" in + gha|cirrus) + ;; + *) + echo "::warning title=Unknown input to configure docker action::Provided value was ${{ inputs.cache-provider }}" + ;; + esac + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: @@ -22,8 +31,12 @@ runs: uses: actions/github-script@v6 with: script: | - core.exportVariable('ACTIONS_CACHE_URL', process.env['ACTIONS_CACHE_URL']) - core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env['ACTIONS_RUNTIME_TOKEN']) + Object.keys(process.env).forEach(function (key) { + if (key.startsWith('ACTIONS_')) { + core.info(`Exporting ${key}`); + core.exportVariable(key, process.env[key]); + } + }); - name: Construct docker build cache args shell: bash diff --git a/.github/actions/configure-environment/action.yml b/.github/actions/configure-environment/action.yml index aae5016bdce..e2a26b7184d 100644 --- a/.github/actions/configure-environment/action.yml +++ b/.github/actions/configure-environment/action.yml @@ -17,7 +17,7 @@ runs: - name: Set cache hashes shell: bash run: | - echo "DEPENDS_HASH=$(git ls-tree HEAD depends "ci/test/$FILE_ENV" | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV + echo "DEPENDS_HASH=$(git ls-tree HEAD depends "$FILE_ENV" | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV echo "PREVIOUS_RELEASES_HASH=$(git ls-tree HEAD test/get_previous_releases.py | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - name: Get container name diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5cae564e4d..c1355c8cf12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -536,7 +536,7 @@ jobs: lint: name: 'lint' needs: runners - runs-on: ${{ needs.runners.outputs.use-cirrus-runners == 'true' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-xs' || 'ubuntu-24.04' }} + runs-on: ${{ needs.runners.outputs.provider == 'cirrus' && 'ghcr.io/cirruslabs/ubuntu-runner-amd64:24.04-xs' || 'ubuntu-24.04' }} if: ${{ vars.SKIP_BRANCH_PUSH != 'true' || github.event_name == 'pull_request' }} timeout-minutes: 20 env: diff --git a/ci/test/00_setup_env_win64.sh b/ci/test/00_setup_env_win64.sh index 13e794bd6d1..f929f34c68f 100755 --- a/ci/test/00_setup_env_win64.sh +++ b/ci/test/00_setup_env_win64.sh @@ -8,7 +8,6 @@ export LC_ALL=C.UTF-8 export CONTAINER_NAME=ci_win64 export CI_IMAGE_NAME_TAG="mirror.gcr.io/ubuntu:24.04" # Check that https://packages.ubuntu.com/noble/g++-mingw-w64-x86-64-posix (version 13.x, similar to guix) can cross-compile -export CI_IMAGE_PLATFORM="linux/amd64" export HOST=x86_64-w64-mingw32 export PACKAGES="g++-mingw-w64-x86-64-posix nsis" export RUN_UNIT_TESTS=false diff --git a/depends/funcs.mk b/depends/funcs.mk index 28baf47147a..9e8d9ddd006 100644 --- a/depends/funcs.mk +++ b/depends/funcs.mk @@ -38,7 +38,7 @@ endef define fetch_file ( test -f $$($(1)_source_dir)/$(4) || \ ( $(call fetch_file_inner,$(1),$(2),$(3),$(4),$(5)) || \ - $(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(3),$(4),$(5)))) + $(call fetch_file_inner,$(1),$(FALLBACK_DOWNLOAD_PATH),$(4),$(4),$(5)))) endef # Shell script to create a source tarball in $(1)_source from local directory diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 9ac4088e81c..608e8cdbff0 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -16,6 +16,7 @@ $(package)_patches += qtbase_avoid_native_float16.patch $(package)_patches += qtbase_avoid_qmain.patch $(package)_patches += qtbase_platformsupport.patch $(package)_patches += qtbase_plugins_cocoa.patch +$(package)_patches += qtbase_plugins_windows11style.patch $(package)_patches += qtbase_skip_tools.patch $(package)_patches += rcc_hardcode_timestamp.patch $(package)_patches += qttools_skip_dependencies.patch @@ -261,6 +262,7 @@ define $(package)_preprocess_cmds patch -p1 -i $($(package)_patch_dir)/qtbase_avoid_qmain.patch && \ patch -p1 -i $($(package)_patch_dir)/qtbase_platformsupport.patch && \ patch -p1 -i $($(package)_patch_dir)/qtbase_plugins_cocoa.patch && \ + patch -p1 -i $($(package)_patch_dir)/qtbase_plugins_windows11style.patch && \ patch -p1 -i $($(package)_patch_dir)/qtbase_skip_tools.patch && \ patch -p1 -i $($(package)_patch_dir)/rcc_hardcode_timestamp.patch endef diff --git a/depends/patches/qt/qtbase_plugins_windows11style.patch b/depends/patches/qt/qtbase_plugins_windows11style.patch new file mode 100644 index 00000000000..16d1d3ab5ae --- /dev/null +++ b/depends/patches/qt/qtbase_plugins_windows11style.patch @@ -0,0 +1,113 @@ +QWindows11Style: Calculate Spinbox size based on CommonStyle size +Use the calculation from Commonstyle and add the increased padding and +horizontally layouted buttons to the horizontal size hint. + +Fixes: QTBUG-130288 +Change-Id: I7932b782e7873a0178091a51379f17453eb585fd + +Upstream commits: + - Qt 6.8.1: 9107817eaceaacc968dbc767c24594566d637b8c + - Qt 6.9.0: 96d46cad43517adefa2eb7cb8819a0b2cc9241e6 + +--- a/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp ++++ b/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp +@@ -2048,39 +2048,22 @@ QSize QWindows11Style::sizeFromContents(ContentsType type, const QStyleOption *o + } + break; + #endif ++#if QT_CONFIG(spinbox) + case QStyle::CT_SpinBox: { + if (const auto *spinBoxOpt = qstyleoption_cast(option)) { + // Add button + frame widths +- int width = 0; +- +- if (const QDateTimeEdit *spinBox = qobject_cast(widget)) { +- const QSize textSizeMin = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->minimumDateTime().toString(spinBox->displayFormat())); +- const QSize textSizeMax = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->maximumDateTime().toString(spinBox->displayFormat())); +- width = qMax(textSizeMin.width(),textSizeMax.width()); +- } else if (const QSpinBox *spinBox = qobject_cast(widget)) { +- const QSize textSizeMin = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->minimum())); +- const QSize textSizeMax = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->maximum())); +- width = qMax(textSizeMin.width(),textSizeMax.width()); +- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->prefix()).width(); +- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->suffix()).width(); +- +- } else if (const QDoubleSpinBox *spinBox = qobject_cast(widget)) { +- const QSize textSizeMin = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->minimum())); +- const QSize textSizeMax = spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, QString::number(spinBox->maximum())); +- width = qMax(textSizeMin.width(),textSizeMax.width()); +- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->prefix()).width(); +- width += spinBoxOpt->fontMetrics.size(Qt::TextSingleLine, spinBox->suffix()).width(); +- } + const qreal dpi = QStyleHelper::dpi(option); + const bool hasButtons = (spinBoxOpt->buttonSymbols != QAbstractSpinBox::NoButtons); +- const int buttonWidth = hasButtons ? 2 * qRound(QStyleHelper::dpiScaled(16, dpi)) : 0; ++ const int margins = 8; ++ const int buttonWidth = hasButtons ? qRound(QStyleHelper::dpiScaled(16, dpi)) : 0; + const int frameWidth = spinBoxOpt->frame ? proxy()->pixelMetric(PM_SpinBoxFrameWidth, + spinBoxOpt, widget) : 0; +- contentSize.setWidth(2 * 12 + width); +- contentSize += QSize(buttonWidth + 2 * frameWidth, 2 * frameWidth); ++ ++ contentSize += QSize(2 * buttonWidth + 2 * frameWidth + 2 * margins, 2 * frameWidth); + } + break; + } ++#endif + default: + contentSize = QWindowsVistaStyle::sizeFromContents(type, option, size, widget); + break; + + +Windows11Style: don't set minimum width for QAbstractSpinBox + +There is no need to set a minimum width for QAbstractSpinBox in +QWindows11Style::polish() as this might override the user preferences. +Also the minimum size handling is now properly done within +sizeFromContents(). + +Change-Id: Ibc1fd7a6f862fc85e3739025b9de581aa235d74c + +Upstream commits: + - Qt 6.8.3: f86da3d3f853adb1a5b823c1cc7be6db4a0265f3 + - Qt 6.9.0: b93a8dfdfe6900cb542fdc587dd2682007a6ac53 + - Qt 6.10.0: 2ec4c28470de115c16944653a5d4f6209452d56c + +--- a/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp ++++ b/qtbase/src/plugins/styles/modernwindows/qwindows11style.cpp +@@ -29,7 +29,6 @@ QT_BEGIN_NAMESPACE + + const static int topLevelRoundingRadius = 8; //Radius for toplevel items like popups for round corners + const static int secondLevelRoundingRadius = 4; //Radius for second level items like hovered menu item round corners +-constexpr QLatin1StringView originalWidthProperty("_q_windows11_style_original_width"); + + enum WINUI3Color { + subtleHighlightColor, //Subtle highlight based on alpha used for hovered elements +@@ -2140,13 +2139,6 @@ void QWindows11Style::polish(QWidget* widget) + pal.setColor(QPalette::ButtonText, pal.text().color()); + pal.setColor(QPalette::BrightText, pal.text().color()); + widget->setPalette(pal); +- } else if (widget->inherits("QAbstractSpinBox")) { +- const int minWidth = 2 * 24 + 40; +- const int originalWidth = widget->size().width(); +- if (originalWidth < minWidth) { +- widget->resize(minWidth, widget->size().height()); +- widget->setProperty(originalWidthProperty.constData(), originalWidth); +- } + } else if (widget->inherits("QAbstractButton") || widget->inherits("QToolButton")) { + widget->setAutoFillBackground(false); + auto pal = widget->palette(); +@@ -2191,13 +2183,6 @@ void QWindows11Style::unpolish(QWidget *widget) + scrollarea->viewport()->setPalette(pal); + scrollarea->viewport()->setProperty("_q_original_background_palette", QVariant()); + } +- if (widget->inherits("QAbstractSpinBox")) { +- const QVariant originalWidth = widget->property(originalWidthProperty.constData()); +- if (originalWidth.isValid()) { +- widget->resize(originalWidth.toInt(), widget->size().height()); +- widget->setProperty(originalWidthProperty.constData(), QVariant()); +- } +- } + } + + /* diff --git a/doc/build-freebsd.md b/doc/build-freebsd.md index fee20f84ab5..b5940c0f7ce 100644 --- a/doc/build-freebsd.md +++ b/doc/build-freebsd.md @@ -101,5 +101,5 @@ cmake -B build -DENABLE_WALLET=OFF ```bash cmake --build build # Append "-j N" for N parallel jobs. -ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available. +ctest --test-dir build # Append "-j N" for N parallel tests. ``` diff --git a/doc/build-netbsd.md b/doc/build-netbsd.md index f2631777221..103e62568a4 100644 --- a/doc/build-netbsd.md +++ b/doc/build-netbsd.md @@ -34,7 +34,7 @@ cmake -B build SQLite is required for the wallet: ```bash -pkgin sqlite3 +pkgin install sqlite3 ``` To build Bitcoin Core without the wallet, use `-DENABLE_WALLET=OFF`. @@ -42,7 +42,7 @@ To build Bitcoin Core without the wallet, use `-DENABLE_WALLET=OFF`. Cap'n Proto is needed for IPC functionality (see [multiprocess.md](multiprocess.md)): ```bash -pkgin capnproto +pkgin install capnproto ``` Compile with `-DENABLE_IPC=OFF` if you do not need IPC functionality. @@ -84,7 +84,7 @@ Otherwise, if you don't need QR encoding support, use the `-DWITH_QRENCODE=OFF` Bitcoin Core can provide notifications via ZeroMQ. If the package is installed, support will be compiled in. ```bash -pkgin zeromq +pkgin install zeromq ``` #### Test Suite Dependencies @@ -115,5 +115,5 @@ Build and run the tests: ```bash cmake --build build # Append "-j N" for N parallel jobs. -ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available. +ctest --test-dir build # Append "-j N" for N parallel tests. ``` diff --git a/doc/build-openbsd.md b/doc/build-openbsd.md index 99d4ec25165..fdeafe88d76 100644 --- a/doc/build-openbsd.md +++ b/doc/build-openbsd.md @@ -93,7 +93,7 @@ Run `cmake -B build -LH` to see the full list of available options. ```bash cmake --build build # Append "-j N" for N parallel jobs. -ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available. +ctest --test-dir build # Append "-j N" for N parallel tests. ``` ## Resource limits diff --git a/doc/build-osx.md b/doc/build-osx.md index e1bf037490a..001c0188e8a 100644 --- a/doc/build-osx.md +++ b/doc/build-osx.md @@ -170,7 +170,7 @@ Run the following in your terminal to compile Bitcoin Core: ``` bash cmake --build build # Append "-j N" here for N parallel jobs. -ctest --test-dir build # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available. +ctest --test-dir build # Append "-j N" for N parallel tests. ``` ### 3. Deploy (optional) diff --git a/doc/build-windows-msvc.md b/doc/build-windows-msvc.md index 2d85d223e97..712ff8bb57c 100644 --- a/doc/build-windows-msvc.md +++ b/doc/build-windows-msvc.md @@ -55,7 +55,7 @@ In the following instructions, the "Debug" configuration can be specified instea ``` cmake -B build --preset vs2022-static # It might take a while if the vcpkg binary cache is unpopulated or invalidated. cmake --build build --config Release # Append "-j N" for N parallel jobs. -ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available. +ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests. cmake --install build --config Release # Optional. ``` @@ -64,7 +64,7 @@ cmake --install build --config Release # Optional. ``` cmake -B build --preset vs2022 -DBUILD_GUI=OFF # It might take a while if the vcpkg binary cache is unpopulated or invalidated. cmake --build build --config Release # Append "-j N" for N parallel jobs. -ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests. Some tests are disabled if Python 3 is not available. +ctest --test-dir build --build-config Release # Append "-j N" for N parallel tests. ``` ### 6. vcpkg-specific Issues and Workarounds diff --git a/doc/release-notes.md b/doc/release-notes.md index a855d10cc98..43aff7ec531 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,9 +1,9 @@ -v30.0 Release Notes +v30.x Release Notes =================== -Bitcoin Core version v30.0 is now available from: +Bitcoin Core version v30.x is now available from: - + This release includes new features, various bug fixes and performance improvements, as well as updated translations. @@ -40,399 +40,57 @@ unsupported systems. Notable changes =============== -Policy ------- +### Build -- The maximum number of potentially executed legacy signature operations in a - single standard transaction is now limited to 2500. Signature operations in all - previous output scripts, in all input scripts, as well as all P2SH redeem - scripts (if there are any) are counted toward the limit. The new limit is - assumed to not affect any known typically formed standard transactions. The - change was done to prepare for a possible BIP54 deployment in the future. (#32521) +- #33580 depends: Use `$(package)_file_name` when downloading from the fallback +- #33906 depends: Add patch for Windows11Style plugin -- `-datacarriersize` is increased to 100,000 by default, which effectively uncaps - the limit (as the maximum transaction size limit will be hit first). It can be - overridden with `-datacarriersize=83` to revert to the limit enforced in previous - versions. (#32406) +### IPC -- Multiple data carrier (OP_RETURN) outputs in a transaction are now permitted for - relay and mining. The `-datacarriersize` limit applies to the aggregate size of - the scriptPubKeys across all such outputs in a transaction, not including the - scriptPubKey size itself. (#32406) +- #33229 multiprocess: Don't require bitcoin -m argument when IPC options are used +- #33517 multiprocess: Fix high overhead from message logging +- #33519 Update libmultiprocess subtree in 30.x branch +- #33566 miner: fix empty mempool case for waitNext() +- #33676 interfaces: enable cancelling running waitNext calls -- The minimum block feerate (`-blockmintxfee`) has been changed to 0.001 satoshi per - vB. It can still be changed using the configuration option. This option can be used - by miners to set a minimum feerate on packages added to block templates. (#33106) +### GUI -- The default minimum relay feerate (`-minrelaytxfee`) and incremental relay feerate - (`-incrementalrelayfee`) have been changed to 0.1 satoshis per vB. They can still - be changed using their respective configuration options, but it is recommended to - change both together if you decide to do so. (#33106) +- gui#899 qt: Modernize custom filtering +- gui#901 Add createwallet, createwalletdescriptor, and migratewallet to history filter - Other minimum feerates (e.g. the dust feerate, the minimum returned by the fee - estimator, and all feerates used by the wallet) remain unchanged. The mempool minimum - feerate still changes in response to high volume. +### Test - Note that unless these lower defaults are widely adopted across the network, transactions - created with lower fee rates are not guaranteed to propagate or confirm. The wallet - feerates remain unchanged; `-mintxfee` must be changed before attempting to create - transactions with lower feerates using the wallet. (#33106) +- #33612 test: change log rate limit version gate -P2P and network changes ------------------------ +### Doc -- Opportunistic 1-parent-1-child package relay has been improved to handle - situations when the child already has unconfirmed parent(s) in the mempool. - This means that 1p1c packages can be accepted and propagate, even if they are - connected to broader topologies: multi-parent-1-child (where only 1 parent - requires fee-bumping), grandparent-parent-child (where only parent requires - fee-bumping) etc. (#31385) +- #33630 doc: correct topology requirements in submitpackage helptext +- #33826 scripted-diff: Remove obsolete comment +- #33827 doc: Correct pkgin command usage on NetBSD -- The transaction orphanage, which holds transactions with missing inputs temporarily - while the node attempts to fetch its parents, now has improved Denial of Service protections. - Previously, it enforced a maximum number of unique transactions (default 100, - configurable using `-maxorphantx`). Now, its limits are as follows: the number of - entries (unique by wtxid and peer), plus each unique transaction's input count divided - by 10, must not exceed 3,000. The total weight of unique transactions must not exceed - `404,000` Wu multiplied by the number of peers. (#31829) +### Misc -- The `-maxorphantx` option no longer has any effect, since the orphanage no longer - limits the number of unique transactions. Users should remove this configuration - option if they were using it, as the setting will cause an error in future versions - when it is no longer recognized. (#31829) - -New `bitcoin` command ---------------------- - -- A new `bitcoin` command line tool has been added to make features more discoverable - and convenient to use. The `bitcoin` tool just calls other executables and does not - implement any functionality on its own. Specifically `bitcoin node` is a synonym for - `bitcoind`, `bitcoin gui` is a synonym for `bitcoin-qt`, and `bitcoin rpc` is a synonym - for `bitcoin-cli -named`. Other commands and options can be listed with `bitcoin help`. - The new `bitcoin` command is an alternative to calling other commands directly, but it - doesn't replace them, and there are no plans to deprecate existing commands. (#31375) - -External Signing ----------------- - -- Support for external signing on Windows has been re-enabled. (#29868) - -IPC Mining Interface --------------------- - -- The new `bitcoin` command does support one new feature: an (experimental) IPC Mining - Interface that allows the node to work with Stratum v2 or other mining client software, - see (#31098). When the node is started with `bitcoin -m node -ipcbind=unix` it will - listen on a unix socket for IPC client connections, allowing clients to request block - templates and submit mined blocks. The `-m` option launches a new internal binary - (`bitcoin-node` instead of `bitcoind`) and is currently required but will become optional - in the future (with [#33229](https://github.com/bitcoin/bitcoin/pull/33229)). - -- IPC connectivity introduces new dependencies (see [multiprocess.md](https://github.com/bitcoin/bitcoin/blob/master/doc/multiprocess.md)), - which can be turned off with the `-DENABLE_IPC=OFF` build option if you do not intend - to use IPC. (#31802) - -Install changes ---------------- - -- The `test_bitcoin` executable is now installed in `libexec/` instead of `bin/`. - It can still be executed directly, or accessed through the new `bitcoin` command - as `bitcoin test`. The `libexec/` directory also contains new `bitcoin-node` and - `bitcoin-gui` binaries which support IPC features and are called through the - `bitcoin` tool. In source builds only, `test_bitcoin-qt`, `bench_bitcoin`, and - `bitcoin-chainstate` are also now installed to `libexec/` instead of `bin/` and - can be accessed through the new `bitcoin` command. See `bitcoin help` output for - details. (#31679) - -- On Windows, the installer no longer adds a “(64-bit)” suffix to entries in the - Start Menu (#32132), and it now automatically removes obsolete artifacts during - upgrades (#33422). - -Indexes -------- - -- The implementation of coinstatsindex was changed to prevent an overflow bug that - could already be observed on the default Signet. The new version of the index will - need to be synced from scratch when starting the upgraded node for the first time. - - The new version is stored in `/indexes/coinstatsindex/` in contrast to the old version - which was stored at `/indexes/coinstats/`. The old version of the index is not deleted - by the upgraded node in case the user chooses to downgrade their node in the future. - If the user does not plan to downgrade it is safe for them to remove `/indexes/coinstats/` - from their datadir. A future release of Bitcoin Core may remove the old version of the - index automatically. (#30469) - -Logging -------- -- Unconditional logging to disk is now rate limited by giving each source location - a quota of 1MiB per hour. Unconditional logging is any logging with a log level - higher than debug, that is `info`, `warning`, and `error`. All logs will be - prefixed with `[*]` if there is at least one source location that is currently - being suppressed. (#32604) - -- When `-logsourcelocations` is enabled, the log output now contains the entire - function signature instead of just the function name. (#32604) - -Updated RPCs ------------- - -- The `-paytxfee` startup option and the `settxfee` RPC are now deprecated and - will be removed in Bitcoin Core 31.0. They allowed the user to set a static fee - rate for wallet transactions, which could potentially lead to overpaying or underpaying. - Users should instead rely on fee estimation or specify a fee rate per transaction - using the `fee_rate` argument in RPCs such as `fundrawtransaction`, `sendtoaddress`, - `send`, `sendall`, and `sendmany`. (#31278) - -- Any RPC in which one of the parameters is a descriptor will throw an error - if the provided descriptor contains a whitespace at the beginning or the end - of the public key within a fragment - e.g. `pk( KEY)` or `pk(KEY )`. (#31603) - -- The `submitpackage` RPC, which allows submissions of child-with-parents - packages, no longer requires that all unconfirmed parents be present. The - package may contain other in-mempool ancestors as well. (#31385) - -- The `waitfornewblock` RPC now takes an optional `current_tip` argument. It - is also no longer hidden. (#30635) - -- The `waitforblock` and `waitforblockheight` RPCs are no longer hidden. (#30635) - -- The `psbtbumpfee` and `bumpfee` RPCs allow a replacement under fullrbf and no - longer require BIP-125 signalling. (#31953) - -- Transaction Script validation errors used to return the reason for the error - prefixed by either `mandatory-script-verify-flag-failed` if it was a consensus - error, or `non-mandatory-script-verify-flag` (without "-failed") if it was a - standardness error. This has been changed to `block-script-verify-flag-failed` - and `mempool-script-verify-flag-failed` for all block and mempool errors - respectively. (#33183) - -- The `getmininginfo` RPC now returns "blockmintxfee" result specifying the value of - `-blockmintxfee` configuration. (#33189) - -- The `getmempoolinfo` RPC now returns an additional "permitbaremultisig" and - "maxdatacarriersize" field, reflecting the `-permitbaremultisig` and `-datacarriersize` - config values. (#29954) - -Changes to wallet-related RPCs can be found in the Wallet section below. - -New RPCs --------- - -- A new REST API endpoint (`/rest/spenttxouts/BLOCKHASH`) has been introduced for - efficiently fetching spent transaction outputs using the block's undo data (#32540). - -Build System ------------- - -Updated settings ----------------- - -- The `-maxmempool` and `-dbcache` startup parameters are now capped on 32-bit systems - to 500MB and 1GiB respectively. (#32530) - -- The `-natpmp` option is now set to `1` by default. This means nodes with `-listen` - enabled (the default) but running behind a firewall, such as a local network router, - will be reachable if the firewall/router supports any of the `PCP` or `NAT-PMP` - protocols. (#33004) - -- The `-upnp` setting has now been fully removed. Use `-natpmp` instead. (#32500) - -- Previously, `-proxy` specified the proxy for all networks (except I2P which - uses `-i2psam`) and only the Tor proxy could have been specified separately - via `-onion`. Now, the syntax of `-proxy` has been extended and it is possible - to specify separately the proxy for IPv4, IPv6, Tor and CJDNS by appending `=` - followed by the network name, for example `-proxy=127.0.0.1:5555=ipv6` - configures a proxy only for IPv6. The `-proxy` option can be used multiple - times to define different proxies for different networks, such as - `-proxy=127.0.0.1:4444=ipv4 -proxy=10.0.0.1:6666=ipv6`. Later settings - override earlier ones for the same network; this can be used to remove an - earlier all-networks proxy and use direct connections only for a given - network, for example `-proxy=127.0.0.1:5555 -proxy=0=cjdns`. (#32425) - -- The `-blockmaxweight` startup option has been updated to be debug-only. - It is still available to users, but now hidden from the default `-help` text - and shown only in `-help-debug` (#32654). - -Changes to GUI or wallet related settings can be found in the GUI or Wallet section below. - -Wallet ------- - -- BDB legacy wallets can no longer be created or loaded. They can be migrated - to the new descriptor wallet format. Refer to the `migratewallet` RPC for more - details. - -- The legacy wallet removal drops redundant options in the bitcoin-wallet tool, - such as `-withinternalbdb`, `-legacy`, and `-descriptors`. Moreover, the - legacy-only RPCs `addmultisigaddress`, `dumpprivkey`, `dumpwallet`, - `importaddress`, `importmulti`, `importprivkey`, `importpubkey`, - `importwallet`, `newkeypool`, `sethdseed`, and `upgradewallet`, are removed. - (#32944, #28710, #32438, #31250) - -- Support has been added for spending TRUC transactions received by the - wallet, as well as creating TRUC transactions. The wallet ensures that - TRUC policy rules are being met. The wallet will throw an error if the - user is trying to spend TRUC utxos with utxos of other versions. - Additionally, the wallet will treat unconfirmed TRUC sibling - transactions as mempool conflicts. The wallet will also ensure that - transactions spending TRUC utxos meet the required size restrictions. (#32896) - -- Since descriptor wallets do not allow mixing watchonly and non-watchonly descriptors, - the `include_watchonly` option (and its variants in naming) are removed from all RPCs - that had it. (#32618) - -- The `iswatchonly` field is removed from any RPCs that returned it. (#32618) - -- `unloadwallet` - Return RPC_INVALID_PARAMETER when both the RPC wallet endpoint - and wallet_name parameters are unspecified. Previously the RPC failed with a JSON - parsing error. (#32845) - -- `getdescriptoractivity` - Mark blockhashes and scanobjects arguments as required, - so the user receives a clear help message when either is missing. As in `unloadwallet`, - previously the RPC failed with a JSON parsing error. (#32845) - -- `getwalletinfo` - Removes the fields `balance`, `immature_balance` and - `unconfirmed_balance`. (#32721) - -- `getunconfirmedbalance` - Removes this RPC command. You can query the `getbalances` - RPC and inspect the `["mine"]["untrusted_pending"]` entry within the JSON - response. (#32721) - -- The following RPCs now contain a `version` parameter that allows - the user to create transactions of any standard version number (1-3): - - `createrawtransaction` - - `createpsbt` - - `send` - - `sendall` - - `walletcreatefundedpsbt` - (#32896) - -GUI changes ------------ - -- The GUI has been migrated from Qt 5 to Qt 6. On Windows, dark mode is now supported. - On macOS, the Metal backend is now used. (#30997) - -- A transaction's fee bump is allowed under fullrbf and no longer requires - BIP-125 signalling. (#31953) - -- Custom column widths in the Transactions tab are reset as a side-effect of legacy - wallet removal. (#32459) - -Low-level changes -================= - -- Logs now include which peer sent us a header. Additionally there are fewer - redundant header log messages. A side-effect of this change is that for - some untypical cases new headers aren't logged anymore, e.g. a direct - `BLOCK` message with a previously unknown header and `submitheader` RPC. (#27826) +- #33508 ci: fix buildx gha cache authentication on forks +- #33558 ci: Use native platform for win-cross task +- #33581 ci: Properly include $FILE_ENV in DEPENDS_HASH +- #33744 ci: Fix lint runner selection (and docker cache) Credits ======= Thanks to everyone who directly contributed to this release: -- 0xb10c -- amisha -- Andrew Toth -- Anthony Towns -- Antoine Poinsot - Ava Chow -- benthecarman -- Brandon Odiwuor -- brunoerg -- Bue-von-hon -- Bufo -- Chandra Pratap -- Chris Stewart - Cory Fields -- Daniel Pfeifer -- Daniela Brozzoni -- David Gumberg -- deadmanoz -- dennsikl -- dergoegge -- enoch -- Ethan Heilman - Eugene Siegel -- Eunovo -- Eval EXEC -- Fabian Jahr -- fanquake -- Florian Schmaus -- fuder.eth -- furszy - glozow -- Greg Sanders -- Hao Xu -- Haoran Peng -- Haowen Liu - Hennadii Stepanov -- Hodlinator -- hoffman -- ishaanam - ismaelsadeeq -- Jameson Lopp -- janb84 -- Jiri Jakes -- John Bampton -- Jon Atack -- josibake -- jurraca -- kevkevin -- kevkevinpal -- kilavvy -- Kristaps Kaupe -- l0rinc -- laanwj -- leopardracer -- Lőrinc -- Luis Schwab -- Luke Dashjr - MarcoFalke -- marcofleon -- Martin Zumsande -- Matt Corallo -- Matthew Zipkin -- Max Edwards -- monlovesmango -- Murch -- naiyoma -- nervana21 -- Nicola Leonardo Susca -- Novo -- pablomartin4btc -- Peter Todd -- Pieter Wuille -- Pol Espinasa -- Prabhat Verma -- rkrux -- Roman Zeyde - Ryan Ofsky -- Saikiran -- Salvatore Ingala -- Sebastian Falbesoner -- Sergi Delgado Segura -- Shunsuke Shimizu - Sjors Provoost -- stickies-v -- stratospher -- stringintech -- strmfos -- stutxo -- tdb3 -- TheCharlatan -- Tomás Andróil -- UdjinM6 -- Vasil Dimov -- VolodymyrBg -- w0xlt -- will +- WakeTrainDev - willcl-ark -- William Casarin -- woltx -- yancy -- zaidmstrr As well as to everyone that helped with translations on [Transifex](https://explore.transifex.com/bitcoin/bitcoin/). diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9abbf9586b8..f9934bb5906 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -292,7 +292,7 @@ if(BUILD_BITCOIN_BIN) add_executable(bitcoin bitcoin.cpp) add_windows_resources(bitcoin bitcoin-res.rc) add_windows_application_manifest(bitcoin) - target_link_libraries(bitcoin core_interface bitcoin_util) + target_link_libraries(bitcoin core_interface bitcoin_common bitcoin_util) install_binary_component(bitcoin HAS_MANPAGE) endif() diff --git a/src/bitcoin.cpp b/src/bitcoin.cpp index c3278274153..c1a5fce33af 100644 --- a/src/bitcoin.cpp +++ b/src/bitcoin.cpp @@ -5,6 +5,7 @@ #include // IWYU pragma: keep #include +#include #include #include #include @@ -47,7 +48,7 @@ Run '%s help' to see additional commands (e.g. for testing and debugging). )"; struct CommandLine { - bool use_multiprocess{false}; + std::optional use_multiprocess; bool show_version{false}; bool show_help{false}; std::string_view command; @@ -55,6 +56,7 @@ struct CommandLine { }; CommandLine ParseCommandLine(int argc, char* argv[]); +bool UseMultiprocess(const CommandLine& cmd); static void ExecCommand(const std::vector& args, std::string_view argv0); int main(int argc, char* argv[]) @@ -78,9 +80,9 @@ int main(int argc, char* argv[]) return EXIT_FAILURE; } } else if (cmd.command == "gui") { - args.emplace_back(cmd.use_multiprocess ? "bitcoin-gui" : "bitcoin-qt"); + args.emplace_back(UseMultiprocess(cmd) ? "bitcoin-gui" : "bitcoin-qt"); } else if (cmd.command == "node") { - args.emplace_back(cmd.use_multiprocess ? "bitcoin-node" : "bitcoind"); + args.emplace_back(UseMultiprocess(cmd) ? "bitcoin-node" : "bitcoind"); } else if (cmd.command == "rpc") { args.emplace_back("bitcoin-cli"); // Since "bitcoin rpc" is a new interface that doesn't need to be @@ -143,6 +145,30 @@ CommandLine ParseCommandLine(int argc, char* argv[]) return cmd; } +bool UseMultiprocess(const CommandLine& cmd) +{ + // If -m or -M options were explicitly specified, there is no need to + // further parse arguments to determine which to use. + if (cmd.use_multiprocess) return *cmd.use_multiprocess; + + ArgsManager args; + args.SetDefaultFlags(ArgsManager::ALLOW_ANY); + std::string error_message; + auto argv{cmd.args}; + argv.insert(argv.begin(), nullptr); + if (!args.ParseParameters(argv.size(), argv.data(), error_message)) { + tfm::format(std::cerr, "Warning: failed to parse subcommand command line options: %s\n", error_message); + } + if (!args.ReadConfigFiles(error_message, true)) { + tfm::format(std::cerr, "Warning: failed to parse subcommand config: %s\n", error_message); + } + args.SelectConfigNetwork(args.GetChainTypeString()); + + // If any -ipc* options are set these need to be processed by a + // multiprocess-capable binary. + return args.IsArgSet("-ipcbind") || args.IsArgSet("-ipcconnect") || args.IsArgSet("-ipcfd"); +} + //! Execute the specified bitcoind, bitcoin-qt or other command line in `args` //! using src, bin and libexec directory paths relative to this executable, where //! the path to this executable is specified in `wrapper_argv0`. diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index ceb3c99410c..a4373dafdf2 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -132,11 +132,16 @@ static bool ParseArgs(NodeContext& node, int argc, char* argv[]) return true; } -static bool ProcessInitCommands(ArgsManager& args) +static bool ProcessInitCommands(interfaces::Init& init, ArgsManager& args) { // Process help and version before taking care about datadir if (HelpRequested(args) || args.GetBoolArg("-version", false)) { - std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion() + "\n"; + std::string strUsage = CLIENT_NAME " daemon version " + FormatFullVersion(); + if (const char* exe_name{init.exeName()}) { + strUsage += " "; + strUsage += exe_name; + } + strUsage += "\n"; if (args.GetBoolArg("-version", false)) { strUsage += FormatParagraph(LicenseInfo()); @@ -277,7 +282,7 @@ MAIN_FUNCTION ArgsManager& args = *Assert(node.args); if (!ParseArgs(node, argc, argv)) return EXIT_FAILURE; // Process early info return commands such as -help or -version - if (ProcessInitCommands(args)) return EXIT_SUCCESS; + if (ProcessInitCommands(*init, args)) return EXIT_SUCCESS; // Start application if (!AppInit(node) || !Assert(node.shutdown_signal)->wait()) { diff --git a/src/common/args.cpp b/src/common/args.cpp index ff30ec5b8c1..d44cd4319b6 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -266,7 +266,13 @@ std::optional ArgsManager::GetArgFlags(const std::string& name) co return search->second.m_flags; } } - return std::nullopt; + return m_default_flags; +} + +void ArgsManager::SetDefaultFlags(std::optional flags) +{ + LOCK(cs_args); + m_default_flags = flags; } fs::path ArgsManager::GetPathArg(std::string arg, const fs::path& default_value) const diff --git a/src/common/args.h b/src/common/args.h index da19cbda66f..d907ad7663d 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -137,6 +137,7 @@ protected: std::string m_network GUARDED_BY(cs_args); std::set m_network_only_args GUARDED_BY(cs_args); std::map> m_available_args GUARDED_BY(cs_args); + std::optional m_default_flags GUARDED_BY(cs_args){}; bool m_accept_any_command GUARDED_BY(cs_args){true}; std::list m_config_sections GUARDED_BY(cs_args); std::optional m_config_path GUARDED_BY(cs_args); @@ -375,10 +376,15 @@ protected: /** * Return Flags for known arg. - * Return nullopt for unknown arg. + * Return default flags for unknown arg. */ std::optional GetArgFlags(const std::string& name) const; + /** + * Set default flags to return for an unknown arg. + */ + void SetDefaultFlags(std::optional); + /** * Get settings file path, or return false if read-write settings were * disabled with -nosettings. diff --git a/src/init/bitcoin-gui.cpp b/src/init/bitcoin-gui.cpp index eae30bc995a..aca3fbe1c44 100644 --- a/src/init/bitcoin-gui.cpp +++ b/src/init/bitcoin-gui.cpp @@ -39,6 +39,7 @@ public: // bitcoin-node accepts the option, and bitcoin-gui accepts all bitcoin-node // options and will start the node with those options. bool canListenIpc() override { return true; } + const char* exeName() override { return EXE_NAME; } node::NodeContext m_node; std::unique_ptr m_ipc; }; diff --git a/src/init/bitcoin-node.cpp b/src/init/bitcoin-node.cpp index 3f8c50b8d66..e5f1411fdb5 100644 --- a/src/init/bitcoin-node.cpp +++ b/src/init/bitcoin-node.cpp @@ -38,6 +38,7 @@ public: std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } interfaces::Ipc* ipc() override { return m_ipc.get(); } bool canListenIpc() override { return true; } + const char* exeName() override { return EXE_NAME; } node::NodeContext& m_node; std::unique_ptr m_ipc; }; diff --git a/src/init/bitcoin-qt.cpp b/src/init/bitcoin-qt.cpp index 5209c729731..05f3bc32d7d 100644 --- a/src/init/bitcoin-qt.cpp +++ b/src/init/bitcoin-qt.cpp @@ -16,6 +16,8 @@ namespace init { namespace { +const char* EXE_NAME = "bitcoin-qt"; + class BitcoinQtInit : public interfaces::Init { public: @@ -32,6 +34,7 @@ public: return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } + const char* exeName() override { return EXE_NAME; } node::NodeContext m_node; }; } // namespace diff --git a/src/init/bitcoind.cpp b/src/init/bitcoind.cpp index 48be8831d25..0b0ab3f8fa8 100644 --- a/src/init/bitcoind.cpp +++ b/src/init/bitcoind.cpp @@ -18,6 +18,8 @@ using node::NodeContext; namespace init { namespace { +const char* EXE_NAME = "bitcoind"; + class BitcoindInit : public interfaces::Init { public: @@ -34,6 +36,7 @@ public: return MakeWalletLoader(chain, *Assert(m_node.args)); } std::unique_ptr makeEcho() override { return interfaces::MakeEcho(); } + const char* exeName() override { return EXE_NAME; } NodeContext& m_node; }; } // namespace diff --git a/src/interfaces/init.h b/src/interfaces/init.h index b496ada05f4..030ce306c00 100644 --- a/src/interfaces/init.h +++ b/src/interfaces/init.h @@ -38,6 +38,7 @@ public: virtual std::unique_ptr makeEcho() { return nullptr; } virtual Ipc* ipc() { return nullptr; } virtual bool canListenIpc() { return false; } + virtual const char* exeName() { return nullptr; } }; //! Return implementation of Init interface for the node process. If the argv diff --git a/src/interfaces/mining.h b/src/interfaces/mining.h index 150295e5b7b..a30c4afb526 100644 --- a/src/interfaces/mining.h +++ b/src/interfaces/mining.h @@ -72,6 +72,11 @@ public: * the tip is more than 20 minutes old. */ virtual std::unique_ptr waitNext(const node::BlockWaitOptions options = {}) = 0; + + /** + * Interrupts the current wait for the next block template. + */ + virtual void interruptWait() = 0; }; //! Interface giving clients (RPC, Stratum v2 Template Provider in the future) diff --git a/src/ipc/capnp/mining.capnp b/src/ipc/capnp/mining.capnp index 8ee4745b858..ed01e44a32a 100644 --- a/src/ipc/capnp/mining.capnp +++ b/src/ipc/capnp/mining.capnp @@ -33,6 +33,7 @@ interface BlockTemplate $Proxy.wrap("interfaces::BlockTemplate") { getCoinbaseMerklePath @8 (context: Proxy.Context) -> (result: List(Data)); submitSolution @9 (context: Proxy.Context, version: UInt32, timestamp: UInt32, nonce: UInt32, coinbase :Data) -> (result: Bool); waitNext @10 (context: Proxy.Context, options: BlockWaitOptions) -> (result: BlockTemplate); + interruptWait @11() -> (); } struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") { diff --git a/src/ipc/capnp/protocol.cpp b/src/ipc/capnp/protocol.cpp index 4150f9f4664..27ef73e84e5 100644 --- a/src/ipc/capnp/protocol.cpp +++ b/src/ipc/capnp/protocol.cpp @@ -30,10 +30,36 @@ namespace ipc { namespace capnp { namespace { -void IpcLogFn(bool raise, std::string message) + +BCLog::Level ConvertIPCLogLevel(mp::Log level) { - LogDebug(BCLog::IPC, "%s\n", message); - if (raise) throw Exception(message); + switch (level) { + case mp::Log::Trace: return BCLog::Level::Trace; + case mp::Log::Debug: return BCLog::Level::Debug; + case mp::Log::Info: return BCLog::Level::Info; + case mp::Log::Warning: return BCLog::Level::Warning; + case mp::Log::Error: return BCLog::Level::Error; + case mp::Log::Raise: return BCLog::Level::Error; + } // no default case, so the compiler can warn about missing cases + + // Be conservative and assume that if MP ever adds a new log level, it + // should only be shown at our most verbose level. + return BCLog::Level::Trace; +} + +mp::Log GetRequestedIPCLogLevel() +{ + if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Trace)) return mp::Log::Trace; + if (LogAcceptCategory(BCLog::IPC, BCLog::Level::Debug)) return mp::Log::Debug; + + // Info, Warning, and Error are logged unconditionally + return mp::Log::Info; +} + +void IpcLogFn(mp::LogMessage message) +{ + LogPrintLevel(BCLog::IPC, ConvertIPCLogLevel(message.level), "%s\n", message.message); + if (message.level == mp::Log::Raise) throw Exception(message.message); } class CapnpProtocol : public Protocol @@ -62,7 +88,11 @@ public: { assert(!m_loop); mp::g_thread_context.thread_name = mp::ThreadName(exe_name); - m_loop.emplace(exe_name, &IpcLogFn, &m_context); + mp::LogOptions opts = { + .log_fn = IpcLogFn, + .log_level = GetRequestedIPCLogLevel() + }; + m_loop.emplace(exe_name, std::move(opts), &m_context); if (ready_fn) ready_fn(); mp::ServeStream(*m_loop, fd, init); m_parent_connection = &m_loop->m_incoming_connections.back(); @@ -90,7 +120,11 @@ public: std::promise promise; m_loop_thread = std::thread([&] { util::ThreadRename("capnp-loop"); - m_loop.emplace(exe_name, &IpcLogFn, &m_context); + mp::LogOptions opts = { + .log_fn = IpcLogFn, + .log_level = GetRequestedIPCLogLevel() + }; + m_loop.emplace(exe_name, std::move(opts), &m_context); m_loop_ref.emplace(*m_loop); promise.set_value(); m_loop->loop(); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index fd3fa226cae..509528d9c06 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -918,15 +918,21 @@ public: std::unique_ptr waitNext(BlockWaitOptions options) override { - auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options); + auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options, m_interrupt_wait); if (new_template) return std::make_unique(m_assemble_options, std::move(new_template), m_node); return nullptr; } + void interruptWait() override + { + InterruptWait(notifications(), m_interrupt_wait); + } + const BlockAssembler::Options m_assemble_options; const std::unique_ptr m_block_template; + bool m_interrupt_wait{false}; ChainstateManager& chainman() { return *Assert(m_node.chainman); } KernelNotifications& notifications() { return *Assert(m_node.notifications); } NodeContext& m_node; diff --git a/src/node/miner.cpp b/src/node/miner.cpp index e75bc3a603b..39f0879ce2c 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -29,6 +29,7 @@ #include #include +#include namespace node { @@ -453,12 +454,20 @@ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t block.hashMerkleRoot = BlockMerkleRoot(block); } +void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait) +{ + LOCK(kernel_notifications.m_tip_block_mutex); + interrupt_wait = true; + kernel_notifications.m_tip_block_cv.notify_all(); +} + std::unique_ptr WaitAndCreateNewBlock(ChainstateManager& chainman, KernelNotifications& kernel_notifications, CTxMemPool* mempool, const std::unique_ptr& block_template, const BlockWaitOptions& options, - const BlockAssembler::Options& assemble_options) + const BlockAssembler::Options& assemble_options, + bool& interrupt_wait) { // Delay calculating the current template fees, just in case a new block // comes in before the next tick. @@ -483,8 +492,12 @@ std::unique_ptr WaitAndCreateNewBlock(ChainstateManager& chainma // method on BlockTemplate and no template could have been // generated before a tip exists. tip_changed = Assume(tip_block) && tip_block != block_template->block.hashPrevBlock; - return tip_changed || chainman.m_interrupt; + return tip_changed || chainman.m_interrupt || interrupt_wait; }); + if (interrupt_wait) { + interrupt_wait = false; + return nullptr; + } } if (chainman.m_interrupt) return nullptr; @@ -522,18 +535,13 @@ std::unique_ptr WaitAndCreateNewBlock(ChainstateManager& chainma // Calculate the original template total fees if we haven't already if (current_fees == -1) { - current_fees = 0; - for (CAmount fee : block_template->vTxFees) { - current_fees += fee; - } + current_fees = std::accumulate(block_template->vTxFees.begin(), block_template->vTxFees.end(), CAmount{0}); } - CAmount new_fees = 0; - for (CAmount fee : new_tmpl->vTxFees) { - new_fees += fee; - Assume(options.fee_threshold != MAX_MONEY); - if (new_fees >= current_fees + options.fee_threshold) return new_tmpl; - } + // Check if fees increased enough to return the new template + const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0}); + Assume(options.fee_threshold != MAX_MONEY); + if (new_fees >= current_fees + options.fee_threshold) return new_tmpl; } now = NodeClock::now(); diff --git a/src/node/miner.h b/src/node/miner.h index a9a88b39cf2..0790835d8f1 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -238,6 +238,9 @@ void ApplyArgsManOptions(const ArgsManager& gArgs, BlockAssembler::Options& opti /* Compute the block's merkle root, insert or replace the coinbase transaction and the merkle root into the block */ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce); + +/* Interrupt the current wait for the next block template. */ +void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait); /** * Return a new block template when fees rise to a certain threshold or after a * new tip; return nullopt if timeout is reached. @@ -247,7 +250,8 @@ std::unique_ptr WaitAndCreateNewBlock(ChainstateManager& chainma CTxMemPool* mempool, const std::unique_ptr& block_template, const BlockWaitOptions& options, - const BlockAssembler::Options& assemble_options); + const BlockAssembler::Options& assemble_options, + bool& interrupt_wait); /* Locks cs_main and returns the block hash and block height of the active chain if it exists; otherwise, returns nullopt.*/ std::optional GetTip(ChainstateManager& chainman); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 958c82fda22..d6d2be7b393 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -72,6 +72,9 @@ namespace { // don't add private key handling cmd's to the history const QStringList historyFilter = QStringList() + << "createwallet" + << "createwalletdescriptor" + << "migratewallet" << "signmessagewithprivkey" << "signrawtransactionwithkey" << "walletpassphrase" diff --git a/src/qt/transactionfilterproxy.cpp b/src/qt/transactionfilterproxy.cpp index 1ad77fd767f..89099260a9c 100644 --- a/src/qt/transactionfilterproxy.cpp +++ b/src/qt/transactionfilterproxy.cpp @@ -52,32 +52,78 @@ bool TransactionFilterProxy::filterAcceptsRow(int sourceRow, const QModelIndex & void TransactionFilterProxy::setDateRange(const std::optional& from, const std::optional& to) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); +#endif + dateFrom = from; dateTo = to; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + endFilterChange(QSortFilterProxyModel::Direction::Rows); +#else invalidateFilter(); +#endif } void TransactionFilterProxy::setSearchString(const QString &search_string) { if (m_search_string == search_string) return; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); +#endif + m_search_string = search_string; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + endFilterChange(QSortFilterProxyModel::Direction::Rows); +#else invalidateFilter(); +#endif } void TransactionFilterProxy::setTypeFilter(quint32 modes) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); +#endif + this->typeFilter = modes; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + endFilterChange(QSortFilterProxyModel::Direction::Rows); +#else invalidateFilter(); +#endif } void TransactionFilterProxy::setMinAmount(const CAmount& minimum) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); +#endif + this->minAmount = minimum; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + endFilterChange(QSortFilterProxyModel::Direction::Rows); +#else invalidateFilter(); +#endif } void TransactionFilterProxy::setShowInactive(bool _showInactive) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); +#endif + this->showInactive = _showInactive; + +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + endFilterChange(QSortFilterProxyModel::Direction::Rows); +#else invalidateFilter(); +#endif } diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 919c30464dd..286ac5e3537 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -936,8 +936,9 @@ static RPCHelpMan submitpackage() , { {"package", RPCArg::Type::ARR, RPCArg::Optional::NO, "An array of raw transactions.\n" - "The package must solely consist of a child transaction and all of its unconfirmed parents, if any. None of the parents may depend on each other.\n" - "The package must be topologically sorted, with the child being the last element in the array.", + "The package must consist of a transaction with (some, all, or none of) its unconfirmed parents. A single transaction is permitted.\n" + "None of the parents may depend on each other. Parents that are already in mempool do not need to be present in the package.\n" + "The package must be topologically sorted, with the child being the last element in the array if there are multiple elements.", { {"rawtx", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED, ""}, }, diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 652ec25fe89..94ade598439 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -113,6 +113,22 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const options.coinbase_output_script = scriptPubKey; LOCK(tx_mempool.cs); + BOOST_CHECK(tx_mempool.size() == 0); + + // Block template should only have a coinbase when there's nothing in the mempool + std::unique_ptr block_template = mining->createNewBlock(options); + BOOST_REQUIRE(block_template); + CBlock block{block_template->getBlock()}; + BOOST_REQUIRE_EQUAL(block.vtx.size(), 1U); + + // waitNext() on an empty mempool should return nullptr because there is no better template + auto should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1}); + BOOST_REQUIRE(should_be_nullptr == nullptr); + + // Unless fee_threshold is 0 + block_template = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 0}); + BOOST_REQUIRE(block_template); + // Test the ancestor feerate transaction selection. TestMemPoolEntryHelper entry; @@ -144,9 +160,9 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const const auto high_fee_tx{entry.Fee(50000).Time(Now()).SpendsCoinbase(false).FromTx(tx)}; AddToMempool(tx_mempool, high_fee_tx); - std::unique_ptr block_template = mining->createNewBlock(options); + block_template = mining->createNewBlock(options); BOOST_REQUIRE(block_template); - CBlock block{block_template->getBlock()}; + block = block_template->getBlock(); BOOST_REQUIRE_EQUAL(block.vtx.size(), 4U); BOOST_CHECK(block.vtx[1]->GetHash() == hashParentTx); BOOST_CHECK(block.vtx[2]->GetHash() == hashHighFeeTx); @@ -184,7 +200,7 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const AddToMempool(tx_mempool, entry.Fee(feeToUse).FromTx(tx)); // waitNext() should return nullptr because there is no better template - auto should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1}); + should_be_nullptr = block_template->waitNext({.timeout = MillisecondsDouble{0}, .fee_threshold = 1}); BOOST_REQUIRE(should_be_nullptr == nullptr); block = block_template->getBlock(); diff --git a/test/functional/interface_ipc.py b/test/functional/interface_ipc.py index 4231e95b56a..2c9a0022442 100755 --- a/test/functional/interface_ipc.py +++ b/test/functional/interface_ipc.py @@ -153,6 +153,7 @@ class IPCInterfaceTest(BitcoinTestFramework): self.log.debug("Wait for a new template") waitoptions = self.capnp_modules['mining'].BlockWaitOptions() waitoptions.timeout = timeout + waitoptions.feeThreshold = 1 waitnext = template.result.waitNext(ctx, waitoptions) self.generate(self.nodes[0], 1) template2 = await waitnext @@ -168,6 +169,7 @@ class IPCInterfaceTest(BitcoinTestFramework): block3 = await self.parse_and_deserialize_block(template4, ctx) assert_equal(len(block3.vtx), 2) self.log.debug("Wait again, this should return the same template, since the fee threshold is zero") + waitoptions.feeThreshold = 0 template5 = await template4.result.waitNext(ctx, waitoptions) block4 = await self.parse_and_deserialize_block(template5, ctx) assert_equal(len(block4.vtx), 2) @@ -182,6 +184,28 @@ class IPCInterfaceTest(BitcoinTestFramework): template7 = await template6.result.waitNext(ctx, waitoptions) assert_equal(template7.to_dict(), {}) + self.log.debug("interruptWait should abort the current wait") + wait_started = asyncio.Event() + async def wait_for_block(): + new_waitoptions = self.capnp_modules['mining'].BlockWaitOptions() + new_waitoptions.timeout = waitoptions.timeout * 60 # 1 minute wait + new_waitoptions.feeThreshold = 1 + wait_started.set() + return await template6.result.waitNext(ctx, new_waitoptions) + + async def interrupt_wait(): + await wait_started.wait() # Wait for confirmation wait started + await asyncio.sleep(0.1) # Minimal buffer + template6.result.interruptWait() + miniwallet.send_self_transfer(fee_rate=10, from_node=self.nodes[0]) + + wait_task = asyncio.create_task(wait_for_block()) + interrupt_task = asyncio.create_task(interrupt_wait()) + + result = await wait_task + await interrupt_task + assert_equal(result.to_dict(), {}) + current_block_height = self.nodes[0].getchaintips()[0]["height"] check_opts = self.capnp_modules['mining'].BlockCheckOptions() template = await mining.result.createNewBlock(opts) diff --git a/test/functional/test_framework/test_node.py b/test/functional/test_framework/test_node.py index 1ec2fe8a657..3b55b915efd 100755 --- a/test/functional/test_framework/test_node.py +++ b/test/functional/test_framework/test_node.py @@ -161,7 +161,7 @@ class TestNode(): self.args.append("-logsourcelocations") if self.version_is_at_least(239000): self.args.append("-loglevel=trace") - if self.version_is_at_least(299900): + if self.version_is_at_least(290100): self.args.append("-nologratelimit") # Default behavior from global -v2transport flag is added to args to persist it over restarts. diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index fc86033800e..9d556d1cd7b 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -336,6 +336,7 @@ BASE_SCRIPTS = [ 'p2p_tx_privacy.py', 'rpc_getdescriptoractivity.py', 'rpc_scanblocks.py', + 'tool_bitcoin.py', 'p2p_sendtxrcncl.py', 'rpc_scantxoutset.py', 'feature_unsupported_utxo_db.py', diff --git a/test/functional/tool_bitcoin.py b/test/functional/tool_bitcoin.py new file mode 100755 index 00000000000..1112e02e090 --- /dev/null +++ b/test/functional/tool_bitcoin.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 +# Copyright (c) The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +"""Test the bitcoin wrapper tool.""" +from test_framework.test_framework import ( + BitcoinTestFramework, + SkipTest, +) +from test_framework.util import ( + append_config, + assert_equal, +) + +import platform +import re + + +class ToolBitcoinTest(BitcoinTestFramework): + def set_test_params(self): + self.setup_clean_chain = True + self.num_nodes = 1 + + def skip_test_if_missing_module(self): + # Skip test on windows because currently when `bitcoin node -version` is + # run on windows, python doesn't capture output from the child + # `bitcoind` and `bitcoin-node` process started with _wexecvp, and + # stdout/stderr are always empty. See + # https://github.com/bitcoin/bitcoin/pull/33229#issuecomment-3265524908 + if platform.system() == "Windows": + raise SkipTest("Test does not currently work on windows") + + def setup_network(self): + """Set up nodes normally, but save a copy of their arguments before starting them.""" + self.add_nodes(self.num_nodes, self.extra_args) + node_argv = self.get_binaries().node_argv() + self.node_options = [node.args[len(node_argv):] for node in self.nodes] + assert all(node.args[:len(node_argv)] == node_argv for node in self.nodes) + + def set_cmd_args(self, node, args): + """Set up node so it will be started through bitcoin wrapper command with specified arguments.""" + node.args = [self.binary_paths.bitcoin_bin] + args + ["node"] + self.node_options[node.index] + + def test_args(self, cmd_args, node_args, expect_exe=None, expect_error=None): + node = self.nodes[0] + self.set_cmd_args(node, cmd_args) + extra_args = node_args + ["-version"] + if expect_error is not None: + node.assert_start_raises_init_error(expected_msg=expect_error, extra_args=extra_args) + else: + assert expect_exe + node.start(extra_args=extra_args) + ret, out, err = get_node_output(node) + try: + assert_equal(get_exe_name(out), expect_exe.encode()) + assert_equal(err, b"") + except Exception as e: + raise RuntimeError(f"Unexpected output from {node.args + extra_args}: {out=!r} {err=!r} {ret=!r}") from e + + def run_test(self): + node = self.nodes[0] + + self.log.info("Ensure bitcoin node command invokes bitcoind by default") + self.test_args([], [], expect_exe="bitcoind") + + self.log.info("Ensure bitcoin -M invokes bitcoind") + self.test_args(["-M"], [], expect_exe="bitcoind") + + self.log.info("Ensure bitcoin -M does not accept -ipcbind") + self.test_args(["-M"], ["-ipcbind=unix"], expect_error='Error: Error parsing command line arguments: Invalid parameter -ipcbind=unix') + + if self.is_ipc_compiled(): + self.log.info("Ensure bitcoin -m invokes bitcoin-node") + self.test_args(["-m"], [], expect_exe="bitcoin-node") + + self.log.info("Ensure bitcoin -m does accept -ipcbind") + self.test_args(["-m"], ["-ipcbind=unix"], expect_exe="bitcoin-node") + + self.log.info("Ensure bitcoin accepts -ipcbind by default") + self.test_args([], ["-ipcbind=unix"], expect_exe="bitcoin-node") + + self.log.info("Ensure bitcoin recognizes -ipcbind in config file") + append_config(node.datadir_path, ["ipcbind=unix"]) + self.test_args([], [], expect_exe="bitcoin-node") + + +def get_node_output(node): + ret = node.process.wait(timeout=60) + node.stdout.seek(0) + node.stderr.seek(0) + out = node.stdout.read() + err = node.stderr.read() + node.stdout.close() + node.stderr.close() + + # Clean up TestNode state + node.running = False + node.process = None + node.rpc_connected = False + node.rpc = None + + return ret, out, err + + +def get_exe_name(version_str): + """Get exe name from last word of first line of version string.""" + return re.match(rb".*?(\S+)\s*?(?:\n|$)", version_str.strip()).group(1) + + +if __name__ == '__main__': + ToolBitcoinTest(__file__).main() diff --git a/test/lint/check-doc.py b/test/lint/check-doc.py index 3e9e5ba230a..e47ff30f31c 100755 --- a/test/lint/check-doc.py +++ b/test/lint/check-doc.py @@ -23,7 +23,7 @@ CMD_GREP_WALLET_ARGS = r"git grep --function-context 'void WalletInit::AddWallet CMD_GREP_WALLET_HIDDEN_ARGS = r"git grep --function-context 'void DummyWalletInit::AddWalletOptions' -- {}".format(CMD_ROOT_DIR) CMD_GREP_DOCS = r"git grep --perl-regexp '{}' {}".format(REGEX_DOC, CMD_ROOT_DIR) # list unsupported, deprecated and duplicate args as they need no documentation -SET_DOC_OPTIONAL = set(['-h', '-?', '-dbcrashratio', '-forcecompactdb']) +SET_DOC_OPTIONAL = set(['-h', '-?', '-dbcrashratio', '-forcecompactdb', '-ipcconnect', '-ipcfd']) def lint_missing_argument_documentation():