invoices: Semantically separate invoice details from invoice.

In preparation for removing in-memory invoice structures.
Invoice details are requested rarely anyway.
This commit is contained in:
ZmnSCPxj 2018-02-20 01:07:30 +00:00 committed by Christian Decker
parent fc3e6782dd
commit 9b4c6699f9
7 changed files with 166 additions and 72 deletions

View File

@ -19,7 +19,7 @@
#include <sodium/randombytes.h>
#include <wire/wire_sync.h>
static const char *invoice_status_str(const struct invoice *inv)
static const char *invoice_status_str(const struct invoice_details *inv)
{
if (inv->state == PAID)
return "paid";
@ -29,7 +29,7 @@ static const char *invoice_status_str(const struct invoice *inv)
}
static void json_add_invoice(struct json_result *response,
const struct invoice *inv,
const struct invoice_details *inv,
bool modern)
{
json_object_start(response, NULL);
@ -60,9 +60,11 @@ static void json_add_invoice(struct json_result *response,
static void tell_waiter(struct command *cmd, const struct invoice *inv)
{
struct json_result *response = new_json_result(cmd);
struct invoice_details details;
json_add_invoice(response, inv, true);
if (inv->state == PAID)
wallet_invoice_details(cmd, cmd->ld->wallet, inv, &details);
json_add_invoice(response, &details, true);
if (details.state == PAID)
command_success(cmd, response);
else
command_fail_detailed(cmd, -2, response,
@ -103,6 +105,7 @@ static void json_invoice(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
const struct invoice *invoice;
struct invoice_details details;
jsmntok_t *msatoshi, *label, *desc, *exp, *fallback;
u64 *msatoshi_val;
const char *label_val;
@ -199,11 +202,14 @@ static void json_invoice(struct command *cmd,
return;
}
/* Get details */
wallet_invoice_details(cmd, cmd->ld->wallet, invoice, &details);
/* Construct bolt11 string. */
b11 = new_bolt11(cmd, invoice->msatoshi);
b11 = new_bolt11(cmd, details.msatoshi);
b11->chain = get_chainparams(cmd->ld);
b11->timestamp = time_now().ts.tv_sec;
b11->payment_hash = invoice->rhash;
b11->payment_hash = details.rhash;
b11->receiver_id = cmd->ld->id;
b11->min_final_cltv_expiry = cmd->ld->config.cltv_final;
b11->expiry = expiry;
@ -217,10 +223,10 @@ static void json_invoice(struct command *cmd,
json_object_start(response, NULL);
json_add_hex(response, "payment_hash",
&invoice->rhash, sizeof(invoice->rhash));
&details.rhash, sizeof(details.rhash));
if (deprecated_apis)
json_add_u64(response, "expiry_time", invoice->expiry_time);
json_add_u64(response, "expires_at", invoice->expiry_time);
json_add_u64(response, "expiry_time", details.expiry_time);
json_add_u64(response, "expires_at", details.expiry_time);
json_add_string(response, "bolt11", b11enc);
json_object_end(response);
@ -240,15 +246,17 @@ static void json_add_invoices(struct json_result *response,
bool modern)
{
const struct invoice *i;
struct invoice_details details;
char *lbl = NULL;
if (label)
lbl = tal_strndup(response, &buffer[label->start], label->end - label->start);
i = NULL;
while ((i = wallet_invoice_iterate(wallet, i)) != NULL) {
if (lbl && !streq(i->label, lbl))
wallet_invoice_details(response, wallet, i, &details);
if (lbl && !streq(details.label, lbl))
continue;
json_add_invoice(response, i, modern);
json_add_invoice(response, &details, modern);
}
}
@ -311,6 +319,7 @@ static void json_delinvoice(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
const struct invoice *i;
struct invoice_details details;
jsmntok_t *labeltok, *statustok;
struct json_result *response = new_json_result(cmd);
const char *label, *status, *actual_status;
@ -330,12 +339,13 @@ static void json_delinvoice(struct command *cmd,
command_fail(cmd, "Unknown invoice");
return;
}
wallet_invoice_details(cmd, cmd->ld->wallet, i, &details);
status = tal_strndup(cmd, buffer + statustok->start,
statustok->end - statustok->start);
/* This is time-sensitive, so only call once; otherwise error msg
* might not make sense if it changed! */
actual_status = invoice_status_str(i);
actual_status = invoice_status_str(&details);
if (!streq(actual_status, status)) {
command_fail(cmd, "Invoice status is %s not %s",
actual_status, status);
@ -344,7 +354,7 @@ static void json_delinvoice(struct command *cmd,
/* Get invoice details before attempting to delete, as
* otherwise the invoice will be freed. */
json_add_invoice(response, i, true);
json_add_invoice(response, &details, true);
if (!wallet_invoice_delete(wallet, i)) {
log_broken(cmd->ld->log,
@ -415,6 +425,7 @@ static void json_waitinvoice(struct command *cmd,
const char *buffer, const jsmntok_t *params)
{
const struct invoice *i;
struct invoice_details details;
struct wallet *wallet = cmd->ld->wallet;
jsmntok_t *labeltok;
const char *label = NULL;
@ -430,7 +441,10 @@ static void json_waitinvoice(struct command *cmd,
if (!i) {
command_fail(cmd, "Label not found");
return;
} else if (i->state == PAID || i->state == EXPIRED) {
}
wallet_invoice_details(cmd, cmd->ld->wallet, i, &details);
if (details.state == PAID || details.state == EXPIRED) {
tell_waiter(cmd, i);
return;
} else {

View File

@ -230,7 +230,9 @@ static void handle_localpay(struct htlc_in *hin,
{
enum onion_type failcode;
const struct invoice *invoice;
struct invoice_details details;
struct lightningd *ld = hin->key.channel->peer->ld;
const tal_t *tmpctx = tal_tmpctx(ld);
/* BOLT #4:
*
@ -265,6 +267,7 @@ static void handle_localpay(struct htlc_in *hin,
failcode = WIRE_UNKNOWN_PAYMENT_HASH;
goto fail;
}
wallet_invoice_details(tmpctx, ld->wallet, invoice, &details);
/* BOLT #4:
*
@ -276,10 +279,10 @@ static void handle_localpay(struct htlc_in *hin,
*
* 1. type: PERM|16 (`incorrect_payment_amount`)
*/
if (invoice->msatoshi != NULL && hin->msatoshi < *invoice->msatoshi) {
if (details.msatoshi != NULL && hin->msatoshi < *details.msatoshi) {
failcode = WIRE_INCORRECT_PAYMENT_AMOUNT;
goto fail;
} else if (invoice->msatoshi != NULL && hin->msatoshi > *invoice->msatoshi * 2) {
} else if (details.msatoshi != NULL && hin->msatoshi > *details.msatoshi * 2) {
failcode = WIRE_INCORRECT_PAYMENT_AMOUNT;
goto fail;
}
@ -300,17 +303,21 @@ static void handle_localpay(struct htlc_in *hin,
}
log_info(ld->log, "Resolving invoice '%s' with HTLC %"PRIu64,
invoice->label, hin->key.id);
details.label, hin->key.id);
log_debug(ld->log, "%s: Actual amount %"PRIu64"msat, HTLC expiry %u",
invoice->label, hin->msatoshi, cltv_expiry);
fulfill_htlc(hin, &invoice->r);
details.label, hin->msatoshi, cltv_expiry);
fulfill_htlc(hin, &details.r);
wallet_invoice_resolve(ld->wallet, invoice, hin->msatoshi);
tal_free(tmpctx);
return;
fail:
/* Final hop never sends an UPDATE. */
assert(!(failcode & UPDATE));
local_fail_htlc(hin, failcode, NULL);
tal_free(tmpctx);
}
/*

View File

@ -42,36 +42,38 @@ static void trigger_invoice_waiter(struct invoice_waiter *w,
w->cb(invoice, w->cbarg);
}
static bool wallet_stmt2invoice(sqlite3_stmt *stmt, struct invoice *inv)
static bool wallet_stmt2invoice_details(sqlite3_stmt *stmt,
struct invoice *invoice,
struct invoice_details *dtl)
{
inv->id = sqlite3_column_int64(stmt, 0);
inv->state = sqlite3_column_int(stmt, 1);
invoice->id = sqlite3_column_int64(stmt, 0);
dtl->state = sqlite3_column_int(stmt, 1);
assert(sqlite3_column_bytes(stmt, 2) == sizeof(struct preimage));
memcpy(&inv->r, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
memcpy(&dtl->r, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
assert(sqlite3_column_bytes(stmt, 3) == sizeof(struct sha256));
memcpy(&inv->rhash, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3));
memcpy(&dtl->rhash, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3));
inv->label = tal_strndup(inv, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
dtl->label = tal_strndup(dtl, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4));
if (sqlite3_column_type(stmt, 5) != SQLITE_NULL) {
inv->msatoshi = tal(inv, u64);
*inv->msatoshi = sqlite3_column_int64(stmt, 5);
dtl->msatoshi = tal(dtl, u64);
*dtl->msatoshi = sqlite3_column_int64(stmt, 5);
} else {
inv->msatoshi = NULL;
dtl->msatoshi = NULL;
}
inv->expiry_time = sqlite3_column_int64(stmt, 6);
dtl->expiry_time = sqlite3_column_int64(stmt, 6);
if (inv->state == PAID) {
inv->pay_index = sqlite3_column_int64(stmt, 7);
inv->msatoshi_received = sqlite3_column_int64(stmt, 8);
inv->paid_timestamp = sqlite3_column_int64(stmt, 9);
if (dtl->state == PAID) {
dtl->pay_index = sqlite3_column_int64(stmt, 7);
dtl->msatoshi_received = sqlite3_column_int64(stmt, 8);
dtl->paid_timestamp = sqlite3_column_int64(stmt, 9);
}
list_head_init(&inv->waitone_waiters);
inv->expiration_timer = NULL;
list_head_init(&invoice->waitone_waiters);
invoice->expiration_timer = NULL;
return true;
}
@ -102,7 +104,7 @@ static void trigger_expiration(struct invoice *i)
sqlite3_stmt *stmt;
struct invoice_waiter *w;
assert(i->state == UNPAID);
assert(i->details->state == UNPAID);
/* Timer already triggered, destroy the timer object. */
i->expiration_timer = tal_free(i->expiration_timer);
@ -111,11 +113,11 @@ static void trigger_expiration(struct invoice *i)
* (used by the timer system) and time_now (used
* by the expiry time measurements). So check that
* time_now is reached. */
if (i->expiry_time <= now) {
if (i->details->expiry_time <= now) {
const tal_t *tmpctx = tal_tmpctx(i);
/* Update in-memory and db. */
i->state = EXPIRED;
i->details->state = EXPIRED;
stmt = db_prepare(invoices->db,
"UPDATE invoices"
" SET state = ?"
@ -143,11 +145,11 @@ static void install_expiration_timer(struct invoices *invoices,
struct timeabs expiry;
struct timeabs now = time_now();
assert(i->state == UNPAID);
assert(i->details->state == UNPAID);
assert(!i->expiration_timer);
memset(&expiry, 0, sizeof(expiry));
expiry.ts.tv_sec = i->expiry_time;
expiry.ts.tv_sec = i->details->expiry_time;
/* now > expiry */
if (time_after(now, expiry))
@ -197,12 +199,13 @@ bool invoices_load(struct invoices *invoices)
while (sqlite3_step(stmt) == SQLITE_ROW) {
i = tal(invoices, struct invoice);
i->owner = invoices;
if (!wallet_stmt2invoice(stmt, i)) {
i->details = tal(i, struct invoice_details);
if (!wallet_stmt2invoice_details(stmt, i, i->details)) {
log_broken(invoices->log, "Error deserializing invoice");
sqlite3_finalize(stmt);
return false;
}
if (i->state == UNPAID)
if (i->details->state == UNPAID)
install_expiration_timer(invoices, i);
list_add_tail(&invoices->invlist, &i->list);
count++;
@ -272,12 +275,13 @@ const struct invoice *invoices_create(struct invoices *invoices,
invoice->owner = invoices;
invoice->id = sqlite3_last_insert_rowid(invoices->db->sql);
invoice->state = UNPAID;
invoice->label = tal_strdup(invoice, label);
invoice->msatoshi = tal_dup(invoice, u64, msatoshi); /* Works even if msatoshi == NULL. */
memcpy(&invoice->r, &r, sizeof(invoice->r));
memcpy(&invoice->rhash, &rhash, sizeof(invoice->rhash));
invoice->expiry_time = expiry_time;
invoice->details = tal(invoice, struct invoice_details);
invoice->details->state = UNPAID;
invoice->details->label = tal_strdup(invoice->details, label);
invoice->details->msatoshi = tal_dup(invoice->details, u64, msatoshi); /* Works even if msatoshi == NULL. */
memcpy(&invoice->details->r, &r, sizeof(invoice->details->r));
memcpy(&invoice->details->rhash, &rhash, sizeof(invoice->details->rhash));
invoice->details->expiry_time = expiry_time;
list_head_init(&invoice->waitone_waiters);
invoice->expiration_timer = NULL;
@ -298,7 +302,7 @@ const struct invoice *invoices_find_by_label(struct invoices *invoices,
/* FIXME: Use something better than a linear scan. */
list_for_each(&invoices->invlist, i, list) {
if (streq(i->label, label))
if (streq(i->details->label, label))
return i;
}
return NULL;
@ -310,8 +314,9 @@ const struct invoice *invoices_find_unpaid(struct invoices *invoices,
struct invoice *i;
list_for_each(&invoices->invlist, i, list) {
if (structeq(rhash, &i->rhash) && i->state == UNPAID) {
if (time_now().ts.tv_sec > i->expiry_time)
if (structeq(rhash, &i->details->rhash) &&
i->details->state == UNPAID) {
if (time_now().ts.tv_sec > i->details->expiry_time)
break;
return i;
}
@ -407,10 +412,10 @@ void invoices_resolve(struct invoices *invoices,
db_exec_prepared(invoices->db, stmt);
/* Update in-memory structure. */
invoice->state = PAID;
invoice->pay_index = pay_index;
invoice->msatoshi_received = msatoshi_received;
invoice->paid_timestamp = paid_timestamp;
invoice->details->state = PAID;
invoice->details->pay_index = pay_index;
invoice->details->msatoshi_received = msatoshi_received;
invoice->details->paid_timestamp = paid_timestamp;
invoice->expiration_timer = tal_free(invoice->expiration_timer);
/* Tell all the waitany waiters about the new paid invoice. */
@ -506,7 +511,7 @@ void invoices_waitone(const tal_t *ctx,
void *cbarg)
{
struct invoice *invoice = (struct invoice*) cinvoice;
if (invoice->state == PAID || invoice->state == EXPIRED) {
if (invoice->details->state == PAID || invoice->details->state == EXPIRED) {
cb(invoice, cbarg);
return;
}
@ -514,3 +519,24 @@ void invoices_waitone(const tal_t *ctx,
/* Not yet paid. */
add_invoice_waiter(ctx, &invoice->waitone_waiters, cb, cbarg);
}
void invoices_get_details(const tal_t *ctx,
struct invoices *invoices,
const struct invoice *invoice,
struct invoice_details *dtl)
{
dtl->state = invoice->details->state;
dtl->r = invoice->details->r;
dtl->rhash = invoice->details->rhash;
dtl->label = tal_strdup(ctx, invoice->details->label);
dtl->msatoshi =
invoice->details->msatoshi ?
tal_dup(ctx, u64, invoice->details->msatoshi) :
/*otherwise*/ NULL ;
dtl->expiry_time = invoice->details->expiry_time;
if (dtl->state == PAID) {
dtl->pay_index = invoice->details->pay_index;
dtl->msatoshi_received = invoice->details->msatoshi_received;
dtl->paid_timestamp = invoice->details->paid_timestamp;
}
}

View File

@ -7,6 +7,7 @@
struct db;
struct invoice;
struct invoice_details;
struct invoices;
struct log;
struct sha256;
@ -159,4 +160,17 @@ void invoices_waitone(const tal_t *ctx,
void (*cb)(const struct invoice *, void*),
void *cbarg);
/**
* invoices_get_details - Get the invoice_details of an invoice.
*
* @ctx - the owner of the label and msatoshi fields returned.
* @invoices - the invoice handler,
* @invoice - the invoice to get details on.
* @details - pointer to details object to load.
*/
void invoices_get_details(const tal_t *ctx,
struct invoices *invoices,
const struct invoice *invoice,
struct invoice_details *details);
#endif /* LIGHTNING_WALLET_INVOICES_H */

View File

@ -112,6 +112,12 @@ const struct invoice *invoices_find_by_label(struct invoices *invoices UNNEEDED,
const struct invoice *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 */
void invoices_get_details(const tal_t *ctx UNNEEDED,
struct invoices *invoices UNNEEDED,
const struct invoice *invoice UNNEEDED,
struct invoice_details *details UNNEEDED)
{ fprintf(stderr, "invoices_get_details called!\n"); abort(); }
/* Generated stub for invoices_iterate */
const struct invoice *invoices_iterate(struct invoices *invoices UNNEEDED,
const struct invoice *invoice UNNEEDED)

View File

@ -1314,7 +1314,13 @@ void wallet_invoice_waitone(const tal_t *ctx,
{
invoices_waitone(ctx, wallet->invoices, invoice, cb, cbarg);
}
void wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet,
const struct invoice *invoice,
struct invoice_details *details)
{
invoices_get_details(ctx, wallet->invoices, invoice, details);
}
struct htlc_stub *wallet_htlc_stubs(const tal_t *ctx, struct wallet *wallet,

View File

@ -368,6 +368,28 @@ enum invoice_status {
EXPIRED,
};
/* The information about an invoice */
struct invoice_details {
/* Current invoice state */
enum invoice_status state;
/* Preimage for this invoice */
struct preimage r;
/* Hash of preimage r */
struct sha256 rhash;
/* Label assigned by user */
const char *label;
/* NULL if they specified "any" */
u64 *msatoshi;
/* Absolute UNIX epoch time this will expire */
u64 expiry_time;
/* Set if state == PAID; order to be returned by waitanyinvoice */
u64 pay_index;
/* Set if state == PAID; amount received */
u64 msatoshi_received;
/* Set if state == PAID; time paid */
u64 paid_timestamp;
};
struct invoice {
/* Internal, rest of lightningd should not use */
/* List off ld->wallet->invoices. Must be first or else
@ -381,21 +403,8 @@ struct invoice {
struct oneshot *expiration_timer;
/* The owning invoices object. */
struct invoices *owner;
/* Publicly-usable fields. */
enum invoice_status state;
const char *label;
/* NULL if they specified "any" */
u64 *msatoshi;
/* Set if state == PAID */
u64 msatoshi_received;
/* Set if state == PAID */
u64 paid_timestamp;
struct preimage r;
u64 expiry_time;
struct sha256 rhash;
/* Set if state == PAID */
u64 pay_index;
/* Loaded details. */
struct invoice_details *details;
};
#define INVOICE_MAX_LABEL_LEN 128
@ -540,6 +549,18 @@ void wallet_invoice_waitone(const tal_t *ctx,
void (*cb)(const struct invoice *, void*),
void *cbarg);
/**
* wallet_invoice_details - Get the invoice_details of an invoice.
*
* @ctx - the owner of the label and msatoshi fields returned.
* @wallet - the wallet to query.
* @invoice - the invoice to get details on.
* @details - pointer to details object to load.
*/
void wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet,
const struct invoice *invoice,
struct invoice_details *details);
/**
* wallet_htlc_stubs - Retrieve HTLC stubs for the given channel