Skip to content

fix: drop deferred peer TLC updates when the peer disconnects - #1700

Open
Alephant6 wants to merge 1 commit into
nervosnetwork:developfrom
Alephant6:fix/deferred-peer-updates-on-disconnect
Open

Alephant6 wants to merge 1 commit into
nervosnetwork:developfrom
Alephant6:fix/deferred-peer-updates-on-disconnect

Conversation

@Alephant6

Copy link
Copy Markdown
Contributor

Fixes #1689.

Problem

During a dual-owed replay (#1111), a channel defers the peer's TLC updates until the peer's next CommitmentSigned. The queue isn't cleared on disconnect, so after a reconnect the peer replays the same updates and the queue holds them twice. The flush fails on the duplicate and drops the CommitmentSigned, which leaves the commitment numbers out of step: a BadSignature force close, or an ack that never comes.

Change

Clear defer_peer_tlc_updates and deferred_peer_tlc_updates in on_peer_disconnected. The peer replays what was queued, and both fields are already skip_store, so a reconnect now behaves like a restart.

Tests

  • New test_deferred_peer_tlc_updates_do_not_outlive_the_connection: fails on develop (the payment never completes) and passes with this change (5/5).
  • cargo fmt, both native clippy runs with -D warnings, typos, make check-migrate, the fnn lib suite (rocksdb and sqlite) and test_node_restart pass.
  • One lib test, mpp::test_mpp_waiting_ack_settlement_hands_off_to_durable_retry_queue, also fails on develop since fix: scope invoice settlement to local payments #1672; it is unrelated.
  • Randomized schedules (a harness outside this PR): 18 of 6,000 failed on develop, none with this change.

I'm assuming the peer always replays the updates queued here. Is that right?

While a channel replays a CommitmentSigned and a RevokeAndAck that the peer
is still owed (the dual-owed case of nervosnetwork#1111), it defers the TLC updates the
peer sends and flushes them right before it verifies the peer's next
CommitmentSigned. The deferral flag and the queue are not tied to the
connection: if the connection drops before that CommitmentSigned arrives,
both stay as they are. After the reconnect the peer replays every update it
has not had acknowledged, including the ones already queued, so the queue
holds the same update twice. The flush applies the first copy and fails on
the second ("Received tlc id .. is not the expected next id .." for an
AddTlc, "TLC is already removed" for a RemoveTlc), and the error is returned
out of handle_commitment_signed_peer_message, which drops the
CommitmentSigned the flush was done for.

The peer then never gets a RevokeAndAck for that CommitmentSigned, and the
RevokeAndAck that follows it is processed on its own. The two sides end up
with their commitment numbers out of step, which later shows up as
Musig2VerifyError(BadSignature) and a force close, or as a side that waits
for an ack forever.

Clear the flag and the queue when the peer disconnects. The peer replays what
was queued, so nothing is lost.

Add a test that queues an update, loses the connection together with the
CommitmentSigned behind it, and reconnects: on develop the payment never
completes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deferred peer TLC updates survive a disconnect: a replayed update is queued twice and the CommitmentSigned behind it is dropped

1 participant