Commit Graph

4974 Commits

Author SHA1 Message Date
Ava Chow
d692e07228 Merge bitcoin/bitcoin#32894: FUZZ: Test that BnB finds best solution
54d0393058 FUZZ: Test that BnB finds best solution (Murch)

Pull request description:

  BnB’s solution is the input set with the lowest waste score, excluding any supersets of other solution candidates.
  This fuzz test compares a brute force search with the BnB result to ensure that BnB succeeds.

ACKs for top commit:
  achow101:
    ACK 54d0393058
  brunoerg:
    ACK 54d0393058

Tree-SHA512: 96b6a822f53311d9a76abe8c217794e0a2dd5bd713db0a15dc70e065099b8245c430e1174e24133e0a802218ff0f2943dfcc3d638c3716485d5607c452854e7d
2026-02-05 13:32:24 -08:00
Murch
54d0393058 FUZZ: Test that BnB finds best solution
BnB’s solution is the input set with the lowest waste score, excluding
any supersets of other solution candidates.
This fuzz test compares a brute force search with the BnB result to
ensure that BnB succeeds.
2026-02-04 13:59:35 -08:00
Ava Chow
4ae00e9a71 Merge bitcoin/bitcoin#32636: Split CWallet::Create() into CreateNew and LoadExisting
db2effaca4 scripted-diff: refactor: CWallet::Create() -> CreateNew() (David Gumberg)
27e021ebc0 wallet: Correctly log stats for encrypted messages. (David Gumberg)
d8bec61be2 wallet: remove loading logic from CWallet::Create (David Gumberg)
f35acc893f refactor: wallet: Factor out `WriteVersion()` from `PopulateWalletFromDB()` (David Gumberg)
e12ff8aca0 test: wallet: Split create and load (David Gumberg)
70dbc79b09 wallet: Use CWallet::LoadExisting() for loading existing wallets. (David Gumberg)
ae66e01164 wallet: Create separate function for wallet load (David Gumberg)
bc69070416 refactor: Wallet stats logging in its own function (David Gumberg)
a9d64cd49c wallet: Remove redundant birth time update (David Gumberg)
b4a49cc727 wallet: Move argument parsing to before DB load (David Gumberg)
b15a94a618 refactor: Split out wallet argument loading (David Gumberg)
a02c4a82d8 refactor: Move -walletbroadcast setting init (David Gumberg)
411caf7281 wallet: refactor: PopulateWalletFromDB use switch statement. (David Gumberg)
a48e23f566 refactor: wallet: move error handling to PopulateWalletFromDB() (David Gumberg)
0972785fd7 wallet: Delete unnecessary PopulateWalletFromDB() calls (David Gumberg)
f0a046094e scripted-diff: refactor: CWallet::LoadWallet->PopulateWalletFromDB (David Gumberg)

Pull request description:

  This PR is mostly a refactor which splits out logic used for creating wallets and for loading wallets, both of which are presently contained in `CWallet::Create()` into `CWallet::CreateNew()` and `CWallet::LoadExisting()`

  The real win of this PR is that `CWallet::Create()` uses a very bad heuristic for trying to guess whether or not it is supposed to be creating a new wallet or loading an existing wallet:

  370c592612/src/wallet/wallet.cpp (L2882-L2885)

  This heuristic assumes that wallets with no `ScriptPubKeyMans` are being created, which sounds reasonable, but as demonstrated in #32112 and #32111, this can happen when the user tries to load a wallet file that is corrupted, both issues are fixed by this PR and any other misbehavior for wallet files which succeeded the broken heuristic's sniff test for new wallets.

  It was already the case that every caller of `CWallet::Create()` knows whether it is creating a wallet or loading one, so we can avoid replacing this bad heuristic with another one, and just shift the burden to the caller.

ACKs for top commit:
  achow101:
    ACK db2effaca4
  polespinasa:
    approach ACK db2effaca4
  w0xlt:
    reACK db2effaca4
  murchandamus:
    ACK db2effaca4
  rkrux:
    ACK db2effaca4

Tree-SHA512: c28d60e0a3001058da3fd2bdbe0726c7ebe742a4b900a1dee2e5132eccc22e49619cb747a99b4032b000eafd4aa2fdd4ec244c32be2012aba809fdc94b5f6ecd
2026-02-04 11:06:36 -08:00
Ava Chow
705705e5b1 Merge bitcoin/bitcoin#33701: test: add case where TOTAL_TRIES is exceeded yet solution remains
b189a34557 test: add case where `TOTAL_TRIES` is exceeded yet solution remains (yancy)

Pull request description:

  Show that `CoinGrider` halts searching when the number of attempts exceeds `TOTAL_TRIES`.  To do so, show that a solution is found, then add one more entry to the same set of inputs.  Since the search orders by `effective_value`, the solution is constructed such that only values with the lowest `effective_value` have the least weight.  Only the lowest weight values will not exceed the `max_selection_weight`. Therefore, `CoinGrinder` will not evaluate all lowest weight solutions together before exceeding `TOTAL_TRIES` since they are last found.

  This test case was inspired by a similar test for `BnB` currently named `bnb_test`.

ACKs for top commit:
  frankomosh:
    Code review ACK b189a34
  achow101:
    ACK b189a34557
  murchandamus:
    ACK b189a34557

Tree-SHA512: 1df0b6e29ae219edbeed14cfa97f0ad4688d6bf97ed946719ba3c3b69e004f3dee82991578eb5aceb554914b70c5b68feff9e321283c1fc8bc0fedf08df2cb4c
2026-01-30 18:26:07 -08:00
Hennadii Stepanov
516be10bb5 wallet: Rename RecordType::DELETE to RecordType::DELETE_FLAG
On Windows, the `winnt.h` header defines `DELETE` as a macro for a
"Standard Access Right" bitmask (0x00010000L).

