Why Nostr? What is Njump?
2023-06-09 13:01:17
in reply to

ZmnSCPxj [ARCHIVE] on Nostr: 📅 Original date posted:2020-10-23 📝 Original message: Good morning t-bast, > > ...

📅 Original date posted:2020-10-23
📝 Original message:
Good morning t-bast,


> > And in this case C earns.
>
> > Can C delay the refund to D to after the grace period even if D settled the HTLC quickly?
>
> Yes C earns, but D has misbehaved. As a final recipient, D isn't dependent on anyone downstream.
> An honest D should settle the HTLC before the `grace_period` ends. If D chooses to hold the HTLC
> for a while, then it's fair that he pays C for this.


Okay, now let us consider the case where the supposedly-delaying party is not the final destination.

So, suppose D indicates to C that it should fail the HTLC.
In this case, C cannot immediately propagate the `update_fail_htlc` upstream, since the latest commitment transaction for the C<->D channel still contains the HTLC.

In addition, our state machine is hand-over-hand, i.e. there is a small window where there are two valid commitment transactions.
What happens is we sign the next commitment transaction and *then* revoke the previous one.

So I think C can only safely propagate its own upstream `update_fail_htlc` once it receives the `revoke_and_ack` from D.

So the time measured for the grace period between C and D should be from C sending `update_add_htlc` to C receiving `revoke_and_ack` from D, in case the HTLC fails.
This is the time period that D is allowed to consume, and if it exceeds the grace period, it is penalized.

(In this situation, it is immaterial if D is the destination: C cannot know this fact.)

So let us diagram this better:

C D
|----update_add_htlc--->| ---
|---commitment_signed-->| ^
|<----revoke_and_ack----| |
|<--commitment_signed---| |
|-----revoke_and_ack--->| |
| | grace period
|<--update_fail_htlc----| |
|<--commitment_signed---| |
|-----revoke_and_ack--->| |
|---commitment_signed-->| v <--- grief point!
|<----revoke_and_ack----| ---

(somebody *else* better make sure my understanding of the state machine is correct!)

C can trivially grief D here, making it look like D is delaying, by delaying its own `commitment_signed` containing the *removal* of the HTLC.
D cannot send its own `revoke_and_ack` until it receives the signature for its own next commitment, as if it did so, it would lose the ability to close the channel unilaterally; it has to wait for C to send the `commitment_signed`.

Thus, it seems to me that C can grief D here.

The question is: does the above C-can-grief-D matter?

I think D does have a defense against C griefing in the above case:

* If the time between D->`update_fail_htlc`->C and the corresponding C->`commitment_signed`->D becomes too long:
* D drops the channel onchain.
* The dropped commitment tx still contains the HTLC, since it is the "previous" commitment that D happens to hold that has not yet had the `update_fail_htlc` committed.

If D performs the above, then C is forced to wait *even longer* (it has to wait out the HTLC timelock) before it can safely propagate the `update_fail_htlc`: D could be fooling with it and actually knows the preimage and claim it onchain, so C for its own safety *must* wait out the onchain timelock.

Does that make sense?
Does it sensibly protect against this griefing?
Is it too much of a punishment and could potentially hurt D more than it hurts C if C is a heavily-connected node that will not miss the channel while D has fewer channels and opened the C<->D channel in the first place?

--

For success case `update_fulfill_htlc`, I believe C can immediately propagate this back to its upstream since it can now.
Thus, in that case, we can stop the timer at the `update_fulfill_htlc`.

So at least for the *end point* of the grace period, I think the end point should be:

* If the HTLC failed:
* When both participants have sent `revoke_and_ack`.
* If the HTLC succeeded:
* When the downstream participant has sent `update_fulfill_htlc`.

For the *start point*, it seems the C->`commitment_signed`->D containing the HTLC would work as the start point.
In particular, it seems to me that C can also deliberately defer its own C->`revoke_and_ack`->D:

C D
|----update_add_htlc--->|
|---commitment_signed-->| ---
|<----revoke_and_ack----| ^
|<--commitment_signed---| |
|-----revoke_and_ack--->| | <--- grief point!
| | grace period
|<--update_fail_htlc----| |
|<--commitment_signed---| |
|-----revoke_and_ack--->| |
|---commitment_signed-->| v
|<----revoke_and_ack----| ---

(If D deliberately delays, then it is penalized, so we should consider the case where C attempts to trigger the reverse case).
D cannot safely fulfill the HTLC until after the previous commitment transactions of *both* sides have been revoked ("irrevocably committed" state).
So D can use the same defense, I think: if C is taking too long to send the `revoke_and_ack` pointed at above, it drops the channel onchain with the HTLC instantiated (which is why the *start time* has to be the C->`commitment_signed`->D that contains the new HTLC).

Thus the D grace period has two smaller grace periods that D imposes on C, using the threat of channel drop to protect against the C-side griefing.



Sorry for dropping into details already but so far this is the only griefing attack I can think of right now.



Regards,
ZmnSCPxj
Author Public Key
npub1g5zswf6y48f7fy90jf3tlcuwdmjn8znhzaa4vkmtxaeskca8hpss23ms3l