Browse Source

peer: explicitly store the previous revocation hash when sending new update.

We want to stop keeping old commitment information (except the minimal
txid to commitment-number mapping).  One place we currently use it is
after sending a commitment signature, and before we've received the
revocation for the old commitment.  For this duration, there are two
valid commitment transactions.

So we store "their_prev_revocation_hash" explicitly for this duration.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
ppa-0.6.1
Rusty Russell 9 years ago
parent
commit
4319f3ac70
  1. 26
      daemon/packets.c
  2. 3
      daemon/packets.h
  3. 158
      daemon/peer.c
  4. 5
      daemon/peer.h

26
daemon/packets.c

@ -200,8 +200,7 @@ void queue_pkt_revocation(struct peer *peer,
update_revocation__init(u); update_revocation__init(u);
u->revocation_preimage u->revocation_preimage = sha256_to_proto(u, preimage);
= sha256_to_proto(u, peer->local.commit->prev->revocation_preimage);
u->next_revocation_hash u->next_revocation_hash
= sha256_to_proto(u, &peer->local.next_revocation_hash); = sha256_to_proto(u, &peer->local.next_revocation_hash);
@ -468,15 +467,13 @@ Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt,
return NULL; return NULL;
} }
Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt, Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt)
struct commit_info *ci)
{ {
const UpdateRevocation *r = pkt->update_revocation; const UpdateRevocation *r = pkt->update_revocation;
struct sha256 h; struct sha256 h, preimage;
assert(!ci->revocation_preimage); assert(peer->their_prev_revocation_hash);
ci->revocation_preimage = tal(ci, struct sha256); proto_to_sha256(r->revocation_preimage, &preimage);
proto_to_sha256(r->revocation_preimage, ci->revocation_preimage);
/* BOLT #2: /* BOLT #2:
* *
@ -484,16 +481,21 @@ Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt,
* SHA256 hash of `revocation_preimage` matches the previous commitment * SHA256 hash of `revocation_preimage` matches the previous commitment
* transaction, and MUST fail if it does not. * transaction, and MUST fail if it does not.
*/ */
sha256(&h, ci->revocation_preimage, sizeof(*ci->revocation_preimage)); sha256(&h, &preimage, sizeof(preimage));
if (!structeq(&h, &ci->revocation_hash)) if (!structeq(&h, peer->their_prev_revocation_hash))
return pkt_err(peer, "complete preimage incorrect"); return pkt_err(peer, "complete preimage incorrect");
// save revocation preimages in shachain // save revocation preimages in shachain
if (!shachain_add_hash(&peer->their_preimages, if (!shachain_add_hash(&peer->their_preimages,
0xFFFFFFFFFFFFFFFFL - ci->commit_num, 0xFFFFFFFFFFFFFFFFL
ci->revocation_preimage)) - (peer->remote.commit->commit_num - 1),
&preimage))
return pkt_err(peer, "preimage not next in shachain"); return pkt_err(peer, "preimage not next in shachain");
/* Clear the previous revocation hash. */
peer->their_prev_revocation_hash
= tal_free(peer->their_prev_revocation_hash);
/* Save next revocation hash. */ /* Save next revocation hash. */
proto_to_sha256(r->next_revocation_hash, proto_to_sha256(r->next_revocation_hash,
&peer->remote.next_revocation_hash); &peer->remote.next_revocation_hash);

3
daemon/packets.h

@ -51,8 +51,7 @@ Pkt *accept_pkt_update_accept(struct peer *peer, const Pkt *pkt);
Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt, Pkt *accept_pkt_commit(struct peer *peer, const Pkt *pkt,
struct bitcoin_signature *sig); struct bitcoin_signature *sig);
Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt, Pkt *accept_pkt_revocation(struct peer *peer, const Pkt *pkt);
struct commit_info *ci);
Pkt *accept_pkt_close_clearing(struct peer *peer, const Pkt *pkt); Pkt *accept_pkt_close_clearing(struct peer *peer, const Pkt *pkt);

158
daemon/peer.c

