mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-01 09:40:19 +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:
parent
cf7a7a7273
commit
2bf43f1ebd
4 changed files with 207 additions and 105 deletions
255
daemon/packets.c
255
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);
|
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)
|
void queue_pkt_open(struct peer *peer, OpenChannel__AnchorOffer anchor)
|
||||||
{
|
{
|
||||||
OpenChannel *o = tal(peer, OpenChannel);
|
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);
|
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,
|
void queue_pkt_htlc_add(struct peer *peer,
|
||||||
const struct htlc_progress *htlc_prog)
|
const struct htlc_progress *htlc_prog)
|
||||||
{
|
{
|
||||||
|
@ -208,31 +186,24 @@ void queue_pkt_htlc_add(struct peer *peer,
|
||||||
u->route = tal(u, Routing);
|
u->route = tal(u, Routing);
|
||||||
routing__init(u->route);
|
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,
|
if (!funding_add_htlc(peer->remote.staging_cstate,
|
||||||
htlc_prog->stage.add.htlc.msatoshis,
|
htlc_prog->stage.add.htlc.msatoshis,
|
||||||
&htlc_prog->stage.add.htlc.expiry,
|
&htlc_prog->stage.add.htlc.expiry,
|
||||||
&htlc_prog->stage.add.htlc.rhash,
|
&htlc_prog->stage.add.htlc.rhash,
|
||||||
htlc_prog->stage.add.htlc.id, THEIRS))
|
htlc_prog->stage.add.htlc.id, THEIRS))
|
||||||
fatal("Could not add HTLC?");
|
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);
|
peer_add_htlc_expiry(peer, &htlc_prog->stage.add.htlc.expiry);
|
||||||
|
|
||||||
their_commit_changed(peer);
|
queue_pkt(peer, PKT__PKT_UPDATE_ADD_HTLC, u);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void queue_pkt_htlc_fulfill(struct peer *peer,
|
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->id = htlc_prog->stage.fulfill.id;
|
||||||
f->r = sha256_to_proto(f, &htlc_prog->stage.fulfill.r);
|
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);
|
n = funding_htlc_by_id(peer->remote.staging_cstate, f->id, OURS);
|
||||||
assert(n != -1);
|
assert(n != -1);
|
||||||
funding_fulfill_htlc(peer->remote.staging_cstate, n, OURS);
|
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,
|
remote_changes_pending(peer);
|
||||||
fulfill_their_htlc_ourside, int2ptr(f->id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Once they ack, we can fail it on our side. */
|
queue_pkt(peer, PKT__PKT_UPDATE_FULFILL_HTLC, f);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void queue_pkt_htlc_fail(struct peer *peer,
|
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);
|
f->reason = tal(f, FailReason);
|
||||||
fail_reason__init(f->reason);
|
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);
|
n = funding_htlc_by_id(peer->remote.staging_cstate, f->id, OURS);
|
||||||
assert(n != -1);
|
assert(n != -1);
|
||||||
funding_fail_htlc(peer->remote.staging_cstate, n, OURS);
|
funding_fail_htlc(peer->remote.staging_cstate, n, OURS);
|
||||||
|
add_unacked(&peer->remote, &htlc_prog->stage);
|
||||||
|
|
||||||
their_commit_changed(peer);
|
remote_changes_pending(peer);
|
||||||
queue_pkt_with_ack(peer, PKT__PKT_UPDATE_FAIL_HTLC, f,
|
queue_pkt(peer, PKT__PKT_UPDATE_FAIL_HTLC, f);
|
||||||
fail_their_htlc_ourside, int2ptr(f->id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* OK, we're sending a signature for their pending changes. */
|
/* 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. */
|
/* Create new commit info for this commit tx. */
|
||||||
ci->prev = peer->remote.commit;
|
ci->prev = peer->remote.commit;
|
||||||
ci->revocation_hash = peer->remote.next_revocation_hash;
|
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->cstate = copy_funding(ci, peer->remote.staging_cstate);
|
||||||
ci->tx = create_commit_tx(ci,
|
ci->tx = create_commit_tx(ci,
|
||||||
&peer->remote.finalkey,
|
&peer->remote.finalkey,
|
||||||
|
@ -342,6 +317,51 @@ void queue_pkt_commit(struct peer *peer)
|
||||||
queue_pkt(peer, PKT__PKT_UPDATE_COMMIT, u);
|
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
|
/* Send a preimage for the old commit tx. The one we've just committed to is
|
||||||
* in peer->local.commit. */
|
* in peer->local.commit. */
|
||||||
void queue_pkt_revocation(struct peer *peer)
|
void queue_pkt_revocation(struct peer *peer)
|
||||||
|
@ -371,6 +391,22 @@ void queue_pkt_revocation(struct peer *peer)
|
||||||
u->ack = peer_outgoing_ack(peer);
|
u->ack = peer_outgoing_ack(peer);
|
||||||
|
|
||||||
queue_pkt(peer, PKT__PKT_UPDATE_REVOCATION, u);
|
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, ...)
|
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;
|
const UpdateAddHtlc *u = pkt->update_add_htlc;
|
||||||
struct sha256 rhash;
|
struct sha256 rhash;
|
||||||
struct abs_locktime expiry;
|
struct abs_locktime expiry;
|
||||||
|
struct channel_htlc *htlc;
|
||||||
|
union htlc_staging stage;
|
||||||
|
|
||||||
/* BOLT #2:
|
/* 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)
|
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);
|
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:
|
/* BOLT #2:
|
||||||
*
|
*
|
||||||
* A node MUST NOT offer `amount_msat` it cannot pay for in
|
* 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
|
/* FIXME: This is wrong! We may have already added more txs to
|
||||||
* them.staging_cstate, driving that fee up.
|
* them.staging_cstate, driving that fee up.
|
||||||
* We should check against the last version they acknowledged. */
|
* We should check against the last version they acknowledged. */
|
||||||
if (!funding_add_htlc(peer->remote.staging_cstate,
|
if (!htlc)
|
||||||
u->amount_msat, &expiry, &rhash, u->id, OURS))
|
|
||||||
return pkt_err(peer, "Cannot afford %"PRIu64" milli-satoshis"
|
return pkt_err(peer, "Cannot afford %"PRIu64" milli-satoshis"
|
||||||
" in your commitment tx",
|
" in your commitment tx",
|
||||||
u->amount_msat);
|
u->amount_msat);
|
||||||
|
|
||||||
/* If we fail here, we've already changed them.staging_cstate, so
|
stage.add.add = HTLC_ADD;
|
||||||
* MUST terminate. */
|
stage.add.htlc = *htlc;
|
||||||
if (!funding_add_htlc(peer->local.staging_cstate,
|
add_unacked(&peer->local, &stage);
|
||||||
u->amount_msat, &expiry, &rhash, u->id, THEIRS))
|
|
||||||
return pkt_err(peer, "Cannot afford %"PRIu64" milli-satoshis"
|
|
||||||
" in our commitment tx",
|
|
||||||
u->amount_msat);
|
|
||||||
|
|
||||||
peer_add_htlc_expiry(peer, &expiry);
|
peer_add_htlc_expiry(peer, &expiry);
|
||||||
their_commit_changed(peer);
|
|
||||||
|
|
||||||
/* FIXME: Fees must be sufficient. */
|
/* FIXME: Fees must be sufficient. */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Pkt *find_commited_htlc(struct peer *peer, uint64_t id,
|
static Pkt *find_commited_htlc(struct peer *peer, uint64_t id, size_t *n_local)
|
||||||
size_t *n_us, size_t *n_them)
|
|
||||||
{
|
{
|
||||||
|
size_t n;
|
||||||
|
|
||||||
/* BOLT #2:
|
/* BOLT #2:
|
||||||
*
|
*
|
||||||
* A node MUST check that `id` corresponds to an HTLC in its
|
* A node MUST check that `id` corresponds to an HTLC in its
|
||||||
* current commitment transaction, and MUST fail the
|
* current commitment transaction, and MUST fail the
|
||||||
* connection if it does not.
|
* connection if it does not.
|
||||||
*/
|
*/
|
||||||
*n_us = funding_htlc_by_id(peer->local.commit->cstate, id, OURS);
|
n = funding_htlc_by_id(peer->local.commit->cstate, id, OURS);
|
||||||
if (*n_us == -1)
|
if (n == -1)
|
||||||
return pkt_err(peer, "Did not find HTLC %"PRIu64, id);
|
return pkt_err(peer, "Did not find HTLC %"PRIu64, id);
|
||||||
|
|
||||||
/* They must not fail/fulfill twice, so it should be in staging, too. */
|
/* 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);
|
*n_local = funding_htlc_by_id(peer->local.staging_cstate, id, OURS);
|
||||||
if (*n_us == -1)
|
if (*n_local == -1)
|
||||||
return pkt_err(peer, "Already removed HTLC %"PRIu64, id);
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *accept_pkt_htlc_fail(struct peer *peer, const Pkt *pkt)
|
Pkt *accept_pkt_htlc_fail(struct peer *peer, const Pkt *pkt)
|
||||||
{
|
{
|
||||||
const UpdateFailHtlc *f = pkt->update_fail_htlc;
|
const UpdateFailHtlc *f = pkt->update_fail_htlc;
|
||||||
size_t n_us, n_them;
|
size_t n_local;
|
||||||
Pkt *err;
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/* FIXME: Save reason. */
|
/* FIXME: Save reason. */
|
||||||
|
|
||||||
funding_fail_htlc(peer->local.staging_cstate, n_us, OURS);
|
funding_fail_htlc(peer->local.staging_cstate, n_local, OURS);
|
||||||
funding_fail_htlc(peer->remote.staging_cstate, n_them, THEIRS);
|
|
||||||
their_commit_changed(peer);
|
/* 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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Pkt *accept_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt)
|
Pkt *accept_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt)
|
||||||
{
|
{
|
||||||
const UpdateFulfillHtlc *f = pkt->update_fulfill_htlc;
|
const UpdateFulfillHtlc *f = pkt->update_fulfill_htlc;
|
||||||
size_t n_us, n_them;
|
size_t n_local;
|
||||||
struct sha256 r, rhash;
|
struct sha256 r, rhash;
|
||||||
Pkt *err;
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -676,15 +720,20 @@ Pkt *accept_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt)
|
||||||
proto_to_sha256(f->r, &r);
|
proto_to_sha256(f->r, &r);
|
||||||
sha256(&rhash, &r, sizeof(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);
|
return pkt_err(peer, "Invalid r for %"PRIu64, f->id);
|
||||||
|
|
||||||
/* Same ID must have same rhash */
|
/* BOLT #2:
|
||||||
assert(structeq(&rhash, &peer->remote.staging_cstate->side[THEIRS].htlcs[n_them].rhash));
|
*
|
||||||
|
* ... 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);
|
stage.fulfill.fulfill = HTLC_FULFILL;
|
||||||
funding_fulfill_htlc(peer->remote.staging_cstate, n_them, THEIRS);
|
stage.fulfill.id = f->id;
|
||||||
their_commit_changed(peer);
|
stage.fulfill.r = r;
|
||||||
|
add_unacked(&peer->local, &stage);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -697,6 +746,13 @@ Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt)
|
||||||
/* Create new commit info for this commit tx. */
|
/* Create new commit info for this commit tx. */
|
||||||
ci->prev = peer->local.commit;
|
ci->prev = peer->local.commit;
|
||||||
ci->revocation_hash = peer->local.next_revocation_hash;
|
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->cstate = copy_funding(ci, peer->local.staging_cstate);
|
||||||
ci->tx = create_commit_tx(ci,
|
ci->tx = create_commit_tx(ci,
|
||||||
&peer->local.finalkey,
|
&peer->local.finalkey,
|
||||||
|
@ -744,6 +800,12 @@ Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt)
|
||||||
{
|
{
|
||||||
const UpdateRevocation *r = pkt->update_revocation;
|
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. */
|
/* FIXME: Save preimage in shachain too. */
|
||||||
if (!check_preimage(r->revocation_preimage,
|
if (!check_preimage(r->revocation_preimage,
|
||||||
&peer->remote.commit->prev->revocation_hash))
|
&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,
|
proto_to_sha256(r->next_revocation_hash,
|
||||||
&peer->remote.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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -444,17 +444,17 @@ static void try_commit(struct peer *peer)
|
||||||
queue_cmd(peer, do_commit, (struct command *)NULL);
|
queue_cmd(peer, do_commit, (struct command *)NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void their_commit_changed(struct peer *peer)
|
void remote_changes_pending(struct peer *peer)
|
||||||
{
|
{
|
||||||
log_debug(peer->log, "their_commit_changed: changes=%u",
|
log_debug(peer->log, "remote_changes_pending: changes=%u",
|
||||||
peer->remote.staging_cstate->changes);
|
peer->remote.staging_cstate->changes);
|
||||||
if (!peer->commit_timer) {
|
if (!peer->commit_timer) {
|
||||||
log_debug(peer->log, "their_commit_changed: adding timer");
|
log_debug(peer->log, "remote_changes_pending: adding timer");
|
||||||
peer->commit_timer = new_reltimer(peer->dstate, peer,
|
peer->commit_timer = new_reltimer(peer->dstate, peer,
|
||||||
peer->dstate->config.commit_time,
|
peer->dstate->config.commit_time,
|
||||||
try_commit, peer);
|
try_commit, peer);
|
||||||
} else
|
} else
|
||||||
log_debug(peer->log, "their_commit_changed: timer already exists");
|
log_debug(peer->log, "remote_changes_pending: timer already exists");
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct peer *new_peer(struct lightningd_state *dstate,
|
static struct peer *new_peer(struct lightningd_state *dstate,
|
||||||
|
@ -1894,6 +1894,14 @@ const struct bitcoin_tx *bitcoin_anchor(struct peer *peer)
|
||||||
return peer->anchor.tx;
|
return peer->anchor.tx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_unacked(struct peer_visible_state *which,
|
||||||
|
const union htlc_staging *stage)
|
||||||
|
{
|
||||||
|
size_t n = tal_count(which->unacked_changes);
|
||||||
|
tal_resize(&which->unacked_changes, n+1);
|
||||||
|
which->unacked_changes[n] = *stage;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sets up the initial cstate and commit tx for both nodes: false if
|
/* Sets up the initial cstate and commit tx for both nodes: false if
|
||||||
* insufficient funds. */
|
* insufficient funds. */
|
||||||
bool setup_first_commit(struct peer *peer)
|
bool setup_first_commit(struct peer *peer)
|
||||||
|
@ -1944,6 +1952,10 @@ bool setup_first_commit(struct peer *peer)
|
||||||
|
|
||||||
peer->local.staging_cstate = copy_funding(peer, peer->local.commit->cstate);
|
peer->local.staging_cstate = copy_funding(peer, peer->local.commit->cstate);
|
||||||
peer->remote.staging_cstate = copy_funding(peer, peer->remote.commit->cstate);
|
peer->remote.staging_cstate = copy_funding(peer, peer->remote.commit->cstate);
|
||||||
|
|
||||||
|
peer->local.unacked_changes = tal_arr(peer, union htlc_staging, 0);
|
||||||
|
peer->remote.unacked_changes = tal_arr(peer, union htlc_staging, 0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2022,9 +2034,14 @@ static void json_getpeers(struct command *cmd,
|
||||||
|
|
||||||
/* Any changes since then? */
|
/* Any changes since then? */
|
||||||
if (p->local.staging_cstate->changes != last->changes)
|
if (p->local.staging_cstate->changes != last->changes)
|
||||||
json_add_num(response, "staged_changes",
|
json_add_num(response, "local_staged_changes",
|
||||||
p->local.staging_cstate->changes
|
p->local.staging_cstate->changes
|
||||||
- last->changes);
|
- last->changes);
|
||||||
|
if (p->remote.staging_cstate->changes
|
||||||
|
!= p->remote.commit->cstate->changes)
|
||||||
|
json_add_num(response, "remote_staged_changes",
|
||||||
|
p->remote.staging_cstate->changes
|
||||||
|
- p->remote.commit->cstate->changes);
|
||||||
json_object_end(response);
|
json_object_end(response);
|
||||||
}
|
}
|
||||||
json_array_end(response);
|
json_array_end(response);
|
||||||
|
|
|
@ -84,8 +84,11 @@ struct peer_visible_state {
|
||||||
struct sha256 next_revocation_hash;
|
struct sha256 next_revocation_hash;
|
||||||
/* Commit txs: last one is current. */
|
/* Commit txs: last one is current. */
|
||||||
struct commit_info *commit;
|
struct commit_info *commit;
|
||||||
|
|
||||||
/* cstate to generate next commitment tx. */
|
/* cstate to generate next commitment tx. */
|
||||||
struct channel_state *staging_cstate;
|
struct channel_state *staging_cstate;
|
||||||
|
/* unacked changes (already applied to staging_cstate) */
|
||||||
|
union htlc_staging *unacked_changes;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct htlc_progress {
|
struct htlc_progress {
|
||||||
|
@ -236,6 +239,13 @@ void set_peer_state(struct peer *peer, enum state newstate, const char *why);
|
||||||
/* Call this after commit changes, or revocation accepted/sent. */
|
/* Call this after commit changes, or revocation accepted/sent. */
|
||||||
void peer_check_if_cleared(struct peer *peer);
|
void peer_check_if_cleared(struct peer *peer);
|
||||||
|
|
||||||
|
/* Set up timer: we have something we can commit. */
|
||||||
|
void remote_changes_pending(struct peer *peer);
|
||||||
|
|
||||||
|
/* Add this unacked change */
|
||||||
|
void add_unacked(struct peer_visible_state *which,
|
||||||
|
const union htlc_staging *stage);
|
||||||
|
|
||||||
void peer_add_htlc_expiry(struct peer *peer,
|
void peer_add_htlc_expiry(struct peer *peer,
|
||||||
const struct abs_locktime *expiry);
|
const struct abs_locktime *expiry);
|
||||||
|
|
||||||
|
@ -244,8 +254,5 @@ struct bitcoin_tx *peer_create_close_tx(struct peer *peer, u64 fee);
|
||||||
uint64_t commit_tx_fee(const struct bitcoin_tx *commit,
|
uint64_t commit_tx_fee(const struct bitcoin_tx *commit,
|
||||||
uint64_t anchor_satoshis);
|
uint64_t anchor_satoshis);
|
||||||
|
|
||||||
/* We have something pending for them. */
|
|
||||||
void their_commit_changed(struct peer *peer);
|
|
||||||
|
|
||||||
bool resolve_one_htlc(struct peer *peer, u64 id, const struct sha256 *preimage);
|
bool resolve_one_htlc(struct peer *peer, u64 id, const struct sha256 *preimage);
|
||||||
#endif /* LIGHTNING_DAEMON_PEER_H */
|
#endif /* LIGHTNING_DAEMON_PEER_H */
|
||||||
|
|
|
@ -149,10 +149,11 @@ check_status()
|
||||||
check_staged()
|
check_staged()
|
||||||
{
|
{
|
||||||
lcli="$1"
|
lcli="$1"
|
||||||
num_htlcs="$2"
|
what="$2"
|
||||||
|
num_htlcs="$3"
|
||||||
|
|
||||||
if check "$lcli getpeers | tr -s '\012\011\" ' ' ' | $FGREP 'staged_changes : '$num_htlcs"; then :; else
|
if check "$lcli getpeers | tr -s '\012\011\" ' ' ' | $FGREP ${what}_'staged_changes : '$num_htlcs"; then :; else
|
||||||
echo Cannot find $lcli output: '"staged_changes" : '$num_htlcs >&2
|
echo Cannot find $lcli output: '"'${what}_'staged_changes" : '$num_htlcs >&2
|
||||||
$lcli getpeers | tr -s '\012\011 ' ' ' >&2
|
$lcli getpeers | tr -s '\012\011 ' ' ' >&2
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
@ -351,15 +352,17 @@ lcli1 newhtlc $ID2 $HTLC_AMOUNT $EXPIRY $RHASH
|
||||||
if [ -n "$MANUALCOMMIT" ]; then
|
if [ -n "$MANUALCOMMIT" ]; then
|
||||||
# Nothing should have changed!
|
# Nothing should have changed!
|
||||||
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE ""
|
check_status $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE ""
|
||||||
# But 2 should register a staged htlc.
|
# But they should register a staged htlc.
|
||||||
check_staged lcli2 1
|
check_staged lcli2 local 1
|
||||||
|
check_staged lcli1 remote 1
|
||||||
|
|
||||||
# Now commit it.
|
# Now commit it.
|
||||||
lcli1 commit $ID2
|
lcli1 commit $ID2
|
||||||
|
|
||||||
# Node 1 hasn't got it committed, but node2 should have told it to stage.
|
# Node 1 hasn't got it committed, but node2 should have told it to stage.
|
||||||
check_status_single lcli1 $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE ""
|
check_status_single lcli1 $A_AMOUNT $A_FEE "" $B_AMOUNT $B_FEE ""
|
||||||
check_staged lcli1 1
|
check_staged lcli1 local 1
|
||||||
|
check_staged lcli2 remote 1
|
||||||
|
|
||||||
# Check channel status
|
# Check channel status
|
||||||
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT))
|
A_AMOUNT=$(($A_AMOUNT - $EXTRA_FEE - $HTLC_AMOUNT))
|
||||||
|
|
Loading…
Add table
Reference in a new issue