This introduces a fragile dependency on header inclusion order: if
Windows headers happen to be included before this enum definition,
the preprocessor expands `DELETE` into a numeric literal, causing
syntax errors.

Rename the enumerator to `DELETE_FLAG` to remove this fragility and
avoid the collision entirely.
2026-01-30 14:21:17 +00:00
merge-script
8cdf1dcca0 Merge bitcoin/bitcoin#34373: refactor: Remove remaining std::bind, check via clang-tidy
fad042235b refactor: Remove remaining std::bind, check via clang-tidy (MarcoFalke)

Pull request description:

  `std::bind` has many issues:

  * It is verbose in a meaningless way
  * Overriden args are silently accepted and dropped at runtime without a compile error. Same for accidental duplicates.

  One could use `std::bind_front` similar to commit fa267551c4. Though, I think the remaining cases are better off with lambdas.

  So do that here, and enable the `modernize-avoid-bind` clang-tidy rule to avoid `std::bind` bugs in the future.

ACKs for top commit:
  fjahr:
    Code review ACK fad042235b
  purpleKarrot:
    Code review ACK fad042235b

Tree-SHA512: 38b17e26eda3ae47d84a8c34298309dc1eeb4ed434fda58b5803ef031c4c2edfb17222f5208f37af727bf340e32b37c7f81784f461d2b65fbc6227f3cd53eea4
2026-01-29 09:50:54 +00:00
Ava Chow
cd1af852fa Merge bitcoin/bitcoin#34358: wallet: fix removeprunedfunds bug with conflicting transactions
1f60ca360e wallet: fix removeprunedfunds bug with conflicting transactions (Martin Zumsande)

Pull request description:

  `removeprunedfunds` removes all entries from `mapTxSpends` for the inputs of the pruned tx. However, this is incorrect, because there could be multiple entries from conflicting transactions (that shouldn't be removed as well). This could lead to the wallet creating invalid transactions, trying to double spend utxos.
  The bug persists when the conflicting tx was mined, because the wallet trusts its internal accounting instead of calling `AddToSpends` again.

  The added test should fail on master.

ACKs for top commit:
  achow101:
    ACK 1f60ca360e
  fjahr:
    tACK 1f60ca360e
  furszy:
    utACK 1f60ca360e
  vasild:
    ACK 1f60ca360e

Tree-SHA512: 3cc9ed547530fd53e25721177b76ab2e1eae16ce2c0e63fc01b20fdbf8bd02655dae51167ad56f9dec748d34c61ce65d38f993370820601f8257c73b876a3347
2026-01-28 11:25:28 -08:00
MarcoFalke
fad042235b refactor: Remove remaining std::bind, check via clang-tidy 2026-01-28 14:44:22 +01:00
merge-script
d9e651f995 Merge bitcoin/bitcoin#33622: docs: add doc comment for SRD selection algorithm
3400db8040 doc: add missing param description to SRD (yancy)

Pull request description:

  The params documentation is missing `change_fee` and the description is lacking recent changes.

ACKs for top commit:
  murchandamus:
    ACK 3400db8040
  brunoerg:
    code review ACK 3400db8040

Tree-SHA512: 8f6fac0d92873c5c9f77b19fbc0c6ecfb425b2a6b3d5f5ad69c82ed706b21cf4627e68c71acbc43661000e6063e8f8dbcd3b8ff60e3c727bdcba497d13ee1383
2026-01-28 14:38:16 +01:00
yancy
3400db8040 doc: add missing param description to SRD
Co-authored-by: Mark "Murch" Erhardt <murch@murch.one>
2026-01-27 13:55:32 -06:00
Martin Zumsande
1f60ca360e wallet: fix removeprunedfunds bug with conflicting transactions
removeprunedfunds removes all entries from mapTxSpends for the
inputs of the pruned tx. However, this is incorrect, because there could be
multiple entries from conflicting transactions (that shouldn't be
removed as well). This could lead to the wallet creating invalid
transactions, trying to double spend utxos.
The bug persists when the conflicting tx was mined, because
the wallet trusts its internal accounting instead of calling
AddToSpends again.
2026-01-26 09:06:52 +07:00
Lőrinc
a73a3ec553 doc: fix invalid arg name hints for bugprone validation
The extra leading `=` or missing trailing `=` prevented clang-tidy's `bugprone-argument-comment` check from validating the parameter name, as it only matches comments formatted strictly as `/*arg=*/` (see https://clang.llvm.org/extra/clang-tidy/checks/bugprone/argument-comment.html).
2026-01-24 00:44:22 +01:00
merge-script
1b079becf1 Merge bitcoin/bitcoin#34317: fuzz: Exclude too expensive inputs in descriptor_parse targets
fab2f3df4b fuzz: Exclude too expensive inputs in descriptor_parse targets (MarcoFalke)

Pull request description:

  Accepting "expensive" fuzz inputs which have no real use-case is problematic, because it prevents the fuzz engine from spending time on the next useful fuzz input.

  For example, those will take several seconds (!) and the flamegraph shows that base58 encoding is the cause:

  ```
  curl -fLO 'f5abf41608'
  curl -fLO '78cb317546'

  FUZZ=mocked_descriptor_parse ./bld-cmake/bin/fuzz ./f5abf41608addcef3538da61d8096c2050235032
  FUZZ=descriptor_parse ./bld-cmake/bin/fuzz ./78cb3175467f53b467b949883ee6072e92dbb267
  ```

  This will also break 32-bit fuzzing, see https://github.com/bitcoin/bitcoin/issues/34110#issuecomment-3759461248.

  Fix all issues by checking for `HasTooLargeLeafSize`.

  Sorry for creating several pull requests to fix this class of issue, but I think this one should be the last one. 😅

ACKs for top commit:
  brunoerg:
    reACK fab2f3df4b
  frankomosh:
    re-ACK fab2f3df4b

Tree-SHA512: 4ecf98ec4adc39f6e014370945fb1598cdd3ceba60f7209b00789ac1164b6d20e82a69d71f8419d9a40d57ee3fea36ef593c47fe48b584b6e8344c44f20a15c1
2026-01-23 09:54:22 +00:00
David Gumberg
db2effaca4 scripted-diff: refactor: CWallet::Create() -> CreateNew()
Aside from being more legible, changing the name of `CWallet::Create()`
also validates that every instance where a new wallet is `Create()`'ed
is handled in this branch.

-BEGIN VERIFY SCRIPT-
sed -i 's|\bCreate(|CreateNew(|g' src/wallet/wallet.cpp  src/wallet/wallet.h  src/wallet/test/util.cpp src/wallet/test/wallet_tests.cpp
-END VERIFY SCRIPT-
2026-01-22 13:24:06 -08:00
David Gumberg
27e021ebc0 wallet: Correctly log stats for encrypted messages.
Previously creating an encrypted wallet would result in the keypool size
incorrectly being reported as 0.

See: https://github.com/bitcoin/bitcoin/pull/32636#discussion_r2150021064
2026-01-22 13:24:06 -08:00
David Gumberg
d8bec61be2 wallet: remove loading logic from CWallet::Create 2026-01-22 13:24:06 -08:00
David Gumberg
f35acc893f refactor: wallet: Factor out WriteVersion() from PopulateWalletFromDB()
Writing the wallet's `CLIENT_VERSION` (which indicates the last version
to have touched a wallet) needs to be done on both wallet creation and
wallet loading.

