delforward: allow deletion of "unknown in_htlc_id" and fix autoclean to use it.

Note the caveats: we will delete *all* of them at once!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-09-27 09:13:36 +09:30
parent 6eac8dfe3c
commit 68f15f17bb
7 changed files with 74 additions and 22 deletions

View file

@ -20,6 +20,11 @@ has no effect on the running of your node.
You cannot delete forwards which have status *offered* (i.e. are
currently active).
Note: for **listforwards** entries without an *in_htlc_id* entry (no
longer created in v22.11, but can exist from older versions), a value
of 18446744073709551615 can be used, but then it will delete *all*
entries without *in_htlc_id* for this *in_channel* and *status*.
RETURN VALUE
------------

View file

@ -461,6 +461,11 @@ accepted, and ignored.
How old invoices which were not paid (and cannot be) (`expired` in listinvoices `status`) before deletion (default 0, meaning never).
Note: prior to v22.11, forwards for channels which were closed were
not easily distinguishable. As a result, autoclean may delete more
than one of these at once, and then suffer failures when it fails to
delete the others.
### Payment control options:
* **disable-mpp** [plugin `pay`]

View file

@ -3010,8 +3010,14 @@ static struct command_result *json_delforward(struct command *cmd,
NULL))
return command_param_failed();
#ifdef COMPAT_V0121
/* Special value used if in_htlc_id is missing */
if (*htlc_id == HTLC_INVALID_ID)
htlc_id = NULL;
#endif
if (!wallet_forward_delete(cmd->ld->wallet,
chan_in, *htlc_id, *status))
chan_in, htlc_id, *status))
return command_fail(cmd, DELFORWARD_NOT_FOUND,
"Could not find that forward");

View file

@ -345,7 +345,15 @@ static struct command_result *listforwards_done(struct command *cmd,
req = del_request_start("delforward", cinfo, subsys);
json_add_tok(req->js, "in_channel", inchan, buf);
json_add_tok(req->js, "in_htlc_id", inid, buf);
/* This can be missing if it was a forwards record from an old
* closed channel in version <= 0.12.1. This is a special value
* but we will delete them *all*, resulting in some failures! */
#ifdef COMPAT_V0121
if (!inid)
json_add_u64(req->js, "in_htlc_id", -1ULL);
else
#endif
json_add_tok(req->js, "in_htlc_id", inid, buf);
json_add_tok(req->js, "status", status, buf);
send_outreq(plugin, req);
}

View file

@ -507,3 +507,10 @@ def test_db_forward_migrate(bitcoind, node_factory):
assert l1.rpc.getinfo()['fees_collected_msat'] == 4
assert len(l1.rpc.listforwards()['forwards']) == 4
# Make sure autoclean can handle these!
l1.stop()
l1.daemon.opts['autoclean-succeededforwards-age'] = 2
l1.daemon.opts['autoclean-cycle'] = 1
l1.start()
wait_for(lambda: l1.rpc.listforwards()['forwards'] == [])

View file

@ -4694,7 +4694,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
bool wallet_forward_delete(struct wallet *w,
const struct short_channel_id *chan_in,
u64 htlc_id,
const u64 *htlc_id,
enum forward_status state)
{
struct db_stmt *stmt;
@ -4703,21 +4703,32 @@ bool wallet_forward_delete(struct wallet *w,
/* When deleting settled ones, we have to add to deleted_forward_fees! */
if (state == FORWARD_SETTLED) {
/* Of course, it might not be settled: don't add if they're wrong! */
stmt = db_prepare_v2(w->db, SQL("SELECT"
" in_msatoshi - out_msatoshi"
" FROM forwards "
" WHERE in_channel_scid = ?"
" AND in_htlc_id = ?"
" AND state = ?;"));
db_bind_scid(stmt, 0, chan_in);
db_bind_u64(stmt, 1, htlc_id);
db_bind_int(stmt, 2, wallet_forward_status_in_db(FORWARD_SETTLED));
if (htlc_id) {
stmt = db_prepare_v2(w->db, SQL("SELECT"
" CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)"
" FROM forwards "
" WHERE in_channel_scid = ?"
" AND in_htlc_id = ?"
" AND state = ?;"));
db_bind_scid(stmt, 0, chan_in);
db_bind_u64(stmt, 1, *htlc_id);
db_bind_int(stmt, 2, wallet_forward_status_in_db(FORWARD_SETTLED));
} else {
stmt = db_prepare_v2(w->db, SQL("SELECT"
" CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)"
" FROM forwards "
" WHERE in_channel_scid = ?"
" AND in_htlc_id IS NULL"
" AND state = ?;"));
db_bind_scid(stmt, 0, chan_in);
db_bind_int(stmt, 1, wallet_forward_status_in_db(FORWARD_SETTLED));
}
db_query_prepared(stmt);
if (db_step(stmt)) {
struct amount_msat deleted;
db_col_amount_msat(stmt, "in_msatoshi - out_msatoshi", &deleted);
db_col_amount_msat(stmt, "CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)", &deleted);
deleted.millisatoshis += /* Raw: db access */
db_get_intvar(w->db, "deleted_forward_fees", 0);
db_set_intvar(w->db, "deleted_forward_fees",
@ -4726,14 +4737,24 @@ bool wallet_forward_delete(struct wallet *w,
tal_free(stmt);
}
stmt = db_prepare_v2(w->db,
SQL("DELETE FROM forwards"
" WHERE in_channel_scid = ?"
" AND in_htlc_id = ?"
" AND state = ?"));
db_bind_scid(stmt, 0, chan_in);
db_bind_u64(stmt, 1, htlc_id);
db_bind_int(stmt, 2, wallet_forward_status_in_db(state));
if (htlc_id) {
stmt = db_prepare_v2(w->db,
SQL("DELETE FROM forwards"
" WHERE in_channel_scid = ?"
" AND in_htlc_id = ?"
" AND state = ?"));
db_bind_scid(stmt, 0, chan_in);
db_bind_u64(stmt, 1, *htlc_id);
db_bind_int(stmt, 2, wallet_forward_status_in_db(state));
} else {
stmt = db_prepare_v2(w->db,
SQL("DELETE FROM forwards"
" WHERE in_channel_scid = ?"
" AND in_htlc_id IS NULL"
" AND state = ?"));
db_bind_scid(stmt, 0, chan_in);
db_bind_int(stmt, 1, wallet_forward_status_in_db(state));
}
db_exec_prepared_v2(stmt);
changed = db_count_changes(stmt) != 0;
tal_free(stmt);

View file

@ -1389,7 +1389,7 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w,
*/
bool wallet_forward_delete(struct wallet *w,
const struct short_channel_id *chan_in,
u64 htlc_id,
const u64 *htlc_id,
enum forward_status state);
/**