delinvoice: allow desconly arg to only remove the description.

Means that field is now optional in JSON output.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Added: JSON-RPC: `delinvoice` has a new parameter `desconly` to remove description.
This commit is contained in:
Rusty Russell 2022-03-24 10:27:28 +10:30
parent ccaf04d268
commit aad4495f56
13 changed files with 110 additions and 36 deletions

View file

@ -77,6 +77,7 @@ static const errcode_t INVOICE_WAIT_TIMED_OUT = 904;
static const errcode_t INVOICE_NOT_FOUND = 905;
static const errcode_t INVOICE_STATUS_UNEXPECTED = 906;
static const errcode_t INVOICE_OFFER_INACTIVE = 907;
static const errcode_t INVOICE_NO_DESCRIPTION = 908;
/* Errors from HSM crypto operations. */
static const errcode_t HSM_ECDH_FAILED = 800;

View file

@ -566,13 +566,14 @@ class LightningRpc(UnixDomainSocketRpc):
}
return self.call("delexpiredinvoice", payload)
def delinvoice(self, label, status):
def delinvoice(self, label, status, desconly=None):
"""
Delete unpaid invoice {label} with {status}.
Delete unpaid invoice {label} with {status} (or, with {desconly} true, remove its description).
"""
payload = {
"label": label,
"status": status
"status": status,
"desconly": desconly,
}
return self.call("delinvoice", payload)

View file

@ -1,20 +1,24 @@
lightning-delinvoice -- Command for removing an invoice
=======================================================
lightning-delinvoice -- Command for removing an invoice (or just its description)
=================================================================================
SYNOPSIS
--------
**delinvoice** *label* *status*
**delinvoice** *label* *status* [*desconly*]
DESCRIPTION
-----------
The **delinvoice** RPC command removes an invoice with *status* as given
in **listinvoices**.
in **listinvoices**, or with *desconly* set, removes its description.
The caller should be particularly aware of the error case caused by the
*status* changing just before this command is invoked!
If *desconly* is set, the invoice is not deleted, but has its
description removed (this can save space with very large descriptions,
as would be used with lightning-invoice(7) *deschashonly*.
RETURN VALUE
------------
@ -55,6 +59,7 @@ The following errors may be reported:
*current_status* and *expected_status* fields.
This is most likely due to the *status* of the invoice
changing just before this command is invoked.
- 908: The invoice already has no description, and *desconly* was set.
AUTHOR
------
@ -73,4 +78,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:cd3b009a6ef0c220ca21c6d8e3a5716ca2080997016cf00a2e26defc03cfac73)
[comment]: # ( SHA256STAMP:73d9097734e85d438de90844ab78bdd737e6df53620542887170c7564a86b90b)

View file

@ -23,10 +23,10 @@ RETURN VALUE
[comment]: # (GENERATE-FROM-SCHEMA-START)
On success, an object containing **invoices** is returned. It is an array of objects, where each object contains:
- **label** (string): unique label supplied at invoice creation
- **description** (string): description used in the invoice
- **payment_hash** (hex): the hash of the *payment_preimage* which will prove payment (always 64 characters)
- **status** (string): Whether it's paid, unpaid or unpayable (one of "unpaid", "paid", "expired")
- **expires_at** (u64): UNIX timestamp of when it will become / became unpayable
- **description** (string, optional): description used in the invoice
- **amount_msat** (msat, optional): the amount required to pay this invoice
- **bolt11** (string, optional): the BOLT11 string (always present unless *bolt12* is)
- **bolt12** (string, optional): the BOLT12 string (always present unless *bolt11* is)
@ -56,4 +56,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:3dc5d5b8f7796d29e0d174d96e93915cbc7131b173a1547de022e021c55e8db6)
[comment]: # ( SHA256STAMP:d1328ecc2a4e76ede8c9adc3a63d18ce36be305ddcee7cf717039f79642cfd41)

View file

