Skip to content

Commit 555a753

Browse files
committed
db: don't assume HTLCs in order when reconstructing channel state.
We don't have an ordering of HTLCs between peers: we don't know whether your HTLC 0 or my HTLC 0 occurred first. This matters, as we play them all back to reconstruct state (probably overkill anyway). So we add force_* operators which don't do bounds checks, and do bounds checks at the end. We also note that we don't need to apply fee changes: that should be in the database already. Also relax db constraints: IDs are not unique, they are unique per side (we can both have HTLC #0). Signed-off-by: Rusty Russell <[email protected]>
1 parent b22bdfc commit 555a753

File tree

3 files changed

+74
-31
lines changed

3 files changed

+74
-31
lines changed

daemon/channel.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,3 +261,58 @@ struct channel_state *copy_cstate(const tal_t *ctx,
261261
{
262262
return tal_dup(ctx, struct channel_state, cstate);
263263
}
264+
265+
void force_add_htlc(struct channel_state *cstate, const struct htlc *htlc)
266+
{
267+
struct channel_oneside *creator;
268+
269+
creator = &cstate->side[htlc_channel_side(htlc)];
270+
creator->num_htlcs++;
271+
creator->pay_msat -= htlc->msatoshis;
272+
273+
/* Remember to count the new one in total txsize if not dust! */
274+
if (!is_dust(htlc->msatoshis / 1000))
275+
cstate->num_nondust++;
276+
}
277+
278+
static void force_remove_htlc(struct channel_state *cstate,
279+
enum channel_side beneficiary,
280+
const struct htlc *htlc)
281+
{
282+
cstate->side[beneficiary].pay_msat += htlc->msatoshis;
283+
cstate->side[htlc_channel_side(htlc)].num_htlcs--;
284+
if (!is_dust(htlc->msatoshis / 1000))
285+
cstate->num_nondust--;
286+
}
287+
288+
void force_fail_htlc(struct channel_state *cstate, const struct htlc *htlc)
289+
{
290+
force_remove_htlc(cstate, htlc_channel_side(htlc), htlc);
291+
}
292+
293+
void force_fulfill_htlc(struct channel_state *cstate, const struct htlc *htlc)
294+
{
295+
force_remove_htlc(cstate, !htlc_channel_side(htlc), htlc);
296+
}
297+
298+
bool balance_after_force(struct channel_state *cstate)
299+
{
300+
/* We should not spend more than anchor */
301+
if (cstate->side[OURS].pay_msat + cstate->side[THEIRS].pay_msat
302+
> cstate->anchor * 1000)
303+
return false;
304+
305+
/* Check for wrap. */
306+
if (cstate->side[OURS].pay_msat > cstate->anchor * 1000)
307+
return false;
308+
if (cstate->side[THEIRS].pay_msat > cstate->anchor * 1000)
309+
return false;
310+
311+
if (cstate->num_nondust
312+
> cstate->side[OURS].num_htlcs + cstate->side[THEIRS].num_htlcs)
313+
return false;
314+
315+
/* Recalc fees. */
316+
adjust_fee(cstate, cstate->fee_rate);
317+
return true;
318+
}

daemon/channel.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,10 @@ bool force_fee(struct channel_state *cstate, uint64_t fee);
128128
*/
129129
uint64_t fee_by_feerate(size_t txsize, uint64_t fee_rate);
130130

131+
/* Routines to db to force HTLC changes out-of-order which may wrap. */
132+
void force_add_htlc(struct channel_state *cstate, const struct htlc *htlc);
133+
void force_fail_htlc(struct channel_state *cstate, const struct htlc *htlc);
134+
void force_fulfill_htlc(struct channel_state *cstate, const struct htlc *htlc);
135+
bool balance_after_force(struct channel_state *cstate);
136+
131137
#endif /* LIGHTNING_DAEMON_CHANNEL_H */

daemon/db.c

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ static void load_peer_commit_info(struct peer *peer)
435435
fatal("load_peer_commit_info:no remote commit info found");
436436
}
437437

438+
/* Because their HTLCs are not ordered wrt to ours, we can go negative
439+
* and do normally-impossible things in intermediate states. So we
440+
* mangle cstate balances manually. */
438441
static void apply_htlc(struct channel_state *cstate, const struct htlc *htlc,
439442
enum htlc_side side)
440443
{
@@ -444,39 +447,24 @@ static void apply_htlc(struct channel_state *cstate, const struct htlc *htlc,
444447
return;
445448

446449
log_debug(htlc->peer->log, " %s committed", sidestr);
447-
if (!cstate_add_htlc(cstate, htlc))
448-
fatal("load_peer_htlcs:can't add %s HTLC", sidestr);
450+
force_add_htlc(cstate, htlc);
449451

450452
if (!htlc_has(htlc, HTLC_FLAG(side, HTLC_F_COMMITTED))) {
451453
log_debug(htlc->peer->log, " %s %s",
452454
sidestr, htlc->r ? "resolved" : "failed");
453455
if (htlc->r)
454-
cstate_fulfill_htlc(cstate, htlc);
456+
force_fulfill_htlc(cstate, htlc);
455457
else
456-
cstate_fail_htlc(cstate, htlc);
458+
force_fail_htlc(cstate, htlc);
457459
}
458460
}
459461

460-
static void apply_feechange(struct channel_state *cstate,
461-
const struct feechange *f,
462-
enum htlc_side side)
463-
{
464-
/* We only ever apply feechanges to the owner. */
465-
if (feechange_side(f->state) != side)
466-
return;
467-
468-
if (!feechange_has(f, HTLC_FLAG(side,HTLC_F_WAS_COMMITTED)))
469-
return;
470-
471-
adjust_fee(cstate, f->fee_rate);
472-
}
473-
474462
/* As we load the HTLCs, we apply them to get the final channel_state.
475463
* We also get the last used htlc id.
476464
* This is slow, but sure. */
477465
static void load_peer_htlcs(struct peer *peer)
478466
{
479-
int err, i;
467+
int err;
480468
sqlite3_stmt *stmt;
481469
sqlite3 *sql = peer->dstate->db->sql;
482470
char *ctx = tal(peer, char);
@@ -518,7 +506,7 @@ static void load_peer_htlcs(struct peer *peer)
518506
if (sqlite3_column_count(stmt) != 10)
519507
fatal("load_peer_htlcs:step gave %i cols, not 10",
520508
sqlite3_column_count(stmt));
521-
/* CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id)); */
509+
/* CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id, state)); */
522510
sha256_from_sql(stmt, 5, &rhash);
523511

524512
hstate = htlc_state_from_name(sqlite3_column_str(stmt, 2));
@@ -596,16 +584,9 @@ static void load_peer_htlcs(struct peer *peer)
596584
fatal("load_peer_htlcs:finalize gave %s:%s",
597585
sqlite3_errstr(err), sqlite3_errmsg(sql));
598586

599-
/* Apply feechanges from oldest to newest (newest counts). */
600-
for (i = FEECHANGE_STATE_INVALID - 1; i >= SENT_FEECHANGE; i--) {
601-
if (!peer->feechanges[i])
602-
continue;
603-
604-
apply_feechange(peer->local.commit->cstate,
605-
peer->feechanges[i], LOCAL);
606-
apply_feechange(peer->remote.commit->cstate,
607-
peer->feechanges[i], REMOTE);
608-
}
587+
if (!balance_after_force(peer->local.commit->cstate)
588+
|| !balance_after_force(peer->remote.commit->cstate))
589+
fatal("load_peer_htlcs:channel didn't balance");
609590

610591
/* Update commit->tx and commit->map */
611592
peer->local.commit->tx = create_commit_tx(peer->local.commit,
@@ -1041,7 +1022,8 @@ void db_init(struct lightningd_state *dstate)
10411022
errmsg = db_exec(dstate, dstate,
10421023
"CREATE TABLE wallet (privkey "SQL_PRIVKEY");"
10431024
"CREATE TABLE anchors (peer "SQL_PUBKEY", txid "SQL_TXID", idx INT, amount INT, ok_depth INT, min_depth INT, bool ours, PRIMARY KEY(peer));"
1044-
"CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id));"
1025+
/* FIXME: state in primary key is overkill: just need side */
1026+
"CREATE TABLE htlcs (peer "SQL_PUBKEY", id INT, state TEXT, msatoshis INT, expiry INT, rhash "SQL_RHASH", r "SQL_R", routing "SQL_ROUTING", src_peer "SQL_PUBKEY", src_id INT, PRIMARY KEY(peer, id, state));"
10451027
"CREATE TABLE feechanges (peer "SQL_PUBKEY", state TEXT, fee_rate INT, PRIMARY KEY(peer,state));"
10461028
"CREATE TABLE commit_info (peer "SQL_PUBKEY", side TEXT, commit_num INT, revocation_hash "SQL_SHA256", xmit_order INT, sig "SQL_SIGNATURE", prev_revocation_hash "SQL_SHA256", PRIMARY KEY(peer, side));"
10471029
"CREATE TABLE shachain (peer "SQL_PUBKEY", shachain BINARY(%zu), PRIMARY KEY(peer));"

0 commit comments

Comments
 (0)