@ -740,6 +740,7 @@ static bool closing_pkt_in(struct peer *peer, const Pkt *pkt)
static Pkt *handle_pkt_commit(struct peer *peer, const Pkt *pkt) static Pkt *handle_pkt_commit(struct peer *peer, const Pkt *pkt)
{ {
Pkt *err; Pkt *err;
struct sha256 preimage;
struct commit_info *ci = new_commit_info(peer); struct commit_info *ci = new_commit_info(peer);
/* FIXME: We can actually merge these two... */ /* FIXME: We can actually merge these two... */
static const struct state_table commit_changes[] = { static const struct state_table commit_changes[] = {
@ -805,27 +806,23 @@ static Pkt *handle_pkt_commit(struct peer *peer, const Pkt *pkt)
peer->their_commitsigs++; peer->their_commitsigs++;
/* Now, send the revocation. */ /* Now, send the revocation. */
ci = peer->local.commit->prev;
assert(ci);
assert(!ci->revocation_preimage);
/* We have their signature on the current one, right? */ /* We have their signature on the current one, right? */
assert(peer->local.commit->sig); assert(peer->local.commit->sig);
assert(peer->local.commit->commit_num > 0);
if (!htlcs_changestate(peer, revocation_changes, if (!htlcs_changestate(peer, revocation_changes,
ARRAY_SIZE(revocation_changes))) ARRAY_SIZE(revocation_changes)))
fatal("sent revoke with no changes"); fatal("sent revoke with no changes");
ci->revocation_preimage = tal(ci, struct sha256); peer_get_revocation_preimage(peer, peer->local.commit->commit_num - 1,
peer_get_revocation_preimage(peer, ci->commit_num, &preimage);
ci->revocation_preimage);
/* Fire off timer if this ack caused new changes */ /* Fire off timer if this ack caused new changes */
if (peer_uncommitted_changes(peer)) if (peer_uncommitted_changes(peer))
remote_changes_pending(peer); remote_changes_pending(peer);
queue_pkt_revocation(peer, ci->revocation_preimage, queue_pkt_revocation(peer, &preimage, &peer->local.next_revocation_hash);
&peer->local.next_revocation_hash);
return NULL; return NULL;
} }
@ -899,7 +896,6 @@ static Pkt *handle_pkt_htlc_fulfill(struct peer *peer, const Pkt *pkt)
static Pkt *handle_pkt_revocation(struct peer *peer, const Pkt *pkt) static Pkt *handle_pkt_revocation(struct peer *peer, const Pkt *pkt)
{ {
struct commit_info *ci = peer->remote.commit->prev;
Pkt *err; Pkt *err;
static const struct state_table changes[] = { static const struct state_table changes[] = {
{ SENT_ADD_COMMIT, RCVD_ADD_REVOCATION }, { SENT_ADD_COMMIT, RCVD_ADD_REVOCATION },
@ -908,7 +904,7 @@ static Pkt *handle_pkt_revocation(struct peer *peer, const Pkt *pkt)
{ SENT_REMOVE_COMMIT, RCVD_REMOVE_REVOCATION } { SENT_REMOVE_COMMIT, RCVD_REMOVE_REVOCATION }
}; };
err = accept_pkt_revocation(peer, pkt, ci); err = accept_pkt_revocation(peer, pkt);
if (err) if (err)
return err; return err;
@ -1546,6 +1542,11 @@ static void do_commit(struct peer *peer, struct command *jsoncmd)
peer->commit_jsoncmd = jsoncmd; peer->commit_jsoncmd = jsoncmd;
ci = new_commit_info(peer); ci = new_commit_info(peer);
assert(!peer->their_prev_revocation_hash);
peer->their_prev_revocation_hash
= tal_dup(peer, struct sha256,
&peer->remote.commit->revocation_hash);
/* BOLT #2: /* BOLT #2:
* *
* A node MUST NOT send an `update_commit` message which does * A node MUST NOT send an `update_commit` message which does
@ -1653,6 +1654,7 @@ static struct peer *new_peer(struct lightningd_state *dstate,
peer->closing_onchain.ci = NULL; peer->closing_onchain.ci = NULL;
peer->commit_timer = NULL; peer->commit_timer = NULL;
peer->nc = NULL; peer->nc = NULL;
peer->their_prev_revocation_hash = NULL;
/* Make it different from other node (to catch bugs!), but a /* Make it different from other node (to catch bugs!), but a
* round number for simple eyeballing. */ * round number for simple eyeballing. */
peer->htlc_id_counter = pseudorand(1ULL << 32) * 1000; peer->htlc_id_counter = pseudorand(1ULL << 32) * 1000;
@ -2632,11 +2634,22 @@ static void resolve_our_unilateral(struct peer *peer,
* Similarly, when node A sees a *commitment tx* from B: * Similarly, when node A sees a *commitment tx* from B:
*/ */
static void resolve_their_unilateral(struct peer *peer, static void resolve_their_unilateral(struct peer *peer,
const struct bitcoin_tx *tx) const struct bitcoin_tx *tx,
const struct sha256_double *txid,
struct htlc **htlcs,
const u8 **wscripts,
int to_us_idx, int to_them_idx)
{ {
const struct commit_info *ci = peer->closing_onchain.ci; const struct commit_info *ci;
size_t num_ours, num_theirs; size_t num_ours, num_theirs;
/* FIXME: Remove closing_onchain.ci */
if (structeq(&peer->remote.commit->txid, txid))
peer->closing_onchain.ci = peer->remote.commit;
else
peer->closing_onchain.ci = peer->remote.commit->prev;
ci = peer->closing_onchain.ci;
peer->closing_onchain.resolved peer->closing_onchain.resolved
= tal_arrz(tx, const struct bitcoin_tx *, tal_count(ci->map)); = tal_arrz(tx, const struct bitcoin_tx *, tal_count(ci->map));
@ -2761,43 +2774,19 @@ static bool find_their_old_tx(struct peer *peer,
return false; return false;
} }
static bool resolve_their_steal(struct peer *peer, static void resolve_their_steal(struct peer *peer,
const struct bitcoin_tx *tx) const struct bitcoin_tx *tx,
const struct sha256_double *txid,
const struct sha256 *revocation_preimage,
struct htlc **htlcs,
const u8 **wscripts,
int to_us_idx, int to_them_idx)
{ {
u64 commit_num; int i, n;
struct sha256_double txid;
struct sha256 revocation_preimage, rhash;
int to_us_idx, to_them_idx, i, n;
struct htlc **htlcs;
const u8 **wscripts;
struct bitcoin_tx *steal_tx; struct bitcoin_tx *steal_tx;
size_t wsize = 0; size_t wsize = 0;
u64 input_total = 0, fee; u64 input_total = 0, fee;
bitcoin_txid(tx, &txid);
if (!find_their_old_tx(peer, &txid, &commit_num)) {
log_broken_struct(peer->log, "Could not find revoked txid %s",
struct sha256_double, &txid);
return false;
}
if (!shachain_get_hash(&peer->their_preimages,
0xFFFFFFFFFFFFFFFFL - commit_num,
&revocation_preimage)) {
log_broken(peer->log, "Could not get shachain for %"PRIu64,
commit_num);
return false;
}
sha256(&rhash, &revocation_preimage, sizeof(revocation_preimage));
htlcs = tx_map_outputs(peer, &rhash, tx, REMOTE, commit_num,
&to_us_idx, &to_them_idx, &wscripts);
if (!htlcs) {
log_broken(peer->log, "Could not map_htlcs for %"PRIu64,
commit_num);
return false;
}
/* FIXME: Caller should allocate this. */ /* FIXME: Caller should allocate this. */
peer->closing_onchain.resolved peer->closing_onchain.resolved
= tal_arr(tx, const struct bitcoin_tx *, tx->output_count); = tal_arr(tx, const struct bitcoin_tx *, tx->output_count);
@ -2836,7 +2825,7 @@ static bool resolve_their_steal(struct peer *peer,
peer->closing_onchain.resolved[i] = steal_tx; peer->closing_onchain.resolved[i] = steal_tx;
/* Connect it up. */ /* Connect it up. */
steal_tx->input[n].txid = txid; steal_tx->input[n].txid = *txid;
steal_tx->input[n].index = i; steal_tx->input[n].index = i;
steal_tx->input[n].amount = tal_dup(steal_tx, u64, steal_tx->input[n].amount = tal_dup(steal_tx, u64,
&tx->output[i].amount); &tx->output[i].amount);
@ -2858,7 +2847,7 @@ static bool resolve_their_steal(struct peer *peer,
for (i = 0; i < tal_count(peer->closing_onchain.resolved); i++) for (i = 0; i < tal_count(peer->closing_onchain.resolved); i++)
peer->closing_onchain.resolved[i] = tx; peer->closing_onchain.resolved[i] = tx;
tal_free(steal_tx); tal_free(steal_tx);
return true; return;
} }
steal_tx->output[0].amount = input_total - fee; steal_tx->output[0].amount = input_total - fee;
steal_tx->output[0].script = scriptpubkey_p2sh(steal_tx, steal_tx->output[0].script = scriptpubkey_p2sh(steal_tx,
@ -2877,14 +2866,38 @@ static bool resolve_their_steal(struct peer *peer,
steal_tx->input[i].witness steal_tx->input[i].witness
= bitcoin_witness_secret(steal_tx, = bitcoin_witness_secret(steal_tx,
peer->dstate->secpctx, peer->dstate->secpctx,
&revocation_preimage, revocation_preimage,
sizeof(revocation_preimage), sizeof(*revocation_preimage),
&sig, &sig,
wscripts[i]); wscripts[i]);
} }
broadcast_tx(peer, steal_tx); broadcast_tx(peer, steal_tx);
return true; }
static struct sha256 *get_rhash(struct peer *peer, u64 commit_num,
struct sha256 *rhash)
{
struct sha256 preimage;
/* Previous revoked tx? */
if (shachain_get_hash(&peer->their_preimages,
0xFFFFFFFFFFFFFFFFL - commit_num,
&preimage)) {
sha256(rhash, &preimage, sizeof(preimage));
return tal_dup(peer, struct sha256, &preimage);
}
/* Current tx? */
if (commit_num == peer->remote.commit->commit_num) {
*rhash = peer->remote.commit->revocation_hash;
return NULL;
}
/* Last tx, but we haven't got revoke for it yet? */
assert(commit_num == peer->remote.commit->commit_num-1);
*rhash = *peer->their_prev_revocation_hash;
return NULL;
} }
/* We assume the tx is valid! Don't do a blockchain.info and feed this /* We assume the tx is valid! Don't do a blockchain.info and feed this
@ -2899,6 +2912,7 @@ static enum watch_result anchor_spent(struct peer *peer,
enum state newstate; enum state newstate;
struct htlc_map_iter it; struct htlc_map_iter it;
struct htlc *h; struct htlc *h;
u64 commit_num;
assert(input_num < tx->input_count); assert(input_num < tx->input_count);
@ -2935,26 +2949,40 @@ static enum watch_result anchor_spent(struct peer *peer,
err = pkt_err(peer, "Our own unilateral close tx seen"); err = pkt_err(peer, "Our own unilateral close tx seen");
peer->closing_onchain.ci = peer->local.commit; peer->closing_onchain.ci = peer->local.commit;
resolve_our_unilateral(peer, tx); resolve_our_unilateral(peer, tx);
/* Their unilateral */ /* Must be their unilateral */
} else if (structeq(&peer->remote.commit->txid, &txid)) { } else if (find_their_old_tx(peer, &txid, &commit_num)) {
newstate = STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL; struct sha256 *preimage, rhash;
peer->closing_onchain.ci = peer->remote.commit; int to_us_idx, to_them_idx;
err = pkt_err(peer, "Unilateral close tx seen"); struct htlc **htlcs;
resolve_their_unilateral(peer, tx); const u8 **wscripts;
/* Their previous, unrevoked unilateral */
} else if (peer->remote.commit->prev preimage = get_rhash(peer, commit_num, &rhash);
&& !peer->remote.commit->prev->revocation_preimage htlcs = tx_map_outputs(peer, &rhash, tx, REMOTE, commit_num,
&& structeq(&peer->remote.commit->prev->txid, &txid)) { &to_us_idx, &to_them_idx, &wscripts);
newstate = STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL; if (!htlcs) {
peer->closing_onchain.ci = peer->remote.commit->prev; /* Should not happen */
err = pkt_err(peer, "Unilateral close tx seen"); log_broken(peer->log,
resolve_their_unilateral(peer, tx); "Can't resolve known anchor spend %"PRIu64"!",
} else if (resolve_their_steal(peer, tx)) { commit_num);
goto unknown_spend;
}
if (preimage) {
newstate = STATE_CLOSE_ONCHAIN_CHEATED; newstate = STATE_CLOSE_ONCHAIN_CHEATED;
err = pkt_err(peer, "Revoked transaction seen"); err = pkt_err(peer, "Revoked transaction seen");
resolve_their_steal(peer, tx, &txid, preimage,
htlcs, wscripts,
to_us_idx, to_them_idx);
} else {
newstate = STATE_CLOSE_ONCHAIN_THEIR_UNILATERAL;
err = pkt_err(peer, "Unilateral close tx seen");
resolve_their_unilateral(peer, tx, &txid,
htlcs, wscripts,
to_us_idx, to_them_idx);
}
} else { } else {
/* FIXME: Log harder! */ /* FIXME: Log harder! */
log_broken(peer->log, "Unknown anchor spend! Funds may be lost!"); log_broken(peer->log,
"Unknown anchor spend! Funds may be lost!");
goto unknown_spend; goto unknown_spend;
} }

5
daemon/peer.h

@ -54,8 +54,6 @@ struct commit_info {
struct bitcoin_signature *sig; struct bitcoin_signature *sig;
/* Map for permutation: see commit_tx.c */ /* Map for permutation: see commit_tx.c */
int *map; int *map;
/* Revocation preimage (if known). */
struct sha256 *revocation_preimage;
}; };
struct peer_visible_state { struct peer_visible_state {
@ -202,6 +200,9 @@ struct peer {
/* Stuff we have in common. */ /* Stuff we have in common. */
struct peer_visible_state local, remote; struct peer_visible_state local, remote;
/* If we have sent a new commit tx, but not received their revocation */
struct sha256 *their_prev_revocation_hash;
/* this is where we will store their revocation preimages*/ /* this is where we will store their revocation preimages*/
struct shachain their_preimages; struct shachain their_preimages;
}; };

Loading…
Cancel
Save