mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +01:00
daemon: handle HTLC as per BOLT #2 algorithm.
From BOLT#2 (rev 8ee09e749990a11fa53bea03d5961cfde4be4616): Thus each node (conceptually) tracks: ... 3. Two *unacked changesets*: one for the local commitment (their proposals) and one for the remote (our proposals) 4. Two *acked changesets*: one for the local commitment (our proposals, acknowledged) and one for the remote (their proposals, acknowledged). (Note that an implementation MAY optimize this internally, for example, pre-applying the changesets in some cases). In our case, we apply the unacked changes immediately into staging_cstate, and save them in an unacked_changes array. That array gets applied to staging_cstate as soon as it's acked (we only allow one outstanding update_commit, so we only need one array). Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
257
daemon/packets.c
257
daemon/packets.c
@@ -83,14 +83,6 @@ static void queue_pkt(struct peer *peer, Pkt__PktCase type, const void *msg)
|
||||
queue_raw_pkt(peer, make_pkt(peer, type, msg), NULL, NULL);
|
||||
}
|
||||
|
||||
static void queue_pkt_with_ack(struct peer *peer, Pkt__PktCase type,
|
||||
const void *msg,
|
||||
void (*ack_cb)(struct peer *peer, void *arg),
|
||||
void *ack_arg)
|
||||
{
|
||||
queue_raw_pkt(peer, make_pkt(peer, type, msg), ack_cb, ack_arg);
|
||||
}
|
||||
|
||||
void queue_pkt_open(struct peer *peer, OpenChannel__AnchorOffer anchor)
|
||||
{
|
||||
OpenChannel *o = tal(peer, OpenChannel);
|
||||
@@ -178,20 +170,6 @@ void queue_pkt_open_complete(struct peer *peer)
|
||||
queue_pkt(peer, PKT__PKT_OPEN_COMPLETE, o);
|
||||
}
|
||||
|
||||
/* Once they ack, we can add it on our side. */
|
||||
static void add_our_htlc_ourside(struct peer *peer, void *arg)
|
||||
{
|
||||
struct channel_htlc *htlc = arg;
|
||||
|
||||
/* FIXME: must add even if can't pay fee any more! */
|
||||
if (!funding_add_htlc(peer->local.staging_cstate,
|
||||
htlc->msatoshis, &htlc->expiry,
|
||||
&htlc->rhash, htlc->id, OURS))
|
||||
fatal("FIXME: Failed to add htlc %"PRIu64" to self on ack",
|
||||
htlc->id);
|
||||
tal_free(htlc);
|
||||
}
|
||||
|
||||
void queue_pkt_htlc_add(struct peer *peer,
|
||||
const struct htlc_progress *htlc_prog)
|
||||
{
|
||||
@@ -208,31 +186,24 @@ void queue_pkt_htlc_add(struct peer *peer,
|
||||
u->route = tal(u, Routing);
|
||||
routing__init(u->route);
|
||||
|
||||
/* We're about to send this, so their side will have it from now on. */
|
||||
/* BOLT #2:
|
||||
*
|
||||
* The sending node MUST add the HTLC addition to the unacked
|
||||
* changeset for its remote commitment
|
||||
*/
|
||||
if (!funding_add_htlc(peer->remote.staging_cstate,
|
||||
htlc_prog->stage.add.htlc.msatoshis,
|
||||
&htlc_prog->stage.add.htlc.expiry,
|
||||
&htlc_prog->stage.add.htlc.rhash,
|
||||
htlc_prog->stage.add.htlc.id, THEIRS))
|
||||
fatal("Could not add HTLC?");
|
||||
add_unacked(&peer->remote, &htlc_prog->stage);
|
||||
|
||||
remote_changes_pending(peer);
|
||||
|
||||
peer_add_htlc_expiry(peer, &htlc_prog->stage.add.htlc.expiry);
|
||||
|
||||
their_commit_changed(peer);
|
||||
queue_pkt_with_ack(peer, PKT__PKT_UPDATE_ADD_HTLC, u,
|
||||
add_our_htlc_ourside,
|
||||
tal_dup(peer, struct channel_htlc,
|
||||
&htlc_prog->stage.add.htlc));
|
||||
}
|
||||
|
||||
/* Once they ack, we can fulfill it on our side. */
|
||||
static void fulfill_their_htlc_ourside(struct peer *peer, void *arg)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
n = funding_htlc_by_id(peer->local.staging_cstate, ptr2int(arg), THEIRS);
|
||||
assert(n != -1);
|
||||
funding_fulfill_htlc(peer->local.staging_cstate, n, THEIRS);
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_ADD_HTLC, u);
|
||||
}
|
||||
|
||||
void queue_pkt_htlc_fulfill(struct peer *peer,
|
||||
@@ -247,24 +218,19 @@ void queue_pkt_htlc_fulfill(struct peer *peer,
|
||||
f->id = htlc_prog->stage.fulfill.id;
|
||||
f->r = sha256_to_proto(f, &htlc_prog->stage.fulfill.r);
|
||||
|
||||
/* We're about to send this, so their side will have it from now on. */
|
||||
/* BOLT #2:
|
||||
*
|
||||
* The sending node MUST add the HTLC fulfill/fail to the
|
||||
* unacked changeset for its remote commitment
|
||||
*/
|
||||
n = funding_htlc_by_id(peer->remote.staging_cstate, f->id, OURS);
|
||||
assert(n != -1);
|
||||
funding_fulfill_htlc(peer->remote.staging_cstate, n, OURS);
|
||||
their_commit_changed(peer);
|
||||
add_unacked(&peer->remote, &htlc_prog->stage);
|
||||
|
||||
queue_pkt_with_ack(peer, PKT__PKT_UPDATE_FULFILL_HTLC, f,
|
||||
fulfill_their_htlc_ourside, int2ptr(f->id));
|
||||
}
|
||||
remote_changes_pending(peer);
|
||||
|
||||
/* Once they ack, we can fail it on our side. */
|
||||
static void fail_their_htlc_ourside(struct peer *peer, void *arg)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
n = funding_htlc_by_id(peer->local.staging_cstate, ptr2int(arg), THEIRS);
|
||||
assert(n != -1);
|
||||
funding_fail_htlc(peer->local.staging_cstate, n, THEIRS);
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_FULFILL_HTLC, f);
|
||||
}
|
||||
|
||||
void queue_pkt_htlc_fail(struct peer *peer,
|
||||
@@ -281,14 +247,18 @@ void queue_pkt_htlc_fail(struct peer *peer,
|
||||
f->reason = tal(f, FailReason);
|
||||
fail_reason__init(f->reason);
|
||||
|
||||
/* We're about to send this, so their side will have it from now on. */
|
||||
/* BOLT #2:
|
||||
*
|
||||
* The sending node MUST add the HTLC fulfill/fail to the
|
||||
* unacked changeset for its remote commitment
|
||||
*/
|
||||
n = funding_htlc_by_id(peer->remote.staging_cstate, f->id, OURS);
|
||||
assert(n != -1);
|
||||
funding_fail_htlc(peer->remote.staging_cstate, n, OURS);
|
||||
add_unacked(&peer->remote, &htlc_prog->stage);
|
||||
|
||||
their_commit_changed(peer);
|
||||
queue_pkt_with_ack(peer, PKT__PKT_UPDATE_FAIL_HTLC, f,
|
||||
fail_their_htlc_ourside, int2ptr(f->id));
|
||||
remote_changes_pending(peer);
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_FAIL_HTLC, f);
|
||||
}
|
||||
|
||||
/* OK, we're sending a signature for their pending changes. */
|
||||
@@ -300,6 +270,11 @@ void queue_pkt_commit(struct peer *peer)
|
||||
/* Create new commit info for this commit tx. */
|
||||
ci->prev = peer->remote.commit;
|
||||
ci->revocation_hash = peer->remote.next_revocation_hash;
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A sending node MUST apply all remote acked and unacked
|
||||
* changes except unacked fee changes to the remote commitment
|
||||
* before generating `sig`. */
|
||||
ci->cstate = copy_funding(ci, peer->remote.staging_cstate);
|
||||
ci->tx = create_commit_tx(ci,
|
||||
&peer->remote.finalkey,
|
||||
@@ -342,6 +317,51 @@ void queue_pkt_commit(struct peer *peer)
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_COMMIT, u);
|
||||
}
|
||||
|
||||
/* At revocation time, we apply the changeset to the other side. */
|
||||
static void apply_changeset(struct peer *peer,
|
||||
struct peer_visible_state *which,
|
||||
const union htlc_staging *changes,
|
||||
size_t num_changes)
|
||||
{
|
||||
size_t i;
|
||||
size_t n;
|
||||
|
||||
for (i = 0; i < num_changes; i++) {
|
||||
switch (changes[i].type) {
|
||||
case HTLC_ADD:
|
||||
n = funding_htlc_by_id(which->staging_cstate,
|
||||
changes[i].add.htlc.id, OURS);
|
||||
if (n != -1)
|
||||
fatal("Can't add duplicate HTLC id %"PRIu64,
|
||||
changes[i].add.htlc.id);
|
||||
if (!funding_add_htlc(which->staging_cstate,
|
||||
changes[i].add.htlc.msatoshis,
|
||||
&changes[i].add.htlc.expiry,
|
||||
&changes[i].add.htlc.rhash,
|
||||
changes[i].add.htlc.id, OURS))
|
||||
fatal("Adding HTLC failed");
|
||||
continue;
|
||||
case HTLC_FAIL:
|
||||
n = funding_htlc_by_id(which->staging_cstate,
|
||||
changes[i].fail.id, THEIRS);
|
||||
if (n == -1)
|
||||
fatal("Can't fail non-exisent HTLC id %"PRIu64,
|
||||
changes[i].fail.id);
|
||||
funding_fail_htlc(which->staging_cstate, n, THEIRS);
|
||||
continue;
|
||||
case HTLC_FULFILL:
|
||||
n = funding_htlc_by_id(which->staging_cstate,
|
||||
changes[i].fulfill.id, THEIRS);
|
||||
if (n == -1)
|
||||
fatal("Can't fulfill non-exisent HTLC id %"PRIu64,
|
||||
changes[i].fulfill.id);
|
||||
funding_fulfill_htlc(which->staging_cstate, n, THEIRS);
|
||||
continue;
|
||||
}
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
/* Send a preimage for the old commit tx. The one we've just committed to is
|
||||
* in peer->local.commit. */
|
||||
void queue_pkt_revocation(struct peer *peer)
|
||||
@@ -371,6 +391,22 @@ void queue_pkt_revocation(struct peer *peer)
|
||||
u->ack = peer_outgoing_ack(peer);
|
||||
|
||||
queue_pkt(peer, PKT__PKT_UPDATE_REVOCATION, u);
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* The node sending `update_revocation` MUST add the local unacked
|
||||
* changes to the set of remote acked changes.
|
||||
*/
|
||||
apply_changeset(peer, &peer->remote,
|
||||
peer->local.unacked_changes,
|
||||
tal_count(peer->local.unacked_changes));
|
||||
|
||||
if (tal_count(peer->local.unacked_changes))
|
||||
remote_changes_pending(peer);
|
||||
|
||||
/* Reset for next time. */
|
||||
tal_free(peer->local.unacked_changes);
|
||||
peer->local.unacked_changes = tal_arr(peer, union htlc_staging, 0);
|
||||
}
|
||||
|
||||
Pkt *pkt_err(struct peer *peer, const char *msg, ...)
|
||||
@@ -546,6 +582,8 @@ Pkt *accept_pkt_htlc_add(struct peer *peer, const Pkt *pkt)
|
||||
const UpdateAddHtlc *u = pkt->update_add_htlc;
|
||||
struct sha256 rhash;
|
||||
struct abs_locktime expiry;
|
||||
struct channel_htlc *htlc;
|
||||
union htlc_staging stage;
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
@@ -584,6 +622,13 @@ Pkt *accept_pkt_htlc_add(struct peer *peer, const Pkt *pkt)
|
||||
if (funding_htlc_by_id(peer->local.staging_cstate, u->id, THEIRS) != -1)
|
||||
return pkt_err(peer, "HTLC id %"PRIu64" clashes for us", u->id);
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* ...and the receiving node MUST add the HTLC addition to the
|
||||
* unacked changeset for its local commitment. */
|
||||
htlc = funding_add_htlc(peer->local.staging_cstate,
|
||||
u->amount_msat, &expiry, &rhash, u->id, THEIRS);
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A node MUST NOT offer `amount_msat` it cannot pay for in
|
||||
@@ -595,80 +640,79 @@ Pkt *accept_pkt_htlc_add(struct peer *peer, const Pkt *pkt)
|
||||
/* FIXME: This is wrong! We may have already added more txs to
|
||||
* them.staging_cstate, driving that fee up.
|
||||
* We should check against the last version they acknowledged. */
|
||||
if (!funding_add_htlc(peer->remote.staging_cstate,
|
||||
u->amount_msat, &expiry, &rhash, u->id, OURS))
|
||||
if (!htlc)
|
||||
return pkt_err(peer, "Cannot afford %"PRIu64" milli-satoshis"
|
||||
" in your commitment tx",
|
||||
u->amount_msat);
|
||||
|
||||
/* If we fail here, we've already changed them.staging_cstate, so
|
||||
* MUST terminate. */
|
||||
if (!funding_add_htlc(peer->local.staging_cstate,
|
||||
u->amount_msat, &expiry, &rhash, u->id, THEIRS))
|
||||
return pkt_err(peer, "Cannot afford %"PRIu64" milli-satoshis"
|
||||
" in our commitment tx",
|
||||
u->amount_msat);
|
||||
stage.add.add = HTLC_ADD;
|
||||
stage.add.htlc = *htlc;
|
||||
add_unacked(&peer->local, &stage);
|
||||
|
||||
peer_add_htlc_expiry(peer, &expiry);
|
||||
their_commit_changed(peer);
|
||||
|
||||
|
||||
/* FIXME: Fees must be sufficient. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static Pkt *find_commited_htlc(struct peer *peer, uint64_t id,
|
||||
size_t *n_us, size_t *n_them)
|
||||
static Pkt *find_commited_htlc(struct peer *peer, uint64_t id, size_t *n_local)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A node MUST check that `id` corresponds to an HTLC in its
|
||||
* current commitment transaction, and MUST fail the
|
||||
* connection if it does not.
|
||||
*/
|
||||
*n_us = funding_htlc_by_id(peer->local.commit->cstate, id, OURS);
|
||||
if (*n_us == -1)
|
||||
n = funding_htlc_by_id(peer->local.commit->cstate, id, OURS);
|
||||
if (n == -1)
|
||||
return pkt_err(peer, "Did not find HTLC %"PRIu64, id);
|
||||
|
||||
/* They must not fail/fulfill twice, so it should be in staging, too. */
|
||||
*n_us = funding_htlc_by_id(peer->local.staging_cstate, id, OURS);
|
||||
if (*n_us == -1)
|
||||
*n_local = funding_htlc_by_id(peer->local.staging_cstate, id, OURS);
|
||||
if (*n_local == -1)
|
||||
return pkt_err(peer, "Already removed HTLC %"PRIu64, id);
|
||||
|
||||
/* FIXME: Assert this... */
|
||||
/* Note: these should match. */
|
||||
*n_them = funding_htlc_by_id(peer->remote.staging_cstate, id, THEIRS);
|
||||
if (*n_them == -1)
|
||||
return pkt_err(peer, "Did not find your HTLC %"PRIu64, id);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Pkt *accept_pkt_htlc_fail(struct peer *peer, const Pkt *pkt)
|
||||
{
|
||||
const UpdateFailHtlc *f = pkt->update_fail_htlc;
|
||||
size_t n_us, n_them;
|
||||
size_t n_local;
|
||||
Pkt *err;
|
||||
union htlc_staging stage;
|
||||
|
||||
err = find_commited_htlc(peer, f->id, &n_us, &n_them);
|
||||
err = find_commited_htlc(peer, f->id, &n_local);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* FIXME: Save reason. */
|
||||
|
||||
funding_fail_htlc(peer->local.staging_cstate, n_us, OURS);
|
||||
funding_fail_htlc(peer->remote.staging_cstate, n_them, THEIRS);
|
||||
their_commit_changed(peer);
|
||||
funding_fail_htlc(peer->local.staging_cstate, n_local, OURS);
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* ... and the receiving node MUST add the HTLC fulfill/fail
|
||||
* to the unacked changeset for its local commitment.
|
||||
*/
|
||||
stage.fail.fail = HTLC_FAIL;
|
||||
stage.fail.id = f->id;
|
||||
add_unacked(&peer->local, &stage);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Pkt *accept_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt)
|
||||
{
|
||||
const UpdateFulfillHtlc *f = pkt->update_fulfill_htlc;
|
||||
size_t n_us, n_them;
|
||||
size_t n_local;
|
||||
struct sha256 r, rhash;
|
||||
Pkt *err;
|
||||
union htlc_staging stage;
|
||||
|
||||
err = find_commited_htlc(peer, f->id, &n_us, &n_them);
|
||||
err = find_commited_htlc(peer, f->id, &n_local);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -676,15 +720,20 @@ Pkt *accept_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt)
|
||||
proto_to_sha256(f->r, &r);
|
||||
sha256(&rhash, &r, sizeof(r));
|
||||
|
||||
if (!structeq(&rhash, &peer->local.staging_cstate->side[OURS].htlcs[n_us].rhash))
|
||||
if (!structeq(&rhash, &peer->local.staging_cstate->side[OURS].htlcs[n_local].rhash))
|
||||
return pkt_err(peer, "Invalid r for %"PRIu64, f->id);
|
||||
|
||||
/* Same ID must have same rhash */
|
||||
assert(structeq(&rhash, &peer->remote.staging_cstate->side[THEIRS].htlcs[n_them].rhash));
|
||||
/* BOLT #2:
|
||||
*
|
||||
* ... and the receiving node MUST add the HTLC fulfill/fail
|
||||
* to the unacked changeset for its local commitment.
|
||||
*/
|
||||
funding_fulfill_htlc(peer->local.staging_cstate, n_local, OURS);
|
||||
|
||||
funding_fulfill_htlc(peer->local.staging_cstate, n_us, OURS);
|
||||
funding_fulfill_htlc(peer->remote.staging_cstate, n_them, THEIRS);
|
||||
their_commit_changed(peer);
|
||||
stage.fulfill.fulfill = HTLC_FULFILL;
|
||||
stage.fulfill.id = f->id;
|
||||
stage.fulfill.r = r;
|
||||
add_unacked(&peer->local, &stage);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -697,6 +746,13 @@ Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt)
|
||||
/* Create new commit info for this commit tx. */
|
||||
ci->prev = peer->local.commit;
|
||||
ci->revocation_hash = peer->local.next_revocation_hash;
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* A receiving node MUST apply all local acked and unacked
|
||||
* changes except unacked fee changes to the local commitment
|
||||
*/
|
||||
/* (We already applied them to staging_cstate as we went) */
|
||||
ci->cstate = copy_funding(ci, peer->local.staging_cstate);
|
||||
ci->tx = create_commit_tx(ci,
|
||||
&peer->local.finalkey,
|
||||
@@ -716,7 +772,7 @@ Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt)
|
||||
*/
|
||||
if (ci->prev->cstate->changes == ci->cstate->changes)
|
||||
return pkt_err(peer, "Empty commit");
|
||||
|
||||
|
||||
err = check_and_save_commit_sig(peer, ci, c->sig);
|
||||
if (err)
|
||||
return err;
|
||||
@@ -744,6 +800,12 @@ Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt)
|
||||
{
|
||||
const UpdateRevocation *r = pkt->update_revocation;
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* The receiver of `update_revocation` MUST check that the
|
||||
* SHA256 hash of `revocation_preimage` matches the previous commitment
|
||||
* transaction, and MUST fail if it does not.
|
||||
*/
|
||||
/* FIXME: Save preimage in shachain too. */
|
||||
if (!check_preimage(r->revocation_preimage,
|
||||
&peer->remote.commit->prev->revocation_hash))
|
||||
@@ -762,6 +824,19 @@ Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt)
|
||||
proto_to_sha256(r->next_revocation_hash,
|
||||
&peer->remote.next_revocation_hash);
|
||||
|
||||
/* BOLT #2:
|
||||
*
|
||||
* The receiver of `update_revocation`... MUST add the remote
|
||||
* unacked changes to the set of local acked changes.
|
||||
*/
|
||||
apply_changeset(peer, &peer->local,
|
||||
peer->remote.unacked_changes,
|
||||
tal_count(peer->remote.unacked_changes));
|
||||
|
||||
/* Reset for next time. */
|
||||
tal_free(peer->remote.unacked_changes);
|
||||
peer->remote.unacked_changes = tal_arr(peer, union htlc_staging, 0);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user