The next commit removes the `PopulateWalletFromDatabase()` call from
wallet creation, but this behavior needs to be preserved, so this commit
factors setting `CLIENT_VERSION` out of `PopulateWalletFromDatabase()`
so that wallet creation can use it in the next commit.
2026-01-22 13:24:06 -08:00
David Gumberg
e12ff8aca0 test: wallet: Split create and load 2026-01-22 13:24:06 -08:00
David Gumberg
70dbc79b09 wallet: Use CWallet::LoadExisting() for loading existing wallets. 2026-01-22 13:24:06 -08:00
David Gumberg
ae66e01164 wallet: Create separate function for wallet load
Splits out logic relevant only to existing wallets in
`CWallet::Create()` into `CWallet::LoadExisting()`
2026-01-22 13:24:06 -08:00
David Gumberg
bc69070416 refactor: Wallet stats logging in its own function
This will avoid repetition when wallet creation and loading are
separated.
2026-01-22 13:24:06 -08:00
David Gumberg
a9d64cd49c wallet: Remove redundant birth time update
Checking every SPKM in `CWallet::Create()` is not necessary, since the
only way presently for an SPKM to get added to `m_spk_managers` (the
return value of `GetAllScriptPubKeyMans()`) is through
`AddScriptPubKeyMan()`, which already invokes `MaybeUpdateBirthTime()`.
2026-01-22 13:24:06 -08:00
David Gumberg
b4a49cc727 wallet: Move argument parsing to before DB load
`m_keypool_size` must be set before `CWallet::PopulateWalletFromDB()`,
in order to move parsing of `-keypool` into `CWallet::LoadWalletArgs`,
`LoadWalletArgs()` invocation in `CWallet::Create()` must be moved
before `PopulateWalletFromDB()` is called.
2026-01-22 13:24:06 -08:00
David Gumberg
b15a94a618 refactor: Split out wallet argument loading
This section is necessarily repetitive, makes CWallet::Create() easier
to read, and splits out functionality that will be useful when wallet
creation and loading are separated.

Review with `-color-moved=dimmed-zebra`
2026-01-22 13:24:06 -08:00
MarcoFalke
fab2f3df4b fuzz: Exclude too expensive inputs in descriptor_parse targets
Also, fixup iwyu warnings in the util module.

Also, fixup a typo.

The moved part can be reviewed with the git option:
--color-moved=dimmed-zebra
2026-01-22 21:01:55 +01:00
merge-script
2a1234001c Merge bitcoin/bitcoin#34269: wallet: disallow creating new or restoring to an unnamed (default) wallet
75b704df9d wallettool: Disallow creating new unnamed wallets (Ava Chow)
5875a9c502 wallet: disallow unnamed wallets in createwallet and restorewallet (Ava Chow)
d30ad4a912 wallet, rpc: Use HandleWalletError in createwallet (Ava Chow)

Pull request description:

  We've been moving in the direction that all wallets must have a name. Therefore, we shouldn't allow creating new unnamed wallets. `createwallet`, `restorewallet`, and the wallet tool's `create` and `createfromdump` all now require the user to provide a non-empty wallet name when creating/restoring a wallet.

  The GUI is already enforcing this, but we were not enforcing it for RPCs or in the underlying `CreateWallet` and `RestoreWallet` functions.

  Wallet migration does still need to be able to restore unnamed wallets, so there is a new argument to `RestoreWallet` to explicitly allow that behavior for migration only.

ACKs for top commit:
  rkrux:
    lgtm ACK 75b704df9d
  polespinasa:
    re ACK 75b704df9d

Tree-SHA512: 8bde76d0b091e9276788c69412934af3426da2a7a69a00f94072d36c1a075cd41744ecdd5fef2b72870c1351b76aae061f124f716bb23f4839be20c464fc5ebd
2026-01-21 22:56:59 +01:00
merge-script
52096de212 Merge bitcoin/bitcoin#34032: util: Add some more Unexpected and Expected methods
faa59b3679 util: Add Expected::swap() (MarcoFalke)
fabb47e4e3 util: Implement Expected::operator*()&& (MarcoFalke)
fab9721430 util: Implement Expected::value()&& and Expected::error()&& (MarcoFalke)
fac4800959 util: Add Expected<void, E> specialization (MarcoFalke)
fa6575d6c2 util: Make Expected::value() throw (MarcoFalke)
fa1de1103f util: Add Unexpected::error() (MarcoFalke)
faa109f8be test: refactor: Use BOOST_CHECK_EQUAL over BOOST_CHECK == (MarcoFalke)
fad4a9fe2b Set bugprone-unused-return-value.AllowCastToVoid (MarcoFalke)

Pull request description:

  Reviewers requested more member functions In https://github.com/bitcoin/bitcoin/pull/34006.

  They are currently unused, but bring the port closer to the original `std::expected` implementation:

  * Make `Expected::value()` throw when no value exists
  * Add `Unexpected::error()` methods
  * Add `Expected<void, E>` specialization
  * Add `Expected::value()&&` and `Expected::error()&&` methods
  * Add `Expected::swap()`

  Also, include a tiny tidy fixup:

  * tidy: Set `AllowCastToVoid` in the `bugprone-unused-return-value` check

ACKs for top commit:
  stickies-v:
    re-ACK faa59b3679
  ryanofsky:
    Code review ACK faa59b3679. Thanks for the update. The commit I objected to is fixed now and the rest of the implementation seems good enough for code that's probably temporary.
  hodlinator:
    re-ACK faa59b3679

Tree-SHA512: b6ac28c1e7241837d9db83fe7534d713ca1283c20a77d2273743157d329f041ec0b503658d14b2f4425211808b61a88fed115d77149e0546825acd3bd9198edf
2026-01-21 13:23:43 +01:00
Ava Chow
f7e88e298a Merge bitcoin/bitcoin#32471: wallet/rpc: fix listdescriptors RPC fails to return descriptors with private key information when wallet contains descriptors missing any key
9c7e4771b1 test: Test listdescs with priv works even with missing priv keys (Novo)
ed945a6854 walletrpc: reject listdes with priv key on w-only wallets (Novo)
9e5e9824f1 descriptor: ToPrivateString() pass if  at least 1 priv key exists (Novo)
5c4db25b61 descriptor: refactor ToPrivateString for providers (Novo)
2dc74e3f4e wallet/migration: use HavePrivateKeys in place of ToPrivateString (Novo)
e842eb90bb descriptors: add HavePrivateKeys() (Novo)

