#36597 wallet: return broadcast errors from CommitTransaction

ReviewabilityReviewsAgreementSize
#36597 pseudoramdom wallet: return broadcast errors from CommitTransactionWaiting on author0Positive150 + 110 tests
  • Let wallet frontends learn whether a transaction was accepted into the mempool.
  • Stops interfaces like the QML GUI from reporting transactions as sent when rejected by the node.
  • Ready for review.
  • Concept approval without stated reasons (w0xlt, Naomi-Gift)
  • Author clarified why synchronous broadcast errors are needed rather than async alerts (maflcko)
  • Open request to add interface test coverage for mempool rejections (ViniciusCestarii)
  • 150 lines added or modified outside tests
  • 110 lines added or modified in tests
  • 116 lines removed in total
  • 22 files, 4 commits
IPC / multiprocess#14 of 18
P3 · unblocks #1008
  • P3 because it passes structured broadcast errors across process boundaries to frontends like the QML GUI.
  • Unblocks out-of-process frontends from detecting and handling rejected transactions.
Wallet#47 of 92
P3 · bug fix
  • P3 after comparing with the other PRs here (assessed alone as P2): Lowered from P2: no behavior change for current Core callers; the payoff accrues to downstream gui-qml, like #36685.
  • P2 because it fixes a flaw where broadcast errors were discarded and only logged internally.
  • Prevents callers from falsely reporting success when the node rejects a transaction.
Reviewability: ReadyStalePaused
Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets
Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs.
Size: lines added or modified outside tests, then in tests; greener = smaller.

Goal

Updates wallet commit and transaction broadcast interfaces to return an `Expected<void, TransactionBroadcastError>` instead of `void` or error codes with separate output strings. Introduces `TransactionBroadcastError` pairing a `TransactionError` code with an untranslated message, removes `TransactionError::OK`, and adds a `SUBMISSION_SKIPPED` code for transactions that the wallet chooses not to relay. Existing callers such as RPCs and the Qt widgets interface currently discard the result to preserve existing behavior.

Problem: Frontends relying on `interfaces::Wallet::commitTransaction` cannot know if a transaction was accepted into the mempool because `CWallet::CommitTransaction()` only logged errors and returned `void`. Consequently, interfaces such as the QML GUI report 'Transaction sent' even when the node rejected the transaction.

