Skip to content

wallet: Identify transactions spending 0-value outputs, and add tests for anchor outputs in a wallet - #33268

Merged
fanquake merged 5 commits into
bitcoin:masterfrom
achow101:zero-value-from-me
Sep 12, 2025
Merged

wallet: Identify transactions spending 0-value outputs, and add tests for anchor outputs in a wallet#33268
fanquake merged 5 commits into
bitcoin:masterfrom
achow101:zero-value-from-me

Conversation

@achow101

@achow101 achow101 commented Aug 28, 2025

Copy link
Copy Markdown
Member

One of the ways that the wallet would determine if a transaction was sent from the wallet was by checking if the total amount being spent by a transaction from outputs known to the wallet was greater than 0. This has worked fine until recently since there was no reason for 0-value outputs to be created. However, with ephemeral dust and P2A, it is possible to create standard 0-value outputs, and the wallet was not correctly identifying the spends of such outputs. This PR updates IsFromMe to only check whether the wallet knows any of the inputs, rather than checking the debit amount of a transaction.

Additionally, a new functional test is added to test for this case, as well as a few other anchor output related scenarios. This also revealed a bug in sendall which would cause an assertion error when trying to spend all of the outputs in a wallet that has anchor outputs.

Fixes #33265

@DrahtBot

DrahtBot commented Aug 28, 2025

Copy link
Copy Markdown
Contributor

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/33268.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK rkrux, enirox001, furszy
Stale ACK jsarenik, w0xlt, kannapoix

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #31615 (validation: ensure assumevalid is always used during reindex by Eunovo)
  • #27865 (wallet: Track no-longer-spendable TXOs separately by achow101)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@achow101
achow101 force-pushed the zero-value-from-me branch 2 times, most recently from bae350c to 7d1c3ce Compare August 28, 2025 22:52
@DrahtBot

Copy link
Copy Markdown
Contributor

🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/49140364057
LLM reason (✨ experimental): Lint failure (py_lint) due to an unused import in test_wallet_anchor.py (CTransaction) flagged by ruff.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

@jsarenik

jsarenik commented Aug 29, 2025

Copy link
Copy Markdown

Tested ACK 7d1c3ce7f0f0. I have compiled it and ran the script from issue #33265 with no output other than the current UTC date. Works for me.

Thanks!

@jsarenik

jsarenik commented Aug 29, 2025

Copy link
Copy Markdown

After it gets merged into master branch, I would recommend backporting it to current stable release branch which already contains the ephemeral outputs support (29.x).

Comment thread src/wallet/wallet.cpp Outdated
Comment thread test/functional/wallet_anchor.py Outdated
@instagibbs

Copy link
Copy Markdown
Member

@jsarenik technically this issue could happen if miners mine 0-value outputs of any kind, but given that it's now relay-possible, seems like a good idea to backport

@jsarenik

Copy link
Copy Markdown

Tested ACK de98ffb

@furszy furszy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: is IsFromMe() method still relevant? It seems we could directly call IsMine() on the inputs, which is also cached now and should be slightly safer for migration.

@achow101

Copy link
Copy Markdown
Member Author

is IsFromMe() method still relevant?

Yes, transactions that we make are still treated specially.

@furszy

furszy commented Aug 31, 2025

Copy link
Copy Markdown
Member

is IsFromMe() method still relevant?

Yes, transactions that we make are still treated specially.

Where?. IsFromMe() seems to be only ever called in AddToWalletIfInvolvingMe() and ApplyMigrationData() next to the IsMine() call. In both places we effectively treat it as is_mine = IsMine(outputs) || IsFromMe(inputs).

@achow101

Copy link
Copy Markdown
Member Author

Where?

Hmm, I forgot that CachedTxIsFromMe does not use IsFromMe. Might need to change that in this PR as well.

@achow101

achow101 commented Sep 1, 2025

Copy link
Copy Markdown
Member Author

