Skip to content

Commit

Permalink
lightningd: allow delpay to delete a specific payment.
Browse files Browse the repository at this point in the history
This is actually what the autoclean plugin wants, especially since
you can't otherwise delete a payment which has failed then succeeded.

But insist on neither or both being specified, at least for now.

Changelog-Added: JSON-RPC: `delpay` takes optional `groupid` and `partid` parameters to specify exactly what payment to delete.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
  • Loading branch information
rustyrussell committed Sep 14, 2022
1 parent fafb87f commit bf1dbc6
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 21 deletions.
62 changes: 50 additions & 12 deletions lightningd/pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -1629,6 +1629,29 @@ static const struct json_command listsendpays_command = {
};
AUTODATA(json_command, &listsendpays_command);

static struct command_result *
param_payment_status_nopending(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
enum wallet_payment_status **status)
{
struct command_result *res;

res = param_payment_status(cmd, name, buffer, tok, status);
if (res)
return res;

switch (**status) {
case PAYMENT_COMPLETE:
case PAYMENT_FAILED:
break;
case PAYMENT_PENDING:
return command_fail_badparam(cmd, name, buffer, tok,
"Cannot delete pending status");
}
return NULL;
}

static struct command_result *json_delpay(struct command *cmd,
const char *buffer,
Expand All @@ -1639,29 +1662,35 @@ static struct command_result *json_delpay(struct command *cmd,
const struct wallet_payment **payments;
enum wallet_payment_status *status;
struct sha256 *payment_hash;
u64 *groupid, *partid;
bool found;

if (!param(cmd, buffer, params,
p_req("payment_hash", param_sha256, &payment_hash),
p_req("status", param_payment_status, &status),
NULL))
p_req("payment_hash", param_sha256, &payment_hash),
p_req("status", param_payment_status_nopending, &status),
p_opt("partid", param_u64, &partid),
p_opt("groupid", param_u64, &groupid),
NULL))
return command_param_failed();

switch (*status) {
case PAYMENT_COMPLETE:
case PAYMENT_FAILED:
break;
case PAYMENT_PENDING:
return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Invalid status: %s",
payment_status_to_string(*status));
}
if ((partid != NULL) != (groupid != NULL))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Must set both partid and groupid, or neither");

payments = wallet_payment_list(cmd, cmd->ld->wallet, payment_hash);

if (tal_count(payments) == 0)
return command_fail(cmd, PAY_NO_SUCH_PAYMENT, "Unknown payment with payment_hash: %s",
type_to_string(tmpctx, struct sha256, payment_hash));

found = false;
for (int i = 0; i < tal_count(payments); i++) {
if (groupid && payments[i]->groupid != *groupid)
continue;
if (partid && payments[i]->partid != *partid)
continue;

found = true;
if (payments[i]->status != *status) {
return command_fail(cmd, PAY_STATUS_UNEXPECTED, "Payment with hash %s has %s status but it should be %s",
type_to_string(tmpctx, struct sha256, payment_hash),
Expand All @@ -1670,11 +1699,20 @@ static struct command_result *json_delpay(struct command *cmd,
}
}

wallet_payment_delete_by_hash(cmd->ld->wallet, payment_hash);
if (!found) {
return command_fail(cmd, PAY_NO_SUCH_PAYMENT,
"No payment for that payment_hash with that partid and groupid");
}

wallet_payment_delete(cmd->ld->wallet, payment_hash, partid, groupid);

response = json_stream_success(cmd);
json_array_start(response, "payments");
for (int i = 0; i < tal_count(payments); i++) {
if (groupid && payments[i]->groupid != *groupid)
continue;
if (partid && payments[i]->partid != *partid)
continue;
json_object_start(response, NULL);
json_add_payment_fields(response, payments[i]);
json_object_end(response);
Expand Down
9 changes: 9 additions & 0 deletions plugins/autoclean.c
Original file line number Diff line number Diff line change
Expand Up @@ -272,10 +272,19 @@ static struct command_result *listsendpays_done(struct command *cmd,
if (paytime <= now - cinfo->subsystem_age[subsys]) {
struct out_req *req;
const jsmntok_t *phash = json_get_member(buf, t, "payment_hash");
const jsmntok_t *groupid = json_get_member(buf, t, "groupid");
const jsmntok_t *partidtok = json_get_member(buf, t, "partid");
u64 partid;
if (partidtok)
json_to_u64(buf, partidtok, &partid);
else
partid = 0;

req = del_request_start("delpay", cinfo, subsys);
json_add_tok(req->js, "payment_hash", phash, buf);
json_add_tok(req->js, "status", status, buf);
json_add_tok(req->js, "groupid", groupid, buf);
json_add_u64(req->js, "partid", partid);
send_outreq(plugin, req);
}
}
Expand Down
24 changes: 19 additions & 5 deletions wallet/wallet.c
Original file line number Diff line number Diff line change
Expand Up @@ -3163,13 +3163,27 @@ u64 wallet_payment_get_groupid(struct wallet *wallet,
return groupid;
}

void wallet_payment_delete_by_hash(struct wallet *wallet,
const struct sha256 *payment_hash)
void wallet_payment_delete(struct wallet *wallet,
const struct sha256 *payment_hash,
const u64 *groupid,
const u64 *partid)
{
struct db_stmt *stmt;
stmt = db_prepare_v2(
wallet->db, SQL("DELETE FROM payments WHERE payment_hash = ?"));

if (groupid) {
assert(partid);
stmt = db_prepare_v2(wallet->db,
SQL("DELETE FROM payments"
" WHERE payment_hash = ?"
" AND groupid = ?"
" AND partid = ?"));
db_bind_u64(stmt, 1, *groupid);
db_bind_u64(stmt, 2, *partid);
} else {
assert(!partid);
stmt = db_prepare_v2(wallet->db,
SQL("DELETE FROM payments"
" WHERE payment_hash = ?"));
}
db_bind_sha256(stmt, 0, payment_hash);
db_exec_prepared_v2(take(stmt));
}
Expand Down
12 changes: 8 additions & 4 deletions wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -1088,12 +1088,16 @@ void wallet_payment_store(struct wallet *wallet,
struct wallet_payment *payment TAKES);

/**
* wallet_payment_delete_by_hash - Remove a payment
* wallet_payment_delete - Remove a payment
*
* Removes the payment from the database by hash; if it is a MPP payment
* it remove all parts with a single query.
* Removes the payment from the database by hash; groupid and partid
* may both be NULL to delete all entries, otherwise deletes only that
* group/partid.
*/
void wallet_payment_delete_by_hash(struct wallet *wallet, const struct sha256 *payment_hash);
void wallet_payment_delete(struct wallet *wallet,
const struct sha256 *payment_hash,
const u64 *groupid,
const u64 *partid);

/**
* wallet_local_htlc_out_delete - Remove a local outgoing failed HTLC
Expand Down

0 comments on commit bf1dbc6

Please sign in to comment.