mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 05:12:45 +01:00
invoice: order by when they were paid.
We need some ordering to deliver them to the JSON "waitinvoice" command; we use a counter where 0 means "unpaid". We keep two lists now, one for unpaid and one for paid invoices. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
0761c12381
commit
27715f7732
23
daemon/db.c
23
daemon/db.c
@ -1088,23 +1088,22 @@ static void db_load_invoice(struct lightningd_state *dstate)
|
||||
|
||||
while ((err = sqlite3_step(stmt)) != SQLITE_DONE) {
|
||||
struct rval r;
|
||||
u64 msatoshi;
|
||||
bool complete;
|
||||
u64 msatoshi, paid_num;
|
||||
const char *label;
|
||||
|
||||
if (err != SQLITE_ROW)
|
||||
fatal("db_load_invoice:step gave %s:%s",
|
||||
sqlite3_errstr(err),
|
||||
sqlite3_errmsg(dstate->db->sql));
|
||||
if (sqlite3_column_count(stmt) != 3)
|
||||
fatal("db_load_pay:step gave %i cols, not 3",
|
||||
if (sqlite3_column_count(stmt) != 4)
|
||||
fatal("db_load_invoice:step gave %i cols, not 4",
|
||||
sqlite3_column_count(stmt));
|
||||
|
||||
from_sql_blob(stmt, 0, &r, sizeof(r));
|
||||
msatoshi = sqlite3_column_int64(stmt, 1);
|
||||
label = (const char *)sqlite3_column_text(stmt, 2);
|
||||
complete = sqlite3_column_int(stmt, 3);
|
||||
invoice_add(dstate, &r, msatoshi, label, complete);
|
||||
paid_num = sqlite3_column_int64(stmt, 3);
|
||||
invoice_add(dstate, &r, msatoshi, label, paid_num);
|
||||
}
|
||||
tal_free(ctx);
|
||||
}
|
||||
@ -1199,8 +1198,8 @@ void db_init(struct lightningd_state *dstate)
|
||||
"PRIMARY KEY(rhash)")
|
||||
TABLE(invoice,
|
||||
SQL_R(r), SQL_U64(msatoshi), SQL_INVLABEL(label),
|
||||
SQL_BOOL(complete),
|
||||
"PRIMARY KEY(r)")
|
||||
SQL_U64(paid_num),
|
||||
"PRIMARY KEY(label)")
|
||||
TABLE(anchors,
|
||||
SQL_PUBKEY(peer),
|
||||
SQL_TXID(txid), SQL_U32(idx), SQL_U64(amount),
|
||||
@ -1953,7 +1952,8 @@ bool db_new_invoice(struct lightningd_state *dstate,
|
||||
return !errmsg;
|
||||
}
|
||||
|
||||
bool db_resolve_invoice(struct lightningd_state *dstate, const struct rval *r)
|
||||
bool db_resolve_invoice(struct lightningd_state *dstate,
|
||||
const char *label, u64 paid_num)
|
||||
{
|
||||
const char *errmsg, *ctx = tal(dstate, char);
|
||||
|
||||
@ -1961,9 +1961,8 @@ bool db_resolve_invoice(struct lightningd_state *dstate, const struct rval *r)
|
||||
|
||||
assert(dstate->db->in_transaction);
|
||||
|
||||
errmsg = db_exec(ctx, dstate, "UPDATE invoice SET complete=%s WHERE r=x'%s';",
|
||||
sql_bool(true),
|
||||
tal_hexstr(ctx, r, sizeof(*r)));
|
||||
errmsg = db_exec(ctx, dstate, "UPDATE invoice SET paid_num=%"PRIu64" WHERE label=x'%s';",
|
||||
paid_num, tal_hexstr(ctx, label, strlen(label)));
|
||||
if (errmsg)
|
||||
log_broken(dstate->base_log, "%s:%s", __func__, errmsg);
|
||||
tal_free(ctx);
|
||||
|
@ -55,7 +55,8 @@ bool db_update_htlc_state(struct peer *peer, const struct htlc *htlc,
|
||||
enum htlc_state oldstate);
|
||||
bool db_complete_pay_command(struct lightningd_state *dstate,
|
||||
const struct htlc *htlc);
|
||||
bool db_resolve_invoice(struct lightningd_state *dstate, const struct rval *r);
|
||||
bool db_resolve_invoice(struct lightningd_state *dstate,
|
||||
const char *label, u64 paid_num);
|
||||
bool db_update_feechange_state(struct peer *peer,
|
||||
const struct feechange *f,
|
||||
enum htlc_state oldstate);
|
||||
|
@ -7,24 +7,36 @@
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <sodium/randombytes.h>
|
||||
|
||||
struct invoice *find_invoice(struct lightningd_state *dstate,
|
||||
const struct sha256 *rhash)
|
||||
static struct invoice *find_inv(const struct list_head *list,
|
||||
const struct sha256 *rhash)
|
||||
{
|
||||
struct invoice *i;
|
||||
|
||||
list_for_each(&dstate->invoices, i, list) {
|
||||
list_for_each(list, i, list) {
|
||||
if (structeq(rhash, &i->rhash))
|
||||
return i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct invoice *find_invoice_by_label(struct lightningd_state *dstate,
|
||||
struct invoice *find_unpaid(struct lightningd_state *dstate,
|
||||
const struct sha256 *rhash)
|
||||
{
|
||||
return find_inv(&dstate->unpaid, rhash);
|
||||
}
|
||||
|
||||
static struct invoice *find_paid(struct lightningd_state *dstate,
|
||||
const struct sha256 *rhash)
|
||||
{
|
||||
return find_inv(&dstate->paid, rhash);
|
||||
}
|
||||
|
||||
static struct invoice *find_invoice_by_label(const struct list_head *list,
|
||||
const char *label)
|
||||
{
|
||||
struct invoice *i;
|
||||
|
||||
list_for_each(&dstate->invoices, i, list) {
|
||||
list_for_each(list, i, list) {
|
||||
if (streq(i->label, label))
|
||||
return i;
|
||||
}
|
||||
@ -35,18 +47,33 @@ void invoice_add(struct lightningd_state *dstate,
|
||||
const struct rval *r,
|
||||
u64 msatoshi,
|
||||
const char *label,
|
||||
bool complete)
|
||||
u64 paid_num)
|
||||
{
|
||||
struct invoice *invoice = tal(dstate, struct invoice);
|
||||
|
||||
invoice->msatoshi = msatoshi;
|
||||
invoice->r = *r;
|
||||
invoice->complete = complete;
|
||||
invoice->paid_num = paid_num;
|
||||
invoice->label = tal_strdup(invoice, label);
|
||||
sha256(&invoice->rhash, invoice->r.r, sizeof(invoice->r.r));
|
||||
list_add(&dstate->invoices, &invoice->list);
|
||||
|
||||
if (paid_num) {
|
||||
list_add(&dstate->paid, &invoice->list);
|
||||
if (paid_num > dstate->invoices_completed)
|
||||
dstate->invoices_completed = paid_num;
|
||||
} else
|
||||
list_add(&dstate->unpaid, &invoice->list);
|
||||
}
|
||||
|
||||
bool resolve_invoice(struct lightningd_state *dstate,
|
||||
struct invoice *invoice)
|
||||
{
|
||||
invoice->paid_num = ++dstate->invoices_completed;
|
||||
list_del_from(&dstate->unpaid, &invoice->list);
|
||||
list_add_tail(&dstate->paid, &invoice->list);
|
||||
return db_resolve_invoice(dstate, invoice->label, invoice->paid_num);
|
||||
}
|
||||
|
||||
static void json_invoice(struct command *cmd,
|
||||
const char *buffer, const jsmntok_t *params)
|
||||
{
|
||||
@ -75,7 +102,8 @@ static void json_invoice(struct command *cmd,
|
||||
randombytes_buf(invoice->r.r, sizeof(invoice->r.r));
|
||||
|
||||
sha256(&invoice->rhash, invoice->r.r, sizeof(invoice->r.r));
|
||||
if (find_invoice(cmd->dstate, &invoice->rhash)) {
|
||||
if (find_unpaid(cmd->dstate, &invoice->rhash)
|
||||
|| find_paid(cmd->dstate, &invoice->rhash)) {
|
||||
command_fail(cmd, "Duplicate r value '%.*s'",
|
||||
r->end - r->start, buffer + r->start);
|
||||
return;
|
||||
@ -91,7 +119,8 @@ static void json_invoice(struct command *cmd,
|
||||
|
||||
invoice->label = tal_strndup(invoice, buffer + label->start,
|
||||
label->end - label->start);
|
||||
if (find_invoice_by_label(cmd->dstate, invoice->label)) {
|
||||
if (find_invoice_by_label(&cmd->dstate->paid, invoice->label)
|
||||
|| find_invoice_by_label(&cmd->dstate->unpaid, invoice->label)) {
|
||||
command_fail(cmd, "Duplicate label '%s'", invoice->label);
|
||||
return;
|
||||
}
|
||||
@ -100,7 +129,7 @@ static void json_invoice(struct command *cmd,
|
||||
INVOICE_MAX_LABEL_LEN);
|
||||
return;
|
||||
}
|
||||
invoice->complete = false;
|
||||
invoice->paid_num = 0;
|
||||
|
||||
if (!db_new_invoice(cmd->dstate, invoice->msatoshi, invoice->label,
|
||||
&invoice->r)) {
|
||||
@ -109,7 +138,7 @@ static void json_invoice(struct command *cmd,
|
||||
}
|
||||
/* OK, connect it to main state, respond with hash */
|
||||
tal_steal(cmd->dstate, invoice);
|
||||
list_add(&cmd->dstate->invoices, &invoice->list);
|
||||
list_add(&cmd->dstate->unpaid, &invoice->list);
|
||||
|
||||
json_object_start(response, NULL);
|
||||
json_add_hex(response, "rhash",
|
||||
@ -126,10 +155,27 @@ const struct json_command invoice_command = {
|
||||
"Returns the {rhash} on success. "
|
||||
};
|
||||
|
||||
static void json_add_invoices(struct json_result *response,
|
||||
const struct list_head *list,
|
||||
const char *buffer, const jsmntok_t *label)
|
||||
{
|
||||
struct invoice *i;
|
||||
|
||||
list_for_each(list, i, list) {
|
||||
if (label && !json_tok_streq(buffer, label, i->label))
|
||||
continue;
|
||||
json_object_start(response, NULL);
|
||||
json_add_string(response, "label", i->label);
|
||||
json_add_hex(response, "rhash", &i->rhash, sizeof(i->rhash));
|
||||
json_add_u64(response, "msatoshi", i->msatoshi);
|
||||
json_add_bool(response, "complete", i->paid_num != 0);
|
||||
json_object_end(response);
|
||||
}
|
||||
}
|
||||
|
||||
static void json_listinvoice(struct command *cmd,
|
||||
const char *buffer, const jsmntok_t *params)
|
||||
{
|
||||
struct invoice *i;
|
||||
jsmntok_t *label = NULL;
|
||||
struct json_result *response = new_json_result(cmd);
|
||||
|
||||
@ -143,16 +189,8 @@ static void json_listinvoice(struct command *cmd,
|
||||
|
||||
json_object_start(response, NULL);
|
||||
json_array_start(response, NULL);
|
||||
list_for_each(&cmd->dstate->invoices, i, list) {
|
||||
if (label && !json_tok_streq(buffer, label, i->label))
|
||||
continue;
|
||||
json_object_start(response, NULL);
|
||||
json_add_string(response, "label", i->label);
|
||||
json_add_hex(response, "rhash", &i->rhash, sizeof(i->rhash));
|
||||
json_add_u64(response, "msatoshi", i->msatoshi);
|
||||
json_add_bool(response, "complete", i->complete);
|
||||
json_object_end(response);
|
||||
}
|
||||
json_add_invoices(response, &cmd->dstate->paid, buffer, label);
|
||||
json_add_invoices(response, &cmd->dstate->unpaid, buffer, label);
|
||||
json_array_end(response);
|
||||
json_object_end(response);
|
||||
command_success(cmd, response);
|
||||
@ -182,20 +220,16 @@ static void json_delinvoice(struct command *cmd,
|
||||
|
||||
label = tal_strndup(cmd, buffer + labeltok->start,
|
||||
labeltok->end - labeltok->start);
|
||||
i = find_invoice_by_label(cmd->dstate, label);
|
||||
i = find_invoice_by_label(&cmd->dstate->unpaid, label);
|
||||
if (!i) {
|
||||
command_fail(cmd, "Unknown invoice");
|
||||
return;
|
||||
}
|
||||
if (i->complete) {
|
||||
command_fail(cmd, "Invoice already paid");
|
||||
return;
|
||||
}
|
||||
if (!db_remove_invoice(cmd->dstate, i->label)) {
|
||||
command_fail(cmd, "Database error");
|
||||
return;
|
||||
}
|
||||
list_del_from(&cmd->dstate->invoices, &i->list);
|
||||
list_del_from(&cmd->dstate->unpaid, &i->list);
|
||||
|
||||
json_object_start(response, NULL);
|
||||
json_add_string(response, "label", i->label);
|
||||
|
@ -11,7 +11,7 @@ struct invoice {
|
||||
u64 msatoshi;
|
||||
struct rval r;
|
||||
struct sha256 rhash;
|
||||
bool complete;
|
||||
u64 paid_num;
|
||||
};
|
||||
|
||||
#define INVOICE_MAX_LABEL_LEN 128
|
||||
@ -21,9 +21,12 @@ void invoice_add(struct lightningd_state *dstate,
|
||||
const struct rval *r,
|
||||
u64 msatoshi,
|
||||
const char *label,
|
||||
bool complete);
|
||||
u64 complete);
|
||||
|
||||
struct invoice *find_invoice(struct lightningd_state *dstate,
|
||||
const struct sha256 *rhash);
|
||||
bool resolve_invoice(struct lightningd_state *dstate,
|
||||
struct invoice *invoice);
|
||||
|
||||
struct invoice *find_unpaid(struct lightningd_state *dstate,
|
||||
const struct sha256 *rhash);
|
||||
|
||||
#endif /* LIGHTNING_DAEMON_INVOICE_H */
|
||||
|
@ -255,7 +255,9 @@ static struct lightningd_state *lightningd_state(void)
|
||||
default_config(&dstate->config);
|
||||
list_head_init(&dstate->bitcoin_req);
|
||||
list_head_init(&dstate->wallet);
|
||||
list_head_init(&dstate->invoices);
|
||||
list_head_init(&dstate->unpaid);
|
||||
list_head_init(&dstate->paid);
|
||||
dstate->invoices_completed = 0;
|
||||
list_head_init(&dstate->addresses);
|
||||
dstate->dev_never_routefail = false;
|
||||
dstate->bitcoin_req_running = false;
|
||||
|
@ -111,7 +111,8 @@ struct lightningd_state {
|
||||
struct list_head wallet;
|
||||
|
||||
/* Payments for r values we know about. */
|
||||
struct list_head invoices;
|
||||
struct list_head paid, unpaid;
|
||||
u64 invoices_completed;
|
||||
|
||||
/* All known nodes. */
|
||||
struct node_map *nodes;
|
||||
|
@ -535,7 +535,7 @@ static void their_htlc_added(struct peer *peer, struct htlc *htlc,
|
||||
case ROUTE_STEP__NEXT_END:
|
||||
if (only_dest)
|
||||
return;
|
||||
invoice = find_invoice(peer->dstate, &htlc->rhash);
|
||||
invoice = find_unpaid(peer->dstate, &htlc->rhash);
|
||||
if (!invoice) {
|
||||
log_unusual(peer->log, "No invoice for HTLC %"PRIu64,
|
||||
htlc->id);
|
||||
@ -561,28 +561,16 @@ static void their_htlc_added(struct peer *peer, struct htlc *htlc,
|
||||
return;
|
||||
}
|
||||
|
||||
/* This is a courtesy: we could simply take your money! */
|
||||
if (invoice->complete) {
|
||||
log_unusual(peer->log,
|
||||
"Repeated payment for '%s' HTLC %"PRIu64,
|
||||
invoice->label, htlc->id);
|
||||
command_htlc_set_fail(peer, htlc,
|
||||
UNAUTHORIZED_401,
|
||||
"already received payment");
|
||||
return;
|
||||
}
|
||||
|
||||
log_info(peer->log, "Immediately resolving '%s' HTLC %"PRIu64,
|
||||
invoice->label, htlc->id);
|
||||
|
||||
if (!db_resolve_invoice(peer->dstate, &invoice->r)) {
|
||||
if (!resolve_invoice(peer->dstate, invoice)) {
|
||||
command_htlc_set_fail(peer, htlc,
|
||||
INTERNAL_SERVER_ERROR_500,
|
||||
"database error");
|
||||
return;
|
||||
}
|
||||
|
||||
invoice->complete = true;
|
||||
set_htlc_rval(peer, htlc, &invoice->r);
|
||||
command_htlc_fulfill(peer, htlc);
|
||||
goto free_rest;
|
||||
|
Loading…
Reference in New Issue
Block a user