@ -13,7 +13,6 @@
"additionalProperties": true,
"required": [
"label",
"description",
"payment_hash",
"status",
"expires_at"

View file

@ -1382,15 +1382,17 @@ static struct command_result *json_delinvoice(struct command *cmd,
const jsmntok_t *params)
{
struct invoice i;
const struct invoice_details *details;
struct invoice_details *details;
struct json_stream *response;
const char *status, *actual_status;
struct json_escape *label;
struct wallet *wallet = cmd->ld->wallet;
bool *deldesc;
if (!param(cmd, buffer, params,
p_req("label", param_label, &label),
p_req("status", param_string, &status),
p_opt_def("desconly", param_bool, &deldesc, false),
NULL))
return command_param_failed();
@ -1415,12 +1417,27 @@ static struct command_result *json_delinvoice(struct command *cmd,
return command_failed(cmd, js);
}
if (!wallet_invoice_delete(wallet, i)) {
log_broken(cmd->ld->log,
"Error attempting to remove invoice %"PRIu64,
i.id);
/* FIXME: allocate a generic DATABASE_ERROR code. */
return command_fail(cmd, LIGHTNINGD, "Database error");
if (*deldesc) {
if (!details->description)
return command_fail(cmd, INVOICE_NO_DESCRIPTION,
"Invoice description already removed");
if (!wallet_invoice_delete_description(wallet, i)) {
log_broken(cmd->ld->log,
"Error attempting to delete description of invoice %"PRIu64,
i.id);
/* FIXME: allocate a generic DATABASE_ERROR code. */
return command_fail(cmd, LIGHTNINGD, "Database error");
}
details->description = tal_free(details->description);
} else {
if (!wallet_invoice_delete(wallet, i)) {
log_broken(cmd->ld->log,
"Error attempting to remove invoice %"PRIu64,
i.id);
/* FIXME: allocate a generic DATABASE_ERROR code. */
return command_fail(cmd, LIGHTNINGD, "Database error");
}
}
response = json_stream_success(cmd);

View file

@ -753,14 +753,18 @@ bool wallet_invoice_create(struct wallet *wallet UNNEEDED,
bool wallet_invoice_delete(struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "wallet_invoice_delete called!\n"); abort(); }
/* Generated stub for wallet_invoice_delete_description */
bool wallet_invoice_delete_description(struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "wallet_invoice_delete_description called!\n"); abort(); }
/* Generated stub for wallet_invoice_delete_expired */
void wallet_invoice_delete_expired(struct wallet *wallet UNNEEDED,
u64 max_expiry_time UNNEEDED)
{ fprintf(stderr, "wallet_invoice_delete_expired called!\n"); abort(); }
/* Generated stub for wallet_invoice_details */
const struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED)
struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED,
struct wallet *wallet UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "wallet_invoice_details called!\n"); abort(); }
/* Generated stub for wallet_invoice_find_by_label */
bool wallet_invoice_find_by_label(struct wallet *wallet UNNEEDED,

View file

@ -733,3 +733,10 @@ def test_invoice_deschash(node_factory, chainparams):
# Make sure we can pay it!
l1.rpc.pay(inv['bolt11'])
# Try removing description.
l2.rpc.delinvoice('label', "paid", desconly=True)
assert 'description' not in only_one(l2.rpc.listinvoices()['invoices'])
with pytest.raises(RpcError, match=r'description already removed'):
l2.rpc.delinvoice('label', "paid", desconly=True)

View file

@ -421,6 +421,23 @@ bool invoices_delete(struct invoices *invoices, struct invoice invoice)
return true;
}
bool invoices_delete_description(struct invoices *invoices, struct invoice invoice)
{
struct db_stmt *stmt;
int changes;
stmt = db_prepare_v2(invoices->db, SQL("UPDATE invoices"
" SET description = NULL"
" WHERE ID = ?;"));
db_bind_u64(stmt, 0, invoice.id);
db_exec_prepared_v2(stmt);
changes = db_count_changes(stmt);
tal_free(stmt);
return changes == 1;
}
void invoices_delete_expired(struct invoices *invoices,
u64 max_expiry_time)
{
@ -648,9 +665,9 @@ void invoices_waitone(const tal_t *ctx,
false, invoice.id, cb, cbarg);
}
const struct invoice_details *invoices_get_details(const tal_t *ctx,
struct invoices *invoices,
struct invoice invoice)
struct invoice_details *invoices_get_details(const tal_t *ctx,
struct invoices *invoices,
struct invoice invoice)
{
struct db_stmt *stmt;
bool res;

View file

@ -108,6 +108,17 @@ bool invoices_find_unpaid(struct invoices *invoices,
bool invoices_delete(struct invoices *invoices,
struct invoice invoice);
/**
* invoices_delete_description - Remove description from an invoice
*
* @invoices - the invoice handler.
* @invoice - the invoice to remove description from.
*
* Return false on failure.
*/
bool invoices_delete_description(struct invoices *invoices,
struct invoice invoice);
/**
* invoices_delete_expired - Delete all expired invoices
* with expiration time less than or equal to the given.
@ -213,8 +224,8 @@ void invoices_waitone(const tal_t *ctx,
* @invoice - the invoice to get details on.
* @return pointer to the invoice details allocated off of `ctx`.
*/
const struct invoice_details *invoices_get_details(const tal_t *ctx,
struct invoices *invoices,
struct invoice invoice);
struct invoice_details *invoices_get_details(const tal_t *ctx,
struct invoices *invoices,
struct invoice invoice);
#endif /* LIGHTNING_WALLET_INVOICES_H */

View file

@ -212,6 +212,10 @@ bool invoices_create(struct invoices *invoices UNNEEDED,
bool invoices_delete(struct invoices *invoices UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "invoices_delete called!\n"); abort(); }
/* Generated stub for invoices_delete_description */
bool invoices_delete_description(struct invoices *invoices UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "invoices_delete_description called!\n"); abort(); }
/* Generated stub for invoices_delete_expired */
void invoices_delete_expired(struct invoices *invoices UNNEEDED,
u64 max_expiry_time UNNEEDED)
@ -232,9 +236,9 @@ bool invoices_find_unpaid(struct invoices *invoices UNNEEDED,
const struct sha256 *rhash UNNEEDED)
{ fprintf(stderr, "invoices_find_unpaid called!\n"); abort(); }
/* Generated stub for invoices_get_details */
const struct invoice_details *invoices_get_details(const tal_t *ctx UNNEEDED,
struct invoices *invoices UNNEEDED,
struct invoice invoice UNNEEDED)
struct invoice_details *invoices_get_details(const tal_t *ctx UNNEEDED,
struct invoices *invoices UNNEEDED,
struct invoice invoice UNNEEDED)
{ fprintf(stderr, "invoices_get_details called!\n"); abort(); }
/* Generated stub for invoices_iterate */
bool invoices_iterate(struct invoices *invoices UNNEEDED,

View file

@ -2850,6 +2850,11 @@ bool wallet_invoice_delete(struct wallet *wallet,
{
return invoices_delete(wallet->invoices, invoice);
}
bool wallet_invoice_delete_description(struct wallet *wallet,
struct invoice invoice)
{
return invoices_delete_description(wallet->invoices, invoice);
}
void wallet_invoice_delete_expired(struct wallet *wallet, u64 e)
{
invoices_delete_expired(wallet->invoices, e);
@ -2888,9 +2893,9 @@ void wallet_invoice_waitone(const tal_t *ctx,
invoices_waitone(ctx, wallet->invoices, invoice, cb, cbarg);
}
const struct invoice_details *wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet,
struct invoice invoice)
struct invoice_details *wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet,
struct invoice invoice)
{
return invoices_get_details(ctx, wallet->invoices, invoice);
}

View file

@ -893,6 +893,9 @@ bool wallet_invoice_find_unpaid(struct wallet *wallet,
bool wallet_invoice_delete(struct wallet *wallet,
struct invoice invoice);
bool wallet_invoice_delete_description(struct wallet *wallet,
struct invoice invoice);
/**
* wallet_invoice_delete_expired - Delete all expired invoices
* with expiration time less than or equal to the given.
@ -999,9 +1002,9 @@ void wallet_invoice_waitone(const tal_t *ctx,
* @invoice - the invoice to get details on.
* @return pointer to the invoice details allocated off of `ctx`.
*/
const struct invoice_details *wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet,
struct invoice invoice);
struct invoice_details *wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet,
struct invoice invoice);
/**
* wallet_htlc_stubs - Retrieve HTLC stubs for the given channel