Skip to content

Support constructing transactions whose output exceeds input (SIGHASH_ANYONECANPAY workflows)#43

Open
151henry151 wants to merge 2 commits into
sparrowwallet:masterfrom
151henry151:anyonecanpay-pledge-psbt
Open

Support constructing transactions whose output exceeds input (SIGHASH_ANYONECANPAY workflows)#43
151henry151 wants to merge 2 commits into
sparrowwallet:masterfrom
151henry151:anyonecanpay-pledge-psbt

Conversation

@151henry151

@151henry151 151henry151 commented Jun 3, 2026

Copy link
Copy Markdown

createWalletTransaction refuses to build any transaction whose outputs are worth more than its inputs, throwing InsufficientFundsException before construction. That guard is correct for an ordinary spend, but it rules out the family of constructions built on SIGHASH_ANYONECANPAY, where a signer commits an input to a set of outputs and other parties add their own inputs afterwards. Because each signature commits to the outputs but not to the full input set, no one can be cheated and nothing is broadcastable until the inputs collectively cover the outputs. The same primitive underlies assurance contracts and pledges (Lighthouse, Semaphore), atomic swaps (the inscription marketplaces use SINGLE|ANYONECANPAY), and collaborative payments where several parties co-fund one set of outputs. This is the construction requested in #706.

The change adds a boolean anyoneCanPay field to TransactionParameters. When it's set, createWalletTransaction takes a dedicated path that spends the wallet's available inputs into the requested outputs without requiring that the inputs cover them. The field defaults to false at every construction site, so existing behaviour is fully preserved and the standard path — both pre-fund checks and the selection loop — is left untouched. The new path keeps an empty-wallet guard, so an anyonecanpay request with nothing to contribute still fails clearly, and it rejects silent payments rather than building toward a placeholder address.

I've kept this to transaction construction on purpose and left sighash selection to signing, where it already lives. drongo and Sparrow already sign ALL|ANYONECANPAY correctly, and the transaction view already offers a sighash selector, so the only missing piece was the ability to build the transaction at all. Setting a sighash here would also be premature, since the right variant depends on the use case — ALL|ANYONECANPAY for pledges, SINGLE|ANYONECANPAY for swaps — and is really the signer's choice rather than the builder's.

Three tests cover the new path: one builds a transaction whose output is twice its single input, one confirms the resulting PSBT signs as ALL|ANYONECANPAY (sighash 0x81), and one confirms that an ordinary request with the flag unset still throws.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant