mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-04 11:08:03 +01:00
autoclean: handle cleaning of old payments (not just invoices).
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
660c9af1d9
commit
4cab396cc8
2 changed files with 87 additions and 2 deletions
|
@ -8,11 +8,15 @@
|
||||||
#include <plugins/libplugin.h>
|
#include <plugins/libplugin.h>
|
||||||
|
|
||||||
enum subsystem {
|
enum subsystem {
|
||||||
|
SUCCEEDEDPAYS,
|
||||||
|
FAILEDPAYS,
|
||||||
PAIDINVOICES,
|
PAIDINVOICES,
|
||||||
EXPIREDINVOICES,
|
EXPIREDINVOICES,
|
||||||
#define NUM_SUBSYSTEM (EXPIREDINVOICES + 1)
|
#define NUM_SUBSYSTEM (EXPIREDINVOICES + 1)
|
||||||
};
|
};
|
||||||
static const char *subsystem_str[] = {
|
static const char *subsystem_str[] = {
|
||||||
|
"succeededpays",
|
||||||
|
"failedpays",
|
||||||
"paidinvoices",
|
"paidinvoices",
|
||||||
"expiredinvoices",
|
"expiredinvoices",
|
||||||
};
|
};
|
||||||
|
@ -169,11 +173,73 @@ static struct command_result *listinvoices_done(struct command *cmd,
|
||||||
return set_next_timer(plugin);
|
return set_next_timer(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct command_result *listsendpays_done(struct command *cmd,
|
||||||
|
const char *buf,
|
||||||
|
const jsmntok_t *result,
|
||||||
|
char *unused)
|
||||||
|
{
|
||||||
|
const jsmntok_t *t, *pays = json_get_member(buf, result, "payments");
|
||||||
|
size_t i;
|
||||||
|
u64 now = time_now().ts.tv_sec;
|
||||||
|
|
||||||
|
json_for_each_arr(i, t, pays) {
|
||||||
|
const jsmntok_t *status = json_get_member(buf, t, "status");
|
||||||
|
const jsmntok_t *time;
|
||||||
|
enum subsystem subsys;
|
||||||
|
u64 paytime;
|
||||||
|
|
||||||
|
if (json_tok_streq(buf, status, "failed")) {
|
||||||
|
subsys = FAILEDPAYS;
|
||||||
|
} else if (json_tok_streq(buf, status, "complete")) {
|
||||||
|
subsys = SUCCEEDEDPAYS;
|
||||||
|
} else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Continue if we don't care. */
|
||||||
|
if (subsystem_age[subsys] == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
time = json_get_member(buf, t, "created_at");
|
||||||
|
if (!json_to_u64(buf, time, &paytime)) {
|
||||||
|
plugin_err(plugin, "Bad created_at '%.*s'",
|
||||||
|
json_tok_full_len(time),
|
||||||
|
json_tok_full(buf, time));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paytime <= now - subsystem_age[subsys]) {
|
||||||
|
struct out_req *req;
|
||||||
|
const jsmntok_t *phash = json_get_member(buf, t, "payment_hash");
|
||||||
|
|
||||||
|
req = jsonrpc_request_start(plugin, NULL, "delpay",
|
||||||
|
del_done, del_failed,
|
||||||
|
int2ptr(subsys));
|
||||||
|
json_add_tok(req->js, "payment_hash", phash, buf);
|
||||||
|
json_add_tok(req->js, "status", status, buf);
|
||||||
|
send_outreq(plugin, req);
|
||||||
|
plugin_log(plugin, LOG_DBG, "Cleaning up %.*s",
|
||||||
|
json_tok_full_len(phash), json_tok_full(buf, phash));
|
||||||
|
cleanup_reqs_remaining++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cleanup_reqs_remaining)
|
||||||
|
return command_still_pending(cmd);
|
||||||
|
return set_next_timer(plugin);
|
||||||
|
}
|
||||||
|
|
||||||
static void do_clean(void *unused)
|
static void do_clean(void *unused)
|
||||||
{
|
{
|
||||||
struct out_req *req = NULL;
|
struct out_req *req = NULL;
|
||||||
|
|
||||||
assert(cleanup_reqs_remaining == 0);
|
assert(cleanup_reqs_remaining == 0);
|
||||||
|
if (subsystem_age[SUCCEEDEDPAYS] != 0
|
||||||
|
|| subsystem_age[FAILEDPAYS] != 0) {
|
||||||
|
req = jsonrpc_request_start(plugin, NULL, "listsendpays",
|
||||||
|
listsendpays_done, cmd_failed,
|
||||||
|
(char *)"listsendpays");
|
||||||
|
send_outreq(plugin, req);
|
||||||
|
}
|
||||||
|
|
||||||
if (subsystem_age[EXPIREDINVOICES] != 0
|
if (subsystem_age[EXPIREDINVOICES] != 0
|
||||||
|| subsystem_age[PAIDINVOICES] != 0) {
|
|| subsystem_age[PAIDINVOICES] != 0) {
|
||||||
req = jsonrpc_request_start(plugin, NULL, "listinvoices",
|
req = jsonrpc_request_start(plugin, NULL, "listinvoices",
|
||||||
|
|
|
@ -2943,6 +2943,7 @@ def test_autoclean(node_factory):
|
||||||
l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20)
|
l1.rpc.invoice(amount_msat=12300, label='inv2', description='description2', expiry=20)
|
||||||
l1.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20)
|
l1.rpc.invoice(amount_msat=12300, label='inv3', description='description3', expiry=20)
|
||||||
inv4 = l1.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000)
|
inv4 = l1.rpc.invoice(amount_msat=12300, label='inv4', description='description4', expiry=2000)
|
||||||
|
inv5 = l1.rpc.invoice(amount_msat=12300, label='inv5', description='description5', expiry=2000)
|
||||||
l1.rpc.autoclean(subsystem='expiredinvoices', age=2)
|
l1.rpc.autoclean(subsystem='expiredinvoices', age=2)
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2
|
||||||
|
@ -2959,8 +2960,9 @@ def test_autoclean(node_factory):
|
||||||
wait_for(lambda: l1.rpc.listinvoices('inv1')['invoices'] == [])
|
wait_for(lambda: l1.rpc.listinvoices('inv1')['invoices'] == [])
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1
|
||||||
|
|
||||||
# Keeps settings across restarts.
|
# Keeps settings across restarts
|
||||||
l1.restart()
|
l1.restart()
|
||||||
|
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['enabled'] is True
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['age'] == 2
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 1
|
||||||
|
@ -2985,13 +2987,18 @@ def test_autoclean(node_factory):
|
||||||
|
|
||||||
# Now enable: they will get autocleaned
|
# Now enable: they will get autocleaned
|
||||||
l1.rpc.autoclean(subsystem='expiredinvoices', age=2)
|
l1.rpc.autoclean(subsystem='expiredinvoices', age=2)
|
||||||
wait_for(lambda: len(l1.rpc.listinvoices()['invoices']) == 1)
|
wait_for(lambda: len(l1.rpc.listinvoices()['invoices']) == 2)
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3
|
||||||
|
|
||||||
# Reconnect, l0 pays invoice, we test paid expiry.
|
# Reconnect, l0 pays invoice, we test paid expiry.
|
||||||
l1.rpc.connect(l0.info['id'], 'localhost', l0.port)
|
l1.rpc.connect(l0.info['id'], 'localhost', l0.port)
|
||||||
l0.rpc.pay(inv4['bolt11'])
|
l0.rpc.pay(inv4['bolt11'])
|
||||||
|
|
||||||
|
# We manually delete inv5 so we can have l0 fail a payment.
|
||||||
|
l1.rpc.delinvoice('inv5', 'unpaid')
|
||||||
|
with pytest.raises(RpcError, match='WIRE_INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS'):
|
||||||
|
l0.rpc.pay(inv5['bolt11'])
|
||||||
|
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False
|
assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['enabled'] is False
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0
|
assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 0
|
||||||
l1.rpc.autoclean(subsystem='paidinvoices', age=1)
|
l1.rpc.autoclean(subsystem='paidinvoices', age=1)
|
||||||
|
@ -3001,6 +3008,18 @@ def test_autoclean(node_factory):
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3
|
assert l1.rpc.autoclean_status()['autoclean']['expiredinvoices']['cleaned'] == 3
|
||||||
assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 1
|
assert l1.rpc.autoclean_status()['autoclean']['paidinvoices']['cleaned'] == 1
|
||||||
|
|
||||||
|
assert only_one(l0.rpc.listpays(inv5['bolt11'])['pays'])['status'] == 'failed'
|
||||||
|
assert only_one(l0.rpc.listpays(inv4['bolt11'])['pays'])['status'] == 'complete'
|
||||||
|
l0.rpc.autoclean(subsystem='failedpays', age=2)
|
||||||
|
|
||||||
|
wait_for(lambda: l0.rpc.listpays(inv5['bolt11'])['pays'] == [])
|
||||||
|
assert l0.rpc.autoclean_status()['autoclean']['failedpays']['cleaned'] == 1
|
||||||
|
assert l0.rpc.autoclean_status()['autoclean']['succeededpays']['cleaned'] == 0
|
||||||
|
|
||||||
|
l0.rpc.autoclean(subsystem='succeededpays', age=2)
|
||||||
|
wait_for(lambda: l0.rpc.listpays(inv4['bolt11'])['pays'] == [])
|
||||||
|
assert l0.rpc.listsendpays() == {'payments': []}
|
||||||
|
|
||||||
|
|
||||||
def test_block_added_notifications(node_factory, bitcoind):
|
def test_block_added_notifications(node_factory, bitcoind):
|
||||||
"""Test if a plugin gets notifications when a new block is found"""
|
"""Test if a plugin gets notifications when a new block is found"""
|
||||||
|
|
Loading…
Add table
Reference in a new issue