From 097a8e72d18c30ec2311283c4a93b25e6cde4e65 Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Sun, 6 May 2018 13:32:01 +0000 Subject: [PATCH] channel_control: Forget if unconfirmed for a long time and we are fundee. We should forget this as it is a potential DoS if we remember every funding txid that an attacker gave in a `funding_created` but never broadcasted. --- lightningd/chaintopology.c | 1 + lightningd/chaintopology.h | 1 - lightningd/channel_control.c | 94 ++++++++++++++++++++++++++++++ lightningd/channel_control.h | 3 + lightningd/lightningd.c | 9 +++ lightningd/lightningd.h | 3 + lightningd/peer_htlcs.c | 2 +- lightningd/peer_htlcs.h | 2 + lightningd/test/run-find_my_path.c | 7 +++ 9 files changed, 120 insertions(+), 2 deletions(-) diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 986a5e522..031c5edcc 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* Mutual recursion via timer. */ diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index c32896cc5..26ee80afc 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -158,7 +158,6 @@ void begin_topology(struct chain_topology *topo); struct txlocator *locate_tx(const void *ctx, const struct chain_topology *topo, const struct bitcoin_txid *txid); -void notify_new_block(struct lightningd *ld, unsigned int height); void notify_feerate_change(struct lightningd *ld); #if DEVELOPER diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index 380d4b76c..7c5a79efe 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -1,6 +1,10 @@ +#include #include #include #include +#include +#include +#include #include #include #include @@ -327,3 +331,93 @@ bool channel_tell_funding_locked(struct lightningd *ld, return true; } + +/* Check if we are the fundee of this channel, the channel + * funding transaction is still not yet seen onchain, and + * it has been too long since the channel was first opened. + * If so, we should forget the channel. */ +static bool +is_fundee_should_forget(struct lightningd *ld, + struct channel *channel, + u32 block_height) +{ + u32 max_no_funding_tx = 2016; + + /* FIXME: we should be getting max_no_funding_tx + * from an lightningd option, which is why we get + * it as an argument. */ + (void) ld; + + /* BOLT #2: + * + * A non-funding node (fundee): + * - SHOULD forget the channel if it does not see the + * funding transaction after a reasonable timeout. + */ + + /* Only applies if we are fundee. */ + if (channel->funder == LOCAL) + return false; + + /* Does not apply if we already saw the funding tx. */ + if (channel->scid) + return false; + + /* Not even reached previous starting blocknum. + * (e.g. if --rescan option is used) */ + if (block_height < channel->first_blocknum) + return false; + + /* Timeout in blocks not yet reached. */ + if (block_height - channel->first_blocknum < max_no_funding_tx) + return false; + + /* Ah forget it! */ + return true; +} + +/* Notify all channels of new blocks. */ +void channel_notify_new_block(struct lightningd *ld, + u32 block_height) +{ + struct peer *peer; + struct channel *channel; + struct channel **to_forget = tal_arr(NULL, struct channel *, 0); + size_t i; + + list_for_each (&ld->peers, peer, list) { + list_for_each (&peer->channels, channel, list) + if (is_fundee_should_forget(ld, channel, block_height)) { + i = tal_count(to_forget); + tal_resize(&to_forget, i + 1); + to_forget[i] = channel; + } + } + + /* Need to forget in a separate loop, else the above + * nested loops may crash due to the last channel of + * a peer also deleting the peer, making the inner + * loop crash. + * list_for_each_safe does not work because it is not + * just the freeing of the channel that occurs, but the + * potential destruction of the peer that invalidates + * memory the inner loop is accessing. */ + for (i = 0; i < tal_count(to_forget); ++i) { + channel = to_forget[i]; + /* Report it first. */ + log_unusual(channel->log, + "Forgetting channel: " + "It has been %"PRIu32" blocks without the " + "funding transaction %s getting deeply " + "confirmed. " + "We are fundee and can forget channel without " + "loss of funds.", + block_height - channel->first_blocknum, + type_to_string(tmpctx, struct bitcoin_txid, + &channel->funding_txid)); + /* And forget it. */ + delete_channel(channel); + } + + tal_free(to_forget); +} diff --git a/lightningd/channel_control.h b/lightningd/channel_control.h index 27c0a037a..72928d46e 100644 --- a/lightningd/channel_control.h +++ b/lightningd/channel_control.h @@ -19,5 +19,8 @@ bool channel_tell_funding_locked(struct lightningd *ld, struct channel *channel, const struct bitcoin_txid *txid, u32 depth); +/* Notify channels of new blocks. */ +void channel_notify_new_block(struct lightningd *ld, + u32 block_height); #endif /* LIGHTNING_LIGHTNINGD_CHANNEL_CONTROL_H */ diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 8e7f8c7a1..bef71131e 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -292,6 +293,14 @@ static int io_poll_lightningd(struct pollfd *fds, nfds_t nfds, int timeout) return io_poll_debug(fds, nfds, timeout); } +void notify_new_block(struct lightningd *ld, + u32 block_height) +{ + /* Inform our subcomponents individually. */ + htlcs_notify_new_block(ld, block_height); + channel_notify_new_block(ld, block_height); +} + int main(int argc, char *argv[]) { struct lightningd *ld; diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 5069dd95d..812cb6945 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -192,4 +192,7 @@ struct backtrace_state *backtrace_state; /* Check we can run subdaemons, and check their versions */ void test_daemons(const struct lightningd *ld); +/* Notify lightningd about new blocks. */ +void notify_new_block(struct lightningd *ld, u32 block_height); + #endif /* LIGHTNING_LIGHTNINGD_LIGHTNINGD_H */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 3dbfc49ad..560f57626 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1514,7 +1514,7 @@ static u32 htlc_in_deadline(const struct lightningd *ld, return hin->cltv_expiry - (ld->config.cltv_expiry_delta + 1)/2; } -void notify_new_block(struct lightningd *ld, u32 height) +void htlcs_notify_new_block(struct lightningd *ld, u32 height) { bool removed; diff --git a/lightningd/peer_htlcs.h b/lightningd/peer_htlcs.h index 60892b0fa..3bc84b01b 100644 --- a/lightningd/peer_htlcs.h +++ b/lightningd/peer_htlcs.h @@ -57,4 +57,6 @@ void onchain_failed_our_htlc(const struct channel *channel, const char *why); void onchain_fulfilled_htlc(struct channel *channel, const struct preimage *preimage); + +void htlcs_notify_new_block(struct lightningd *ld, u32 height); #endif /* LIGHTNING_LIGHTNINGD_PEER_HTLCS_H */ diff --git a/lightningd/test/run-find_my_path.c b/lightningd/test/run-find_my_path.c index e58ce373e..dd123f81b 100644 --- a/lightningd/test/run-find_my_path.c +++ b/lightningd/test/run-find_my_path.c @@ -11,6 +11,10 @@ void activate_peers(struct lightningd *ld UNNEEDED) /* Generated stub for begin_topology */ void begin_topology(struct chain_topology *topo UNNEEDED) { fprintf(stderr, "begin_topology called!\n"); abort(); } +/* Generated stub for channel_notify_new_block */ +void channel_notify_new_block(struct lightningd *ld UNNEEDED, + u32 block_height UNNEEDED) +{ fprintf(stderr, "channel_notify_new_block called!\n"); abort(); } /* Generated stub for daemon_setup */ void daemon_setup(const char *argv0 UNNEEDED, void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, @@ -58,6 +62,9 @@ size_t hash_htlc_key(const struct htlc_key *htlc_key UNNEEDED) /* Generated stub for hsm_init */ void hsm_init(struct lightningd *ld UNNEEDED) { fprintf(stderr, "hsm_init called!\n"); abort(); } +/* Generated stub for htlcs_notify_new_block */ +void htlcs_notify_new_block(struct lightningd *ld UNNEEDED, u32 height UNNEEDED) +{ fprintf(stderr, "htlcs_notify_new_block called!\n"); abort(); } /* Generated stub for json_escape */ struct json_escaped *json_escape(const tal_t *ctx UNNEEDED, const char *str TAKES UNNEEDED) { fprintf(stderr, "json_escape called!\n"); abort(); }