Skip to content

Interactive transaction construction for channel establishment v2 - #240

Draft
erickcestari wants to merge 6 commits into
lnfuzz:masterfrom
erickcestari:interactive-tx-construction
Draft

Interactive transaction construction for channel establishment v2#240
erickcestari wants to merge 6 commits into
lnfuzz:masterfrom
erickcestari:interactive-tx-construction

Conversation

@erickcestari

Copy link
Copy Markdown
Contributor

First slice of #239, split out so the dual funding work can be reviewed incrementally. This PR contains only library code in the smite crate: no IR, executor, or scenario changes.

  • The v2 channel id derivation (BOLT 2: SHA256 of the lexicographically sorted revocation basepoints), plus the temporary id built from the zero basepoint.
  • bitcoind RPC wrappers for createrawtransaction, decoderawtransaction, and signrawtransactionwithwallet so the wallet can sign only its own inputs of a collaboratively built funding transaction.
  • channel_tx::interactive_tx: builds the shared funding transaction from both sides' inputs and outputs, ordered by serial id per BOLT 2, and computes the fee the initiator owes for the common fields.

Depends on #222 and #221

Channel establishment v2 identifies a channel by
SHA256(lesser-revocation-basepoint || greater-revocation-basepoint) rather
than by the funding outpoint, so the id is stable across the interactive
transaction negotiation. Before accept_channel2 reveals the peer's
basepoint, a zeroed basepoint stands in for the non-initiator.

BOLT 2 gives no test vector for either derivation, so the expected digests
were computed independently and are pinned as regression vectors.
@erickcestari
erickcestari force-pushed the interactive-tx-construction branch from 2c34656 to 7cca921 Compare September 9, 2026 14:39
Channel establishment v2 needs two things the bitcoin-cli wrapper could not
do. tx_add_input carries the serialized previous transaction, so add
get_raw_transaction. tx_signatures carries our witnesses for a transaction
the peer broadcasts, so add sign_tx, which signs without broadcasting and,
unlike sign_and_broadcast_tx, does not require signing to be complete: a
dual-funded transaction also carries the peer's inputs, which our wallet
cannot sign, and the partially signed result still holds our own witnesses.

Both share a signrawtransactionwithwallet helper with sign_and_broadcast_tx,
whose complete=false assertion stays as-is since a v1 funding transaction
spends only our own inputs.

Also tolerate an already-broadcast transaction. In v2 the peer broadcasts
the funding transaction too, so it can already be in the mempool by the time
we get there. Only a confirmed transaction was handled before, which left
the mempool case panicking.
BOLT 2 interactive transaction construction has both peers contribute
inputs and outputs to one shared transaction, each tagged with a serial_id.
SharedTransaction accumulates those contributions and assembles the
transaction both peers must agree on: inputs and outputs sorted by ascending
serial_id, nVersion 2, and nLockTime from open_channel2.

The reconstruction is checked against the BOLT 3 Appendix G dual-funding
vectors, rebuilding the spec's unsigned funding transaction byte for byte
from the tx_add_input and tx_add_output messages it says each peer sends.

Contributions from a peer are total by construction: a prevtx that does not
parse, or a prevtx_vout past the end of it, yields an unknown prevout rather
than an error, since a peer is free to send nonsense and it is then the peer
that must fail the negotiation. Input and output counts are capped at the
BOLT limit of 252 so a mutated program cannot grow the session without
bound.

Also add signs_first, the BOLT 2 rule deciding who sends tx_signatures
first: lowest total contributed input value, with the lexicographically
lower node_id breaking a tie.
BOLT 2 splits fee responsibility for the shared transaction: the initiator
pays for the common fields, and each peer pays for the inputs and outputs it
contributed. SharedTransaction::local_fee_sat computes our share, which is
what makes a change output's value computable before the output exists.

The result is rounded up. BOLT 3 Appendix G's worked example has weight 609
at 253 sat/kw and states a fee of 155, not the 154 that truncating gives;
underpaying by a single satoshi makes the peer fail the negotiation at
tx_complete, which would silently kill every generated program.

Witness weight is charged at 108 per input rather than Appendix G's minimum
of 107, since our wallet inputs are P2WPKH and the appendix charges the
maximum of the two. Overestimating is the safe direction: the peer fails the
negotiation when our feerate falls short, never when it exceeds.
@erickcestari
erickcestari force-pushed the interactive-tx-construction branch from 7cca921 to 2a4e71a Compare September 9, 2026 14:43
@erickcestari
erickcestari marked this pull request as draft September 10, 2026 17:10
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