Repository navigation
Conversation
Roasbeef
left a comment
There was a problem hiding this comment.
Nice to have this follow up!
When sending `tx_add_input`, we previously required transmitting the entire transaction being spent to prevent malleability issues where attackers replace a `p2wpkh` input by a `p2pkh` input after signing. When both participants of the interactive-tx protocol sign a taproot input, this malleability issue disappears. We thus allow transmitting only the utxo amount and script in that case. For simplicity, we only allow this when splicing a taproot channels (where both participants sign the previous funding output) or when *all* inputs are using taproot (we could be less strict, but being too smart here may risk losing funds, so we err on the cautious side).
We add musig2 nonce fields in various `interactive-tx` messages to allow using taproot channels with dual funding and splicing.
Roasbeef
left a comment
There was a problem hiding this comment.
Took another pass at this. The overall shape looks right to me, but the musig2 nonce lifecycle needs a few more requirements before implementations can line up on it. Roughly in order of importance:
1. Nonce freshness and reuse
Nothing here says the funding_nonce and commit_nonces must be freshly generated for each negotiation, including each RBF attempt of a splice. Nothing says a node must never produce two different partial signatures with the same secret nonce either. With RBF in the picture, that's the easy way to leak the funding key: sign the shared input of attempt 1, then reuse the nonce for attempt 2 and sign a different message. I'd add something like:
- MUST generate a fresh `funding_nonce` (and `commit_nonces`) for every
interactive-tx session, including each RBF attempt.
- MUST NOT create more than one partial signature with a given secret nonce.
2. Reconnecting between tx_complete and tx_signatures
If peers disconnect after exchanging tx_complete but before a node has sent its tx_signatures, that node still has to produce its partial signature for the shared input after reconnecting. channel_reestablish only gains current_commit_nonce, so there's no way to exchange new funding nonces. Implementations are left with two choices: persist the secret nonce across restarts (which makes the reuse in (1) much easier to hit), or get stuck. I think channel_reestablish also needs a fresh funding nonce from each side when next_funding is set and tx_signatures hasn't been sent, with the partial signature made over those. If a node already sent tx_signatures, it should retransmit the exact same partial signature it sent before rather than re-signing.
3. Which tx_complete carries the nonces
tx_complete is sent several times in a session (every time a peer has nothing more to add), and the splice requirement only says "MUST include ... in funding_nonce" without naming the message. It should say which instance counts, e.g. the nonces MUST be included in every tx_complete and the receiver uses the ones from the final, consecutive tx_complete. Otherwise one side may sign with a nonce the other side thinks was superseded.
4. Dual-funding isn't covered
The description says this adds support for dual-funding taproot channels as well, but every new requirement is scoped to "When splicing a taproot channel". The open_channel2 flow gets no requirement for commit_nonces, so there's nothing that says where the first commitment's verification nonces come from in a dual-funded taproot open.
5. When prevtx may be omitted
The sender rule is "If the transaction splices an existing taproot channel or only uses taproot inputs". The first disjunct allows omitting prevtx in a taproot splice that also has segwit v0 inputs, which the tx_complete check then rejects. The second can't be known when tx_add_input is sent, as the peer may still add inputs. Since tx_complete is the real enforcement point, the sender rule could just be "MAY omit prevtx for a taproot input", with the rationale explaining that the session fails if any input isn't taproot.
"Taproot input" also isn't defined, while the tx_add_input check accepts any witness v1 to v16 program of 2 to 40 bytes. A v2 input passes tx_add_input but presumably isn't "taproot" for tx_complete. I'd define it once as OP_1 followed by a 32-byte push and use that in both places.
It'd also help to spell out the safety argument in the rationale. Omitting prevtx is fine because every signature in the transaction is BIP 341 with a sighash that commits to all prevout amounts and scripts, so a peer that lies in prevtx_details only makes our own signatures invalid. That also implies nodes MUST NOT use SIGHASH_ANYONECANPAY for their inputs here.
6. Smaller things
- With
prevtx_lenset to0, atx_add_inputwith bothshared_input_txidandprevtx_detailsset isn't rejected. - The duplicate check for
prevtx_detailsshould compare outpoints(prevtx_txid, prevtx_vout)against every previously added input, including ones that were added with a fullprevtx. shared_input_partial_signaturecarries apublic_nonce, which is redundant with thefunding_noncefromtx_complete. Either drop it (32 bytes), or require the receiver to fail if it doesn't match the nonce fromtx_complete.- Typos: "Ommitting" (and the existing "ommitted"), and "scriptpubKey".
We add support for splicing and dual-funding taproot channels. Each commit adds a specific feature that leverages taproot and splicing:
prevtxfield when using taproot: there are caveats because of malleability issues (see https://delvingbitcoin.org/t/malleability-issues-when-creating-shared-transactions-with-segwit-v0/497 for more details)