Pull request description:

  _TLDR:
  Currently, `listdescriptors [private=true]` will fail for a non-watch-only wallet if any descriptor has a missing private key(e.g `tr()`, `multi()`, etc.). This PR changes that while making sure `listdescriptors [private=true]` still fails if there no private keys. Closes #32078_

  In non-watch-only wallets, it's possible to import descriptors as long as at least one private key is included. It's important that users can still view these descriptors when they need to create a backup—even if some private keys are missing ([#32078 (comment)](https://github.com/bitcoin/bitcoin/issues/32078#issuecomment-2781428475)). This change makes it possible to do so.

  This change also helps prevent `listdescriptors true` from failing completely, because one descriptor is missing some private keys.

  ### Notes
  - The new behaviour is applied to all descriptors including miniscript descriptors
  - `listdescriptors true` still fails for watch-only wallets to preserve existing behaviour https://github.com/bitcoin/bitcoin/pull/24361#discussion_r920801352
  - Wallet migration logic previously used `Descriptor::ToPrivateString()` to determine which descriptor was watchonly. This means that modifying the `ToPrivateString()` behaviour caused descriptors that were previously recognized as "watchonly" to be "non-watchonly". **In order to keep the scope of this PR limited to the RPC behaviour, this PR uses a different method to determine `watchonly` descriptors for the purpose of wallet migration.** A follow-up PR can be opened to update migration logic to exclude descriptors with some private keys from the `watchonly` migration wallet.

  ### Relevant PRs
  https://github.com/bitcoin/bitcoin/pull/24361
  https://github.com/bitcoin/bitcoin/pull/32186

  ### Testing
  Functional tests were added to test the new behaviour

  EDIT
  **`listdescriptors [private=true]` will still fail when there are no private keys because non-watchonly wallets must have private keys and calling `listdescriptors [private=true]` for watchonly wallet returns an error**

ACKs for top commit:
  Sjors:
    ACK 9c7e4771b1
  achow101:
    ACK 9c7e4771b1
  w0xlt:
    reACK 9c7e4771b1 with minor nits
  rkrux:
    re-ACK 9c7e4771b1

Tree-SHA512: f9b3b2c3e5425a26e158882e39e82e15b7cb13ffbfb6a5fa2868c79526e9b178fcc3cd88d3e2e286f64819d041f687353780bbcf5a355c63a136fb8179698b60
2026-01-20 12:17:19 -08:00
Ava Chow
75b704df9d wallettool: Disallow creating new unnamed wallets 2026-01-19 10:57:13 -08:00
Ava Chow
5875a9c502 wallet: disallow unnamed wallets in createwallet and restorewallet
Migration still needs to be able to restore unnamed wallets, so
allow_unnamed is added to RestoreWallet to explicitly allow that
behavior for migration only.
2026-01-19 10:55:55 -08:00
merge-script
c57fbbe99d Merge bitcoin/bitcoin#31650: refactor: Avoid copies by using const references or by move-construction
fa64d8424b refactor: Enforce readability-avoid-const-params-in-decls (MarcoFalke)
faf0c2d942 refactor: Avoid copies by using const references or by move-construction (MarcoFalke)

Pull request description:

  Top level `const` in declarations is problematic for many reasons:

  * It is often a typo, where one wanted to denote a const reference. For example `bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, ...` is missing the `&`. This will create a redundant copy of the value.
  * In constructors it prevents move construction.
  * It can incorrectly imply some data is const, like in an imaginary example `std::span<int> Shuffle(const std::span<int>);`, where the `int`s are *not* const.
  * The compiler ignores the `const` from the declaration in the implementation.
  * It isn't used consistently anyway, not even on the same line.

  Fix some issues by:

  * Using a const reference to avoid a copy, where read-only of the value is intended. This is only done for values that may be expensive to copy.
  * Using move-construction to avoid a copy
  * Applying `readability-avoid-const-params-in-decls` via clang-tidy

ACKs for top commit:
  l0rinc:
    diff reACK fa64d8424b
  hebasto:
    ACK fa64d8424b, I have reviewed the code and it looks OK.
  sedited:
    ACK fa64d8424b

Tree-SHA512: 293c000b4ebf8fdcc75259eb0283a2e4e7892c73facfb5c3182464d6cb6a868b7f4a6682d664426bf2edecd665cf839d790bef0bae43a8c3bf1ddfdd3d068d38
2026-01-19 11:44:04 +01:00
David Gumberg
a02c4a82d8 refactor: Move -walletbroadcast setting init
Modifying `fBroadcastTransactions` does not require any locks,
initialization of this wallet parameter can be relocated with all of the
other argument parsing in this function.
2026-01-16 11:29:11 -08:00
David Gumberg
411caf7281 wallet: refactor: PopulateWalletFromDB use switch statement.
Co-authored-by: @w0xlt <w0xlt@users.noreply.github.com>
2026-01-16 11:29:11 -08:00
David Gumberg
a48e23f566 refactor: wallet: move error handling to PopulateWalletFromDB() 2026-01-16 11:29:11 -08:00
David Gumberg
0972785fd7 wallet: Delete unnecessary PopulateWalletFromDB() calls 2026-01-15 18:04:46 -08:00
David Gumberg
f0a046094e scripted-diff: refactor: CWallet::LoadWallet->PopulateWalletFromDB
There are too many functions in CWallet with names like "Load" and
"Create", disambiguate what CWallet::LoadWallet does by renaming it to
PopulateWalletFromDB.

-BEGIN VERIFY SCRIPT-
sed -i 's|\bLoadWallet()|PopulateWalletFromDB()|g' $(git grep -l 'LoadWallet()' -- ':(exclude)src/wallet/walletdb.cpp')
-END VERIFY SCRIPT-
2026-01-15 18:04:21 -08:00
merge-script
37cb209277 Merge bitcoin/bitcoin#34238: wallet: remove erroneous-on-reorg Assume()
d09a19fd41 test: add coverage for issue 34206 (Greg Sanders)
4c7cfd37ad wallet: remove erroneous-on-reorg Assume() (Greg Sanders)

Pull request description:

  Resolves https://github.com/bitcoin/bitcoin/issues/34206

  I'm not certain the test is worth keeping, but included it for now to show minimal example that crashes without fix. Can be removed.

ACKs for top commit:
  bensig:
    ACK d09a19fd41
  dergoegge:
    utACK d09a19fd41

Tree-SHA512: 7eac19e97be6db8e38af396c406066fdcec532332e685a38bb33f0a988701c7bd5a0967f51426737fd56972847b761a3d873495928ff66efa8512fb267a9622b
2026-01-15 14:17:25 +00:00
MarcoFalke
fa64d8424b refactor: Enforce readability-avoid-const-params-in-decls 2026-01-14 23:04:12 +01:00
MarcoFalke
faf0c2d942 refactor: Avoid copies by using const references or by move-construction 2026-01-14 23:03:47 +01:00
merge-script
4aa80c3b5e Merge bitcoin/bitcoin#34230: fuzz: Reject too large descriptor leaf sizes in scriptpubkeyman target
fa8d56f9f0 fuzz: Reject too large descriptor leaf sizes in scriptpubkeyman target (MarcoFalke)
fabac1b395 fuzz: Reject some more "expensive" descriptors in the scriptpubkeyman target (MarcoFalke)
333333356f fuzz: [refactor] Use std::span over FuzzBufferType in descriptor utils (MarcoFalke)

Pull request description:

  Accepting "expensive" fuzz inputs which have no real use-case is problematic, because it prevents the fuzz engine from spending time on the next useful fuzz input.

  Also, this may lead to problems, where the fuzz target can not be run at all on some platforms. See https://github.com/bitcoin/bitcoin/issues/34110.

  Fixes https://github.com/bitcoin/bitcoin/issues/34110 by rejecting those useless and expensive inputs (via the third commit)

  Can be tested by running the input and checking the time before and after the changes here:

  ```
  curl -fLO '1cf91e0c6b'
  FUZZ=scriptpubkeyman time ./bld-cmake/bin/fuzz ./1cf91e0c6bfff9dafcd4db5b0ba36b1e906f4cf5
  ```

  Also, the second commit fixes https://github.com/bitcoin/bitcoin/issues/31066.

ACKs for top commit:
  brunoerg:
    code review ACK fa8d56f9f0
  marcofleon:
    ACK fa8d56f9f0
  sipa:
    ACK fa8d56f9f0

Tree-SHA512: e683cb89c3047358add438508c173f1cf647827bcadc3564ad42c757e4c99b8e9b777213fd38ebeb46f4c89a72363e0642f47435e20df3960eaeb5b8257dbd32
2026-01-13 15:28:25 -08:00
Ava Chow
d30ad4a912 wallet, rpc: Use HandleWalletError in createwallet
Use the utility function HandleWalletError to deal with wallet creation
errors in createwallet.
2026-01-13 13:12:50 -08:00
Ryan Ofsky
796f18e559 Merge bitcoin/bitcoin#29415: Broadcast own transactions only via short-lived Tor or I2P connections
8937221304 doc: add release notes for 29415 (Vasil Dimov)
582016fa5f test: add unit test for the private broadcast storage (Vasil Dimov)
e74d54e048 test: add functional test for private broadcast (Vasil Dimov)
818b780a05 rpc: use private broadcast from sendrawtransaction RPC if -privatebroadcast is ON (Vasil Dimov)
eab595f9cf net_processing: retry private broadcast (Vasil Dimov)
37b79f9c39 net_processing: stop private broadcast of a transaction after round-trip (Vasil Dimov)
2de53eee74 net_processing: handle ConnectionType::PRIVATE_BROADCAST connections (Vasil Dimov)
30a9853ad3 net_processing: move a debug check in VERACK processing earlier (Vasil Dimov)
d1092e5d48 net_processing: modernize PushNodeVersion() (Vasil Dimov)
9937a12a2f net_processing: move the debug log about receiving VERSION earlier (Vasil Dimov)
a098f37b9e net_processing: reorder the code that handles the VERSION message (Vasil Dimov)
679ce3a0b8 net_processing: store transactions for private broadcast in PeerManager (Vasil Dimov)
a3faa6f944 node: extend node::TxBroadcast with a 3rd option (Vasil Dimov)
95c051e210 net_processing: rename RelayTransaction() to better describe what it does (Vasil Dimov)
bb49d26032 net: implement opening PRIVATE_BROADCAST connections (Vasil Dimov)
01dad4efe2 net: introduce a new connection type for private broadcast (Vasil Dimov)
94aaa5d31b init: introduce a new option to enable/disable private broadcast (Vasil Dimov)
d6ee490e0a log: introduce a new category for private broadcast (Vasil Dimov)

Pull request description:

  _Parts of this PR are isolated in independent smaller PRs to ease review:_

  * [x] _https://github.com/bitcoin/bitcoin/pull/29420_
  * [x] _https://github.com/bitcoin/bitcoin/pull/33454_
  * [x] _https://github.com/bitcoin/bitcoin/pull/33567_
  * [x] _https://github.com/bitcoin/bitcoin/pull/33793_

  ---

  To improve privacy, broadcast locally submitted transactions (from the `sendrawtransaction` RPC) to the P2P network only via Tor or I2P short-lived connections, or to IPv4/IPv6 peers but through the Tor network.

  * Introduce a new connection type for private broadcast of transactions with the following properties:
    * started whenever there are local transactions to be sent
    * opened to Tor or I2P peers or IPv4/IPv6 via the Tor proxy
    * opened regardless of max connections limits
    * after handshake is completed one local transaction is pushed to the peer, `PING` is sent and after receiving `PONG` the connection is closed
    * ignore all incoming messages after handshake is completed (except `PONG`)

  * Broadcast transactions submitted via `sendrawtransaction` using this new mechanism, to a few peers. Keep doing this until we receive back this transaction from one of our ordinary peers (this takes about 1 second on mainnet).

  * The transaction is stored in peerman and does not enter the mempool.

  * Once we get an `INV` from one of our ordinary peers, then the normal flow executes: we request the transaction with `GETDATA`, receive it with a `TX` message, put it in our mempool and broadcast it to all our existent connections (as if we see it for the first time).

  * After we receive the full transaction as a `TX` message, in reply to our `GETDATA` request, only then consider the transaction has propagated through the network and remove it from the storage in peerman, ending the private broadcast attempts.

  The messages exchange should look like this:

  ```
  tx-sender >--- connect -------> tx-recipient
  tx-sender >--- VERSION -------> tx-recipient (dummy VERSION with no revealing data)
  tx-sender <--- VERSION -------< tx-recipient
  tx-sender <--- WTXIDRELAY ----< tx-recipient (maybe)
  tx-sender <--- SENDADDRV2 ----< tx-recipient (maybe)
  tx-sender <--- SENDTXRCNCL ---< tx-recipient (maybe)
  tx-sender <--- VERACK --------< tx-recipient
  tx-sender >--- VERACK --------> tx-recipient
  tx-sender >--- INV/TX --------> tx-recipient
  tx-sender <--- GETDATA/TX ----< tx-recipient
  tx-sender >--- TX ------------> tx-recipient
  tx-sender >--- PING ----------> tx-recipient
  tx-sender <--- PONG ----------< tx-recipient
  tx-sender disconnects
  ```

  Whenever a new transaction is received from `sendrawtransaction` RPC, the node will send it to a few (`NUM_PRIVATE_BROADCAST_PER_TX`) recipients right away. If after some time we still have not heard anything about the transaction from the network, then it will be sent to 1 more peer (see `PeerManagerImpl::ReattemptPrivateBroadcast()`).

  A few considerations:
  * The short-lived private broadcast connections are very cheap and fast wrt network traffic. It is expected that some of those peers could blackhole the transaction. Just one honest/proper peer is enough for successful propagation.
  * The peers that receive the transaction could deduce that this is initial transaction broadcast from the transaction originator. This is ok, they can't identify the sender.

  ---

  <details>
  <summary>How to test this?</summary>

  Thank you, @stratospher and @andrewtoth!

  Start `bitcoind` with `-privatebroadcast=1 -debug=privatebroadcast`.

  Create a wallet and get a new address, go to the Signet faucet and request some coins to that address:
  ```bash
  build/bin/bitcoin-cli -chain="signet" createwallet test
  build/bin/bitcoin-cli -chain="signet" getnewaddress
  ```

  Get a new address for the test transaction recipient:
  ```bash
  build/bin/bitcoin-cli -chain="signet" loadwallet test
  new_address=$(build/bin/bitcoin-cli -chain="signet" getnewaddress)
  ```

  Create the transaction:
  ```bash
  # Option 1: `createrawtransaction` and `signrawtransactionwithwallet`:

  txid=$(build/bin/bitcoin-cli -chain="signet" listunspent | jq -r '.[0] | .txid')
  vout=$(build/bin/bitcoin-cli -chain="signet" listunspent | jq -r '.[0] | .vout')
  echo "txid: $txid"
  echo "vout: $vout"

  tx=$(build/bin/bitcoin-cli -chain="signet" createrawtransaction "[{\"txid\": \"$txid\", \"vout\": $vout}]" "[{\"$new_address\": 0.00001000}]" 0 false)
  echo "tx: $tx"

  signed_tx=$(build/bin/bitcoin-cli -chain="signet" signrawtransactionwithwallet "$tx" | jq -r '.hex')
  echo "signed_tx: $signed_tx"

  # OR Option 2: `walletcreatefundedpsbt` and `walletprocesspsbt`:
  # This makes it not have to worry about inputs and also automatically sends back change to the wallet.
  # Start `bitcoind` with `-fallbackfee=0.00003000` for instance for 3 sat/vbyte fee.

  psbt=$(build/bin/bitcoin-cli -chain="signet" walletcreatefundedpsbt "[]" "[{\"$new_address\": 0.00001000}]" | jq -r '.psbt')
  echo "psbt: $psbt"

  signed_tx=$(build/bin/bitcoin-cli -chain="signet" walletprocesspsbt "$psbt" | jq -r '.hex')
  echo "signed_tx: $signed_tx"
  ```

  Finally, send the transaction:
  ```bash
  raw_tx=$(build/bin/bitcoin-cli -chain="signet" sendrawtransaction "$signed_tx")
  echo "raw_tx: $raw_tx"
  ```

  </details>

  ---

  <details>
  <summary>High-level explanation of the commits</summary>

  * New logging category and config option to enable private broadcast
    * `log: introduce a new category for private broadcast`
    * `init: introduce a new option to enable/disable private broadcast`

  * Implement the private broadcast connection handling on the `CConnman` side:
    * `net: introduce a new connection type for private broadcast`
    * `net: implement opening PRIVATE_BROADCAST connections`

  * Prepare `BroadcastTransaction()` for private broadcast requests:
    * `net_processing: rename RelayTransaction to better describe what it does`
    * `node: extend node::TxBroadcast with a 3rd option`
    * `net_processing: store transactions for private broadcast in PeerManager`

  * Implement the private broadcast connection handling on the `PeerManager` side:
    * `net_processing: reorder the code that handles the VERSION message`
    * `net_processing: move the debug log about receiving VERSION earlier`
    * `net_processing: modernize PushNodeVersion()`
    * `net_processing: move a debug check in VERACK processing earlier`
    * `net_processing: handle ConnectionType::PRIVATE_BROADCAST connections`
    * `net_processing: stop private broadcast of a transaction after round-trip`
    * `net_processing: retry private broadcast`

  * Engage the new functionality from `sendrawtransaction`:
    * `rpc: use private broadcast from sendrawtransaction RPC if -privatebroadcast is ON`

  * New tests:
    * `test: add functional test for private broadcast`
    * `test: add unit test for the private broadcast storage`

  </details>

  ---

  **This PR would resolve the following issues:**
  https://github.com/bitcoin/bitcoin/issues/3828 Clients leak IPs if they are recipients of a transaction
  https://github.com/bitcoin/bitcoin/issues/14692 Can't configure bitocoind to only send tx via Tor but receive clearnet transactions
  https://github.com/bitcoin/bitcoin/issues/19042 Tor-only transaction broadcast onlynet=onion alternative
  https://github.com/bitcoin/bitcoin/issues/24557 Option for receive events with all networks, but send transactions and/or blocks only with anonymous network[s]?
  https://github.com/bitcoin/bitcoin/issues/25450 Ability to broadcast wallet transactions only via dedicated oneshot Tor connections
  https://github.com/bitcoin/bitcoin/issues/32235 Tor: TX circuit isolation

  **Issues that are related, but (maybe?) not to be resolved by this PR:**
  https://github.com/bitcoin/bitcoin/issues/21876 Broadcast a transaction to specific nodes
  https://github.com/bitcoin/bitcoin/issues/28636 new RPC: sendrawtransactiontopeer

  ---

  Further extensions:
  * Have the wallet do the private broadcast as well, https://github.com/bitcoin/bitcoin/issues/11887 would have to be resolved.
  * Have the `submitpackage` RPC do the private broadcast as well, [draft diff in the comment below](https://github.com/bitcoin/bitcoin/pull/29415#pullrequestreview-2972293733), thanks ismaelsadeeq!
  * Add some stats via RPC, so that the user can better monitor what is going on during and after the broadcast. Currently this can be done via the debug log, but that is not convenient.
  * Make the private broadcast storage, currently in peerman, persistent over node restarts.
  * Add (optional) random delay before starting to broadcast the transaction in order to avoid correlating unrelated transactions based on the time when they were broadcast. Suggested independently of this PR [here](https://github.com/bitcoin/bitcoin/issues/30471).
  * Consider periodically sending transactions that did not originate from the node as decoy, discussed [here](https://github.com/bitcoin/bitcoin/pull/29415#discussion_r2035414972).
  * Consider waiting for peer's FEEFILTER message and if the transaction that was sent to the peer is below that threshold, then assume the peer is going to drop it. Then use this knowledge to retry more aggressively with another peer, instead of the current 10 min. See [comment below](https://github.com/bitcoin/bitcoin/pull/29415#issuecomment-3258611648).
  * It may make sense to be able to override the default policy -- eg so submitrawtransaction can go straight to the mempool and relay, even if txs are normally privately relayed. See [comment below](https://github.com/bitcoin/bitcoin/pull/29415#issuecomment-3427086681).
  * As a side effect we have a new metric available - the time it takes for a transaction to reach a random node in the network (from the point of view of the private broadcast recipient the tx originator is a random node somewhere in the network). This can be useful for monitoring, unrelated to privacy characteristics of this feature.

  ---

  _A previous incarnation of this can be found at https://github.com/bitcoin/bitcoin/pull/27509. It puts the transaction in the mempool and (tries to) hide it from the outside observers. This turned out to be too error prone or maybe even impossible._

ACKs for top commit:
  l0rinc:
    code review diff ACK 8937221304
  andrewtoth:
    ACK 8937221304
  pinheadmz:
    ACK 8937221304
  w0xlt:
    ACK 8937221304 with nit https://github.com/bitcoin/bitcoin/pull/29415#discussion_r2654849875
  mzumsande:
    re-ACK 8937221304

Tree-SHA512: d51dadc865c2eb080c903cbe2f669e69a967e5f9fc64e9a20a68f39a67bf0db6ac2ad682af7fa24ef9f0942a41c89959341a16ba7b616475e1c5ab8e563b9b96
2026-01-12 15:02:14 -05:00
Greg Sanders
4c7cfd37ad wallet: remove erroneous-on-reorg Assume() 2026-01-09 08:41:37 -05:00
MarcoFalke
fa8d56f9f0 fuzz: Reject too large descriptor leaf sizes in scriptpubkeyman target 2026-01-08 14:26:29 +01:00
MarcoFalke
fabac1b395 fuzz: Reject some more "expensive" descriptors in the scriptpubkeyman target
The same are rejected in the descriptor_parse target, so it makes sense
to reject them here as well.
2026-01-08 14:26:23 +01:00
merge-script
cd6e4c9235 Merge bitcoin/bitcoin#34215: wallettool: fix unnamed createfromdump failure walletsdir deletion
f78f6f1dc8 wallettool: do not use fs::remove_all in createfromdump cleanup (Ava Chow)

Pull request description:

  As pointed out in https://github.com/bitcoin/bitcoin/pull/34156#issuecomment-3716728670, it is possible for `createfromdump` to also accidentally delete the entire wallets directory if the wallet name is the empty string and the dumpfile contains a checksum error.

  This is also fixed by removing the files created by only removing the directory for named wallets, and avoiding the use of `fs::remove_all`.

ACKs for top commit:
  waketraindev:
    lgtm ACK f78f6f1dc8
  polespinasa:
    code review and tACK f78f6f1dc8
  rkrux:
    Code review and tACK f78f6f1dc8
  willcl-ark:
    ACK f78f6f1dc8
  pablomartin4btc:
    ACK f78f6f1dc8

Tree-SHA512: ff1e7668131ec3632c67d990c99e8fddff28605e7e553c7e20695e61017c88476c3636e22f2007e763a00d527e80e4d1d3d45409f6678d28729b8397430bfe7a
2026-01-07 14:32:01 +00:00
merge-script
90d651a81f Merge bitcoin/bitcoin#34156: wallet: fix unnamed legacy wallet migration failure
b7c34d08dd test: coverage for migration failure when last sync is beyond prune height (furszy)
82caa8193a wallet: migration, fix watch-only and solvables wallets names (furszy)
d70b159c42 wallet: improve post-migration logging (furszy)
f011e0f068 test: restorewallet, coverage for existing dirs, unnamed wallet and prune failure (furszy)
36093bde63 test: add coverage for unnamed wallet migration failure (furszy)
f4c7e28e80 wallet: fix unnamed wallet migration failure (furszy)
4ed0693a3f wallet: RestoreWallet failure, erase only what was created (furszy)

Pull request description:

  Minimal fix for #34128.

  The issue occurs during the migration of a legacy unnamed wallet
  (the legacy "default" wallet). When the migration fails, the cleanup
  logic is triggered to roll back the state, which involves erasing the
  newly created descriptor wallets directories. Normally, this only
  affects the parent directories of named wallets, since they each
  reside in their own directories. However, because the unnamed
  wallet resides directly in the top-level `/wallets/` folder, this
  logic accidentally deletes the main directory.

  The fix ensures that only the wallet.dat file of the unnamed wallet
  is touched and restored, preserving the wallet in BDB format and
  leaving the main `/wallets/` directory intact.

  #### Story Line:
  #32273 fixed a different set of issues and, in doing so, uncovered
  this one.
  Before the mentioned PR, backups were stored in the same directory
  as the wallet.dat file. On a migration failure, the backup was then
  copied to the top-level `/wallets/` directory. For the unnamed legacy
  wallet, the wallet directory is the `/wallets/` directory, so the source
  and destination paths were identical. As a result, we threw early in the
  `fs::copy_file` call ([here](https://github.com/bitcoin/bitcoin/blob/29.x/src/wallet/wallet.cpp#L4572)) because the file already existed, as we
  were trying to copy the file onto itself. This caused the cleanup logic
  to abort early on and never reach the removal line.

  #### Testing Notes:
  Cherry-pick the test commit on top of master and run it. You will
  see the failure and realize the reason by reading the test code.

ACKs for top commit:
  achow101:
    ACK b7c34d08dd
  davidgumberg:
    crACK b7c34d08dd
  w0xlt:
    ACK b7c34d08dd
  willcl-ark:
    ACK b7c34d08dd

Tree-SHA512: d0be14c0ed6417f999c3f2f429652c2407097d0cc18453c91653e57ae4b5375b327ad3b2553d9ea6ff46a3ae00cdbd5ab325b94eba763072c4fc5a773b85618b
2026-01-07 11:08:57 +00:00
Novo
ed945a6854 walletrpc: reject listdes with priv key on w-only wallets 2026-01-07 10:44:43 +01:00
Novo
2dc74e3f4e wallet/migration: use HavePrivateKeys in place of ToPrivateString
ToPrivateString() behaviour will be modified in the following commits.

In order to keep the scope of this PR limited to the RPC behaviour,
this commit updates wallet migration to use 'Descriptor::HavePrivateKeys()'
in place of 'Descriptor::ToPrivateString()' to determine watchonly descriptors.

A follow-up PR can be opened to update migration logic to exclude
descriptors with some private keys from the watchonly migration wallet.
2026-01-07 09:36:18 +01:00
Novo
e842eb90bb descriptors: add HavePrivateKeys()
Previously, to determine if a desc is watchonly, `ToPrivateString()`, was used.
It returns `false` if there is at least one pubkey in the descriptor for which
the provider  does not have a private key.

ToPrivateString() behaviour will change in the following commits to only
return `false` if no priv keys could be found for the pub keys in the descriptor.

HavePrivateKeys() is added here to replace the use of ToPrivateString() for determining
if a descriptor is 'watchonly'.

Co-authored-by: rkrux <rkrux.connect@gmail.com>
2026-01-07 09:34:15 +01:00