From 325588d8294fef24423aab4eebdb3d36a330d438 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 28 Apr 2021 06:56:55 +0930 Subject: [PATCH 1/3] Feature 106/107: option_simplified_update. This feature turns on a simplification of the current update scheme: 1. Turns are taken, with all updates settling before switching sides. 2. update_fee is always a separate commitment by itself. This is a fairly easy addition for existing implementations, but a much-reduced burden for new implementations. Signed-off-by: Rusty Russell --- 02-peer-protocol.md | 104 ++++++++++++++++++++++++++++++++++++++++++++ 09-features.md | 1 + 2 files changed, 105 insertions(+) diff --git a/02-peer-protocol.md b/02-peer-protocol.md index 974e51e9a..f2690f11a 100644 --- a/02-peer-protocol.md +++ b/02-peer-protocol.md @@ -17,6 +17,7 @@ operation, and closing. * [Closing Initiation: `shutdown`](#closing-initiation-shutdown) * [Closing Negotiation: `closing_signed`](#closing-negotiation-closing_signed) * [Normal Operation](#normal-operation) + * [Simplified Operation](#simplified-operation) * [Forwarding HTLCs](#forwarding-htlcs) * [`cltv_expiry_delta` Selection](#cltv_expiry_delta-selection) * [Adding an HTLC: `update_add_htlc`](#adding-an-htlc-update_add_htlc) @@ -644,6 +645,102 @@ transactions may be out of sync indefinitely. This is not concerning: what matters is whether both sides have irrevocably committed to a particular update or not (the final state, above). +### Simplified Operation + +If `option_simplified_update` is negotiated, a subset of the previous +protocol is used, where each side takes turns to propose updates, +which are synchronized before the other side has a turn: + + +-------+ +-------+ + | |--(1)---- update_add_htlc ---->| | + | |--(2)---- update_add_htlc ---->| | + | |--(3)--- commitment_signed --->| | + | A |<-(4)---- revoke_and_ack ------| B | + | |<-(5)--- commitment_signed ----| | + | |--(6)---- revoke_and_ack ----->| | + | | | | + | |<-(7)---- update_add_htlc -----| | + | |<-(8)--- commitment_signed ----| | + | |--(9)---- revoke_and_ack ----->| | + | |--(10)-- commitment_signed --->| | + | |<-(11)--- revoke_and_ack ------| | + +-------+ +-------+ + +If one side sends an update when it is not its turn, the other side +can either ignore it (in favor of its own update), or reply with a +`yield` message to hand the turn over. + +#### Swapping turns: `yield` + +1. type: 138 (`yield`) +2. data: + * [`channel_id`:`channel_id`] + +#### Requirements + +A node: + - MUST NOT send `yield` unless `option_simplified_update` is negotiated. + - MUST track whose turn it is, starting with the peer with the lesser SEC1-encoded node_id. + - MUST give up its turn when: + - sending `revoke_and_ack` + - sending a `yield` + - MUST accept its turn when: + - receiving `revoke_and_ack` + - receiving a `yield` + - During this node's turn: + - if it receives an update message or `commitment_signed`: + - if it has sent its own update or `commitment_signed`: + - MUST ignore the message + - otherwise: + - MUST reply with `yield` and process the message. + - During the other node's turn: + - if it has not received an update message or `commitment_signed`: + - MAY send one or more update message or `commitment_signed`: + - MUST NOT include those changes if it receives an update message or `commitment_signed` in reply. + - MUST include those changes if it receives a `yield` in reply. + +Upon reconnection when `channel_reestablish` is exchanged and +`option_simplified_update` is negotiated: + - If a node sent `next_commitment_number` which exceeds its received + `next_revocation_number`, that node's turn is unfinished. + - If exactly one node's turn is unfinished, it is their turn, + otherwise the turn starts with the peer with the lesser + SEC1-encoded node_id. + - If a node's turn was unfinished: + - That node MUST retransmit the same updates as their previous turn, except + omitting `update_fee` if their previous turn mixed `update_fee` and other updates. + - The receiving node MAY close the channel if it receives different updates + to the previously unfinished turn (except `update_fee` as above). + +#### Rationale + +A simple implementation can send an update message out-of-turn and +wait for a `yield` before sending `commitment_signed`: this ensures +that there only ever one commitment state at any time, at cost of +round-trip latency for any out-of-turn changes. + +A more complex implementation can optimistically send a complete set +of updates and `commitment_signed`, but must handle the possibility of +that commitment being spent on-chain even if the peer does not yield. + +Note that the reconnection logic is similarly simplified for an +implementation which always waits for `yield`: both nodes cannot have +a commitment in flight simultanously, so if messages are lost due to +disconnection that is detectable and it simply replays its turn. The +requirement for exact replay again means that commitments at the same +height will always match. + +Note that if a channel always uses `option_simplified_update` then the +revocation and commitment numbers will be equal on both sides (except +when an update is in progress), but this is not true if a channel +previously operated without this option. However, the "more +commitments sent than revocations received" test will still indicate +which side has uncommitted changes. Similarly, simplified peers will +never mix non-fee updates and HTLC updates, but on the first complete +reestablishment after enabling `option_simplified_update` this is +possible and thus is handled. + + ### Forwarding HTLCs In general, a node offers HTLCs for two reasons: to initiate a payment of its own, @@ -1119,6 +1216,8 @@ given in [BOLT #3](03-transactions.md#fee-calculation). The node _responsible_ for paying the Bitcoin fee: - SHOULD send `update_fee` to ensure the current fee rate is sufficient (by a significant margin) for timely processing of the commitment transaction. + - if `option_simplified_update` is negotiated: + - MUST NOT mix `update_fee` with other updates in the same commitment. The node _not responsible_ for paying the Bitcoin fee: - MUST NOT send `update_fee`. @@ -1155,6 +1254,11 @@ it's simplest to only allow it to set fee levels; however, as the same fee rate applies to HTLC transactions, the receiving node must also care about the reasonableness of the fee. +With `option_simplified_update` the fee logic is again simplified for +implementations, such that there is no question of the HTLC state when +the fee change is proposed. + + ## Message Retransmission Because communication transports are unreliable, and may need to be diff --git a/09-features.md b/09-features.md index ea37b8b5e..9a7692a51 100644 --- a/09-features.md +++ b/09-features.md @@ -40,6 +40,7 @@ The Context column decodes as follows: | 18/19 | `option_support_large_channel` | Can create large channels | IN | | [BOLT #2](02-peer-protocol.md#the-open_channel-message) | | 20/21 | `option_anchor_outputs` | Anchor outputs | IN | `option_static_remotekey` | [BOLT #3](03-transactions.md) | | 22/23 | `option_anchors_zero_fee_htlc_tx` | Anchor commitment type with zero fee HTLC transactions | IN | | [BOLT #3][bolt03-htlc-tx], [lightning-dev][ml-sighash-single-harmful]| +| 106/107 | `option_simplified_update` | Simplified commitment negotiation | IN | | | ## Requirements From 4c69e357e8067fd71fbb1d1c97ddcddc1286db58 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 4 May 2021 14:44:24 +0930 Subject: [PATCH 2/3] fixup! Feature 106/107: option_simplified_update. Add update_noop, clean up whitespace and mark option requirements on yield msg. --- 02-peer-protocol.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/02-peer-protocol.md b/02-peer-protocol.md index f2690f11a..d57c9a6c1 100644 --- a/02-peer-protocol.md +++ b/02-peer-protocol.md @@ -670,9 +670,13 @@ If one side sends an update when it is not its turn, the other side can either ignore it (in favor of its own update), or reply with a `yield` message to hand the turn over. -#### Swapping turns: `yield` +#### Swapping turns: `yield` and `update_noop` -1. type: 138 (`yield`) +1. type: 129 (`update_noop`) (`option_simplified_update`) +2. data: + * [`channel_id`:`channel_id`] + +1. type: 138 (`yield`) (`option_simplified_update`) 2. data: * [`channel_id`:`channel_id`] @@ -693,11 +697,13 @@ A node: - MUST ignore the message - otherwise: - MUST reply with `yield` and process the message. + - if it received `update_noop`: + - MUST otherwise ignore the message - During the other node's turn: - if it has not received an update message or `commitment_signed`: - MAY send one or more update message or `commitment_signed`: - - MUST NOT include those changes if it receives an update message or `commitment_signed` in reply. - - MUST include those changes if it receives a `yield` in reply. + - MUST NOT include those changes if it receives an update message or `commitment_signed` in reply. + - MUST include those changes if it receives a `yield` in reply. Upon reconnection when `channel_reestablish` is exchanged and `option_simplified_update` is negotiated: @@ -710,7 +716,7 @@ Upon reconnection when `channel_reestablish` is exchanged and - That node MUST retransmit the same updates as their previous turn, except omitting `update_fee` if their previous turn mixed `update_fee` and other updates. - The receiving node MAY close the channel if it receives different updates - to the previously unfinished turn (except `update_fee` as above). + to the previously unfinished turn (except `update_fee` as above). #### Rationale @@ -719,6 +725,9 @@ wait for a `yield` before sending `commitment_signed`: this ensures that there only ever one commitment state at any time, at cost of round-trip latency for any out-of-turn changes. +For simplicity, an `update_noop` can be used to prompt a `yield` with +no other effects. + A more complex implementation can optimistically send a complete set of updates and `commitment_signed`, but must handle the possibility of that commitment being spent on-chain even if the peer does not yield. From 90296a2be718adb52645184c6074a7ca8c33bd7e Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 10 May 2021 14:09:46 +0930 Subject: [PATCH 3/3] fixup! Feature 106/107: option_simplified_update. 1. Clarify what happens when optimistic implementation reconnects. 2. Assume we use quiescence for channel feature upgrade, so no complex reestablishment handling --- 02-peer-protocol.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/02-peer-protocol.md b/02-peer-protocol.md index d57c9a6c1..0edc86431 100644 --- a/02-peer-protocol.md +++ b/02-peer-protocol.md @@ -707,16 +707,17 @@ A node: Upon reconnection when `channel_reestablish` is exchanged and `option_simplified_update` is negotiated: + - If it has sent `commitment_signed` on the other peer's turn without receiving `yield`: + - MUST NOT consider that `commitment_signed` sent. - If a node sent `next_commitment_number` which exceeds its received `next_revocation_number`, that node's turn is unfinished. - If exactly one node's turn is unfinished, it is their turn, otherwise the turn starts with the peer with the lesser SEC1-encoded node_id. - If a node's turn was unfinished: - - That node MUST retransmit the same updates as their previous turn, except - omitting `update_fee` if their previous turn mixed `update_fee` and other updates. + - That node MUST retransmit the same updates as their previous turn. - The receiving node MAY close the channel if it receives different updates - to the previously unfinished turn (except `update_fee` as above). + to the previously unfinished turn. #### Rationale @@ -735,19 +736,25 @@ that commitment being spent on-chain even if the peer does not yield. Note that the reconnection logic is similarly simplified for an implementation which always waits for `yield`: both nodes cannot have a commitment in flight simultanously, so if messages are lost due to -disconnection that is detectable and it simply replays its turn. The -requirement for exact replay again means that commitments at the same -height will always match. +disconnection that is detectable and it simply replays its turn. If +an implementation optimistically sends `commitment_signed`, then it +assumes was either lost and does not apply, or the `yield` reply was +lost and the `next_commitment_number` test will correctly indicate its +turn. + +The requirement for exact replay again means that commitments at the +same height will always match. Note that if a channel always uses `option_simplified_update` then the revocation and commitment numbers will be equal on both sides (except when an update is in progress), but this is not true if a channel previously operated without this option. However, the "more commitments sent than revocations received" test will still indicate -which side has uncommitted changes. Similarly, simplified peers will -never mix non-fee updates and HTLC updates, but on the first complete -reestablishment after enabling `option_simplified_update` this is -possible and thus is handled. +which side has uncommitted changes. + +Since upgrading to enable `option_simplified_update` will require a +quiescent period, there can be no issue with retransmission from +before `option_simplified_update` applied. ### Forwarding HTLCs