From ada1eb51069a658d3c7bfe8b6e367b56dcf214aa Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sat, 1 Apr 2017 20:59:39 +1030 Subject: [PATCH] lightningd/channel.c: add callbacks for when HTLCs fully committed/removed. The three cases we care about only happen on specific transitions: 1. They can no longer spend our failed HTLC: we can fail the source now. 2. They are fully committed to their new HTLC htlc: we can forward now. 3. They can no longer timeout their fulfilled HTLC: the funds are ours. Signed-off-by: Rusty Russell --- lightningd/channel.c | 55 ++++++++++++++++++++++++++++++----- lightningd/channel.h | 25 +++++++++++++--- lightningd/channel/channel.c | 3 +- lightningd/test/run-channel.c | 26 ++++++++++------- 4 files changed, 86 insertions(+), 23 deletions(-) diff --git a/lightningd/channel.c b/lightningd/channel.c index 220ee50b8..461b28cca 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -619,11 +619,36 @@ static void htlc_incstate(struct channel *channel, } } +static void check_lockedin(const struct htlc *h, + struct peer *peer, + void (*oursfail)(struct peer *peer, + const struct htlc *htlc), + void (*theirslocked)(struct peer *peer, + const struct htlc *htlc), + void (*theirsfulfilled)(struct peer *peer, + const struct htlc *htlc)) +{ + /* If it was fulfilled, we handled it immediately. */ + if (h->state == RCVD_REMOVE_ACK_REVOCATION && !h->r) + oursfail(peer, h); + else if (h->state == RCVD_ADD_ACK_REVOCATION) + theirslocked(peer, h); + else if (h->state == RCVD_REMOVE_ACK_COMMIT && h->r) + theirsfulfilled(peer, h); +} + /* FIXME: Commit to storage when this happens. */ static bool change_htlcs(struct channel *channel, enum side sidechanged, const enum htlc_state *htlc_states, - size_t n_hstates) + size_t n_hstates, + struct peer *peer, + void (*oursfail)(struct peer *peer, + const struct htlc *htlc), + void (*theirslocked)(struct peer *peer, + const struct htlc *htlc), + void (*theirsfulfilled)(struct peer *peer, + const struct htlc *htlc)) { struct htlc_map_iter it; struct htlc *h; @@ -636,6 +661,10 @@ static bool change_htlcs(struct channel *channel, for (i = 0; i < n_hstates; i++) { if (h->state == htlc_states[i]) { htlc_incstate(channel, h, sidechanged); + check_lockedin(h, peer, + oursfail, + theirslocked, + theirsfulfilled); changed = true; } } @@ -651,10 +680,16 @@ bool channel_sent_commit(struct channel *channel) SENT_ADD_REVOCATION, SENT_REMOVE_HTLC }; status_trace("sent commit"); - return change_htlcs(channel, REMOTE, states, ARRAY_SIZE(states)); + return change_htlcs(channel, REMOTE, states, ARRAY_SIZE(states), + NULL, NULL, NULL, NULL); } -bool channel_rcvd_revoke_and_ack(struct channel *channel) +bool channel_rcvd_revoke_and_ack(struct channel *channel, + struct peer *peer, + void (*oursfail)(struct peer *peer, + const struct htlc *htlc), + void (*theirslocked)(struct peer *peer, + const struct htlc *htlc)) { const enum htlc_state states[] = { SENT_ADD_COMMIT, SENT_REMOVE_ACK_COMMIT, @@ -662,11 +697,15 @@ bool channel_rcvd_revoke_and_ack(struct channel *channel) SENT_REMOVE_COMMIT }; status_trace("received revoke_and_ack"); - return change_htlcs(channel, LOCAL, states, ARRAY_SIZE(states)); + return change_htlcs(channel, LOCAL, states, ARRAY_SIZE(states), + peer, oursfail, theirslocked, NULL); } /* FIXME: We can actually merge these two... */ -bool channel_rcvd_commit(struct channel *channel) +bool channel_rcvd_commit(struct channel *channel, + struct peer *peer, + void (*theirsfulfilled)(struct peer *peer, + const struct htlc *htlc)) { const enum htlc_state states[] = { RCVD_ADD_REVOCATION, RCVD_REMOVE_HTLC, @@ -674,7 +713,8 @@ bool channel_rcvd_commit(struct channel *channel) RCVD_REMOVE_REVOCATION }; status_trace("received commit"); - return change_htlcs(channel, LOCAL, states, ARRAY_SIZE(states)); + return change_htlcs(channel, LOCAL, states, ARRAY_SIZE(states), + peer, NULL, NULL, theirsfulfilled); } bool channel_sent_revoke_and_ack(struct channel *channel) @@ -684,7 +724,8 @@ bool channel_sent_revoke_and_ack(struct channel *channel) RCVD_ADD_COMMIT, RCVD_REMOVE_ACK_COMMIT }; status_trace("sent revoke_and_ack"); - return change_htlcs(channel, REMOTE, states, ARRAY_SIZE(states)); + return change_htlcs(channel, REMOTE, states, ARRAY_SIZE(states), + NULL, NULL, NULL, NULL); } static char *fmt_channel_view(const tal_t *ctx, const struct channel_view *view) diff --git a/lightningd/channel.h b/lightningd/channel.h index d40590c91..d02ab3f1d 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -10,6 +10,7 @@ #include #include +struct peer; struct signature; /* View from each side */ @@ -330,18 +331,34 @@ bool channel_sent_commit(struct channel *channel); /** * channel_rcvd_revoke_and_ack: accept ack on remote committed changes. * @channel: the channel + * @peer: argument to pass through to @ourhtlcfail & @theirhtlclocked + * @oursfail: callback for any unfilfilled htlcs which are now fully removed. + * @theirslocked: callback for any new htlcs which are now fully committed. * * This is where we commit to pending changes we've added; returns true if - * anything changed. */ -bool channel_rcvd_revoke_and_ack(struct channel *channel); + * anything changed. + */ +bool channel_rcvd_revoke_and_ack(struct channel *channel, + struct peer *peer, + void (*oursfail)(struct peer *peer, + const struct htlc *htlc), + void (*theirslocked)(struct peer *peer, + const struct htlc *htlc)); /** * channel_rcvd_commit: commit all local outstanding changes. * @channel: the channel + * @peer: argument to pass through to @theirsfulfilled + * @theirsfulfilled: they are irrevocably committed to removal of htlc. * * This is where we commit to pending changes we've added; returns true if - * anything changed. */ -bool channel_rcvd_commit(struct channel *channel); + * anything changed. @theirsfulfilled is called for any HTLC we fulfilled + * which they are irrevocably committed to, and is in our current commitment. + */ +bool channel_rcvd_commit(struct channel *channel, + struct peer *peer, + void (*theirsfulfilled)(struct peer *peer, + const struct htlc *htlc)); /** * channel_sent_revoke_and_ack: sent ack on local committed changes. diff --git a/lightningd/channel/channel.c b/lightningd/channel/channel.c index 9cafbb764..abc7210ec 100644 --- a/lightningd/channel/channel.c +++ b/lightningd/channel/channel.c @@ -369,7 +369,8 @@ static void handle_peer_commit_sig(struct peer *peer, const u8 *msg) const u8 **wscripts; size_t i; - if (!channel_rcvd_commit(peer->channel)) { + /* FIXME: Handle theirsfulfilled! */ + if (!channel_rcvd_commit(peer->channel, peer, NULL)) { /* BOLT #2: * * A node MUST NOT send a `commitment_signed` message which diff --git a/lightningd/test/run-channel.c b/lightningd/test/run-channel.c index 97979f618..c64adc917 100644 --- a/lightningd/test/run-channel.c +++ b/lightningd/test/run-channel.c @@ -81,6 +81,10 @@ static u64 feerates[] = { 9651936 }; +static void do_nothing(struct peer *peer, const struct htlc *htlc) +{ +} + /* BOLT #3: * * htlc 0 direction: remote->local @@ -150,11 +154,11 @@ static const struct htlc **include_htlcs(struct channel *channel, enum side side /* Now make HTLCs fully committed. */ channel_sent_commit(channel); - channel_rcvd_revoke_and_ack(channel); - channel_rcvd_commit(channel); + channel_rcvd_revoke_and_ack(channel, NULL, NULL, NULL); + channel_rcvd_commit(channel, NULL, NULL); channel_sent_revoke_and_ack(channel); channel_sent_commit(channel); - channel_rcvd_revoke_and_ack(channel); + channel_rcvd_revoke_and_ack(channel, NULL, NULL, do_nothing); return htlcs; } @@ -236,27 +240,27 @@ static void send_and_fulfill_htlc(struct channel *channel, if (sender == LOCAL) { /* Step through a complete cycle. */ channel_sent_commit(channel); - channel_rcvd_revoke_and_ack(channel); - channel_rcvd_commit(channel); + channel_rcvd_revoke_and_ack(channel, NULL, NULL, NULL); + channel_rcvd_commit(channel, NULL, NULL); channel_sent_revoke_and_ack(channel); assert(channel_fulfill_htlc(channel, REMOTE, 1337, &r) == CHANNEL_ERR_REMOVE_OK); - channel_rcvd_commit(channel); + channel_rcvd_commit(channel, NULL, NULL); channel_sent_revoke_and_ack(channel); channel_sent_commit(channel); - channel_rcvd_revoke_and_ack(channel); + channel_rcvd_revoke_and_ack(channel, NULL, NULL, NULL); assert(channel_get_htlc(channel, sender, 1337)->state == RCVD_REMOVE_ACK_REVOCATION); } else { - channel_rcvd_commit(channel); + channel_rcvd_commit(channel, NULL, NULL); channel_sent_revoke_and_ack(channel); channel_sent_commit(channel); - channel_rcvd_revoke_and_ack(channel); + channel_rcvd_revoke_and_ack(channel, NULL, NULL, do_nothing); assert(channel_fulfill_htlc(channel, LOCAL, 1337, &r) == CHANNEL_ERR_REMOVE_OK); channel_sent_commit(channel); - channel_rcvd_revoke_and_ack(channel); - channel_rcvd_commit(channel); + channel_rcvd_revoke_and_ack(channel, NULL, NULL, NULL); + channel_rcvd_commit(channel, NULL, do_nothing); channel_sent_revoke_and_ack(channel); assert(channel_get_htlc(channel, sender, 1337)->state == SENT_REMOVE_ACK_REVOCATION);