diff --git a/02-peer-protocol.md b/02-peer-protocol.md index 58bea2c1d..a5149f968 100644 --- a/02-peer-protocol.md +++ b/02-peer-protocol.md @@ -33,6 +33,11 @@ operation, and closing. * [Sharing funding signatures: `tx_signatures`](#sharing-funding-signatures-tx_signatures) * [Fee bumping: `tx_init_rbf` and `tx_ack_rbf`](#fee-bumping-tx_init_rbf-and-tx_ack_rbf) * [Channel Quiescence](#channel-quiescence) + * [Channel Splicing](#channel-splicing) + * [The `splice_init` Message](#the-splice_init-message) + * [The `splice_ack` Message](#the-splice_ack-message) + * [Splice Transaction Construction](#splice-transaction-construction) + * [Splice Completion](#splice-completion) * [Channel Close](#channel-close) * [Closing Initiation: `shutdown`](#closing-initiation-shutdown) * [Closing Negotiation: `closing_complete` and `closing_sig`](#closing-negotiation-closing_complete-and-closing_sig) @@ -42,6 +47,7 @@ operation, and closing. * [`cltv_expiry_delta` Selection](#cltv_expiry_delta-selection) * [Adding an HTLC: `update_add_htlc`](#adding-an-htlc-update_add_htlc) * [Removing an HTLC: `update_fulfill_htlc`, `update_fail_htlc`, and `update_fail_malformed_htlc`](#removing-an-htlc-update_fulfill_htlc-update_fail_htlc-and-update_fail_malformed_htlc) + * [Batching channel messages](#batching-channel-messages) * [Committing Updates So Far: `commitment_signed`](#committing-updates-so-far-commitment_signed) * [Completing the Transition to the Updated State: `revoke_and_ack`](#completing-the-transition-to-the-updated-state-revoke_and_ack) * [Updating Fees: `update_fee`](#updating-fees-update_fee) @@ -200,6 +206,18 @@ This message contains a transaction input. * [`prevtx_len*byte`:`prevtx`] * [`u32`:`prevtx_vout`] * [`u32`:`sequence`] + * [`tx_add_input_tlvs`:`tlvs`] + +1. `tlv_stream`: `tx_add_input_tlvs` +2. types: + 1. type: 0 (`shared_input_txid`) + 2. data: + * [`sha256`:`funding_txid`] + 1. type: 2 (`prevtx_details`) + 2. data: + * [`sha256`:`prevtx_txid`] + * [`u64`:`amount_satoshis`] + * [`...*byte`:`scriptpubkey`] #### Requirements @@ -208,6 +226,9 @@ The sending node: - MUST use a unique `serial_id` for each input currently added to the transaction - MUST set `sequence` to be less than or equal to 4294967293 (`0xFFFFFFFD`) + - If the transaction splices an existing taproot channel or only uses taproot inputs: + - MAY omit `prevtx` (and set `prevtx_len` to `0`) and provide the amount and script + of the output being spent in `prevtx_details` instead - MUST NOT re-transmit inputs it has received from the peer - if is the *initiator*: - MUST send even `serial_id`s @@ -218,11 +239,22 @@ The receiving node: - MUST add all received inputs to the transaction - MUST fail the negotiation if: - `sequence` is set to `0xFFFFFFFE` or `0xFFFFFFFF` - - the `prevtx` and `prevtx_vout` are identical to a previously added - (and not removed) input's - - `prevtx` is not a valid transaction - - `prevtx_vout` is greater or equal to the number of outputs on `prevtx` - - the `scriptPubKey` of the `prevtx_vout` output of `prevtx` is not exactly a 1-byte push opcode (for the numeric values `0` to `16`) followed by a data push between 2 and 40 bytes + - if `prevtx_len` is `0`: + - `shared_input_txid` is not set and `prevtx_details` is not set either + - if `shared_input_txid` is set: + - `shared_input_txid` and `prevtx_vout` don't match the previous funding output + - a previously added (and not removed) input already exists with `shared_input_txid` set + - if `prevtx_details` is set: + - `prevtx_details` and `prevtx_vout` are identical to a previously added (and not removed) input + - the `scriptpubKey` in `prevtx_details` is not exactly a 1-byte push opcode (for the numeric + values `1` to `16`) followed by a data push between 2 and 40 bytes + - if `prevtx_len` is not `0`: + - `prevtx_details` is also set + - `prevtx` and `prevtx_vout` are identical to a previously added (and not removed) input + - `prevtx` is not a valid transaction + - `prevtx_vout` is greater or equal to the number of outputs on `prevtx` + - the `scriptPubKey` of the `prevtx_vout` output of `prevtx` is not exactly a 1-byte push + opcode (for the numeric values `0` to `16`) followed by a data push between 2 and 40 bytes - the `serial_id` is already included in the transaction - the `serial_id` has the wrong parity - if has received 4096 `tx_add_input` messages during this negotiation @@ -235,8 +267,12 @@ MAY omit this message. `serial_id` is a randomly chosen number which uniquely identifies this input. Inputs in the constructed transaction MUST be sorted by `serial_id`. -`prevtx` is the serialized transaction that contains the output -this input spends. Used to verify that the input is non-malleable. +`prevtx` is the serialized transaction that contains the output this input +spends, used to verify that the input is non-malleable. It can be ommitted +(`prevtx_len` set to `0`) when both peers already know that the input is +non-malleable (e.g. when it is the previous funding output or when both +participants sign a taproot input). Ommitting this field allows spending +transactions that exceed 65kB and saves bandwidth. `prevtx_vout` is the index of the output being spent. @@ -353,6 +389,16 @@ contributions. 2. data: * [`channel_id`:`channel_id`] +1. `tlv_stream`: `tx_complete_tlvs` +2. types: + 1. type: 4 (`commit_nonces`) + 2. data: + * [`66*byte`: `current_local_commit_nonce`] + * [`66*byte`: `next_local_commit_nonce`] + 1. type: 6 (`funding_nonce`) + 2. data: + * [`66*byte`: `local_funding_nonce`] + #### Requirements The nodes: @@ -370,6 +416,8 @@ The receiving node: - the *initiator*'s fees do not cover the `common` fields - there are more than 252 inputs - there are more than 252 outputs + - there are inputs that use `prevtx_details` instead of providing the + whole `prevtx` but some inputs are not taproot inputs - the estimated weight of the tx is greater than 400,000 (`MAX_STANDARD_TX_WEIGHT`) #### Rationale @@ -385,6 +433,9 @@ For the `minimum fee` calculation see [BOLT #3](03-transactions.md#calculating-f The maximum inputs and outputs are capped at 252. This effectively fixes the byte size of the input and output counts on the transaction to one (1). +Using `prevtx_details` instead of providing the whole `prevtx` is restricted +to taproot inputs, otherwise the transaction can be malleated. + ### The `tx_signatures` Message 1. type: 71 (`tx_signatures`) @@ -393,12 +444,22 @@ the byte size of the input and output counts on the transaction to one (1). * [`sha256`:`txid`] * [`u16`:`num_witnesses`] * [`num_witnesses*witness`:`witnesses`] + * [`tx_signatures_tlvs`:`tlvs`] 1. subtype: `witness` 2. data: * [`u16`:`len`] * [`len*byte`:`witness_data`] +1. `tlv_stream`: `tx_signatures_tlvs` +2. types: + 1. type: 0 (`shared_input_signature`) + 2. data: + * [`signature`:`signature`] + 1. type: 2 (`shared_input_partial_signature`) + 2. data: + * [`98*byte`: `partial_signature || public_nonce`] + #### Requirements The sending node: @@ -1502,6 +1563,515 @@ channel state significantly more complex to maintain. This introduces the derivative property that it is impossible to batch multiple downstream protocols in the same quiescence session. +## Channel Splicing + +Splicing is the term given for replacing the funding transaction with +a new one. For simplicity, splicing takes place once a channel is +[quiescent](#channel-quiescence). + +Operation returns to normal once the splice transaction has been signed +(while waiting for one of the splice transactions to confirm), at which +point the channel isn't quiescent anymore. + +The splice is finally terminated when both sides send `splice_locked` +to indicate that one of the splice transactions reached acceptable depth. + + +-------+ +-------+ + | |--- splice_init -------------->| | + | A |<--------------- splice_ack ---| B | + | | | | + | |--- tx_add_input ------------->| | + | |<------------- tx_add_input ---| | + | |--- tx_add_input ------------->| | + | |<------------ tx_add_output ---| | + | |--- tx_add_output ------------>| | + | |<-------------- tx_complete ---| | + | |--- tx_add_output ------------>| | + | |<-------------- tx_complete ---| | + | |--- tx_complete -------------->| | + | | | | + | |--- commit_sig --------------->| | + | |<--------------- commit_sig ---| | + | |--- tx_signatures ------------>| | + | |<------------ tx_signatures ---| | + | | | | + | | | | + | | | | + | |--- update_add_htlc ---------->| | + | |--- commit_sig --------------->| | + | |--- commit_sig --------------->| | + | |<----------- revoke_and_ack ---| | + | |<--------------- commit_sig ---| | + | |<--------------- commit_sig ---| | + | |--- revoke_and_ack ----------->| | + | | | | + | | | | + | | | | + | |<-------------- tx_init_rbf ---| | + | |--- tx_ack_rbf --------------->| | + | |<------------- tx_add_input ---| | + | |--- tx_add_input ------------->| | + | |<------------- tx_add_input ---| | + | |--- tx_add_output ------------>| | + | |<------------ tx_add_output ---| | + | |--- tx_complete -------------->| | + | |<------------ tx_add_output ---| | + | |--- tx_complete -------------->| | + | |<-------------- tx_complete ---| | + | | | | + | |<--------------- commit_sig ---| | + | |--- commit_sig --------------->| | + | |--- tx_signatures ------------>| | + | |<------------ tx_signatures ---| | + | | | | + | | | | + | | | | + | |--- update_add_htlc ---------->| | + | |--- commit_sig --------------->| | + | |--- commit_sig --------------->| | + | |--- commit_sig --------------->| | + | |<----------- revoke_and_ack ---| | + | |<--------------- commit_sig ---| | + | |<--------------- commit_sig ---| | + | |<--------------- commit_sig ---| | + | |--- revoke_and_ack ----------->| | + | | | | + | | | | + | | | | + | |--- splice_locked ------------>| | + | |<------------ splice_locked ---| | + | | | | + | | | | + | | | | + | |--- update_add_htlc ---------->| | + | |--- commit_sig --------------->| | + | |<----------- revoke_and_ack ---| | + | |<--------------- commit_sig ---| | + | |--- revoke_and_ack ----------->| | + | | | | + +-------+ +-------+ + +### The `splice_init` Message + +1. type: 80 (`splice_init`) +2. data: + * [`channel_id`:`channel_id`] + * [`s64`:`funding_contribution_satoshis`] + * [`u32`:`funding_feerate_perkw`] + * [`u32`:`locktime`] + * [`point`:`funding_pubkey`] + * [`splice_init_tlvs`:`tlvs`] + +1. `tlv_stream`: `splice_init_tlvs` +2. types: + 1. type: 2 (`require_confirmed_inputs`) + +`funding_contribution_satoshis` is the amount the sender is adding to their +channel balance (splice-in) or removing from their channel balance (splice-out). + +#### Requirements + +The sending node: + - MUST NOT send `splice_init` if the channel is not quiescent. + - MUST NOT send `splice_init` if it is not the quiescence initiator. + - MUST NOT send `splice_init` before sending and receiving `channel_ready`. + - MUST NOT send `splice_init` while another splice is being negotiated. + - MUST NOT send `splice_init` if another splice has been negotiated but + `splice_locked` has not been sent and received. + - MUST NOT send `splice_init` if it has previously sent `shutdown`. + - MUST set `funding_feerate_perkw` to the feerate for the splice transaction. + - If it is splicing funds out of the channel: + - MUST set `funding_contribution_satoshis` to a negative value matching + the amount that will be subtracted from its current channel balance. + - If it is splicing funds into the channel: + - MUST set `funding_contribution_satoshis` to a positive value matching + the amount that will be added to its current channel balance. + - If it requires the receiving node to only use confirmed inputs: + - MUST set `require_confirmed_inputs`. + - SHOULD use a different `funding_pubkey` than the one used for the + previous funding transaction. + +The receiving node: + - If the channel is not quiescent: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If the sending node is not the quiescence initiator: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If another splice is already being negotiated: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If another splice has been negotiated but isn't locked yet: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If it has received `shutdown`: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If the `funding_feerate_perkw` is unacceptable: + - MUST respond with `tx_abort`. + - If `funding_contribution_satoshis` is negative and its absolute value is + greater than the sending node's current channel balance: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If it accepts the splice attempt: + - MUST respond with `splice_ack`. + - Otherwise (it rejects the splice): + - MUST respond with `tx_abort`. + +### The `splice_ack` Message + +1. type: 81 (`splice_ack`) +2. data: + * [`channel_id`:`channel_id`] + * [`s64`:`funding_contribution_satoshis`] + * [`point`:`funding_pubkey`] + * [`splice_ack_tlvs`:`tlvs`] + +1. `tlv_stream`: `splice_ack_tlvs` +2. types: + 1. type: 2 (`require_confirmed_inputs`) + +#### Requirements + +The sending node: + - SHOULD use a different `funding_pubkey` than the one used for the + previous funding transaction. + - MAY set `funding_contribution_satoshis` to `0` if they don't want + to contribute to the splice. + - If it requires the receiving node to only use confirmed inputs: + - MUST set `require_confirmed_inputs`. + +The receiving node: + - If it has sent `splice_init`: + - If `funding_contribution_satoshis` is negative and its absolute value is + greater than the sending node's current channel balance: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If it accepts the splice attempt: + - MUST start an `interactive-tx` session to create the splice transaction. + - Otherwise: + - MUST reject the splice attempt by sending `tx_abort`. + - Otherwise (it has not sent `splice_init`): + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + +### Splice Transaction Construction + +The splice transaction is created using the +[Interactive Transaction Construction](#interactive-transaction-construction) +protocol, with the following additional requirements. + +#### The `tx_add_input` Message + +##### Requirements + +The sending node: + - If it is the splice initiator: + - MUST add the current channel input to the splice transaction by + sending `tx_add_input` with `shared_input_txid` containing the + `txid` of the previous funding transaction. + - MUST NOT include `prevtx` for that shared input. + - MUST set `prevtx_vout` to the previous funding output index. + - If the receiver set `require_confirmed_inputs` in `splice_init`, + `splice_ack`, `tx_init_rbf` or `tx_ack_rbf`: + - MUST NOT send a `tx_add_input` that contains an unconfirmed input. + +The receiving node: + - If `shared_input_txid` is set: + - If it doesn't match the `txid` of the previous funding transaction: + - MUST fail the negotiation by sending `tx_abort`. + - If `prevtx_vout` doesn't match the previous funding output index: + - MUST fail the negotiation by sending `tx_abort`. + +##### Rationale + +The splice transaction must spend the current channel funding output. The +splice initiator is responsible for adding that input to the transaction, +and pay the fees for its weight. It would be wasteful to transmit the +previous funding transaction in the `prevtx` field, and wouldn't even +be possible for funding transactions that exceed 65kB, so we only transmit +its `txid` using the `shared_input_txid` field. + +#### The `tx_add_output` Message + +##### Requirements + +The sending node: + - If it is the splice initiator: + - MUST send at least one `tx_add_output`, which contains the new + channel's funding output based on the `funding_pubkey`s from + `splice_init` and `splice_ack`. + - MUST set the amount of that `tx_add_output` to the previous + channel capacity with the `funding_contribution_satoshis`s from + `splice_init` and `splice_ack` applied. + +##### Rationale + +The splice initiator is responsible for adding the new channel funding +output to the transaction and paying the fees for its weight. + +#### The `tx_complete` Message + +##### Requirements + +The sending node: + - When splicing a taproot channel: + - MUST include a random musig2 nonce that will be used for their + partial signature of the previous channel output in `funding_nonce`. + - MUST include `commit_nonces`, which contains their verification + nonce for the current and next commitment transactions spending + the interactive transaction being constructed. + +The receiving node: + - MUST compute the channel balance for each side by adding their respective + `funding_contribution_satoshis` to their previous channel balance. + - MUST fail the negotiation by sending `tx_abort` if: + - There is not exactly one input spending the current funding transaction. + - There is not exactly one channel funding output using the funding public + keys and funding contributions from `splice_init` and `splice_ack`. + - The transaction is splicing a taproot channel and `commit_nonces` or + `funding_nonce` is missing. + - This is an RBF attempt and the transaction's total fees is less than + the last successfully negotiated splice transaction's fees. + - Either side has added an output other than the channel funding output + and the balance for that side is less than the channel reserve that + matches the new channel capacity. + +##### Rationale + +If a side does not meet the reserve requirements, that's OK: but if they take +funds out of the channel, they must ensure that they do meet them. If your peer +adds a massive amount to the channel, then you only have to add more reserve if +you want to contribute to the splice (and you can use `tx_remove_output` and/or +`tx_remove_input` part-way through if this happens). + +#### The `commitment_signed` Message + +After exchanging `tx_complete`, both peers send `commitment_signed` to commit +to the splice transaction by creating a commitment transaction spending the +new channel funding output. + +The usual [`commitment_signed`](#committing-updates-so-far-commitment_signed) +requirements apply with the following additions. + +##### Requirements + +The sending node: + - MUST create a commitment transaction that spends the splice funding output and: + - Adds `funding_contribution_satoshis` from `splice_init` and `splice_ack` + to the main balance of their respective sender. + - Uses the same feerate as the existing commitment transaction. + - Uses the same `commitment_number` as the existing commitment transaction. + - MUST send signatures for pending HTLCs. + - MUST remember the details of this splice transaction. + +The receiving node: + - MUST NOT respond with `revoke_and_ack`. + - If it has not already transmitted its `commitment_signed`: + - MUST send `commitment_signed`. + - If it should sign first, as specified in the [`tx_signatures` requirements](#the-tx_signatures-message): + - MUST send `tx_signatures`. + - Note that since the initiator sends `tx_add_input` for the shared input + (corresponding to the previous channel output), 100% of the previous channel + capacity is attributed to the initiator when computing who must send + `tx_signatures` first (instead of using each node's previous balance). + +On reconnection: + - If `next_funding` matches the splice transaction: + - MUST retransmit `commitment_signed`. + +##### Rationale + +Once peers are ready to exchange commitment signatures, they must remember +the details of the splice transaction to allow resuming the signatures +exchange if a disconnection happens. + +#### The `tx_signatures` Message + +##### Requirements + +The sending node: + - When splicing a taproot channel: + - MUST set `shared_input_partial_signature` to a valid musig2 partial + signature for the `tx_add_input` spending the previous channel funding + output using the `funding_nonce`s exchanged in `tx_complete`. + - Otherwise: + - MUST set `shared_input_signature` to a valid ECDSA signature for the + `tx_add_input` spending the previous channel funding output using the + `funding_pubkey` that matches this input. + +The receiving node: + - When splicing a taproot channel: + - If `shared_input_partial_signature` is not set: + - MUST send an `error` and fail the channel. + - If `shared_input_partial_signature` is not a valid partial signature + using the `funding_nonce`s exchanged in `tx_complete`: + - MUST send an `error` and fail the channel. + - Otherwise: + - If `shared_input_signature` is not set: + - MUST send an `error` and fail the channel. + - If `shared_input_signature` is not valid or non-compliant with the + LOW-S-standard rule[LOWS](https://github.com/bitcoin/bitcoin/pull/6769): + - MUST send an `error` and fail the channel. + - MUST consider the channel no longer quiescent. + +On reconnection: + - If `next_funding` matches the splice transaction: + - MUST retransmit `tx_signatures`. + +##### Rationale + +Spending the channel funding output requires a signature from both peers. +Each peer transmits its own signature, which allows creating a valid +witness for the shared input without adding an additional message. + +Once `tx_signatures` have been exchanged, the splice transaction can be +broadcast. The channel is no longer quiescent: normal operation can resume +while waiting for the transaction to confirm and `splice_locked` messages +to be exchanged. + +#### The `tx_init_rbf` Message + +##### Requirements + +The sending node: + - MUST NOT send `tx_init_rbf` if the channel is not quiescent. + - MUST NOT send `tx_init_rbf` if it is not the quiescence initiator. + - MAY send `tx_init_rbf` even if it is not the splice initiator. + - If there are more than 10 pending RBF attempts: + - MUST set a high enough `feerate` to ensure quick confirmation. + - MUST NOT send `tx_init_rbf` if it has previously sent `splice_locked`. + - MUST NOT send `tx_init_rbf` if `option_zeroconf` has been negotiated. + - MAY set `funding_output_contribution` to a different value than the + `funding_contribution_satoshis` used in `splice_init` or `splice_ack`, + or in previous RBF attempts. + +The receiving node: + - If the channel is not quiescent: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If the sending node is not the quiescence initiator: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If another RBF attempt has been created recently: + - SHOULD send `tx_abort` to reject this RBF attempt and wait for the + previous RBF attempt to confirm. + - If there are more than 10 pending RBF attempts and the `feerate` is not + high enough to ensure quick confirmation: + - SHOULD send `tx_abort` to reject the RBF attempt. + - If the sender previously sent `splice_locked`: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If `option_zeroconf` has been negotiated: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + - If `funding_output_contribution` is negative and its absolute value is + greater than the sending node's current channel balance: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + +##### Rationale + +Splice transactions can be RBF-ed to react to changes in the mempool feerate. +We allow both nodes to initiate RBF, because any one of them may want to take +this opportunity to splice additional funds into or out of the channel without +waiting for the initial splice transaction to confirm. + +We limit the number of pending RBF attempts, otherwise we may reach the +`batch_size` limit defined in [`start_batch`](#batching-channel-messages). +We require using a large enough feerate when we've already created many RBF +attempts, and we wait between RBF attempts to allow a previous attempt to +confirm. + +Since splice transactions always spend the current channel funding output, the +RBF attempts automatically double-spend each other. We thus disallow RBF when +`option_zeroconf` has been negotiated, because that creates a risk of losing +funds. + +#### The `tx_ack_rbf` Message + +##### Requirements + +The sending node: + - MAY set `funding_output_contribution` to a different value than the + `funding_contribution_satoshis` used in `splice_init` or `splice_ack`, + or in previous RBF attempts. + +The receiving node: + - If `funding_output_contribution` is negative and its absolute value is + greater than the sending node's current channel balance: + - MUST send a `warning` and close the connection or send an `error` + and fail the channel. + +### Splice Completion + +Once splice transactions have been signed but haven't reached acceptable +depth, channel operations go back to normal and HTLCs can be exchanged, +with the caveat that payments must be valid for all splice transactions. + +Nodes keep track of multiple commitment transactions (one for the current +funding transaction and one for each splice transaction) and exchange +signatures for each of these commitment transactions. + +``` ++------------+ +-----------+ +| Funding Tx |---+--->| Commit Tx | ++------------+ | +-----------+ + | +-----------+ +-----------+ + +--->| Splice Tx |----------->| Commit Tx | + | +-----------+ +-----------+ + | +---------------+ +-----------+ + +--->| Splice RBF #1 |------->| Commit Tx | + | +---------------+ +-----------+ + | +---------------+ +-----------+ + +--->| Splice RBF #2 |------->| Commit Tx | + +---------------+ +-----------+ +``` + +The splice completes by exchanging `splice_locked` messages, at which point +the locked transaction replaces the previous funding transaction. + +#### The `splice_locked` Message + +1. type: 77 (`splice_locked`) +2. data: + * [`channel_id`:`channel_id`] + * [`sha256`:`splice_txid`] + +##### Requirements + +Each node: + - If any splice transaction reaches acceptable depth: + - MUST send `splice_locked` with the `txid` of that transaction. + - If `option_zeroconf` has been negotiated: + - SHOULD send `splice_locked` immediately after exchanging `tx_signatures`. + +The receiving node: + - If `splice_txid` doesn't match any of its pending splice transactions: + - MUST send a `warning` and close the connection, or send an `error` and + fail the channel. + +Once a node has sent and received `splice_locked`: + - If the `splice_txid`s match: + - MUST stop sending `commitment_signed` for RBF attempts and ancestors + of this splice transaction. + - MAY discard RBF attempts and ancestor transactions. + - If `announce_channel` is set for this channel: + - MUST send `announcement_signatures` with `short_channel_id` matching + this splice transaction. + - If the `splice_txid`s are for different RBF candidates: + - SHOULD ignore the message. + - MAY send an `error` and fail the channel. + +##### Rationale + +If nodes are on a different fork of the blockchain, they may disagree on which +RBF attempt has been confirmed: in that case nodes can either close the channel +or simply ignore `splice_locked` and wait for one of the forks to eventually +replace the other, at which point both nodes should agree on which RBF attempt +confirmed and exchange `splice_locked` for the same `splice_txid` to complete +the splice. + ## Channel Close Nodes can negotiate a mutual close of the connection, which unlike a @@ -1612,6 +2182,7 @@ A sending node: - if there are updates pending on the receiving node's commitment transaction: - MUST NOT send a `shutdown`. - MUST NOT send multiple `shutdown` messages. + - MUST NOT send `shutdown` if there is a splice transaction that isn't locked yet. - MUST NOT send an `update_add_htlc` after a `shutdown`. - if no HTLCs remain in either commitment transaction (including dust HTLCs) and neither side has a pending `revoke_and_ack` to send: @@ -2290,6 +2861,8 @@ A sending node: - MUST increase the value of `id` by 1 for each successive offer. - if it is relaying a payment inside a blinded route: - MUST set `path_key` (see [Route Blinding](04-onion-routing.md#route-blinding)) + - if a splice is pending: + - MUST ensure that requirements are met for all commmitment transactions. `id` MUST NOT be reset to 0 after the update is complete (i.e. after `revoke_and_ack` has been received). It MUST continue incrementing instead. @@ -2321,6 +2894,8 @@ A receiving node: - MUST respond with an error as detailed in [Failure Messages](04-onion-routing.md#failure-messages) - Otherwise: - MUST follow the requirements for the reader of `payload` in [Payload Format](04-onion-routing.md#payload-format) + - If a splice is pending: + - MUST ensure that requirements are met for all commmitment transactions. The `onion_routing_packet` contains an obfuscated list of hops and instructions for each hop along the path. It commits to the HTLC by setting the `payment_hash` as associated data, i.e. includes the `payment_hash` in the computation of HMACs. @@ -2356,6 +2931,9 @@ maintaining its channel reserve (because of the increased weight of the commitment transaction), resulting in a degraded channel. See [#728](https://github.com/lightningnetwork/lightning-rfc/issues/728) for more details. +If splicing is supported, there can be more than one commitment transaction +at a time: proposed changes must be valid for all of them. + ### Removing an HTLC: `update_fulfill_htlc`, `update_fail_htlc`, and `update_fail_malformed_htlc` For simplicity, a node can only remove HTLCs added by the other node. @@ -2473,6 +3051,60 @@ such detection is left as an option. Nodes inside a blinded route must use `invalid_onion_blinding` to avoid leaking information to senders trying to probe the blinded route. +### Batching channel messages + +Multiple channel messages can be grouped together as a single logical message +using the `start_batch` message. + +1. type: 127 (`start_batch`) +2. data: + * [`channel_id`:`channel_id`] + * [`u16`:`batch_size`] + +1. `tlv_stream`: `start_batch_tlvs` +2. types: + 1. type: 1 (`message_type`) + 2. data: + * [`u16`:`message_type`] + +#### Requirements + +A sending node: + - MUST set `batch_size` to a value strictly greater than 1. + - MUST set `batch_size` to a value lower than or equal to 20. + - MUST set `message_type` to `132` (`commitment_signed` message type as + defined in [Bolt 1](./01-messaging.md#lightning-message-format)). + - After sending `start_batch`: + - MUST send `batch_size` `commitment_signed` messages for the same + `channel_id`, without any other unrelated messages in-between. + +A receiving node: + - If `batch_size` is not strictly greater than 1: + - MUST ignore the `start_batch` message. + - SHOULD send a `warning`. + - If `batch_size` is strictly greater than 20: + - MUST send a `warning` and close the connection, or send an `error` and + fail the channel. + - MUST group the next `batch_size` messages and process them together. + - If one of those messages is not for the specified `channel_id`: + - MUST send a `warning` and close the connection, or send an `error` and + fail the channel. + - If `message_type` is missing or not set to the type for `commitment_signed`: + - MUST ignore the `start_batch` message and process the following messages + sequentially. + +#### Rationale + +The `start_batch` message is only used when sending multiple `commitment_signed` +messages during splicing. We thus narrow the requirements for this specific +scenario: they can be relaxed in the future if we use `start_batch` for other +features. + +We limit the `batch_size` to 20 elements to protect against excessive queuing +that could be abused to DoS receiving nodes. The `start_batch` message is only +used for splice RBF attempts so far, which shouldn't need that many attempts +to get transactions confirmed. + ### Committing Updates So Far: `commitment_signed` When a node has changes for the remote commitment, it can apply them, @@ -2485,6 +3117,13 @@ sign the resulting transaction (as defined in [BOLT #3](03-transactions.md)), an * [`signature`:`signature`] * [`u16`:`num_htlcs`] * [`num_htlcs*signature`:`htlc_signature`] + * [`commitment_signed_tlvs`:`tlvs`] + +1. `tlv_stream`: `commitment_signed_tlvs` +2. types: + 1. type: 1 (`funding_txid`) + 2. data: + * [`sha256`:`funding_txid`] #### Requirements @@ -2499,8 +3138,19 @@ change the commitment transaction aside from the new revocation number fee changes). - MUST include one `htlc_signature` for every HTLC transaction corresponding to the ordering of the commitment transaction (see [BOLT #3](03-transactions.md#transaction-input-and-output-ordering)). + - MUST set `funding_txid` to the funding transaction spent by this commitment + transaction. - if it has not recently received a message from the remote node: - SHOULD use `ping` and await the reply `pong` before sending `commitment_signed`. + - If there are `N` pending splice transactions (with `N` greater than `0`): + - MUST send `start_batch` first with `batch_size` set to `N + 1` and + `message_type` set to `132` (`commitment_signed`). + - MUST send `commitment_signed` for the current channel funding output. + - MUST send `commitment_signed` for each of the splice transactions. + - MUST set `funding_txid` in each `commitment_signed` message to match the + funding transaction spent by that commitment transaction. + - MUST NOT send any other message before it has sent the batch of + `commitment_signed` messages. A receiving node: - once all pending updates are applied: @@ -2514,7 +3164,28 @@ A receiving node: - if any `htlc_signature` is not valid for the corresponding HTLC transaction OR non-compliant with LOW-S-standard rule [LOWS](https://github.com/bitcoin/bitcoin/pull/6769): - MUST send a `warning` and close the connection, or send an `error` and fail the channel. - - MUST respond with a `revoke_and_ack` message. + - If there are pending splice transactions and the sending node did not + send `start_batch` followed by a batch of `commitment_signed` messages: + - MUST send an `error` and fail the channel. + - If the sending node sent `start_batch` and we are processing a batch of + `commitment_signed` messages: + - If `funding_txid` is missing in one of the `commitment_signed` messages: + - MUST send an `error` and fail the channel. + - If there are pending splice transactions: + - MUST validate each `commitment_signed` based on `funding_txid`. + - If `commitment_signed` is missing for a funding transaction: + - MUST send an `error` and fail the channel. + - Otherwise: + - MUST respond with a `revoke_and_ack` message. + - Otherwise (no pending splice transactions): + - MUST ignore `commitment_signed` where `funding_txid` does not match + the current funding transaction. + - If `commitment_signed` is missing for the current funding transaction: + - MUST send an `error` and fail the channel. + - Otherwise: + - MUST respond with a `revoke_and_ack` message. + - Otherwise: + - MUST respond with a `revoke_and_ack` message. #### Rationale @@ -2537,6 +3208,15 @@ stating time-locks on HTLC outputs. The `option_anchors` allows HTLC transactions to "bring their own fees" by attaching other inputs and outputs, hence the modified signature flags. +Splicing requires us to send and receive additional signatures, as we don't +know which (if any) of the splice transactions will end up being the new +channel funding transaction. We use `start_batch` to send a batch of +`commitment_signed` messages containing signatures for each of the pending +splice transactions and for the current funding transaction. After sending +`splice_locked`, we may receive a batch containing obsolete `commitment_signed` +messages from our peer that they started sending before receiving our +`splice_locked`: we can safely ignore them by filtering on `funding_txid`. + ### Completing the Transition to the Updated State: `revoke_and_ack` Once the recipient of `commitment_signed` checks the signature and knows @@ -2564,6 +3244,8 @@ A sending node: the previous commitment transaction. - MUST set `next_per_commitment_point` to the values for its next commitment transaction. + - MUST send a single `revoke_and_ack` message, even if it is responding to + a batch of `commitment_signed` messages. A receiving node: - if `per_commitment_secret` is not a valid secret key or does not generate the previous `per_commitment_point`: @@ -2619,6 +3301,8 @@ A sending node: - if the dust balance of the local transaction at the updated `feerate_per_kw` is greater than `max_dust_htlc_exposure_msat`: - MAY NOT send `update_fee` - MAY fail the channel + - if a splice is pending: + - MUST ensure that requirements are met for all commmitment transactions. A receiving node: - if the `update_fee` is too low for timely processing, OR is unreasonably large: @@ -2638,6 +3322,8 @@ A receiving node: - MAY fail the channel - if the dust balance of the local transaction at the updated `feerate_per_kw` is greater than `max_dust_htlc_exposure_msat`: - MAY fail the channel + - if a splice is pending: + - MUST ensure that requirements are met for all commmitment transactions. #### Rationale @@ -2666,6 +3352,9 @@ be trimmed at the updated feerate, this could overflow the configured `max_dust_htlc_exposure_msat`. Whether to close the channel preemptively or not is left as a matter of node policy. +If splicing is supported, there can be more than one commitment transaction +at a time: proposed changes must be valid for all of them. + ## Message Retransmission Because communication transports are unreliable, and may need to be @@ -2702,6 +3391,13 @@ messages are), they are independent of requirements here. 2. data: * [`sha256`:`next_funding_txid`] * [`byte`:`retransmit_flags`] + 1. type: 5 (`my_current_funding_locked`) + 2. data: + * [`sha256`:`my_current_funding_locked_txid`] + * [`byte`:`retransmit_flags`] + 1. type: 24 (`current_commit_nonce`) + 2. data: + * [`66*byte`: `current_local_commit_nonce`] `next_commitment_number`: A commitment number is a 48-bit incrementing counter for each commitment transaction; counters @@ -2717,6 +3413,14 @@ corresponding `next_funding_txid` after the reconnection: | ------------- | --------------------| | 0 | `commitment_signed` | +The `my_current_funding_locked.retransmit_flags` bitfield is used +to let our peer know which messages we expect them to retransmit +after the reconnection: + +| Bit Position | Name | +| ------------- | --------------------------| +| 0 | `announcement_signatures` | + ### Requirements A funding node: @@ -2767,15 +3471,33 @@ The sending node: - MUST set `next_funding_txid` to the txid of that interactive transaction. - if it has not received `commitment_signed` for this `next_funding_txid`: - MUST set the `commitment_signed` bit in `retransmit_flags`. + - If this is a taproot channel: + - MUST include its verification nonce in `current_commit_nonce`. - otherwise: - MUST NOT include the `next_funding` TLV. + - if `option_splice` was negotiated: + - if a splice transaction reached acceptable depth while disconnected: + - MUST include `my_current_funding_locked` with the txid of the latest such transaction. + - otherwise, if it has already sent `splice_locked` for any transaction: + - MUST include `my_current_funding_locked` with the txid of the last `splice_locked` it sent. + - otherwise, if it has already sent `channel_ready`: + - MUST include `my_current_funding_locked` with the txid of the channel funding transaction. + - otherwise (it has never sent `channel_ready` or `splice_locked`): + - MUST NOT include `my_current_funding_locked`. + - if `my_current_funding_locked` is included: + - if `announce_channel` is set for this channel: + - if it has not received `announcement_signatures` for that transaction: + - MUST set the `announcement_signatures` bit to `1` in `retransmit_flags`. + - otherwise: + - MUST set the `announcement_signatures` bit to `0` in `retransmit_flags`. A node: - if `next_commitment_number` is zero: - MUST immediately fail the channel and broadcast any relevant latest commitment transaction. - if `next_commitment_number` is 1 in both the `channel_reestablish` it - sent and received: + sent and received, and none of those `channel_reestablish` messages + contain `my_current_funding_locked` or `next_funding` for a splice transaction: - MUST retransmit `channel_ready`. - otherwise: - MUST NOT retransmit `channel_ready`, but MAY send `channel_ready` with @@ -2822,15 +3544,31 @@ A receiving node: - if it has not received `tx_signatures` for that funding transaction: - if the `commitment_signed` bit is set in `retransmit_flags`: - MUST retransmit its `commitment_signed` for that funding transaction. + - If this is a taproot channel: + - MUST use the `current_commit_nonce` provided. - if it has already received `commitment_signed` and it should sign first, as specified in the [`tx_signatures` requirements](#the-tx_signatures-message): - MUST send its `tx_signatures` for that funding transaction. - if it has already received `tx_signatures` for that funding transaction: - MUST send its `tx_signatures` for that funding transaction. + - if it also sets `next_funding` in its own `channel_reestablish`, but the + values don't match: + - MUST send an `error` and fail the channel. - otherwise: - MUST send `tx_abort` to let the sending node know that they can forget this funding transaction. +A receiving node: + - if splice transactions are pending and `my_current_funding_locked` matches one of + those splice transactions, for which it hasn't received `splice_locked` yet: + - MUST process `my_current_funding_locked` as if it was receiving `splice_locked` + for this `txid`. + - if `my_current_funding_locked` is included with the `announcement_signatures` bit + set in the `retransmit_flags`: + - if `announce_channel` is set for this channel and the receiving node is ready + to send `announcement_signatures` for the corresponding splice transaction: + - MUST retransmit `announcement_signatures`. + A node: - MUST NOT assume that previously-transmitted messages were lost, - if it has sent a previous `commitment_signed` message: @@ -2885,20 +3623,10 @@ commitment number 0 is created during opening. `commitment_signed` for commitment number 1 is send and then the revocation for commitment number 0 is received. -`channel_ready` is implicitly acknowledged by the start of normal -operation, which is known to have begun after a `commitment_signed` has been -received — hence, the test for a `next_commitment_number` greater -than 1. - -A previous draft insisted that the funder "MUST remember ...if it has -broadcast the funding transaction, otherwise it MUST NOT": this was in -fact an impossible requirement. A node must either firstly commit to -disk and secondly broadcast the transaction or vice versa. The new -language reflects this reality: it's surely better to remember a -channel which hasn't been broadcast than to forget one which has! -Similarly, for the fundee's `funding_signed` message: it's better to -remember a channel that never opens (and times out) than to let the -funder open it while the fundee has forgotten it. +`channel_ready` is implicitly acknowledged by the start of normal operation, +which is known to have begun after a `commitment_signed` has been received +(hence, the test for a `next_commitment_number` greater than 1) or after a +splice transaction has been created. A node, which has somehow fallen behind (e.g. has been restored from old backup), can detect that it has fallen @@ -2915,6 +3643,17 @@ interactive transaction construction, or safely abort that transaction if it was not signed by one of the peers, who has thus already removed it from its state. +`my_current_funding_locked` is equivalent to sending `splice_locked`, but is +handled atomically during `channel_reestablish` (instead of requiring a +retransmission of the `splice_locked` message). This is useful to avoid race +conditions with channel updates (for more details about this race condition, +see [this example](./bolt02/splicing-test.md#disconnection-with-concurrent-splice_locked)). +It handles the case where the `splice_locked` message was lost during the +disconnection, or when a splice transaction reaches acceptable depth while +peers are disconnected. It also allows requesting a retransmission of the +`announcement_signatures` message for the latest splice transaction, in case +it wasn't received before disconnecting. + # Authors [ FIXME: Insert Author List ] diff --git a/07-routing-gossip.md b/07-routing-gossip.md index a8d936749..d7193b919 100644 --- a/07-routing-gossip.md +++ b/07-routing-gossip.md @@ -86,6 +86,8 @@ A node: - If the `open_channel` message has the `announce_channel` bit set AND a `shutdown` message has not been sent: - After `channel_ready` has been sent and received AND the funding transaction has enough confirmations to ensure that it won't be reorganized: - MUST send `announcement_signatures` for the funding transaction. + - After `splice_locked` has been sent and received AND the splice transaction has enough confirmations to ensure that it won't be reorganized: + - MUST send `announcement_signatures` for the matching splice transaction. - Otherwise: - MUST NOT send the `announcement_signatures` message. - Upon reconnection (once the above timing requirements have been met): @@ -93,19 +95,23 @@ A node: - MUST send its own `announcement_signatures` message. - If it receives `announcement_signatures` for the funding transaction: - MUST respond with its own `announcement_signatures` message. + - If it has NOT previously received `announcement_signatures` for a splice transaction: + - MUST SET the `announcement_signatures` bit in the `retransmit_flags` of `my_current_funding_locked`. + - If the `announcement_signatures` bit is set in the *remote* `retransmit_flags`: + - MUST retransmit its `announcement_signatures` message. A recipient node: - - If the `short_channel_id` is NOT correct: - - SHOULD send a `warning` and close the connection, or send an - `error` and fail the channel. + - If the `short_channel_id` doesn't match one of its funding transactions: + - SHOULD send a `warning`. - If the `node_signature` OR the `bitcoin_signature` is NOT correct: - - MAY send a `warning` and close the connection, or send an - `error` and fail the channel. + - MAY send a `warning` and close the connection, or send an `error` and fail the channel. - If it has sent AND received a valid `announcement_signatures` message: - If the funding transaction has at least 6 confirmations: - SHOULD queue the `channel_announcement` message for its peers. - If it has not sent `channel_ready`: - SHOULD defer handling the `announcement_signatures` until after it has sent `channel_ready`. + - If it has not sent `splice_locked` for the transaction matching this `short_channel_id`: + - SHOULD defer handling the `announcement_signatures` until after it has sent `splice_locked`. ### Rationale @@ -113,6 +119,12 @@ Channels must not be announced before the funding transaction has enough confirmations, because a blockchain reorganization would otherwise invalidate the `short_channel_id`. +When splicing is used, a `channel_announcement` is generated for every splice +transaction once both sides have sent `splice_locked`. This lets the network +know that the transaction spending a currently active channel is a splice and +not a closing transaction, and this channel can still be used with its updated +`short_channel_id`. + ## The `channel_announcement` Message This gossip message contains ownership information regarding a channel. It ties @@ -162,9 +174,18 @@ The origin node: that the channel was opened within: - for the _Bitcoin blockchain_: - MUST set `chain_hash` value (encoded in hex) equal to `6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000`. - - MUST set `short_channel_id` to refer to the confirmed funding transaction, - as specified in [BOLT #2](02-peer-protocol.md#the-channel_ready-message). - - Note: the corresponding output MUST be a P2WSH, as described in [BOLT #3](03-transactions.md#funding-transaction-output). + - When announcing a channel creation: + - MUST set `short_channel_id` to refer to the confirmed funding transaction, + as specified in [BOLT #2](02-peer-protocol.md#the-channel_ready-message). + - When announcing a splice transaction: + - MUST set `short_channel_id` to refer to the confirmed splice transaction + for which `splice_locked` has been sent and received, as specified in + [BOLT #2](02-peer-protocol.md#the-splice_locked-message). + - SHOULD keep relaying payments that use the `short_channel_id`s of its + previous `channel_announcement`s. + - SHOULD send a new `channel_update` using the `short_channel_id` that + matches the latest `channel_announcement`. + - Note: the corresponding output MUST be a P2WSH, as described in [BOLT #3](03-transactions.md#funding-transaction-output). - MUST set `node_id_1` and `node_id_2` to the public keys of the two nodes operating the channel, such that `node_id_1` is the lexicographically-lesser of the two compressed keys sorted in ascending lexicographic order. @@ -253,9 +274,11 @@ optional) features will have _odd_ feature bits, while incompatible features will have _even_ feature bits (["It's OK to be odd!"](00-introduction.md#glossary-and-terminology-guide)). -A delay of 72 blocks is used when forgetting a channel on funding output spend -as to permit a new `channel_announcement` to propagate which indicates this -channel was spliced. +A delay of 72 blocks is used when forgetting a channel after detecting that it +has been spent: this can allow a new `channel_announcement` to propagate to +indicate that this channel was spliced and not closed. Thanks to this delay, +payments can still be relayed on the channel while the splice transaction is +waiting for enough confirmations. ## The `node_announcement` Message diff --git a/09-features.md b/09-features.md index e8726e09c..b0b9cb642 100644 --- a/09-features.md +++ b/09-features.md @@ -55,6 +55,7 @@ The Context column decodes as follows: | 48/49 | `option_payment_metadata` | Payment metadata in tlv record | 9 | | [BOLT #11](11-payment-encoding.md#tagged-fields) | | 50/51 | `option_zeroconf` | Understands zeroconf channel types | INT | `option_scid_alias` | [BOLT #2][bolt02-channel-ready] | | 60/61 | `option_simple_close` | Simplified closing negotiation | IN | `option_shutdown_anysegwit` | [BOLT #2][bolt02-simple-close] | +| 62/63 | `option_splice` | Allows replacing the funding transaction with a new one | IN | | [BOLT #2](02-peer-protocol.md#channel-splicing) | ## Requirements diff --git a/bolt02/splicing-test.md b/bolt02/splicing-test.md new file mode 100644 index 000000000..563a86e07 --- /dev/null +++ b/bolt02/splicing-test.md @@ -0,0 +1,987 @@ +# Splicing Tests + +This file details various [splicing](../02-peer-protocol.md#channel-splicing) protocol flows. +We detail the exact flow of messages for each scenario, and highlight several edge cases that must be correctly handled by implementations. + +## Table of Contents + +* [Terminology](#terminology) +* [Test Vectors](#test-vectors) + * [Successful single splice](#successful-single-splice) + * [Multiple splices with concurrent `splice_locked`](#multiple-splices-with-concurrent-splice_locked) + * [Disconnection with one side sending `commit_sig`](#disconnection-with-one-side-sending-commit_sig) + * [Disconnection with both sides sending `commit_sig`](#disconnection-with-both-sides-sending-commit_sig) + * [Disconnection with one side sending `tx_signatures`](#disconnection-with-one-side-sending-tx_signatures) + * [Disconnection with both sides sending `tx_signatures`](#disconnection-with-both-sides-sending-tx_signatures) + * [Disconnection with both sides sending `tx_signatures` and channel updates](#disconnection-with-both-sides-sending-tx_signatures-and-channel-updates) + * [Disconnection with concurrent `splice_locked`](#disconnection-with-concurrent-splice_locked) + * [Disconnection after exchanging `tx_signatures` and one side sends `commit_sig` for channel update](#disconnection-after-exchanging-tx_signatures-and-one-side-sends-commit_sig-for-channel-update) + * [Disconnection after exchanging `tx_signatures` and both sides send `commit_sig` for channel update; `revoke_and_ack` not received](#disconnection-after-exchanging-tx_signatures-and-both-sides-send-commit_sig-for-channel-update-revoke_and_ack-not-received) + * [Disconnection after exchanging `tx_signatures` and both sides send `commit_sig` for channel update](#disconnection-after-exchanging-tx_signatures-and-both-sides-send-commit_sig-for-channel-update) +## Terminology + +We call "active commitments" the set of valid commitment transactions to which updates (`update_add_htlc`, `update_fulfill_htlc`, `update_fail_htlc`, `update_fail_malformed_htlc`, `update_fee`) must be applied. +While a funding transaction is not locked (ie `splice_locked` hasn't been exchanged), updates must be valid for all active commitments. + +When representing active commitments, we will only draw the corresponding funding transactions for simplicity. +The related commitment transaction simply spends that funding transaction. + +For example, the following diagram displays the active commitments when we have an unconfirmed splice (`FundingTx2a`) and 2 RBF attempts for that splice (`FundingTx2b` and `FundingTx2c`). +We thus have 4 active commitments: + +* the commitment spending `FundingTx1` +* the commitments spending each splice transaction (`FundingTx2a`, `FundingTx2b` and `FundingTx2c`) + +```text ++------------+ +-------------+ +| FundingTx1 |--------+------>| FundingTx2a | ++------------+ | +-------------+ + | + | +-------------+ + +------>| FundingTx2b | + | +-------------+ + | + | +-------------+ + +------>| FundingTx2c | + +-------------+ +``` + +**Peers must always agree on the set of active commitments**, otherwise one side will expect signatures that the other side will not send, which will lead to force-closing the channel. + +## Test Vectors + +In the protocol flows below, we omit the `interactive-tx` messages that build the transaction. +The only `interactive-tx` messages we explicitly list are the consecutive `tx_complete` that mark the end of the `interactive-tx` construction. + +We also assume that both peers use the same `commitment_number` for simplicity. + +Comprehensive testing should include: +* Splice-in, Splice-out and their sequential combinations. +* Peers that have sufficient balance after the initial funding transaction. +* Peers that have sufficient balance via htlcs. +* Peers with different `commitment_number`. + +### Successful single splice + +Let's warm up with the simplest possible flow: a splice transaction that confirms without any disconnection. This flow should be tested for different scenarios, such as: + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | tx_signatures | + |<-----------------------------| + | | The channel is no longer quiescent at that point. + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | update_add_htlc | Alice and Bob use the channel while the splice transaction is unconfirmed. + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 2 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | start_batch | batch_size = 2 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | splice_locked | The splice transaction confirms. + |----------------------------->| + | splice_locked | + |<-----------------------------| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ + | | | FundingTx2 | + | | +------------+ + | | + | update_add_htlc | Alice and Bob can use the channel and forget the previous FundingTx1. + |----------------------------->| + | commit_sig | + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | commit_sig | + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 12 + | | +------------+ + | | | FundingTx2 | + | | +------------+ +``` + +### Multiple splices with concurrent `splice_locked` + +Since nodes have different views of the blockchain, they may send `splice_locked` at slightly different times. +Moreover, nodes may send `splice_locked` concurrently with other channel updates, in which case they will receive some `commit_sig` messages for obsolete commitments. +This is fine: nodes know how many `commit_sig` messages to expect thanks to the `batch_size` field, and they can simply ignore `commit_sig` messages for which the `funding_txid` cannot be found in the active commitments. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | tx_signatures | + |<-----------------------------| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +-------------+ + | | | FundingTx1 |------->| FundingTx2a | + | | +------------+ +-------------+ + | | + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | tx_init_rbf | Alice RBFs the splice attempt. + |----------------------------->| + | tx_ack_rbf | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | tx_signatures | + |<-----------------------------| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +-------------+ + | | | FundingTx1 |---+--->| FundingTx2a | + | | +------------+ | +-------------+ + | | | + | | | +-------------+ + | | +--->| FundingTx2b | + | | +-------------+ + | | + | update_add_htlc | Alice and Bob use the channel while the splice transactions are unconfirmed. + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 3 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2a, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2b, commitment_number = 11 + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | start_batch | batch_size = 3 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2a, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2b, commitment_number = 11 + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ +-------------+ + | | | FundingTx1 |---+--->| FundingTx2a | + | | +------------+ | +-------------+ + | | | + | | | +-------------+ + | | +--->| FundingTx2b | + | | +-------------+ + | | + | splice_locked | splice_txid = FundingTx2a + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 3 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 12 -> this message will be ignored by Bob since FundingTx2a will be locked before the end of the batch + |----------------------------->| + | splice_locked | splice_txid = FundingTx2a + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2a, commitment_number = 12 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2b, commitment_number = 12 -> this message can be ignored by Bob since FundingTx2a has been locked + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | commit_sig | + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 12 + | | +-------------+ + | | | FundingTx2b | + | | +-------------+ +``` + +### Disconnection with one side sending `commit_sig` + +In this scenario, a disconnection happens when one side has sent `commit_sig` but not the other. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before receiving Bob's tx_complete: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + | X----------------------| + | commit_sig | + | X----------------------| + | | Active commitments for Alice: + | | + | | commitment_number = 10 + | | +------------+ + | | | FundingTx1 | + | | +------------+ + | | + | | Active commitments for Bob: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 10, next_revocation_number = 10 + |<-----------------------------| + | tx_abort | + |----------------------------->| + | tx_abort | + |<-----------------------------| + | | Bob can safely forget the splice attempt because he hasn't sent tx_signatures. + | | Active commitments for Alice and Bob: + | | + | | commitment_number = 10 + | | +------------+ + | | | FundingTx1 | + | | +------------+ +``` + +### Disconnection with both sides sending `commit_sig` + +In this scenario, a disconnection happens when both sides have sent `commit_sig`. +They are able to resume the signatures exchange on reconnection. +In this example, Bob is supposed to send `tx_signatures` first. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before receiving Bob's commit_sig: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |--------------------X | + | commit_sig | + | X----------------------| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 10, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 10, next_revocation_number = 10 + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +``` + +### Disconnection with one side sending `tx_signatures` + +In this scenario, a disconnection happens when one side has sent `tx_signatures` but not the other. +They are able to resume the signatures exchange on reconnection. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before receiving Bob's tx_signatures: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + | X----------------------| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10 + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +``` + +### Disconnection with both sides sending `tx_signatures` + +In this scenario, a disconnection happens when both sides have sent `tx_signatures`, but one side has not received it. +They are able to resume the signatures exchange on reconnection. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before Bob receives her tx_signatures: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------X | + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10 + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +``` + +### Disconnection with both sides sending `tx_signatures` and channel updates + +In this scenario, a disconnection happens when both sides have sent `tx_signatures`, but one side has not received it. +The second signer also sent a new signature for additional changes to apply after their `tx_signatures`. +They are able to resume the signatures exchange on reconnection and retransmit new updates. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before Bob receives her tx_signatures and new updates: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------X | + | update_add_htlc | + |----------------------X | + | start_batch | batch_size = 2 + |----------------------X | + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------X | + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------X | + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10 + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 2 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | start_batch | batch_size = 2 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +``` + +### Disconnection with concurrent `splice_locked` + +In this scenario, disconnections happen while nodes are exchanging `splice_locked`. +We use the `my_current_funding_locked` field to "lock" the latest splice on reconnection. + +```text +Initial active commitments: + + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnections happen when exchanging splice_locked: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | splice_locked | + |---------------------X | + | | Active commitments: + | | + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, my_current_funding_locked = funding_tx2 + |----------------------------->| + | channel_reestablish | next_funding_txid = null, my_current_funding_locked = funding_tx1 + |<-----------------------------| + | | + | splice_locked | At that point, Bob has locked funding_tx2, but Alice doesn't know it because she hasn't received splice_locked yet. + | X----------------------| + | | + | channel_reestablish | next_funding_txid = null, my_current_funding_locked = funding_tx2 + |----------------------------->| + | channel_reestablish | next_funding_txid = null, my_current_funding_locked = funding_tx2 + |<-----------------------------| + | | Bob's my_current_funding_locked lets Alice know that Bob has locked funding_tx2 while they were disconnected. + | | She can thus immediately lock it as well even though she hasn't received Bob's splice_locked. + | | + | | Active commitments: + | | + | | +------------+ + | | | FundingTx2 | + | | +------------+ + | | + | update_add_htlc | + |----------------------------->| + | commit_sig | Alice doesn't need to sent commit_sig for funding_tx1 since funding_tx2 was locked. + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | commit_sig | + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| +``` + +### Disconnection after exchanging `tx_signatures` and one side sends `commit_sig` for channel update + +In this scenario, a disconnection happens when both sides have sent and received `tx_signatures`. +The second signer also sent a new signature for additional changes to apply after their `tx_signatures`. +They are able to resume the signature exchange on reconnection and continue with the `commit_sig` exchange. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before Bob receives her commit_sigs for new updates: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 2 + |----------------------X | + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------X | + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------X | + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |<-----------------------------| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 2 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | start_batch | batch_size = 2 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +``` + +### Disconnection after exchanging `tx_signatures` and both sides send `commit_sig` for channel update; `revoke_and_ack` not received + +In this scenario, a disconnection happens when both sides have sent and received `tx_signatures`. +Both signers also send new signatures for additional changes to apply after their `tx_signatures`. +The first signer has **not** received the second signer's `revoke_and_ack` message for the channel update. +They are able to resume the signature exchange on reconnection. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before Bob receives her revoke_and_ack and commit_sigs for new updates: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 2 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------------->| + | revoke_and_ack | + | X----------------------| + | start_batch | batch_size = 2 + | X----------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + | X----------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + | X----------------------| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |----------------------------->| + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |<-----------------------------| + | revoke_and_ack | + |<-----------------------------| + | start_batch | batch_size = 2 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +``` + +### Disconnection after exchanging `tx_signatures` and both sides send `commit_sig` for channel update + +In this scenario, a disconnection happens when both sides have sent and received `tx_signatures`. +Both signers also send new signatures for additional changes to apply after their `tx_signatures`. +The first signer has received the second signer's `revoke_and_ack` message for the channel update. +They are able to resume the signature exchange on reconnection. + +```text +Initial active commitments: + + commitment_number = 10 + +------------+ + | FundingTx1 | + +------------+ + +Alice initiates a splice, but disconnects before she receives Bob's commit_sigs for new updates: + + Alice Bob + | stfu | + |----------------------------->| + | stfu | + |<-----------------------------| + | splice_init | + |----------------------------->| + | splice_ack | + |<-----------------------------| + | | + | | + |<---------------------------->| + | | + | tx_complete | + |----------------------------->| + | tx_complete | + |<-----------------------------| + | commit_sig | + |----------------------------->| + | commit_sig | + |<-----------------------------| + | tx_signatures | + |<-----------------------------| + | tx_signatures | + |----------------------------->| + | update_add_htlc | + |----------------------------->| + | start_batch | batch_size = 2 + |----------------------------->| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |----------------------------->| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |----------------------------->| + | revoke_and_ack | + |<-----------------------------| + | start_batch | batch_size = 2 + | X----------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + | X----------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + | X----------------------| + | | Active commitments: + | | + | | commitment_number = 10 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ + | | + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 11 + |----------------------------->| + | channel_reestablish | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10 + |<-----------------------------| + | start_batch | batch_size = 2 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx1, commitment_number = 11 + |<-----------------------------| + | commit_sig | funding_txid = FundingTx2, commitment_number = 11 + |<-----------------------------| + | revoke_and_ack | + |----------------------------->| + | | Active commitments: + | | + | | commitment_number = 11 + | | +------------+ +------------+ + | | | FundingTx1 |------->| FundingTx2 | + | | +------------+ +------------+ +```