Skip to content

Commit

Permalink
channeld: order htlcs by id before retransmission.
Browse files Browse the repository at this point in the history
We had one report of this, and then Eugene and Roasbeef of Lightning
Labs confirmed it; they saw misordered HTLCs on reconnection too.

Since we didn't enforce this when we receive HTLCs, we never noticed :(

Fixes: #3920
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Fixed: Protocol: fixed retransmission order of multiple new HTLCs (causing channel close with LND)
  • Loading branch information
rustyrussell authored and cdecker committed Oct 13, 2020
1 parent 7f62806 commit 324b846
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 3 deletions.
22 changes: 21 additions & 1 deletion channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/asort/asort.h>
#include <ccan/cast/cast.h>
#include <ccan/container_of/container_of.h>
#include <ccan/crypto/hkdf_sha256/hkdf_sha256.h>
Expand Down Expand Up @@ -2190,7 +2191,20 @@ static void send_fail_or_fulfill(struct peer *peer, const struct htlc *h)
sync_crypto_write(peer->pps, take(msg));
}

static void resend_commitment(struct peer *peer, const struct changed_htlc *last)
static int cmp_changed_htlc_id(const struct changed_htlc *a,
const struct changed_htlc *b,
void *unused)
{
/* ids can be the same (sender and receiver are indep) but in
* that case we don't care about order. */
if (a->id > b->id)
return 1;
else if (a->id < b->id)
return -1;
return 0;
}

static void resend_commitment(struct peer *peer, struct changed_htlc *last)
{
size_t i;
struct bitcoin_signature commit_sig, *htlc_sigs;
Expand All @@ -2204,6 +2218,12 @@ static void resend_commitment(struct peer *peer, const struct changed_htlc *last
channel_feerate(peer->channel, LOCAL),
channel_feerate(peer->channel, REMOTE));

/* Note that HTLCs must be *added* in order. Simplest thing to do
* is to sort them all into ascending ID order here (we could do
* this when we save them in channel_sending_commit, but older versions
* won't have them sorted in the db, so doing it here is better). */
asort(last, tal_count(last), cmp_changed_htlc_id, NULL);

/* BOLT #2:
*
* - if `next_commitment_number` is equal to the commitment
Expand Down
2 changes: 0 additions & 2 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -2193,7 +2193,6 @@ def test_dataloss_protection(node_factory, bitcoind):
assert (closetxid, "confirmed") in set([(o['txid'], o['status']) for o in l2.rpc.listfunds()['outputs']])


@unittest.skip("Broken")
@unittest.skipIf(not DEVELOPER, "needs dev_disconnect")
def test_restart_multi_htlc_rexmit(node_factory, bitcoind, executor):
# l1 disables commit timer once we send first htlc, dies on commit
Expand Down Expand Up @@ -2663,7 +2662,6 @@ def test_connection_timeout(node_factory):
l1.daemon.wait_for_log('conn timed out')


@unittest.skip("Broken")
@unittest.skipIf(not DEVELOPER, "needs --dev-disconnect")
def test_htlc_retransmit_order(node_factory, executor):
NUM_HTLCS=10
Expand Down

0 comments on commit 324b846

Please sign in to comment.