mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-17 19:03:42 +01:00
daemon: implement HTLC expiry.
We do the simplest thing: a timer goes off, and we check all HTLCs for one which has expired more than 30 seconds ago. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
1018823f97
commit
b76858c1a1
@ -163,7 +163,14 @@ Pkt *pkt_htlc_fulfill(const tal_t *ctx, const struct peer *peer,
|
||||
Pkt *pkt_htlc_timedout(const tal_t *ctx, const struct peer *peer,
|
||||
const struct htlc_progress *htlc_prog)
|
||||
{
|
||||
FIXME_STUB(peer);
|
||||
UpdateTimedoutHtlc *t = tal(ctx, UpdateTimedoutHtlc);
|
||||
|
||||
update_timedout_htlc__init(t);
|
||||
|
||||
t->revocation_hash = sha256_to_proto(t, &htlc_prog->our_revocation_hash);
|
||||
t->r_hash = sha256_to_proto(t, &htlc_prog->htlc->rhash);
|
||||
|
||||
return make_pkt(ctx, PKT__PKT_UPDATE_TIMEDOUT_HTLC, t);
|
||||
}
|
||||
|
||||
Pkt *pkt_htlc_routefail(const tal_t *ctx, const struct peer *peer,
|
||||
@ -448,6 +455,7 @@ Pkt *accept_pkt_htlc_update(const tal_t *ctx,
|
||||
/* Add the htlc to their side of channel. */
|
||||
funding_add_htlc(&cur->cstate->b, cur->htlc->msatoshis,
|
||||
&cur->htlc->expiry, &cur->htlc->rhash);
|
||||
peer_add_htlc_expiry(peer, &cur->htlc->expiry);
|
||||
|
||||
peer_get_revocation_hash(peer, peer->num_htlcs+1,
|
||||
&cur->our_revocation_hash);
|
||||
@ -504,6 +512,7 @@ Pkt *accept_pkt_htlc_routefail(const tal_t *ctx,
|
||||
" milli-satoshis", cur->htlc->msatoshis);
|
||||
}
|
||||
funding_remove_htlc(&cur->cstate->a, i);
|
||||
/* FIXME: Remove timer. */
|
||||
|
||||
peer_get_revocation_hash(peer, peer->num_htlcs+1,
|
||||
&cur->our_revocation_hash);
|
||||
@ -526,7 +535,57 @@ fail:
|
||||
Pkt *accept_pkt_htlc_timedout(const tal_t *ctx,
|
||||
struct peer *peer, const Pkt *pkt)
|
||||
{
|
||||
FIXME_STUB(peer);
|
||||
const UpdateTimedoutHtlc *t = pkt->update_timedout_htlc;
|
||||
struct htlc_progress *cur = tal(peer, struct htlc_progress);
|
||||
Pkt *err;
|
||||
size_t i;
|
||||
struct sha256 rhash;
|
||||
|
||||
proto_to_sha256(t->revocation_hash, &cur->their_revocation_hash);
|
||||
proto_to_sha256(t->r_hash, &rhash);
|
||||
|
||||
i = funding_find_htlc(&peer->cstate->a, &rhash);
|
||||
if (i == tal_count(peer->cstate->a.htlcs)) {
|
||||
err = pkt_err(ctx, "Unknown HTLC");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
cur->htlc = &peer->cstate->a.htlcs[i];
|
||||
|
||||
/* Do we agree it has timed out? */
|
||||
if (controlled_time().ts.tv_sec < abs_locktime_to_seconds(&cur->htlc->expiry)) {
|
||||
err = pkt_err(ctx, "HTLC has not yet expired!");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Removing it should not fail: we regain HTLC amount */
|
||||
cur->cstate = copy_funding(cur, peer->cstate);
|
||||
if (!funding_delta(peer->us.offer_anchor == CMD_OPEN_WITH_ANCHOR,
|
||||
peer->anchor.satoshis,
|
||||
0, -cur->htlc->msatoshis,
|
||||
&cur->cstate->a, &cur->cstate->b)) {
|
||||
fatal("Unexpected failure fulfilling HTLC of %"PRIu64
|
||||
" milli-satoshis", cur->htlc->msatoshis);
|
||||
}
|
||||
funding_remove_htlc(&cur->cstate->a, i);
|
||||
/* FIXME: Remove timer. */
|
||||
|
||||
peer_get_revocation_hash(peer, peer->num_htlcs+1,
|
||||
&cur->our_revocation_hash);
|
||||
|
||||
/* Now we create the commit tx pair. */
|
||||
make_commit_txs(cur, peer, &cur->our_revocation_hash,
|
||||
&cur->their_revocation_hash,
|
||||
cur->cstate,
|
||||
&cur->our_commit, &cur->their_commit);
|
||||
|
||||
assert(!peer->current_htlc);
|
||||
peer->current_htlc = cur;
|
||||
return NULL;
|
||||
|
||||
fail:
|
||||
tal_free(cur);
|
||||
return err;
|
||||
}
|
||||
|
||||
Pkt *accept_pkt_htlc_fulfill(const tal_t *ctx,
|
||||
|
@ -1070,6 +1070,64 @@ static void set_htlc_command(struct peer *peer,
|
||||
set_current_command(peer, cmd, peer->current_htlc, jsoncmd);
|
||||
}
|
||||
|
||||
/* FIXME: Keep a timeout for each peer, in case they're unresponsive. */
|
||||
|
||||
static void check_htlc_expiry(struct peer *peer, void *unused)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* Check their htlcs for expiry. */
|
||||
for (i = 0; i < tal_count(peer->cstate->b.htlcs); i++) {
|
||||
struct channel_htlc *htlc = &peer->cstate->b.htlcs[i];
|
||||
struct channel_state *cstate;
|
||||
|
||||
/* Not a seconds-based expiry? */
|
||||
if (!abs_locktime_is_seconds(&htlc->expiry))
|
||||
continue;
|
||||
|
||||
/* Not well-expired? */
|
||||
if (controlled_time().ts.tv_sec - 30
|
||||
< abs_locktime_to_seconds(&htlc->expiry))
|
||||
continue;
|
||||
|
||||
cstate = copy_funding(peer, peer->cstate);
|
||||
|
||||
/* This should never fail! */
|
||||
if (!funding_delta(peer->them.offer_anchor
|
||||
== CMD_OPEN_WITH_ANCHOR,
|
||||
peer->anchor.satoshis,
|
||||
0,
|
||||
-htlc->msatoshis,
|
||||
&cstate->b, &cstate->a)) {
|
||||
fatal("Unexpected failure expirint HTLC of %"PRIu64
|
||||
" milli-satoshis", htlc->msatoshis);
|
||||
}
|
||||
funding_remove_htlc(&cstate->b, i);
|
||||
|
||||
set_htlc_command(peer, cstate, NULL, htlc,
|
||||
CMD_SEND_HTLC_TIMEDOUT, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void htlc_expiry_timeout(struct peer *peer)
|
||||
{
|
||||
log_debug(peer->log, "Expiry timedout!");
|
||||
queue_cmd(peer, check_htlc_expiry, NULL);
|
||||
}
|
||||
|
||||
void peer_add_htlc_expiry(struct peer *peer,
|
||||
const struct abs_locktime *expiry)
|
||||
{
|
||||
time_t when;
|
||||
|
||||
/* Add 30 seconds to be sure peers agree on timeout. */
|
||||
when = abs_locktime_to_seconds(expiry) - controlled_time().ts.tv_sec;
|
||||
when += 30;
|
||||
|
||||
oneshot_timeout(peer->dstate, peer, when, htlc_expiry_timeout, peer);
|
||||
}
|
||||
|
||||
struct newhtlc {
|
||||
struct channel_htlc *htlc;
|
||||
struct command *jsoncmd;
|
||||
@ -1097,6 +1155,7 @@ static void do_newhtlc(struct peer *peer, struct newhtlc *newhtlc)
|
||||
/* Add the htlc to our side of channel. */
|
||||
funding_add_htlc(&cstate->a, newhtlc->htlc->msatoshis,
|
||||
&newhtlc->htlc->expiry, &newhtlc->htlc->rhash);
|
||||
peer_add_htlc_expiry(peer, &newhtlc->htlc->expiry);
|
||||
|
||||
set_htlc_command(peer, cstate, newhtlc->jsoncmd,
|
||||
&cstate->a.htlcs[tal_count(cstate->a.htlcs)-1],
|
||||
|
@ -132,4 +132,7 @@ void make_commit_txs(const tal_t *ctx,
|
||||
const struct channel_state *cstate,
|
||||
struct bitcoin_tx **ours, struct bitcoin_tx **theirs);
|
||||
|
||||
void peer_add_htlc_expiry(struct peer *peer,
|
||||
const struct abs_locktime *expiry);
|
||||
|
||||
#endif /* LIGHTNING_DAEMON_PEER_H */
|
||||
|
@ -122,6 +122,22 @@ $LCLI2 failhtlc $ID1 $RHASH
|
||||
# Back to how we were before.
|
||||
check_status 949999000 49000000 "" 0 1000000 ""
|
||||
|
||||
# Same again, but this time it expires.
|
||||
$LCLI1 newhtlc $ID2 10000000 $EXPIRY $RHASH
|
||||
|
||||
# Check channel status
|
||||
check_status 939999000 49000000 '{ "msatoshis" : 10000000, "expiry" : { "second" : '$EXPIRY' }, "rhash" : "'$RHASH'" } ' 0 1000000 ""
|
||||
|
||||
# Make sure node1 accepts the expiry packet.
|
||||
$LCLI1 dev-mocktime $(($EXPIRY))
|
||||
|
||||
# This should make node2 send it.
|
||||
$LCLI2 dev-mocktime $(($EXPIRY + 31))
|
||||
sleep 1
|
||||
|
||||
# Back to how we were before.
|
||||
check_status 949999000 49000000 "" 0 1000000 ""
|
||||
|
||||
sleep 1
|
||||
|
||||
$LCLI1 stop
|
||||
|
Loading…
Reference in New Issue
Block a user