Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

option_simple_close (features 60/61) #1205

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 259 additions & 13 deletions 02-peer-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ operation, and closing.
* [Channel Close](#channel-close)
* [Closing Initiation: `shutdown`](#closing-initiation-shutdown)
* [Closing Negotiation: `closing_signed`](#closing-negotiation-closing_signed)
* [Closing Negotiation: `closing_complete` and `closing_sig`](#closing-negotiation-closing_complete-and-closing_sig)
* [Normal Operation](#normal-operation)
* [Forwarding HTLCs](#forwarding-htlcs)
* [`cltv_expiry_delta` Selection](#cltv_expiry_delta-selection)
Expand Down Expand Up @@ -1507,19 +1508,85 @@ Closing happens in two stages:
1. one side indicates it wants to clear the channel (and thus will accept no new HTLCs)
2. once all HTLCs are resolved, the final channel close negotiation begins.

+-------+ +-------+
| |--(1)----- shutdown ------->| |
| |<-(2)----- shutdown --------| |
| | | |
| | <complete all pending HTLCs> | |
| A | ... | B |
| | | |
| |--(3)-- closing_signed F1--->| |
| |<-(4)-- closing_signed F2----| |
| | ... | |
| |--(?)-- closing_signed Fn--->| |
| |<-(?)-- closing_signed Fn----| |
+-------+ +-------+
+-------+ +-------+
| | shutdown(scriptA1) | |
| |--------------------------------------------------------->| |
| | shutdown(scriptB1) | |
| |<---------------------------------------------------------| |
| | | |
| | <complete all pending HTLCs> | |
| A | .... | B |
| | | |
| | closing_complete(scriptA1, scriptB1, 1000 sat) | |
| |--------------------------------------------------------->| |
| | closing_complete(scriptB1, scriptA1, 750 sat) | |
| |<---------------------------------------------------------| |
| | closing_sig(scriptA1, scriptB1, 1000 sat) | |
| |<---------------------------------------------------------| |
| | closing_sig(scriptB1, scriptA1, 750 sat) | |
| |--------------------------------------------------------->| |
| | | |
| | <A updates their script> | |
| | | |
| | closing_complete(scriptA2, scriptB1, 1100 sat) | |
| |--------------------------------------------------------->| |
| | closing_sig(scriptA2, scriptB1, 1100 sat) | |
| |<---------------------------------------------------------| |
| | | |
| | .... | |
| | | |
| | <B RBFs their previous tx> | |
| | | |
| | closing_complete(scriptB1, scriptA2, 850 sat) | |
| |<---------------------------------------------------------| |
| | closing_sig(scriptB1, scriptA2, 850 sat) | |
| |--------------------------------------------------------->| |
| | | |
| | .... | |
| | | |
| | <A and B concurrently update their script> | | (*) Note that this is a very rare race condition
| | | |
| | closing_complete(scriptA3, scriptB1, 1250 sat) | |
| |-----------------------------> | |
| | closing_complete(scriptB2, scriptA2, 900 sat) | |
| | <--------------------------------------| |
| | closing_complete | |
| | --------------------------->| |
| | warning | | (*) B sends a warning because A is not using scriptB2
| | <--------------------------------------| |
| | closing_complete | |
| |<------------------ | |
| | warning | | (*) A sends a warning because B is not using scriptA3
| |--------------------------------------------------------->| |
| | warning | |
| |<------------------ | |
| | | |
| | <disconnect> | | (*) A and B reconnect to resolve the race condition
| | | |
| | channel_reestablish | |
| |--------------------------------------------------------->| |
| | channel_reestablish | |
| |<---------------------------------------------------------| |
| | shutdown(scriptA3) | |
| |--------------------------------------------------------->| |
| | shutdown(scriptB2) | |
| |<---------------------------------------------------------| |
| | | |
| | <A retries with B's latest script> | |
| | | |
| | closing_complete(scriptA3, scriptB2, 1250 sat) | |
| |--------------------------------------------------------->| |
| | closing_sig(scriptA3, scriptB2, 1250 sat) | |
| |<---------------------------------------------------------| |
| | | |
| | <B retries with A's latest script> | |
| | | |
| | closing_complete(scriptB2, scriptA3, 900 sat) | |
| |<---------------------------------------------------------| |
| | closing_sig(scriptB2, scriptA3, 900 sat) | |
| |--------------------------------------------------------->| |
| | | |
+-------+ +-------+

### Closing Initiation: `shutdown`

Expand Down Expand Up @@ -1555,6 +1622,10 @@ A sending node:
3. if (and only if) `option_shutdown_anysegwit` is negotiated:
* `OP_1` through `OP_16` inclusive, followed by a single push of 2 to 40 bytes
(witness program versions 1 through 16)
4. if (and only if) `option_simple_close` is negotiated:
* `OP_RETURN` followed by one of:
* `6` to `75` inclusive followed by exactly that many bytes
* `76` followed by `76` to `80` followed by exactly that many bytes

A receiving node:
- if it hasn't received a `funding_signed` (if it is a funder) or a `funding_created` (if it is a fundee):
Expand Down Expand Up @@ -1713,6 +1784,181 @@ satoshis, which is possible if `dust_limit_satoshis` is below 546 satoshis).
No funds are at risk when that happens, but the channel must be force-closed as
the closing transaction will likely never reach miners.

`OP_RETURN` is only standard if followed by PUSH opcodes, and the total script
is 83 bytes or less. We are slightly stricter, to only allow a single PUSH, but
there are two forms in script: one which pushes up to 75 bytes, and a longer
one (`OP_PUSHDATA1`) which is needed for 76-80 bytes.

### Closing Negotiation: `closing_complete` and `closing_sig`

Once shutdown is complete, the channel is empty of HTLCs, there are no commitments
for which a revocation is owed, and all updates are included on both commitments,
the final current commitment transactions will have no HTLCs.

Each peer creates their own closing transaction where they pay the fee, and sends
`closing_complete` to the other peer with the transaction details. The other peer
simply signs that transaction and sends back `closing_sig`. Each peer will thus
independently send `closing_complete` and receive `closing_sig`, resulting in two
independent (but conflicting) closing transactions being created.

The lesser-paid peer (if either is) can opt to omit their own output from the
closing transaction.

This process can be repeated multiple times by sending `closing_complete` again,
which allows increasing the fees and changing the output script.

1. type: 40 (`closing_complete`)
2. data:
* [`channel_id`:`channel_id`]
* [`u16`:`closer_scriptpubkey_len`]
* [`closer_scriptpubkey_len*byte`:`closer_scriptpubkey`]
* [`u16`:`closee_scriptpubkey_len`]
* [`closee_scriptpubkey_len*byte`:`closee_scriptpubkey`]
* [`u64`:`fee_satoshis`]
* [`u32`:`locktime`]
* [`closing_tlvs`:`tlvs`]

1. type: 41 (`closing_sig`)
2. data:
* [`channel_id`:`channel_id`]
* [`u16`:`closer_scriptpubkey_len`]
* [`closer_scriptpubkey_len*byte`:`closer_scriptpubkey`]
* [`u16`:`closee_scriptpubkey_len`]
* [`closee_scriptpubkey_len*byte`:`closee_scriptpubkey`]
* [`u64`:`fee_satoshis`]
* [`u32`:`locktime`]
t-bast marked this conversation as resolved.
Show resolved Hide resolved
* [`closing_tlvs`:`tlvs`]

1. `tlv_stream`: `closing_tlvs`
2. types:
1. type: 1 (`closer_output_only`)
t-bast marked this conversation as resolved.
Show resolved Hide resolved
2. data:
* [`signature`:`sig`]
1. type: 2 (`closee_output_only`)
2. data:
* [`signature`:`sig`]
1. type: 3 (`closer_and_closee_outputs`)
2. data:
* [`signature`:`sig`]

#### Requirements

Note: the details and requirements for the transaction being signed are in [BOLT 3](03-transactions.md#closing-transaction).

An output is *dust* if the amount is less than the [Bitcoin Core Dust Thresholds](03-transactions.md#dust-limits).

Both nodes:
- After a `shutdown` has been sent and received, AND no HTLCs remain in either commitment transaction:
- SHOULD send a `closing_complete` message.

The sender of `closing_complete` (aka. "the closer"):
- MUST set `fee_satoshis` to a fee less than or equal to its outstanding balance, rounded down to whole satoshis.
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- MUST set `fee_satoshis` so that at least one output is not dust.
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- MUST set `closer_scriptpubkey` to its desired output script.
- MUST set `closee_scriptpubkey` to the last script it received from its peer (from `closing_complete` or from the initial `shutdown`).
- MUST set `locktime` to the desired `nLockTime` of the closing transaction.
- MUST generate its closing transaction as specified in [BOLT #3](03-transactions.md#closing-transaction).
- MUST set `signature` fields as valid signature using its `funding_pubkey` of:
- `closer_output_only`: closing transaction with only the local ("closer") output.
- `closee_output_only`: closing transaction with only the remote ("closee") output.
- `closer_and_closee_outputs`: closing transaction with both the closer and closee outputs.
- If the local outstanding balance (in millisatoshi) is less than the remote outstanding balance:
- MUST NOT set `closer_output_only`.
- MUST set `closee_output_only` if the local output amount is dust.
- MAY set `closee_output_only` if it considers the local output amount uneconomical AND its `closer_scriptpubkey` is not `OP_RETURN`.
- Otherwise (not lesser amount, cannot remove its own output):
- MUST NOT set `closee_output_only`.
- If the closee's output amount is dust:
- MUST set `closer_output_only`.
- SHOULD NOT set `closer_and_closee_outputs`.
- Otherwise:
- MUST set both `closer_output_only` and `closer_and_closee_outputs`.
- If it wants to send another `closing_complete` (e.g. with a different `fee_satoshis` or `closer_scriptpubkey`):
- MUST wait until it has received `closing_sig` first.
- SHOULD close the connection if it doesn't receive `closing_sig`.

The receiver of `closing_complete` (aka. "the closee"):
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- If `fee_satoshis` is greater than the closer's outstanding balance:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If `closee_scriptpubkey` does not match the last script it sent (from `closing_complete` or from the initial `shutdown`):
- SHOULD ignore `closing_complete`.
- SHOULD send a `warning`.
- SHOULD close the connection.
- MUST generate the remote closing transaction as specified in [BOLT #3](03-transactions.md#closing-transaction).
- Select a signature for validation:
- If the local output amount is dust:
- MUST use `closer_output_only`.
- Otherwise, if it considers the local output amount uneconomical AND its `closee_scriptpubkey` is not `OP_RETURN`:
- MUST use `closer_output_only`.
- Otherwise, if `closer_and_closee_outputs` is present:
- MUST use `closer_and_closee_outputs`.
- Otherwise:
- MUST use `closee_output_only`.
- If the selected signature field does not exist:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If the signature field is not valid for the corresponding closing transaction specified in [BOLT #3](03-transactions.md#closing-transaction):
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If the signature field is non-compliant with LOW-S-standard rule<sup>[LOWS](https://github.com/bitcoin/bitcoin/pull/6769)</sup>:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- MUST sign and broadcast the corresponding closing transaction.
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- MUST send `closing_sig` with a single valid signature in the same TLV field as the `closing_complete`.
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- MUST use `closer_scriptpubkey` for its own future `closing_complete` messages.

The receiver of `closing_sig`:
- If `closer_scriptpubkey`, `closee_scriptpubkey`, `fee_satoshis` or `locktime` don't match what was sent in `closing_complete`:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If `tlvs` does not contain exactly one signature:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If `tlvs` does not contain one of the TLV fields sent in `closing_complete`:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If the signature field is not valid for the corresponding closing transaction specified in [BOLT #3](03-transactions.md#closing-transaction):
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- If the signature field is non-compliant with LOW-S-standard rule<sup>[LOWS](https://github.com/bitcoin/bitcoin/pull/6769)</sup>:
- MUST either send a `warning` and close the connection, or send an `error` and fail the channel.
- otherwise:
- MUST broadcast the corresponding closing transaction.
- MAY send another `closing_complete` (e.g. with a different `fee_satoshis` or `closer_scriptpubkey`).

### Rationale
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add rationale here for why both of the new messages carry the closing scripts for both parties?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that isn't immediately clear is: why does closing_singed need to replicate these fields? As a peer you MUST remember the last address you sent to make sure they don't trick you into sending all your funds away. Therefore, isn't it redundant to send the scripts again for the response message?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a rationale in 70c943f

This is generally very helpful for debugging (I'm thinking about the debug session we had when doing lnd <-> eclair cross-compat and has a signature mismatch): when you receive closing_complete or closing_sig, the message contains everything that's needed to build exactly the same transaction as the signer. It never hurts to include those and be explicit rather than implicit.

It also makes it easier for nodes to accept a closing_complete message that uses their previous script, which is one way that can resolve the race condition described in the rationale. It's optional, and that's not what I implemented in eclair (I chose to simply disconnect when that race condition happens), but other implementations may choose to keep signing transactions that use their previous script(s) to avoid a disconnection and be free from race conditions.

I hope that makes sense!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A diagram can help make that clearer: if Alice is ok with still using her old script (because a previous closing tx may confirm that uses it anyway), the follow flow may happen:

Alice                                     Bob
  |                                        |
  | closing_complete                       | (*) contains alice_new_script and bob_old_script
  |------------------>                     |
  |                       closing_complete | (*) contains bob_new_script and alice_old_script
  |<---------------------------------------|
  | closing_sig                            | (*) contains bob_new_script and alice_old_script
  |--------------------------------------->|

If the scripts were not included in closing_sig, Bob wouldn't know if Alice is using her new or old script.


The close protocol is designed to avoid any failure scenarios caused by fee disagreement,
since each side offers to pay its own desired fee.

If one side has less funds than the other, it may choose to omit its own output, and in this case
dust MUST be omitted, to ensure that the resulting transaction can be broadcast.

The corner case where fees are so high that both outputs are dust is addressed in two ways: paying
a low fee to avoid the problem, or using an `OP_RETURN` (which is never "dust"). If one side
chooses to use an `OP_RETURN` output, its amount must be 0 to ensure that the resulting transaction
can be broadcast.

Note that there is usually no reason to pay a high fee for rapid processing, since an urgent child
could pay the fee on the closing transactions' behalf. If rapid processing is desired and CPFP is
not an option, the closer can RBF its previous closing transactions by sending `closing_complete`
again.

Sending a new `closing_complete` message overrides previous ones, so you can negotiate again (even
changing the output address if `upfront_shutdown_script` was not negotiated). This creates a rare
race condition if both nodes send `closing_complete` to change their `closer_scriptpubkey` at the
same time: when that happens, the `closing_complete` they receive will be using their previous
output script, so they shouldn't sign the corresponding transaction. When that happens, we simply
reconnect, which provides the opportunity for both nodes to send their latest output script in
`shutdown` and restart the signing flow. We include the closer and closee scripts in `closing_sig`
to allow our peer to detect a script mismatch and correctly ignore the signatures, which also
helps debugging race conditions.

If the closer proposes a transaction which will not relay (an output is dust, or the fee rate it
proposes is too low), it doesn't harm the closee to sign the transaction.

Similarly, if the closer proposes a high fee, it doesn't harm the closee to sign the transaction,
as the closer is paying.

There's a slight game where each side would prefer the other side pay the fee, and proposes a
minimal fee. If neither side proposes a fee which will relay, the negotiation can occur again,
or the final commitment transaction can be spent. In practice, the opener has an incentive to
t-bast marked this conversation as resolved.
Show resolved Hide resolved
offer a reasonable closing fee, as they would pay the fee for the commitment transaction, which
also costs more to spend.

## Normal Operation

Once both nodes have exchanged `channel_ready` (and optionally [`announcement_signatures`](07-routing-gossip.md#the-announcement_signatures-message)), the channel can be used to make payments via Hashed Time Locked Contracts.
Expand Down
Loading