Browse Source

invoices: Remove persistent in-memory invoice structures.

ppa-0.6.1
ZmnSCPxj 7 years ago
committed by Christian Decker
parent
commit
978e5c67d8
  1. 36
      lightningd/invoice.c
  2. 5
      lightningd/peer_htlcs.c
  3. 304
      wallet/invoices.c
  4. 39
      wallet/invoices.h
  5. 27
      wallet/test/run-wallet.c
  6. 35
      wallet/wallet.c
  7. 53
      wallet/wallet.h

36
lightningd/invoice.c

@ -62,7 +62,7 @@ static void tell_waiter(struct command *cmd, const struct invoice *inv)
struct json_result *response = new_json_result(cmd); struct json_result *response = new_json_result(cmd);
struct invoice_details details; struct invoice_details details;
wallet_invoice_details(cmd, cmd->ld->wallet, inv, &details); wallet_invoice_details(cmd, cmd->ld->wallet, *inv, &details);
json_add_invoice(response, &details, true); json_add_invoice(response, &details, true);
if (details.state == PAID) if (details.state == PAID)
command_success(cmd, response); command_success(cmd, response);
@ -104,7 +104,7 @@ static bool hsm_sign_b11(const u5 *u5bytes,
static void json_invoice(struct command *cmd, static void json_invoice(struct command *cmd,
const char *buffer, const jsmntok_t *params) const char *buffer, const jsmntok_t *params)
{ {
const struct invoice *invoice; struct invoice invoice;
struct invoice_details details; struct invoice_details details;
jsmntok_t *msatoshi, *label, *desc, *exp, *fallback; jsmntok_t *msatoshi, *label, *desc, *exp, *fallback;
u64 *msatoshi_val; u64 *msatoshi_val;
@ -117,6 +117,7 @@ static void json_invoice(struct command *cmd,
char *b11enc; char *b11enc;
const u8 *fallback_script; const u8 *fallback_script;
u64 expiry = 3600; u64 expiry = 3600;
bool result;
if (!json_get_params(cmd, buffer, params, if (!json_get_params(cmd, buffer, params,
"msatoshi", &msatoshi, "msatoshi", &msatoshi,
@ -146,7 +147,7 @@ static void json_invoice(struct command *cmd,
/* label */ /* label */
label_val = tal_strndup(cmd, buffer + label->start, label_val = tal_strndup(cmd, buffer + label->start,
label->end - label->start); label->end - label->start);
if (wallet_invoice_find_by_label(wallet, label_val)) { if (wallet_invoice_find_by_label(wallet, &invoice, label_val)) {
command_fail(cmd, "Duplicate label '%s'", label_val); command_fail(cmd, "Duplicate label '%s'", label_val);
return; return;
} }
@ -193,11 +194,12 @@ static void json_invoice(struct command *cmd,
} }
invoice = wallet_invoice_create(cmd->ld->wallet, result = wallet_invoice_create(cmd->ld->wallet,
take(msatoshi_val), &invoice,
take(label_val), take(msatoshi_val),
expiry); take(label_val),
if (!invoice) { expiry);
if (!result) {
command_fail(cmd, "Failed to create invoice on database"); command_fail(cmd, "Failed to create invoice on database");
return; return;
} }
@ -318,7 +320,7 @@ AUTODATA(json_command, &listinvoices_command);
static void json_delinvoice(struct command *cmd, static void json_delinvoice(struct command *cmd,
const char *buffer, const jsmntok_t *params) const char *buffer, const jsmntok_t *params)
{ {
const struct invoice *i; struct invoice i;
struct invoice_details details; struct invoice_details details;
jsmntok_t *labeltok, *statustok; jsmntok_t *labeltok, *statustok;
struct json_result *response = new_json_result(cmd); struct json_result *response = new_json_result(cmd);
@ -334,8 +336,7 @@ static void json_delinvoice(struct command *cmd,
label = tal_strndup(cmd, buffer + labeltok->start, label = tal_strndup(cmd, buffer + labeltok->start,
labeltok->end - labeltok->start); labeltok->end - labeltok->start);
i = wallet_invoice_find_by_label(wallet, label); if (!wallet_invoice_find_by_label(wallet, &i, label)) {
if (!i) {
command_fail(cmd, "Unknown invoice"); command_fail(cmd, "Unknown invoice");
return; return;
} }
@ -359,7 +360,7 @@ static void json_delinvoice(struct command *cmd,
if (!wallet_invoice_delete(wallet, i)) { if (!wallet_invoice_delete(wallet, i)) {
log_broken(cmd->ld->log, log_broken(cmd->ld->log,
"Error attempting to remove invoice %"PRIu64, "Error attempting to remove invoice %"PRIu64,
i->id); i.id);
command_fail(cmd, "Database error"); command_fail(cmd, "Database error");
return; return;
} }
@ -424,7 +425,7 @@ AUTODATA(json_command, &waitanyinvoice_command);
static void json_waitinvoice(struct command *cmd, static void json_waitinvoice(struct command *cmd,
const char *buffer, const jsmntok_t *params) const char *buffer, const jsmntok_t *params)
{ {
const struct invoice *i; struct invoice i;
struct invoice_details details; struct invoice_details details;
struct wallet *wallet = cmd->ld->wallet; struct wallet *wallet = cmd->ld->wallet;
jsmntok_t *labeltok; jsmntok_t *labeltok;
@ -434,18 +435,17 @@ static void json_waitinvoice(struct command *cmd,
return; return;
} }
/* Search in paid invoices, if found return immediately */ /* Search for invoice */
label = tal_strndup(cmd, buffer + labeltok->start, labeltok->end - labeltok->start); label = tal_strndup(cmd, buffer + labeltok->start, labeltok->end - labeltok->start);
i = wallet_invoice_find_by_label(wallet, label); if (!wallet_invoice_find_by_label(wallet, &i, label)) {
if (!i) {
command_fail(cmd, "Label not found"); command_fail(cmd, "Label not found");
return; return;
} }
wallet_invoice_details(cmd, cmd->ld->wallet, i, &details); wallet_invoice_details(cmd, cmd->ld->wallet, i, &details);
/* If paid or expired return immediately */
if (details.state == PAID || details.state == EXPIRED) { if (details.state == PAID || details.state == EXPIRED) {
tell_waiter(cmd, i); tell_waiter(cmd, &i);
return; return;
} else { } else {
/* There is an unpaid one matching, let's wait... */ /* There is an unpaid one matching, let's wait... */

5
lightningd/peer_htlcs.c

@ -229,7 +229,7 @@ static void handle_localpay(struct htlc_in *hin,
u32 outgoing_cltv_value) u32 outgoing_cltv_value)
{ {
enum onion_type failcode; enum onion_type failcode;
const struct invoice *invoice; struct invoice invoice;
struct invoice_details details; struct invoice_details details;
struct lightningd *ld = hin->key.channel->peer->ld; struct lightningd *ld = hin->key.channel->peer->ld;
const tal_t *tmpctx = tal_tmpctx(ld); const tal_t *tmpctx = tal_tmpctx(ld);
@ -262,8 +262,7 @@ static void handle_localpay(struct htlc_in *hin,
goto fail; goto fail;
} }
invoice = wallet_invoice_find_unpaid(ld->wallet, payment_hash); if (!wallet_invoice_find_unpaid(ld->wallet, &invoice, payment_hash)) {
if (!invoice) {
failcode = WIRE_UNKNOWN_PAYMENT_HASH; failcode = WIRE_UNKNOWN_PAYMENT_HASH;
goto fail; goto fail;
} }

304
wallet/invoices.c

@ -37,8 +37,6 @@ struct invoices {
struct log *log; struct log *log;
/* The timers object to use for expirations. */ /* The timers object to use for expirations. */
struct timers *timers; struct timers *timers;
/* The invoice list. */
struct list_head invlist;
/* Waiters waiting for invoices to be paid, expired, or deleted. */ /* Waiters waiting for invoices to be paid, expired, or deleted. */
struct list_head waiters; struct list_head waiters;
/* Earliest time for some invoice to expire */ /* Earliest time for some invoice to expire */
@ -92,37 +90,36 @@ trigger_invoice_waiter_expire_or_delete(struct invoices *invoices,
tal_free(tmpctx); tal_free(tmpctx);
} }
static bool wallet_stmt2invoice_details(sqlite3_stmt *stmt, static void wallet_stmt2invoice_details(const tal_t *ctx,
struct invoice *invoice, sqlite3_stmt *stmt,
struct invoice_details *dtl) struct invoice_details *dtl)
{ {
invoice->id = sqlite3_column_int64(stmt, 0); dtl->state = sqlite3_column_int(stmt, 0);
dtl->state = sqlite3_column_int(stmt, 1);
assert(sqlite3_column_bytes(stmt, 2) == sizeof(struct preimage)); assert(sqlite3_column_bytes(stmt, 1) == sizeof(struct preimage));
memcpy(&dtl->r, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2)); memcpy(&dtl->r, sqlite3_column_blob(stmt, 1), sqlite3_column_bytes(stmt, 1));
assert(sqlite3_column_bytes(stmt, 3) == sizeof(struct sha256)); assert(sqlite3_column_bytes(stmt, 2) == sizeof(struct sha256));
memcpy(&dtl->rhash, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3)); memcpy(&dtl->rhash, sqlite3_column_blob(stmt, 2), sqlite3_column_bytes(stmt, 2));
dtl->label = tal_strndup(dtl, sqlite3_column_blob(stmt, 4), sqlite3_column_bytes(stmt, 4)); dtl->label = tal_strndup(ctx, sqlite3_column_blob(stmt, 3), sqlite3_column_bytes(stmt, 3));
if (sqlite3_column_type(stmt, 5) != SQLITE_NULL) { if (sqlite3_column_type(stmt, 4) != SQLITE_NULL) {
dtl->msatoshi = tal(dtl, u64); dtl->msatoshi = tal(ctx, u64);
*dtl->msatoshi = sqlite3_column_int64(stmt, 5); *dtl->msatoshi = sqlite3_column_int64(stmt, 4);
} else { } else {
dtl->msatoshi = NULL; dtl->msatoshi = NULL;
} }
dtl->expiry_time = sqlite3_column_int64(stmt, 6); dtl->expiry_time = sqlite3_column_int64(stmt, 5);
if (dtl->state == PAID) { if (dtl->state == PAID) {
dtl->pay_index = sqlite3_column_int64(stmt, 7); dtl->pay_index = sqlite3_column_int64(stmt, 6);
dtl->msatoshi_received = sqlite3_column_int64(stmt, 8); dtl->msatoshi_received = sqlite3_column_int64(stmt, 7);
dtl->paid_timestamp = sqlite3_column_int64(stmt, 9); dtl->paid_timestamp = sqlite3_column_int64(stmt, 8);
} }
return true; return;
} }
struct invoices *invoices_new(const tal_t *ctx, struct invoices *invoices_new(const tal_t *ctx,
@ -136,7 +133,6 @@ struct invoices *invoices_new(const tal_t *ctx,
invs->log = log; invs->log = log;
invs->timers = timers; invs->timers = timers;
list_head_init(&invs->invlist);
list_head_init(&invs->waiters); list_head_init(&invs->waiters);
invs->expiration_timer = NULL; invs->expiration_timer = NULL;
@ -159,19 +155,6 @@ static void update_db_expirations(struct invoices *invoices, u64 now)
db_exec_prepared(invoices->db, stmt); db_exec_prepared(invoices->db, stmt);
} }
static struct invoice *invoices_find_by_id(struct invoices *invoices,
u64 id)
{
struct invoice *i;
/* FIXME: Use something better than a linear scan. */
list_for_each(&invoices->invlist, i, list) {
if (i->id == id)
return i;
}
return NULL;
}
struct invoice_id_node { struct invoice_id_node {
struct list_node list; struct list_node list;
u64 id; u64 id;
@ -185,7 +168,7 @@ static void trigger_expiration(struct invoices *invoices)
struct invoice_id_node *idn; struct invoice_id_node *idn;
u64 now = time_now().ts.tv_sec; u64 now = time_now().ts.tv_sec;
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
struct invoice *i; struct invoice i;
/* Free current expiration timer */ /* Free current expiration timer */
invoices->expiration_timer = tal_free(invoices->expiration_timer); invoices->expiration_timer = tal_free(invoices->expiration_timer);
@ -211,13 +194,11 @@ static void trigger_expiration(struct invoices *invoices)
/* Trigger expirations */ /* Trigger expirations */
list_for_each(&idlist, idn, list) { list_for_each(&idlist, idn, list) {
/* Update in-memory structure */
i = invoices_find_by_id(invoices, idn->id);
i->details->state = EXPIRED;
/* Trigger expiration */ /* Trigger expiration */
i.id = idn->id;
trigger_invoice_waiter_expire_or_delete(invoices, trigger_invoice_waiter_expire_or_delete(invoices,
idn->id, idn->id,
i); &i);
} }
install_expiration_timer(invoices); install_expiration_timer(invoices);
@ -271,63 +252,34 @@ static void install_expiration_timer(struct invoices *invoices)
bool invoices_load(struct invoices *invoices) bool invoices_load(struct invoices *invoices)
{ {
int count = 0;
u64 now = time_now().ts.tv_sec; u64 now = time_now().ts.tv_sec;
struct invoice *i;
sqlite3_stmt *stmt;
update_db_expirations(invoices, now); update_db_expirations(invoices, now);
/* Load invoices from db. */
stmt = db_query(__func__, invoices->db,
"SELECT id, state, payment_key, payment_hash"
" , label, msatoshi, expiry_time, pay_index"
" , msatoshi_received, paid_timestamp"
" FROM invoices;");
if (!stmt) {
log_broken(invoices->log, "Could not load invoices");
return false;
}
while (sqlite3_step(stmt) == SQLITE_ROW) {
i = tal(invoices, struct invoice);
i->owner = invoices;
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;
}
list_add_tail(&invoices->invlist, &i->list);
count++;
}
log_debug(invoices->log, "Loaded %d invoices from DB", count);
sqlite3_finalize(stmt);
install_expiration_timer(invoices); install_expiration_timer(invoices);
return true; return true;
} }
const struct invoice *invoices_create(struct invoices *invoices, bool invoices_create(struct invoices *invoices,
u64 *msatoshi TAKES, struct invoice *pinvoice,
const char *label TAKES, u64 *msatoshi TAKES,
u64 expiry) const char *label TAKES,
u64 expiry)
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
struct invoice *invoice; struct invoice dummy;
struct preimage r; struct preimage r;
struct sha256 rhash; struct sha256 rhash;
u64 expiry_time; u64 expiry_time;
u64 now = time_now().ts.tv_sec; u64 now = time_now().ts.tv_sec;
if (invoices_find_by_label(invoices, label)) { if (invoices_find_by_label(invoices, &dummy, label)) {
if (taken(msatoshi)) if (taken(msatoshi))
tal_free(msatoshi); tal_free(msatoshi);
if (taken(label)) if (taken(label))
tal_free(label); tal_free(label);
return NULL; return false;
} }
/* Compute expiration. */ /* Compute expiration. */
@ -363,21 +315,7 @@ const struct invoice *invoices_create(struct invoices *invoices,
db_exec_prepared(invoices->db, stmt); db_exec_prepared(invoices->db, stmt);
/* Create and load in-memory structure. */ pinvoice->id = sqlite3_last_insert_rowid(invoices->db->sql);
invoice = tal(invoices, struct invoice);
invoice->owner = invoices;
invoice->id = sqlite3_last_insert_rowid(invoices->db->sql);
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;
/* Add to invoices object. */
list_add_tail(&invoices->invlist, &invoice->list);
/* Install expiration trigger. */ /* Install expiration trigger. */
if (!invoices->expiration_timer || if (!invoices->expiration_timer ||
@ -387,80 +325,109 @@ const struct invoice *invoices_create(struct invoices *invoices,
install_expiration_timer(invoices); install_expiration_timer(invoices);
} }
return invoice; if (taken(msatoshi))
tal_free(msatoshi);
if (taken(label))
tal_free(label);
return true;
} }
const struct invoice *invoices_find_by_label(struct invoices *invoices, bool invoices_find_by_label(struct invoices *invoices,
const char *label) struct invoice *pinvoice,
const char *label)
{ {
struct invoice *i; sqlite3_stmt *stmt;
/* FIXME: Use something better than a linear scan. */ stmt = db_prepare(invoices->db,
list_for_each(&invoices->invlist, i, list) { "SELECT id"
if (streq(i->details->label, label)) " FROM invoices"
return i; " WHERE label = ?;");
sqlite3_bind_text(stmt, 1, label, strlen(label), SQLITE_TRANSIENT);
if (sqlite3_step(stmt) == SQLITE_ROW) {
pinvoice->id = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
return true;
} else {
sqlite3_finalize(stmt);
return false;
} }
return NULL;
} }
const struct invoice *invoices_find_unpaid(struct invoices *invoices, bool invoices_find_unpaid(struct invoices *invoices,
const struct sha256 *rhash) struct invoice *pinvoice,
const struct sha256 *rhash)
{ {
struct invoice *i; sqlite3_stmt *stmt;
list_for_each(&invoices->invlist, i, list) { stmt = db_prepare(invoices->db,
if (structeq(rhash, &i->details->rhash) && "SELECT id"
i->details->state == UNPAID) { " FROM invoices"
if (time_now().ts.tv_sec > i->details->expiry_time) " WHERE payment_hash = ?"
break; " AND state = ?;");
return i; sqlite3_bind_blob(stmt, 1, rhash, sizeof(*rhash), SQLITE_TRANSIENT);
} sqlite3_bind_int(stmt, 2, UNPAID);
if (sqlite3_step(stmt) == SQLITE_ROW) {
pinvoice->id = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
return true;
} else {
sqlite3_finalize(stmt);
return false;
} }
return NULL;
} }
bool invoices_delete(struct invoices *invoices, bool invoices_delete(struct invoices *invoices,
const struct invoice *cinvoice) struct invoice invoice)
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
struct invoice *invoice = (struct invoice *) cinvoice;
/* Delete from database. */ /* Delete from database. */
stmt = db_prepare(invoices->db, "DELETE FROM invoices WHERE id=?;"); stmt = db_prepare(invoices->db, "DELETE FROM invoices WHERE id=?;");
sqlite3_bind_int64(stmt, 1, invoice->id); sqlite3_bind_int64(stmt, 1, invoice.id);
db_exec_prepared(invoices->db, stmt); db_exec_prepared(invoices->db, stmt);
if (sqlite3_changes(invoices->db->sql) != 1) if (sqlite3_changes(invoices->db->sql) != 1)
return false; return false;
/* Delete from invoices object. */
list_del_from(&invoices->invlist, &invoice->list);
/* Tell all the waiters about the fact that it was deleted. */ /* Tell all the waiters about the fact that it was deleted. */
trigger_invoice_waiter_expire_or_delete(invoices, trigger_invoice_waiter_expire_or_delete(invoices,
invoice->id, NULL); invoice.id, NULL);
/* Free all watchers and the invoice. */
tal_free(invoice);
return true; return true;
} }
bool invoices_iterate(struct invoices *invoices, bool invoices_iterate(struct invoices *invoices,
struct invoice_iterator *it) struct invoice_iterator *it)
{ {
if (it->curr) sqlite3_stmt *stmt;
it->curr = list_next(&invoices->invlist, it->curr, list); int res;
else if (!it->p) {
it->curr = list_top(&invoices->invlist, struct invoice, list); stmt = db_prepare(invoices->db,
return it->curr != NULL; "SELECT state, payment_key, payment_hash"
" , label, msatoshi, expiry_time, pay_index"
" , msatoshi_received, paid_timestamp"
" FROM invoices;");
it->p = stmt;
} else
stmt = it->p;
res = sqlite3_step(stmt);
if (res == SQLITE_DONE) {
sqlite3_finalize(stmt);
it->p = NULL;
return false;
} else {
assert(res == SQLITE_ROW);
return true;
}
} }
void invoices_iterator_deref(const tal_t *ctx, void invoices_iterator_deref(const tal_t *ctx,
struct invoices *invoices, struct invoices *invoices,
const struct invoice_iterator *it, const struct invoice_iterator *it,
struct invoice_details *details) struct invoice_details *details)
{ {
invoices_get_details(ctx, invoices, it->curr, details); assert(it->p);
wallet_stmt2invoice_details(ctx, (sqlite3_stmt*) it->p, details);
} }
static s64 get_next_pay_index(struct db *db) static s64 get_next_pay_index(struct db *db)
@ -476,11 +443,10 @@ static s64 get_next_pay_index(struct db *db)
void invoices_resolve(struct invoices *invoices, void invoices_resolve(struct invoices *invoices,
const struct invoice *cinvoice, struct invoice invoice,
u64 msatoshi_received) u64 msatoshi_received)
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
struct invoice *invoice = (struct invoice *)cinvoice;
s64 pay_index; s64 pay_index;
u64 paid_timestamp; u64 paid_timestamp;
const tal_t *tmpctx = tal_tmpctx(NULL); const tal_t *tmpctx = tal_tmpctx(NULL);
@ -501,17 +467,11 @@ void invoices_resolve(struct invoices *invoices,
sqlite3_bind_int64(stmt, 2, pay_index); sqlite3_bind_int64(stmt, 2, pay_index);
sqlite3_bind_int64(stmt, 3, msatoshi_received); sqlite3_bind_int64(stmt, 3, msatoshi_received);
sqlite3_bind_int64(stmt, 4, paid_timestamp); sqlite3_bind_int64(stmt, 4, paid_timestamp);
sqlite3_bind_int64(stmt, 5, invoice->id); sqlite3_bind_int64(stmt, 5, invoice.id);
db_exec_prepared(invoices->db, stmt); db_exec_prepared(invoices->db, stmt);
/* Update in-memory structure. */
invoice->details->state = PAID;
invoice->details->pay_index = pay_index;
invoice->details->msatoshi_received = msatoshi_received;
invoice->details->paid_timestamp = paid_timestamp;
/* Tell all the waiters about the paid invoice. */ /* Tell all the waiters about the paid invoice. */
trigger_invoice_waiter_resolve(invoices, invoice->id, invoice); trigger_invoice_waiter_resolve(invoices, invoice.id, &invoice);
/* Free all watchers. */ /* Free all watchers. */
tal_free(tmpctx); tal_free(tmpctx);
@ -552,13 +512,12 @@ void invoices_waitany(const tal_t *ctx,
void *cbarg) void *cbarg)
{ {
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
const struct invoice *invoice; struct invoice invoice;
int res; int res;
char const* label;
/* Look for an already-paid invoice. */ /* Look for an already-paid invoice. */
stmt = db_prepare(invoices->db, stmt = db_prepare(invoices->db,
"SELECT label" "SELECT id"
" FROM invoices" " FROM invoices"
" WHERE pay_index NOT NULL" " WHERE pay_index NOT NULL"
" AND pay_index > ?" " AND pay_index > ?"
@ -567,16 +526,10 @@ void invoices_waitany(const tal_t *ctx,
res = sqlite3_step(stmt); res = sqlite3_step(stmt);
if (res == SQLITE_ROW) { if (res == SQLITE_ROW) {
/* Invoice found. Look up the invoice object. */ invoice.id = sqlite3_column_int64(stmt, 0);
label = tal_strndup(ctx, sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
/* The invoice should definitely exist in-memory. */ cb(&invoice, cbarg);
invoice = invoices_find_by_label(invoices, label);
assert(invoice);
tal_free(label);
cb(invoice, cbarg);
return; return;
} }
@ -590,38 +543,53 @@ void invoices_waitany(const tal_t *ctx,
void invoices_waitone(const tal_t *ctx, void invoices_waitone(const tal_t *ctx,
struct invoices *invoices UNUSED, struct invoices *invoices UNUSED,
struct invoice const *cinvoice, struct invoice invoice,
void (*cb)(const struct invoice *, void*), void (*cb)(const struct invoice *, void*),
void *cbarg) void *cbarg)
{ {
struct invoice *invoice = (struct invoice*) cinvoice; sqlite3_stmt *stmt;
if (invoice->details->state == PAID || invoice->details->state == EXPIRED) { int res;
cb(invoice, cbarg); enum invoice_status state;
stmt = db_prepare(invoices->db,
"SELECT state"
" FROM invoices"
" WHERE id = ?;");
sqlite3_bind_int64(stmt, 1, invoice.id);
res = sqlite3_step(stmt);
assert(res == SQLITE_ROW);
state = sqlite3_column_int(stmt, 0);
sqlite3_finalize(stmt);
if (state == PAID || state == EXPIRED) {
cb(&invoice, cbarg);
return; return;
} }
/* Not yet paid. */ /* Not yet paid. */
add_invoice_waiter(ctx, &invoices->waiters, add_invoice_waiter(ctx, &invoices->waiters,
false, invoice->id, cb, cbarg); false, invoice.id, cb, cbarg);
} }
void invoices_get_details(const tal_t *ctx, void invoices_get_details(const tal_t *ctx,
struct invoices *invoices, struct invoices *invoices,
const struct invoice *invoice, struct invoice invoice,
struct invoice_details *dtl) struct invoice_details *dtl)
{ {
dtl->state = invoice->details->state; sqlite3_stmt *stmt;
dtl->r = invoice->details->r; int result;
dtl->rhash = invoice->details->rhash;
dtl->label = tal_strdup(ctx, invoice->details->label); stmt = db_prepare(invoices->db,
dtl->msatoshi = "SELECT state, payment_key, payment_hash"
invoice->details->msatoshi ? " , label, msatoshi, expiry_time, pay_index"
tal_dup(ctx, u64, invoice->details->msatoshi) : " , msatoshi_received, paid_timestamp"
/*otherwise*/ NULL ; " FROM invoices"
dtl->expiry_time = invoice->details->expiry_time; " WHERE id = ?;");
if (dtl->state == PAID) { sqlite3_bind_int64(stmt, 1, invoice.id);
dtl->pay_index = invoice->details->pay_index; result = sqlite3_step(stmt);
dtl->msatoshi_received = invoice->details->msatoshi_received; assert(result == SQLITE_ROW);
dtl->paid_timestamp = invoice->details->paid_timestamp;
} wallet_stmt2invoice_details(ctx, stmt, dtl);
sqlite3_finalize(stmt);
} }

39
wallet/invoices.h

@ -39,6 +39,7 @@ bool invoices_load(struct invoices *invoices);
* invoices_create - Create a new invoice. * invoices_create - Create a new invoice.
* *
* @invoices - the invoice handler. * @invoices - the invoice handler.
* @pinvoice - pointer to location to load new invoice in.
* @msatoshi - the amount the invoice should have, or * @msatoshi - the amount the invoice should have, or
* NULL for any-amount invoices. * NULL for any-amount invoices.
* @label - the unique label for this invoice. Must be * @label - the unique label for this invoice. Must be
@ -46,36 +47,44 @@ bool invoices_load(struct invoices *invoices);
* @expiry - the number of seconds before the invoice * @expiry - the number of seconds before the invoice
* expires * expires
* *
* Returns NULL if label already exists or expiry is 0. * Returns false if label already exists or expiry is 0.
* Returns true if created invoice.
* FIXME: Fallback addresses * FIXME: Fallback addresses
*/ */
const struct invoice *invoices_create(struct invoices *invoices, bool invoices_create(struct invoices *invoices,
u64 *msatoshi TAKES, struct invoice *pinvoice,
const char *label TAKES, u64 *msatoshi TAKES,
u64 expiry); const char *label TAKES,
u64 expiry);
/** /**
* invoices_find_by_label - Search for an invoice by label * invoices_find_by_label - Search for an invoice by label
* *
* @invoices - the invoice handler. * @invoices - the invoice handler.
* @pinvoice - pointer to location to load found invoice in.
* @label - the label to search for. Must be null-terminated. * @label - the label to search for. Must be null-terminated.
* *
* Returns NULL if no invoice with that label exists. * Returns false if no invoice with that label exists.
* Returns true if found.
*/ */
const struct invoice *invoices_find_by_label(struct invoices *invoices, bool invoices_find_by_label(struct invoices *invoices,
const char *label); struct invoice *pinvoice,
const char *label);
/** /**
* invoices_find_unpaid - Search for an unpaid, unexpired invoice by * invoices_find_unpaid - Search for an unpaid, unexpired invoice by
* payment_hash * payment_hash
* *
* @invoices - the invoice handler. * @invoices - the invoice handler.
* @pinvoice - pointer to location to load found invoice in.
* @rhash - the payment_hash to search for. * @rhash - the payment_hash to search for.
* *
* Returns NULL if no invoice with that payment hash exists. * Returns false if no unpaid invoice with that rhash exists.
* Returns true if found.
*/ */
const struct invoice *invoices_find_unpaid(struct invoices *invoices, bool invoices_find_unpaid(struct invoices *invoices,
const struct sha256 *rhash); struct invoice *pinvoice,
const struct sha256 *rhash);
/** /**
* invoices_delete - Delete an invoice * invoices_delete - Delete an invoice
@ -86,7 +95,7 @@ const struct invoice *invoices_find_unpaid(struct invoices *invoices,
* Return false on failure. * Return false on failure.
*/ */
bool invoices_delete(struct invoices *invoices, bool invoices_delete(struct invoices *invoices,
const struct invoice *invoice); struct invoice invoice);
/** /**
* invoices_iterate - Iterate over all existing invoices * invoices_iterate - Iterate over all existing invoices
@ -132,7 +141,7 @@ void invoices_iterator_deref(const tal_t *ctx,
* does not check). * does not check).
*/ */
void invoices_resolve(struct invoices *invoices, void invoices_resolve(struct invoices *invoices,
const struct invoice *invoice, struct invoice invoice,
u64 msatoshi_received); u64 msatoshi_received);
/** /**
@ -173,7 +182,7 @@ void invoices_waitany(const tal_t *ctx,
*/ */
void invoices_waitone(const tal_t *ctx, void invoices_waitone(const tal_t *ctx,
struct invoices *invoices, struct invoices *invoices,
struct invoice const *invoice, struct invoice invoice,
void (*cb)(const struct invoice *, void*), void (*cb)(const struct invoice *, void*),
void *cbarg); void *cbarg);
@ -187,7 +196,7 @@ void invoices_waitone(const tal_t *ctx,
*/ */
void invoices_get_details(const tal_t *ctx, void invoices_get_details(const tal_t *ctx,
struct invoices *invoices, struct invoices *invoices,
const struct invoice *invoice, struct invoice invoice,
struct invoice_details *details); struct invoice_details *details);
#endif /* LIGHTNING_WALLET_INVOICES_H */ #endif /* LIGHTNING_WALLET_INVOICES_H */

27
wallet/test/run-wallet.c

@ -95,27 +95,30 @@ u8 *get_supported_global_features(const tal_t *ctx UNNEEDED)
u8 *get_supported_local_features(const tal_t *ctx UNNEEDED) u8 *get_supported_local_features(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "get_supported_local_features called!\n"); abort(); } { fprintf(stderr, "get_supported_local_features called!\n"); abort(); }
/* Generated stub for invoices_create */ /* Generated stub for invoices_create */
const struct invoice *invoices_create(struct invoices *invoices UNNEEDED, bool invoices_create(struct invoices *invoices UNNEEDED,
u64 *msatoshi TAKES UNNEEDED, struct invoice *pinvoice UNNEEDED,
const char *label TAKES UNNEEDED, u64 *msatoshi TAKES UNNEEDED,
u64 expiry UNNEEDED) const char *label TAKES UNNEEDED,
u64 expiry UNNEEDED)
{ fprintf(stderr, "invoices_create called!\n"); abort(); } { fprintf(stderr, "invoices_create called!\n"); abort(); }
/* Generated stub for invoices_delete */ /* Generated stub for invoices_delete */
bool invoices_delete(struct invoices *invoices UNNEEDED, bool invoices_delete(struct invoices *invoices UNNEEDED,
const struct invoice *invoice UNNEEDED) struct invoice invoice UNNEEDED)
{ fprintf(stderr, "invoices_delete called!\n"); abort(); } { fprintf(stderr, "invoices_delete called!\n"); abort(); }
/* Generated stub for invoices_find_by_label */ /* Generated stub for invoices_find_by_label */
const struct invoice *invoices_find_by_label(struct invoices *invoices UNNEEDED, bool invoices_find_by_label(struct invoices *invoices UNNEEDED,
const char *label UNNEEDED) struct invoice *pinvoice UNNEEDED,
const char *label UNNEEDED)
{ fprintf(stderr, "invoices_find_by_label called!\n"); abort(); } { fprintf(stderr, "invoices_find_by_label called!\n"); abort(); }
/* Generated stub for invoices_find_unpaid */ /* Generated stub for invoices_find_unpaid */
const struct invoice *invoices_find_unpaid(struct invoices *invoices UNNEEDED, bool invoices_find_unpaid(struct invoices *invoices UNNEEDED,
const struct sha256 *rhash UNNEEDED) struct invoice *pinvoice UNNEEDED,
const struct sha256 *rhash UNNEEDED)
{ fprintf(stderr, "invoices_find_unpaid called!\n"); abort(); } { fprintf(stderr, "invoices_find_unpaid called!\n"); abort(); }
/* Generated stub for invoices_get_details */ /* Generated stub for invoices_get_details */
void invoices_get_details(const tal_t *ctx UNNEEDED, void invoices_get_details(const tal_t *ctx UNNEEDED,
struct invoices *invoices UNNEEDED, struct invoices *invoices UNNEEDED,
const struct invoice *invoice UNNEEDED, struct invoice invoice UNNEEDED,
struct invoice_details *details UNNEEDED) struct invoice_details *details UNNEEDED)
{ fprintf(stderr, "invoices_get_details called!\n"); abort(); } { fprintf(stderr, "invoices_get_details called!\n"); abort(); }
/* Generated stub for invoices_iterate */ /* Generated stub for invoices_iterate */
@ -139,7 +142,7 @@ struct invoices *invoices_new(const tal_t *ctx UNNEEDED,
{ fprintf(stderr, "invoices_new called!\n"); abort(); } { fprintf(stderr, "invoices_new called!\n"); abort(); }
/* Generated stub for invoices_resolve */ /* Generated stub for invoices_resolve */
void invoices_resolve(struct invoices *invoices UNNEEDED, void invoices_resolve(struct invoices *invoices UNNEEDED,
const struct invoice *invoice UNNEEDED, struct invoice invoice UNNEEDED,
u64 msatoshi_received UNNEEDED) u64 msatoshi_received UNNEEDED)
{ fprintf(stderr, "invoices_resolve called!\n"); abort(); } { fprintf(stderr, "invoices_resolve called!\n"); abort(); }
/* Generated stub for invoices_waitany */ /* Generated stub for invoices_waitany */
@ -152,7 +155,7 @@ void invoices_waitany(const tal_t *ctx UNNEEDED,
/* Generated stub for invoices_waitone */ /* Generated stub for invoices_waitone */
void invoices_waitone(const tal_t *ctx UNNEEDED, void invoices_waitone(const tal_t *ctx UNNEEDED,
struct invoices *invoices UNNEEDED, struct invoices *invoices UNNEEDED,
struct invoice const *invoice UNNEEDED, struct invoice invoice UNNEEDED,
void (*cb)(const struct invoice * UNNEEDED, void*) UNNEEDED, void (*cb)(const struct invoice * UNNEEDED, void*) UNNEEDED,
void *cbarg UNNEEDED) void *cbarg UNNEEDED)
{ fprintf(stderr, "invoices_waitone called!\n"); abort(); } { fprintf(stderr, "invoices_waitone called!\n"); abort(); }

35
wallet/wallet.c

@ -1266,24 +1266,27 @@ bool wallet_invoice_load(struct wallet *wallet)
{ {
return invoices_load(wallet->invoices); return invoices_load(wallet->invoices);
} }
const struct invoice *wallet_invoice_create(struct wallet *wallet, bool wallet_invoice_create(struct wallet *wallet,
u64 *msatoshi TAKES, struct invoice *pinvoice,
const char *label TAKES, u64 *msatoshi TAKES,
u64 expiry) { const char *label TAKES,
return invoices_create(wallet->invoices, msatoshi, label, expiry); u64 expiry) {
} return invoices_create(wallet->invoices, pinvoice, msatoshi, label, expiry);
const struct invoice *wallet_invoice_find_by_label(struct wallet *wallet, }
const char *label) bool wallet_invoice_find_by_label(struct wallet *wallet,
struct invoice *pinvoice,
const char *label)
{ {
return invoices_find_by_label(wallet->invoices, label); return invoices_find_by_label(wallet->invoices, pinvoice, label);
} }
const struct invoice *wallet_invoice_find_unpaid(struct wallet *wallet, bool wallet_invoice_find_unpaid(struct wallet *wallet,
const struct sha256 *rhash) struct invoice *pinvoice,
const struct sha256 *rhash)
{ {
return invoices_find_unpaid(wallet->invoices, rhash); return invoices_find_unpaid(wallet->invoices, pinvoice, rhash);
} }
bool wallet_invoice_delete(struct wallet *wallet, bool wallet_invoice_delete(struct wallet *wallet,
const struct invoice *invoice) struct invoice invoice)
{ {
return invoices_delete(wallet->invoices, invoice); return invoices_delete(wallet->invoices, invoice);
} }
@ -1300,7 +1303,7 @@ void wallet_invoice_iterator_deref(const tal_t *ctx,
return invoices_iterator_deref(ctx, wallet->invoices, it, details); return invoices_iterator_deref(ctx, wallet->invoices, it, details);
} }
void wallet_invoice_resolve(struct wallet *wallet, void wallet_invoice_resolve(struct wallet *wallet,
const struct invoice *invoice, struct invoice invoice,
u64 msatoshi_received) u64 msatoshi_received)
{ {
invoices_resolve(wallet->invoices, invoice, msatoshi_received); invoices_resolve(wallet->invoices, invoice, msatoshi_received);
@ -1315,7 +1318,7 @@ void wallet_invoice_waitany(const tal_t *ctx,
} }
void wallet_invoice_waitone(const tal_t *ctx, void wallet_invoice_waitone(const tal_t *ctx,
struct wallet *wallet, struct wallet *wallet,
struct invoice const *invoice, struct invoice invoice,
void (*cb)(const struct invoice *, void*), void (*cb)(const struct invoice *, void*),
void *cbarg) void *cbarg)
{ {
@ -1323,7 +1326,7 @@ void wallet_invoice_waitone(const tal_t *ctx,
} }
void wallet_invoice_details(const tal_t *ctx, void wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet, struct wallet *wallet,
const struct invoice *invoice, struct invoice invoice,
struct invoice_details *details) struct invoice_details *details)
{ {
invoices_get_details(ctx, wallet->invoices, invoice, details); invoices_get_details(ctx, wallet->invoices, invoice, details);

53
wallet/wallet.h

@ -394,20 +394,13 @@ struct invoice_details {
struct invoice_iterator { struct invoice_iterator {
/* The contents of this object is subject to change /* The contents of this object is subject to change
* and should not be depended upon */ * and should not be depended upon */
const struct invoice *curr; void *p;
}; };
struct invoice { struct invoice {
/* Internal, rest of lightningd should not use */ /* Internal, rest of lightningd should not use */
/* List off ld->wallet->invoices. Must be first or else
* dev-memleak is driven insane. */
struct list_node list;
/* Database ID */ /* Database ID */
u64 id; u64 id;
/* The owning invoices object. */
struct invoices *owner;
/* Loaded details. */
struct invoice_details *details;
}; };
#define INVOICE_MAX_LABEL_LEN 128 #define INVOICE_MAX_LABEL_LEN 128
@ -430,6 +423,7 @@ bool wallet_invoice_load(struct wallet *wallet);
* wallet_invoice_create - Create a new invoice. * wallet_invoice_create - Create a new invoice.
* *
* @wallet - the wallet to create the invoice in. * @wallet - the wallet to create the invoice in.
* @pinvoice - pointer to location to load new invoice in.
* @msatoshi - the amount the invoice should have, or * @msatoshi - the amount the invoice should have, or
* NULL for any-amount invoices. * NULL for any-amount invoices.
* @label - the unique label for this invoice. Must be * @label - the unique label for this invoice. Must be
@ -437,36 +431,44 @@ bool wallet_invoice_load(struct wallet *wallet);
* @expiry - the number of seconds before the invoice * @expiry - the number of seconds before the invoice
* expires * expires
* *
* Returns NULL if label already exists or expiry is 0. * Returns false if label already exists or expiry is 0.
* Returns true if created invoice.
* FIXME: Fallback addresses * FIXME: Fallback addresses
*/ */
const struct invoice *wallet_invoice_create(struct wallet *wallet, bool wallet_invoice_create(struct wallet *wallet,
u64 *msatoshi TAKES, struct invoice *pinvoice,
const char *label TAKES, u64 *msatoshi TAKES,
u64 expiry); const char *label TAKES,
u64 expiry);
/** /**
* wallet_invoice_find_by_label - Search for an invoice by label * wallet_invoice_find_by_label - Search for an invoice by label
* *
* @wallet - the wallet to search. * @wallet - the wallet to search.
* @pinvoice - pointer to location to load found invoice in.
* @label - the label to search for. Must be null-terminated. * @label - the label to search for. Must be null-terminated.
* *
* Returns NULL if no invoice with that label exists. * Returns false if no invoice with that label exists.
* Returns true if found.
*/ */
const struct invoice *wallet_invoice_find_by_label(struct wallet *wallet, bool wallet_invoice_find_by_label(struct wallet *wallet,
const char *label); struct invoice *pinvoice,
const char *label);
/** /**
* wallet_invoice_find_unpaid - Search for an unpaid, unexpired invoice by * wallet_invoice_find_unpaid - Search for an unpaid, unexpired invoice by
* payment_hash * payment_hash
* *
* @wallet - the wallet to search. * @wallet - the wallet to search.
* @pinvoice - pointer to location to load found invoice in.
* @rhash - the payment_hash to search for. * @rhash - the payment_hash to search for.
* *
* Returns NULL if no invoice with that payment hash exists. * Returns false if no unpaid invoice with that rhash exists.
* Returns true if found.
*/ */
const struct invoice *wallet_invoice_find_unpaid(struct wallet *wallet, bool wallet_invoice_find_unpaid(struct wallet *wallet,
const struct sha256 *rhash); struct invoice *pinvoice,
const struct sha256 *rhash);
/** /**
* wallet_invoice_delete - Delete an invoice * wallet_invoice_delete - Delete an invoice
@ -477,7 +479,7 @@ const struct invoice *wallet_invoice_find_unpaid(struct wallet *wallet,
* Return false on failure. * Return false on failure.
*/ */
bool wallet_invoice_delete(struct wallet *wallet, bool wallet_invoice_delete(struct wallet *wallet,
const struct invoice *invoice); struct invoice invoice);
/** /**
* wallet_invoice_iterate - Iterate over all existing invoices * wallet_invoice_iterate - Iterate over all existing invoices
@ -520,10 +522,10 @@ void wallet_invoice_iterator_deref(const tal_t *ctx,
* @msatoshi_received - the actual amount received. * @msatoshi_received - the actual amount received.
* *
* Precondition: the invoice must not yet be expired (wallet * Precondition: the invoice must not yet be expired (wallet
* does not check). * does not check!).
*/ */
void wallet_invoice_resolve(struct wallet *wallet, void wallet_invoice_resolve(struct wallet *wallet,
const struct invoice *invoice, struct invoice invoice,
u64 msatoshi_received); u64 msatoshi_received);
/** /**
@ -537,7 +539,8 @@ void wallet_invoice_resolve(struct wallet *wallet,
* @cb - the callback to invoke. If an invoice is already * @cb - the callback to invoke. If an invoice is already
* paid with pay_index greater than lastpay_index, this * paid with pay_index greater than lastpay_index, this
* is called immediately, otherwise it is called during * is called immediately, otherwise it is called during
* an invoices_resolve call. * an invoices_resolve call. Will never be given a NULL
* pointer-to-invoice.
* @cbarg - the callback data. * @cbarg - the callback data.
*/ */
void wallet_invoice_waitany(const tal_t *ctx, void wallet_invoice_waitany(const tal_t *ctx,
@ -564,7 +567,7 @@ void wallet_invoice_waitany(const tal_t *ctx,
*/ */
void wallet_invoice_waitone(const tal_t *ctx, void wallet_invoice_waitone(const tal_t *ctx,
struct wallet *wallet, struct wallet *wallet,
struct invoice const *invoice, struct invoice invoice,
void (*cb)(const struct invoice *, void*), void (*cb)(const struct invoice *, void*),
void *cbarg); void *cbarg);
@ -578,7 +581,7 @@ void wallet_invoice_waitone(const tal_t *ctx,
*/ */
void wallet_invoice_details(const tal_t *ctx, void wallet_invoice_details(const tal_t *ctx,
struct wallet *wallet, struct wallet *wallet,
const struct invoice *invoice, struct invoice invoice,
struct invoice_details *details); struct invoice_details *details);
/** /**

Loading…
Cancel
Save