Evidence of importance:

  • PR description: 'The QML GUI shows "Transaction sent" even when the node rejected the transaction, because CWallet::CommitTransaction() only logs the rejection and returns void' (bitcoin-core/gui-qml#1008)

Scope: Modifies public interfaces across `interfaces::Wallet`, `interfaces::Chain`, and `interfaces::Node`, refactoring `BroadcastTransaction` signatures and updating all internal invocation sites.

Risk of a mistake: medium (wallet, gui, rpc_api). A mistake in transaction submission error handling or mempool state tracking in CWallet could cause wallet transactions to incorrectly fail or become desynchronized from the node.

Changed since the previous assessment: Initial description was evaluated without patch data; the full patch confirms the signature refactoring across interfaces and the addition of unit tests in `wallet_interfaces_tests.cpp` and `wallet_tests.cpp`.

Reviewability

Paused · Waiting on author

Open objection from ViniciusCestarii with no author reply for 9 days; review would repeat what has been asked.

Waiting on (from the thread read):

  • author: respond to ViniciusCestarii's test coverage observation 2026-10-23
  • reviewer: maflcko to review author's explanation of use cases 2026-10-23

Agreement

Positive

support without stated reasons, no open objection (w0xlt, Naomi-Gift)

Thread notes: Added maflcko's inquiry questioning the rationale for CommitTransaction errors (answered by pseudoramdom) and ViniciusCestarii's mutation test observation indicating missing test coverage in interface tests.

Objections, suggestions, and questions:

ReviewerTypeHarm, request, or questionStatusMust beAuthor repliedQuote
Naomi-Gift (none)question, interfaceWill the QML GUI treat SUBMISSION_SKIPPED with -walletbroadcast=0 as saved rather than failure?answered · author confirmed that SUBMISSION_SKIPPED is intended to allow callers to differentiate skipped submissions from rejectionsansweredyes2026-10-22: 'Will the QML GUI treat that as "saved, not broadcast" rather than as a failure?'
Status from: 2026-10-22: pseudoramdom: 'Yes, that's the intent. `SUBMISSION_SKIPPED` is a separate code precisely so a caller can differentiate'
maflcko (member)suggestion, usefulnessExplain why returning broadcast errors is needed rather than fixing creation bugs, warning about fees before creation, or reporting asynchronouslyanswered · author explained that imported PSBTs bypass create checks, and sync errors complement async notificationsansweredyes2026-10-23: 'Can you explain why this would be needed? If the wallet creates a consensus-invalid (permanently invalid) transaction, that seems like a severe bug that should be fixed instead.'
Status from: 2026-10-23: pseudoramdom: 'The GUI can import a PSBT that spends the wallet's coins, sign it with the wallet and commit it. Its inputs and fee were chosen elsewhere and none of `createTransaction()`'s checks ran'
ViniciusCestarii (contributor)suggestion, maintenanceAdd test coverage for MEMPOOL_REJECTED in interfaces::Wallet commit_transaction test to kill surviving mutantopenansweredno2026-10-23: 'returning success on `MEMPOOL_REJECTED` survives. `CommitTransactionErrors` covers the rejection on `CWallet`, but the interface test `commit_transaction` (the path gui-qml#1008 uses) only covers `SUBMISSION_SKIPPED` and success.'

Support:

  • w0xlt (contributor) (Concept ACK, 2026-10-21): (no reason given) [not substantive]
  • Naomi-Gift (none) (Concept ACK, 2026-10-22): (no reason given) [not substantive]

Participants:

  • w0xlt (contributor, support, 1 statements 2026-10-21 to 2026-10-21): gave Concept ACK
  • Naomi-Gift (none, support, 1 statements 2026-10-22 to 2026-10-22): Concept ACK; asked how QML GUI handles SUBMISSION_SKIPPED
  • ViniciusCestarii (contributor, neutral, 1 statements 2026-10-23 to 2026-10-23): pointed out surviving mutant from missing test coverage for MEMPOOL_REJECTED
  • maflcko (member, question, 1 statements 2026-10-23 to 2026-10-23): asked why broadcast errors are needed instead of fixing wallet bugs or using async feedback

Dependencies

Enables:

  • bitcoin-core/gui-qml update to report broadcast errors to users (gui-qml#1008)
  • Follow-up PR propagating errors from commitBumpTransaction

Files

116 lines under test/bench/ci.

  • src/wallet/test/wallet_tests.cpp +77/-6
  • src/wallet/wallet.cpp +32/-21
  • src/node/transaction.cpp +21/-12
  • src/wallet/test/wallet_interfaces_tests.cpp +32/-0
  • src/rpc/mempool.cpp +12/-16
  • src/node/interfaces.cpp +8/-13
  • src/interfaces/chain.h +9/-8
  • src/node/transaction.h +8/-9
  • src/net_processing.cpp +9/-5
  • src/node/types.h +13/-1
  • src/wallet/wallet.h +10/-3
  • src/qt/psbtoperationsdialog.cpp +4/-6
  • src/interfaces/node.h +5/-3
  • src/interfaces/wallet.h +5/-2
  • src/net_processing.h +3/-3
  • src/wallet/interfaces.cpp +3/-2
  • src/common/messages.cpp +2/-2
  • src/wallet/rpc/spend.cpp +2/-2
  • src/qt/walletmodel.cpp +1/-1
  • src/rpc/util.cpp +2/-0
  • src/wallet/feebumper.cpp +1/-1
  • src/test/fuzz/kitchen_sink.cpp +1/-0

Card

The short text the ranking pass compares across PRs.

Changes CWallet::CommitTransaction and interfaces::Wallet::commitTransaction to return broadcast errors via util::Expected instead of returning void. This allows interfaces such as the QML GUI to inform users when a transaction has been rejected instead of falsely claiming 'Transaction sent' (bitcoin-core/gui-qml#1008). Existing wallet RPC and Qt widget callers ignore the returned error to maintain existing behavior.

Data

dossier JSON · extract JSON · model ledger, generated 2026-10-24T01:18, confidence high, input hash 5b8ed912367b78fd
thread read through 2026-10-23 (6 statements), head 4e3d167559
labels: Wallet

Categories

IPC / multiprocess

P3 · unblocks #1008

Extends node and wallet interface methods to return structured broadcast errors needed by multiprocess consumers like the QML GUI. The PR description notes that frontends relying on `interfaces::Wallet::commitTransaction` could not detect rejected transactions because the method returned `void`, unblocking gui-qml#1008.

Membership: Modifies public interfaces in src/interfaces/wallet.h, src/interfaces/chain.h, and src/interfaces/node.h to propagate broadcast errors across process boundaries.

Factors: security/stability 0, bug 1, performance 0, user value 2, leverage 2

Wallet

P3 · bug fix (assessed alone as P2; Lowered from P2: no behavior change for current Core callers; the payoff accrues to downstream gui-qml, like #36685.)

Prevents wallet frontends from falsely displaying 'Transaction sent' when the node rejects a transaction. The PR description highlights that `CWallet::CommitTransaction()` previously discarded rejections by returning `void`, masking broadcast failures from the user.

Membership: Labeled 'Wallet' and modifies CWallet::CommitTransaction() alongside wallet spend and bump fee interfaces.

Factors: security/stability 0, bug 1, performance 0, user value 2, leverage 2