Hmm, I forgot that CachedTxIsFromMe does not use IsFromMe. Might need to change that in this PR as well.

I've pulled in 2 commits from #27865 which drop CachedTxIsFromMe and replace it with an IsFromMe check which is stored on disk.

In both places we effectively treat it as is_mine = IsMine(outputs) || IsFromMe(inputs).

While that's true, moving IsFromMe into IsMine does change IsMine's semantics in other places, so I'm not sure that that would necessarily be correct. I think it would be better to explore that in a different PR.

@DrahtBot

DrahtBot commented Sep 1, 2025

Copy link
Copy Markdown
Contributor

🚧 At least one of the CI tasks failed.
Task lint: https://github.com/bitcoin/bitcoin/runs/49358785571
LLM reason (✨ experimental): Lint Python code failure: unused variable fee in test_wallet_listtransactions.py (F841).

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: perhaps it makes review easier.

Suggested change
wallet = self.nodes[0].get_wallet_rpc("fromme")
fromme_wallet = self.nodes[0].get_wallet_rpc("fromme")

@fanquake

Copy link
Copy Markdown
Member

Backported to 29.x in #34268.

glozow pushed a commit to glozow/bitcoin that referenced this pull request Jan 14, 2026
If something is imported into the wallet, it can change the 'from me'
status of a transaction. This status is only visible through
gettransaction's "fee" field which is only shown for transactions that
are 'from me'.

Github-Pull: bitcoin#33268
Rebased-From: e76c2f7
glozow pushed a commit to glozow/bitcoin that referenced this pull request Jan 14, 2026
Instead of checking whether the total amount of inputs known by the
wallet is greater than 0, we should be checking for whether the input is
known by the wallet. This enables us to determine whether a transaction
spends an of output with an amount of 0, which is necessary for marking
0-value dust outputs as spent.

Github-Pull: bitcoin#33268
Rebased-From: 39a7dbd
glozow pushed a commit to glozow/bitcoin that referenced this pull request Jan 14, 2026
glozow pushed a commit to glozow/bitcoin that referenced this pull request Jan 14, 2026
sedited pushed a commit to sedited/bitcoin that referenced this pull request Jan 14, 2026
If something is imported into the wallet, it can change the 'from me'
status of a transaction. This status is only visible through
gettransaction's "fee" field which is only shown for transactions that
are 'from me'.

Github-Pull: bitcoin#33268
Rebased-From: e76c2f7
sedited pushed a commit to sedited/bitcoin that referenced this pull request Jan 14, 2026
Instead of checking whether the total amount of inputs known by the
wallet is greater than 0, we should be checking for whether the input is
known by the wallet. This enables us to determine whether a transaction
spends an of output with an amount of 0, which is necessary for marking
0-value dust outputs as spent.

Github-Pull: bitcoin#33268
Rebased-From: 39a7dbd
sedited pushed a commit to sedited/bitcoin that referenced this pull request Jan 14, 2026
sedited pushed a commit to sedited/bitcoin that referenced this pull request Jan 14, 2026
m_cached_from_me is used to track whether a transaction is "from me", i.e. has
any inputs which belong to the wallet. This is held in memory only in
the same way that a transaction's balances are.

Github-Pull: bitcoin#33268
Rebased-From: 113a422
fanquake added a commit that referenced this pull request Jan 15, 2026
b834447 [doc] generate manpages 29.3rc1 (glozow)
e9c9783 [build] bump version to 29.3rc1 (glozow)
e973b61 [doc] update release notes for 29.3rc1 (glozow)
f4b78c4 test: Add a test for anchor outputs in the wallet (Ava Chow)
c6e7765 wallet: Throw an error in sendall if the tx size cannot be calculated (Ava Chow)
bab1ac8 wallet: Determine IsFromMe by checking for TXOs of inputs (Ava Chow)
71633a9 test: Test wallet 'from me' status change (Ava Chow)
daef585 guix: Fix `osslsigncode` tests (Hennadii Stepanov)
7a71850 Remove unreliable seed from chainparams.cpp, and the associated README (SatsAndSports)
2e46886 miner: fix `addPackageTxs` unsigned integer overflow (ismaelsadeeq)

