packets.c: queue_pkt_* only creates and sends packets.

Move other logic into caller: it grew this way because we used to have
a centralized "state" machine which knew nothing of these internal
details.  But now we want to re-queue packets on reconnect, we really
want these routines to be idempotent.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2016-08-18 14:23:45 +09:30
parent abf4182ef5
commit 6615db32c0
5 changed files with 308 additions and 273 deletions

View File

@@ -69,24 +69,10 @@ static void queue_pkt(struct peer *peer, Pkt__PktCase type, const void *msg)
queue_raw_pkt(peer, make_pkt(peer, type, msg));
}
static struct commit_info *new_commit_info(const tal_t *ctx)
{
struct commit_info *ci = talz(ctx, struct commit_info);
ci->unacked_changes = tal_arr(ci, union htlc_staging, 0);
ci->acked_changes = tal_arr(ci, union htlc_staging, 0);
return ci;
}
void queue_pkt_open(struct peer *peer, OpenChannel__AnchorOffer anchor)
{
OpenChannel *o = tal(peer, OpenChannel);
/* Set up out commit info now: rest gets done in setup_first_commit
* once anchor is established. */
peer->local.commit = new_commit_info(peer);
peer->local.commit->revocation_hash = peer->local.next_revocation_hash;
peer_get_revocation_hash(peer, 1, &peer->local.next_revocation_hash);
open_channel__init(o);
o->revocation_hash = sha256_to_proto(o, &peer->local.commit->revocation_hash);
o->next_revocation_hash = sha256_to_proto(o, &peer->local.next_revocation_hash);
@@ -120,14 +106,6 @@ void queue_pkt_anchor(struct peer *peer)
a->output_index = peer->anchor.index;
a->amount = peer->anchor.satoshis;
/* This shouldn't happen! */
if (!setup_first_commit(peer)) {
queue_pkt_err(peer,
pkt_err(peer,
"Own anchor has insufficient funds"));
return;
}
queue_pkt(peer, PKT__PKT_OPEN_ANCHOR, a);
}
@@ -137,16 +115,6 @@ void queue_pkt_open_commit_sig(struct peer *peer)
open_commit_sig__init(s);
log_debug_struct(peer->log, "Creating sig for %s",
struct bitcoin_tx, peer->remote.commit->tx);
log_add_struct(peer->log, " using key %s",
struct pubkey, &peer->local.commitkey);
peer->remote.commit->sig = tal(peer->remote.commit,
struct bitcoin_signature);
peer->remote.commit->sig->stype = SIGHASH_ALL;
peer_sign_theircommit(peer, peer->remote.commit->tx,
&peer->remote.commit->sig->sig);
s->sig = signature_to_proto(s, peer->dstate->secpctx,
&peer->remote.commit->sig->sig);
@@ -164,7 +132,6 @@ void queue_pkt_open_complete(struct peer *peer)
void queue_pkt_htlc_add(struct peer *peer, struct htlc *htlc)
{
UpdateAddHtlc *u = tal(peer, UpdateAddHtlc);
union htlc_staging stage;
update_add_htlc__init(u);
@@ -180,49 +147,16 @@ void queue_pkt_htlc_add(struct peer *peer, struct htlc *htlc)
0);
u->route->info.len = tal_count(u->route->info.data);
/* BOLT #2:
*
* The sending node MUST add the HTLC addition to the unacked
* changeset for its remote commitment
*/
if (!cstate_add_htlc(peer->remote.staging_cstate, htlc, OURS))
fatal("Could not add HTLC?");
stage.add.add = HTLC_ADD;
stage.add.htlc = htlc;
add_unacked(&peer->remote, &stage);
remote_changes_pending(peer);
queue_pkt(peer, PKT__PKT_UPDATE_ADD_HTLC, u);
}
void queue_pkt_htlc_fulfill(struct peer *peer, struct htlc *htlc,
const struct rval *r)
void queue_pkt_htlc_fulfill(struct peer *peer, struct htlc *htlc)
{
UpdateFulfillHtlc *f = tal(peer, UpdateFulfillHtlc);
union htlc_staging stage;
update_fulfill_htlc__init(f);
f->id = htlc->id;
f->r = rval_to_proto(f, r);
/* BOLT #2:
*
* The sending node MUST add the HTLC fulfill/fail to the
* unacked changeset for its remote commitment
*/
assert(cstate_htlc_by_id(peer->remote.staging_cstate, f->id, THEIRS)
== htlc);
cstate_fulfill_htlc(peer->remote.staging_cstate, htlc, THEIRS);
stage.fulfill.fulfill = HTLC_FULFILL;
stage.fulfill.htlc = htlc;
stage.fulfill.r = *r;
add_unacked(&peer->remote, &stage);
htlc_changestate(htlc, RCVD_ADD_ACK_REVOCATION, SENT_REMOVE_HTLC);
remote_changes_pending(peer);
f->r = rval_to_proto(f, htlc->r);
queue_pkt(peer, PKT__PKT_UPDATE_FULFILL_HTLC, f);
}
@@ -230,7 +164,6 @@ void queue_pkt_htlc_fulfill(struct peer *peer, struct htlc *htlc,
void queue_pkt_htlc_fail(struct peer *peer, struct htlc *htlc)
{
UpdateFailHtlc *f = tal(peer, UpdateFailHtlc);
union htlc_staging stage;
update_fail_htlc__init(f);
f->id = htlc->id;
@@ -239,218 +172,39 @@ void queue_pkt_htlc_fail(struct peer *peer, struct htlc *htlc)
f->reason = tal(f, FailReason);
fail_reason__init(f->reason);
/* BOLT #2:
*
* The sending node MUST add the HTLC fulfill/fail to the
* unacked changeset for its remote commitment
*/
assert(cstate_htlc_by_id(peer->remote.staging_cstate, f->id, THEIRS)
== htlc);
cstate_fail_htlc(peer->remote.staging_cstate, htlc, THEIRS);
stage.fail.fail = HTLC_FAIL;
stage.fail.htlc = htlc;
add_unacked(&peer->remote, &stage);
htlc_changestate(htlc, RCVD_ADD_ACK_REVOCATION, SENT_REMOVE_HTLC);
remote_changes_pending(peer);
queue_pkt(peer, PKT__PKT_UPDATE_FAIL_HTLC, f);
}
struct state_table {
enum htlc_state from, to;
};
static bool htlcs_changestate(struct peer *peer,
const struct state_table *table,
size_t n)
{
struct htlc_map_iter it;
struct htlc *h;
bool changed = false;
for (h = htlc_map_first(&peer->htlcs, &it);
h;
h = htlc_map_next(&peer->htlcs, &it)) {
size_t i;
for (i = 0; i < n; i++) {
if (h->state == table[i].from) {
htlc_changestate(h, table[i].from, table[i].to);
changed = true;
}
}
}
return changed;
}
/* OK, we're sending a signature for their pending changes. */
void queue_pkt_commit(struct peer *peer)
{
UpdateCommit *u = tal(peer, UpdateCommit);
struct commit_info *ci = new_commit_info(peer);
static const struct state_table changes[] = {
{ SENT_ADD_HTLC, SENT_ADD_COMMIT },
{ SENT_REMOVE_REVOCATION, SENT_REMOVE_ACK_COMMIT },
{ SENT_ADD_REVOCATION, SENT_ADD_ACK_COMMIT},
{ SENT_REMOVE_HTLC, SENT_REMOVE_COMMIT}
};
/* BOLT #2:
*
* A node MUST NOT send an `update_commit` message which does
* not include any updates.
*/
if (!htlcs_changestate(peer, changes, ARRAY_SIZE(changes)))
fatal("sent commit with no changes");
/* Create new commit info for this commit tx. */
ci->prev = peer->remote.commit;
ci->commit_num = ci->prev->commit_num + 1;
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_cstate(ci, peer->remote.staging_cstate);
ci->tx = create_commit_tx(ci, peer, &ci->revocation_hash,
ci->cstate, REMOTE, &ci->map);
bitcoin_txid(ci->tx, &ci->txid);
log_debug(peer->log, "Signing tx for %u/%u msatoshis, %zu/%zu htlcs",
ci->cstate->side[OURS].pay_msat,
ci->cstate->side[THEIRS].pay_msat,
tal_count(ci->cstate->side[OURS].htlcs),
tal_count(ci->cstate->side[THEIRS].htlcs));
log_add_struct(peer->log, " (txid %s)", struct sha256_double, &ci->txid);
/* BOLT #2:
*
* A node MUST NOT send an `update_commit` message which does
* not include any updates.
*/
assert(ci->prev->cstate->changes != ci->cstate->changes);
ci->sig = tal(ci, struct bitcoin_signature);
ci->sig->stype = SIGHASH_ALL;
peer_sign_theircommit(peer, ci->tx, &ci->sig->sig);
/* Switch to the new commitment. */
peer->remote.commit = ci;
/* Now send message */
update_commit__init(u);
u->sig = signature_to_proto(u, peer->dstate->secpctx, &ci->sig->sig);
u->sig = signature_to_proto(u, peer->dstate->secpctx,
&peer->remote.commit->sig->sig);
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,
enum channel_side side,
const union htlc_staging *changes,
size_t num_changes)
{
size_t i;
struct htlc *htlc;
for (i = 0; i < num_changes; i++) {
switch (changes[i].type) {
case HTLC_ADD:
htlc = cstate_htlc_by_id(which->staging_cstate,
changes[i].add.htlc->id, side);
if (htlc)
fatal("Can't add duplicate HTLC id %"PRIu64,
changes[i].add.htlc->id);
if (!cstate_add_htlc(which->staging_cstate,
changes[i].add.htlc,
side))
fatal("Adding HTLC to %s failed",
side == OURS ? "ours" : "theirs");
continue;
case HTLC_FAIL:
htlc = cstate_htlc_by_id(which->staging_cstate,
changes[i].fail.htlc->id,
!side);
if (!htlc)
fatal("Can't fail non-exisent HTLC id %"PRIu64,
changes[i].fail.htlc->id);
cstate_fail_htlc(which->staging_cstate, htlc, !side);
continue;
case HTLC_FULFILL:
htlc = cstate_htlc_by_id(which->staging_cstate,
changes[i].fulfill.htlc->id,
!side);
if (!htlc)
fatal("Can't fulfill non-exisent HTLC id %"PRIu64,
changes[i].fulfill.htlc->id);
cstate_fulfill_htlc(which->staging_cstate, htlc, !side);
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)
void queue_pkt_revocation(struct peer *peer,
const struct sha256 *preimage,
const struct sha256 *next_hash)
{
UpdateRevocation *u = tal(peer, UpdateRevocation);
struct commit_info *ci;
static const struct state_table changes[] = {
{ RCVD_ADD_ACK_COMMIT, SENT_ADD_ACK_REVOCATION },
{ RCVD_REMOVE_COMMIT, SENT_REMOVE_REVOCATION },
{ RCVD_ADD_COMMIT, SENT_ADD_REVOCATION },
{ RCVD_REMOVE_ACK_COMMIT, SENT_REMOVE_ACK_REVOCATION }
};
update_revocation__init(u);
assert(peer->local.commit);
ci = peer->local.commit->prev;
assert(ci);
assert(!ci->revocation_preimage);
/* We have their signature on the current one, right? */
assert(peer->local.commit->sig);
if (!htlcs_changestate(peer, changes, ARRAY_SIZE(changes)))
fatal("sent revoke with no changes");
ci->revocation_preimage = tal(ci, struct sha256);
peer_get_revocation_preimage(peer, ci->commit_num,
ci->revocation_preimage);
u->revocation_preimage = sha256_to_proto(u, ci->revocation_preimage);
u->next_revocation_hash = sha256_to_proto(u,
&peer->local.next_revocation_hash);
u->revocation_preimage
= sha256_to_proto(u, peer->local.commit->prev->revocation_preimage);
u->next_revocation_hash
= sha256_to_proto(u, &peer->local.next_revocation_hash);
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.
*/
/* Note: this means the unacked changes as of the commit we're
* revoking */
add_acked_changes(&peer->remote.commit->acked_changes, ci->unacked_changes);
apply_changeset(peer, &peer->remote, THEIRS,
ci->unacked_changes, tal_count(ci->unacked_changes));
if (tal_count(ci->unacked_changes))
remote_changes_pending(peer);
/* We should never look at this again. */
ci->unacked_changes = tal_free(ci->unacked_changes);
/* That revocation has committed us to changes in the current commitment.
* Any acked changes come from their commitment, so those are now committed
* by both of us.
*/
peer_both_committed_to(peer, ci->acked_changes, OURS);
}
Pkt *pkt_err(struct peer *peer, const char *msg, ...)