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

Update closing_signed fee requirement #847

Merged
merged 7 commits into from
Aug 31, 2021
Merged
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
74 changes: 56 additions & 18 deletions 02-peer-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,11 +553,24 @@ the other node then replies similarly, using a fee it thinks is fair. This
exchange continues until both agree on the same fee or when one side fails
the channel.

In the modern method, the funder sends its permissable fee range, and the
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
non-funder has to pick a fee in this range. If the non-funder chooses the same
value, negotiation is complete after two messages, otherwise the funder will
reply with the same value (completing after three messages).

1. type: 39 (`closing_signed`)
2. data:
* [`channel_id`:`channel_id`]
* [`u64`:`fee_satoshis`]
* [`signature`:`signature`]
* [`closing_signed_tlvs`:`tlvs`]

1. `tlv_stream`: `closing_signed_tlvs`
2. types:
1. type: 1 (`fee_range`)
2. data:
* [`u64`:`min_fee_satoshis`]
* [`u64`:`max_fee_satoshis`]

#### Requirements

Expand All @@ -566,12 +579,17 @@ The funding node:
- SHOULD send a `closing_signed` message.

The sending node:
- MUST set `fee_satoshis` less than or equal to the
base fee of the final commitment transaction, as calculated in [BOLT #3](03-transactions.md#fee-calculation).
- SHOULD set the initial `fee_satoshis` according to its
estimate of cost of inclusion in a block.
- MUST set `signature` to the Bitcoin signature of the close
transaction, as specified in [BOLT #3](03-transactions.md#closing-transaction).
- SHOULD set the initial `fee_satoshis` according to its estimate of cost of
inclusion in a block.
- SHOULD set `fee_range` according to the minimum and maximum fees it is
prepared to pay for a close transaction.
- if it doesn't receive a `closing_signed` response after a reasonable amount of time:
- MUST fail the channel
- if it is not the funder:
Roasbeef marked this conversation as resolved.
Show resolved Hide resolved
- SHOULD set `max_fee_satoshis` to at least the `max_fee_satoshis` received
- SHOULD set `min_fee_satoshis` to a fairly low value
- MUST set `signature` to the Bitcoin signature of the close transaction,
as specified in [BOLT #3](03-transactions.md#closing-transaction).

The receiving node:
- if the `signature` is not valid for either variant of closing transaction
Expand All @@ -580,31 +598,51 @@ The receiving node:
- if `fee_satoshis` is equal to its previously sent `fee_satoshis`:
- SHOULD sign and broadcast the final closing transaction.
- MAY close the connection.
- otherwise, if `fee_satoshis` is greater than
the base fee of the final commitment transaction as calculated in
[BOLT #3](03-transactions.md#fee-calculation):
- MUST fail the connection.
- if `fee_satoshis` is not strictly
between its last-sent `fee_satoshis` and its previously-received
`fee_satoshis`, UNLESS it has since reconnected:
- if `fee_satoshis` matches its previously sent `fee_range`:
- SHOULD use `fee_satoshis` to sign and broadcast the final closing transaction
- SHOULD reply with a `closing_signed` with the same `fee_satoshis` value if it is different from its previously sent `fee_satoshis`
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- MAY close the connection.
- if the message contains a `fee_range`:
- if there is no overlap between that and its own `fee_range`:
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- SHOULD fail the connection
- MUST fail the channel if it doesn't receive a satisfying `fee_range` after a reasonable amount of time
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this imply that you are allowed to send multiple closing_signed messages without "waiting your turn" if you send bad fee ranges? If so, this interacts with musig2+taproot channels

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, this is not a turn-based protocol, the initiator may send another closing_signed with a different fee range.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just to clarify, legacy coop close is turn-based, but fee_range isn't?

What is the rationale to make this not turn-based? Shouldn't the recipient of a fee_range ensure that the sent fee_range has overlap, or else don't send one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Just to clarify, legacy coop close is turn-based, but fee_range isn't?

Yes, fee_range allows the funder to update its fee_range if the fundee doesn't respond or sent a warning.

What is the rationale to make this not turn-based?

If you make it strictly turn-based you end up with exactly the previous closing protocol, which fee_range tries to simplify.

Shouldn't the recipient of a fee_range ensure that the sent fee_range has overlap, or else don't send one?

But that's exactly the "don't send one" part that creates the issue and requires the protocol to allow the sender to send a new one out of turn. The only other option is to force-close, which is undesirable.

I don't see why this is an issue for Taproot. If a sender sends a second closing_signed, it just throws away the previous state and the receiver does the same. There is an issue if the receiver was concurrently sending its closing_signed, but in practice 1) it's very unlikely 2) it will result in a force-close which is okay-ish.

A better longer term plan is to migrate closing to use the interactive-tx protocol.

Copy link
Contributor

@Crypt-iQ Crypt-iQ Jun 9, 2022

Choose a reason for hiding this comment

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

reading the spec the non-turn-based aspect wasn't immediately clear to me.

specifically for taproot there is no issue, but for the proposal here #995 it is incompatible with fee_range unless some amount of nonces are shared up front or a deterministic nonce scheme is used (I think determinism is undesirable since if you mess up the scheme and re-use a nonce, you've leaked your private key).

the issue boils down to:

  • the sender cannot send a second closing_signed until the receiver has sent their closing_signed. This is because the sender has effectively used up one of the receiver's nonces and does not know which nonce to use next. This isn't a problem if the flow is turn-based as the receiver will send over the nonces that the sender can use next. This issue doesn't exist during regular channel operation since a sender cannot send two commit_sig in a row without a revoke_and_ack in between.

that said, my question is answered so i'll have to think a bit more on what to do re #995

Copy link
Contributor

Choose a reason for hiding this comment

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

I see now that you've answered my question in that A (funder) is the one that sends multiple closing_signed if B sends a warning

Copy link
Collaborator Author

@t-bast t-bast Jun 15, 2022

Choose a reason for hiding this comment

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

Yes, sorry for the late reply. There are two possible flows for fee_range closing. The first is the happy flow:

  • A sends closing_signed
  • B responds with closing_signed inside the fee_range proposed by A (B is not allowed to answer with a fee outside of A's fee_range)
  • A responds with closing_signed with the same fee as the previous message

The second possible flow is the unhappy flow where B isn't satisfied with A's fee_range:

  • A sends closing_signed
  • B doesn't like the fee_range, sends a warning
  • A sends another closing_signed
  • B still doesn't like it, sends another warning
  • ...
  • A sends another closing_signed
  • B likes that fee_range, responds with closing_signed
  • A responds with closing_signed with the same fee as the previous message

We can make that strictly turn-based by having Bob send a new message (e.g. reject_closing_signed) to express his disagreement with the fee_range (instead of a warning), in which case we could have nonces in both messages.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think there is also the 2-message case where:

  • A sends fee_range [100, 200], fee_satoshis=150
  • B replies with fee_range [120, 180], fee_satoshis=150

The previous idea I was working with was to get rid of signatures in some cases, but it turns out to be complicated given the different message flows. I think a reject message is actually better, but A must only be able to send closing_signed after receipt, so it is essentially coop close v3.

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 don't mind doing a coop close v3 for taproot, with a more strictly specified protocol if it's easier than tacking it on to the existing one!

Copy link
Contributor

Choose a reason for hiding this comment

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

Right I dont mind either, I can draft some coop changes to add onto @Roasbeef taproot proposal

- otherwise:
- if it is the funder:
- if `fee_satoshis` is not in the overlap between the sent and received `fee_range`:
- MUST fail the channel
- otherwise:
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- MUST reply with the same `fee_satoshis`.
Copy link
Contributor

@Crypt-iQ Crypt-iQ Jun 8, 2022

Choose a reason for hiding this comment

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

Sorry to gravedig, but what is meant by overlap here? Is it the union of the two ranges or the intersection? If it's the intersection of the two ranges, then the statement a bit above if fee_satoshis matches its previously sent fee_range: could make this "overlap" statement redundant by changing the SHOULD to a MUST?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, overlap means intersection. We can make the SHOULD a MUST, that would make sense.

- otherwise (it is not the funder):
t-bast marked this conversation as resolved.
Show resolved Hide resolved
- if it has already sent a `closing_signed`:
- if `fee_satoshis` is not the same as the value it sent:
- MUST fail the channel
- otherwise:
- MUST propose a `fee_satoshis` in the overlap between received and (about-to-be) sent `fee_range`.
- otherwise, if `fee_satoshis` is not strictly between its last-sent `fee_satoshis`
and its previously-received `fee_satoshis`, UNLESS it has since reconnected:
- SHOULD fail the connection.
- if the receiver agrees with the fee:
- otherwise, if the receiver agrees with the fee:
- SHOULD reply with a `closing_signed` with the same `fee_satoshis` value.
- otherwise:
- MUST propose a value "strictly between" the received `fee_satoshis`
and its previously-sent `fee_satoshis`.
and its previously-sent `fee_satoshis`.

#### Rationale

The "strictly between" requirement ensures that forward
progress is made, even if only by a single satoshi at a time. To avoid
keeping state and to handle the corner case, where fees have shifted
When `fee_range` is not provided, the "strictly between" requirement ensures
that forward progress is made, even if only by a single satoshi at a time.
To avoid keeping state and to handle the corner case, where fees have shifted
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved
between disconnection and reconnection, negotiation restarts on reconnection.

Note there is limited risk if the closing transaction is
delayed, but it will be broadcast very soon; so there is usually no
reason to pay a premium for rapid processing.

Note that the non-funder is not paying the fee, so there is no reason for it
to have a maximum feerate. It may want a minimum feerate, however, to ensure
that the transaction propagates. It can always use CPFP later to speed up
confirmation if necessary, so that minimum should be low.

## Normal Operation

Once both nodes have exchanged `funding_locked` (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