Pull request description:

  Backports:

  - #34227
  - #33723
  - #33475
  - #33268

  And final changes for 29.3rc1

ACKs for top commit:
  achow101:
    ACK b834447
  janb84:
    ACK b834447
  sedited:
    ACK b834447

Tree-SHA512: 68e02fbde7162f728229f4bfc803bedda6d269e54593ebe40da607f6bd25b2b10bc4297bfa0bc977ce2dc6b558efe6571a7f875090e0f916fc09e5b67432ba30
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
If something is imported into the wallet, it can change the 'from me'
status of a transaction. This status is only visible through
gettransaction's "fee" field which is only shown for transactions that
are 'from me'.

Github-Pull: bitcoin#33268
Rebased-From: afed5f7
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
Instead of checking whether the total amount of inputs known by the
wallet is greater than 0, we should be checking for whether the input is
known by the wallet. This enables us to determine whether a transaction
spends an of output with an amount of 0, which is necessary for marking
0-value dust outputs as spent.

Github-Pull: bitcoin#33268
Rebased-From: aae427c
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
m_cached_from_me is used to track whether a transaction is "from me", i.e. has
any inputs which belong to the wallet. This is held in memory only in
the same way that a transaction's balances are.

Github-Pull: bitcoin#33268
Rebased-From: 71136c5
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
6b222a3 cmake: Install `bitcoin` manpage (Hennadii Stepanov)
6d44e0e net: Do not apply whitelist permission to onion inbounds (Martin Zumsande)
5357c58 test: Add submitblock test in interface_ipc (TheCharlatan)
8247fdd test: Prevent disk space warning during node_init_tests (Ryan Ofsky)
609a121 ci: Enable CI_LIMIT_STACK_SIZE=1 in i686_no_ipc task (MarcoFalke)
f492aec test: Fix CLI_MAX_ARG_SIZE issues (MarcoFalke)
393b45d cmake: Fix regression in `secp256k1.cmake` (Hennadii Stepanov)
a4874b8 wallet: Add m_cached_from_me to cache "from me" status (Ava Chow)
c831d6a test: Add a test for anchor outputs in the wallet (Ava Chow)
0d311f7 wallet: Throw an error in sendall if the tx size cannot be calculated (Ava Chow)
bd80dc7 wallet: Determine IsFromMe by checking for TXOs of inputs (Ava Chow)
19da90f test: Test wallet 'from me' status change (Ava Chow)
6b1d3da trace: Workaround GCC bug compiling with old systemtap (Luke Dashjr)
b776b2e ci: always use tag for LLVM checkout (fanquake)
7c96632 gui: Avoid pathological QT text/markdown behavior... (David Gumberg)

Pull request description:

  Backports:
  * bitcoin#33243
  * bitcoin#33268
  * bitcoin#33310
  * bitcoin#33364
  * bitcoin#33379
  * bitcoin#33380
  * bitcoin#33391
  * bitcoin#33407
  * bitcoin-core/gui#886

ACKs for top commit:
  darosior:
    utACK 6b222a3
  hebasto:
    ACK 6b222a3, I applied all backports locally without conflicts and obtained a zero diff with this PR branch.

Tree-SHA512: 257cc5bd0423fbf2aff62c72957faea3de8731353d809b11e18d0e5cad174c7023dca9dedd0c73e07497eb804b7c48355a055b4461db260e2f0a5712d2514ff6
morozow pushed a commit to morozow/bitcoin_rd that referenced this pull request May 8, 2026
If something is imported into the wallet, it can change the 'from me'
status of a transaction. This status is only visible through
gettransaction's "fee" field which is only shown for transactions that
are 'from me'.

Github-Pull: bitcoin#33268
Rebased-From: afed5f7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zero output not cleared

10 participants