Browse Source

invoices: Move expiration timer to invoices object.

In preparation for removing in-memory invoices objects.
ppa-0.6.1
ZmnSCPxj 7 years ago
committed by Christian Decker
parent
commit
e9112af0bc
  1. 169
      wallet/invoices.c
  2. 2
      wallet/wallet.h

169
wallet/invoices.c

@ -41,6 +41,10 @@ struct invoices {
struct list_head invlist; 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 */
u64 min_expiry_time;
/* Expiration timer */
struct oneshot *expiration_timer;
}; };
static void trigger_invoice_waiter(struct invoice_waiter *w, static void trigger_invoice_waiter(struct invoice_waiter *w,
@ -118,8 +122,6 @@ static bool wallet_stmt2invoice_details(sqlite3_stmt *stmt,
dtl->paid_timestamp = sqlite3_column_int64(stmt, 9); dtl->paid_timestamp = sqlite3_column_int64(stmt, 9);
} }
invoice->expiration_timer = NULL;
return true; return true;
} }
@ -137,58 +139,120 @@ struct invoices *invoices_new(const tal_t *ctx,
list_head_init(&invs->invlist); list_head_init(&invs->invlist);
list_head_init(&invs->waiters); list_head_init(&invs->waiters);
invs->expiration_timer = NULL;
return invs; return invs;
} }
static void install_expiration_timer(struct invoices *invoices, /* Update expirations. */
struct invoice *i); static void update_db_expirations(struct invoices *invoices, u64 now)
static void trigger_expiration(struct invoice *i) {
sqlite3_stmt *stmt;
stmt = db_prepare(invoices->db,
"UPDATE invoices"
" SET state = ?"
" WHERE state = ?"
" AND expiry_time <= ?;");
sqlite3_bind_int(stmt, 1, EXPIRED);
sqlite3_bind_int(stmt, 2, UNPAID);
sqlite3_bind_int64(stmt, 3, now);
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 list_node list;
u64 id;
};
static void install_expiration_timer(struct invoices *invoices);
static void trigger_expiration(struct invoices *invoices)
{ {
struct invoices *invoices = i->owner; const tal_t *tmpctx = tal_tmpctx(invoices);
struct list_head idlist;
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;
assert(i->details->state == UNPAID); /* Free current expiration timer */
invoices->expiration_timer = tal_free(invoices->expiration_timer);
/* Timer already triggered, destroy the timer object. */ /* Acquire all expired invoices and save them in a list */
i->expiration_timer = tal_free(i->expiration_timer); list_head_init(&idlist);
stmt = db_prepare(invoices->db,
"SELECT id"
" FROM invoices"
" WHERE state = ?"
" AND expiry_time <= ?;");
sqlite3_bind_int(stmt, 1, UNPAID);
sqlite3_bind_int64(stmt, 2, now);
while (sqlite3_step(stmt) == SQLITE_ROW) {
idn = tal(tmpctx, struct invoice_id_node);
list_add_tail(&idlist, &idn->list);
idn->id = sqlite3_column_int64(stmt, 0);
}
sqlite3_finalize(stmt);
/* There may be discrepancies between time_mono /* Expire all those invoices */
* (used by the timer system) and time_now (used update_db_expirations(invoices, now);
* by the expiry time measurements). So check that
* time_now is reached. */
if (i->details->expiry_time <= now) {
const tal_t *tmpctx = tal_tmpctx(i);
/* Update in-memory and db. */ /* Trigger expirations */
list_for_each(&idlist, idn, list) {
/* Update in-memory structure */
i = invoices_find_by_id(invoices, idn->id);
i->details->state = EXPIRED; i->details->state = EXPIRED;
stmt = db_prepare(invoices->db, /* Trigger expiration */
"UPDATE invoices" trigger_invoice_waiter_expire_or_delete(invoices,
" SET state = ?" idn->id,
" WHERE id = ?;"); i);
sqlite3_bind_int(stmt, 1, EXPIRED); }
sqlite3_bind_int64(stmt, 2, i->id);
db_exec_prepared(invoices->db, stmt);
/* Wake up all waiters. */
trigger_invoice_waiter_expire_or_delete(invoices, i->id, i);
tal_free(tmpctx);
} else
install_expiration_timer(invoices, i);
install_expiration_timer(invoices);
tal_free(tmpctx);
} }
static void install_expiration_timer(struct invoices *invoices,
struct invoice *i) static void install_expiration_timer(struct invoices *invoices)
{ {
int res;
sqlite3_stmt *stmt;
struct timerel rel; struct timerel rel;
struct timeabs expiry; struct timeabs expiry;
struct timeabs now = time_now(); struct timeabs now = time_now();
assert(i->details->state == UNPAID); assert(!invoices->expiration_timer);
assert(!i->expiration_timer);
/* Find unpaid invoice with nearest expiry time */
stmt = db_prepare(invoices->db,
"SELECT MIN(expiry_time)"
" FROM invoices"
" WHERE state = ?;");
sqlite3_bind_int(stmt, 1, UNPAID);
res = sqlite3_step(stmt);
assert(res == SQLITE_ROW);
if (sqlite3_column_type(stmt, 0) == SQLITE_NULL) {
/* Nothing to install */
sqlite3_finalize(stmt);
return;
} else
invoices->min_expiry_time = sqlite3_column_int64(stmt, 0);
sqlite3_finalize(stmt);
memset(&expiry, 0, sizeof(expiry)); memset(&expiry, 0, sizeof(expiry));
expiry.ts.tv_sec = i->details->expiry_time; expiry.ts.tv_sec = invoices->min_expiry_time;
/* now > expiry */ /* now > expiry */
if (time_after(now, expiry)) if (time_after(now, expiry))
@ -197,13 +261,12 @@ static void install_expiration_timer(struct invoices *invoices,
/* rel = expiry - now */ /* rel = expiry - now */
rel = time_between(expiry, now); rel = time_between(expiry, now);
/* The oneshot is parented on the invoice. Thus if /* Have it called at indicated timerel. */
* the invoice is deleted, the oneshot is destroyed invoices->expiration_timer = new_reltimer(invoices->timers,
* also and this removes the timer. */ invoices,
i->expiration_timer = new_reltimer(invoices->timers, rel,
i, &trigger_expiration,
rel, invoices);
&trigger_expiration, i);
} }
bool invoices_load(struct invoices *invoices) bool invoices_load(struct invoices *invoices)
@ -213,16 +276,7 @@ bool invoices_load(struct invoices *invoices)
struct invoice *i; struct invoice *i;
sqlite3_stmt *stmt; sqlite3_stmt *stmt;
/* Update expirations. */ update_db_expirations(invoices, now);
stmt = db_prepare(invoices->db,
"UPDATE invoices"
" SET state = ?"
" WHERE state = ?"
" AND expiry_time <= ?;");
sqlite3_bind_int(stmt, 1, EXPIRED);
sqlite3_bind_int(stmt, 2, UNPAID);
sqlite3_bind_int64(stmt, 3, now);
db_exec_prepared(invoices->db, stmt);
/* Load invoices from db. */ /* Load invoices from db. */
stmt = db_query(__func__, invoices->db, stmt = db_query(__func__, invoices->db,
@ -244,8 +298,6 @@ bool invoices_load(struct invoices *invoices)
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return false; return false;
} }
if (i->details->state == UNPAID)
install_expiration_timer(invoices, i);
list_add_tail(&invoices->invlist, &i->list); list_add_tail(&invoices->invlist, &i->list);
count++; count++;
} }
@ -253,6 +305,8 @@ bool invoices_load(struct invoices *invoices)
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
install_expiration_timer(invoices);
return true; return true;
} }
@ -321,13 +375,17 @@ const struct invoice *invoices_create(struct invoices *invoices,
memcpy(&invoice->details->r, &r, sizeof(invoice->details->r)); memcpy(&invoice->details->r, &r, sizeof(invoice->details->r));
memcpy(&invoice->details->rhash, &rhash, sizeof(invoice->details->rhash)); memcpy(&invoice->details->rhash, &rhash, sizeof(invoice->details->rhash));
invoice->details->expiry_time = expiry_time; invoice->details->expiry_time = expiry_time;
invoice->expiration_timer = NULL;
/* Add to invoices object. */ /* Add to invoices object. */
list_add_tail(&invoices->invlist, &invoice->list); list_add_tail(&invoices->invlist, &invoice->list);
/* Install expiration trigger. */ /* Install expiration trigger. */
install_expiration_timer(invoices, invoice); if (!invoices->expiration_timer ||
expiry_time < invoices->min_expiry_time) {
invoices->expiration_timer
= tal_free(invoices->expiration_timer);
install_expiration_timer(invoices);
}
return invoice; return invoice;
} }
@ -443,7 +501,6 @@ void invoices_resolve(struct invoices *invoices,
invoice->details->pay_index = pay_index; invoice->details->pay_index = pay_index;
invoice->details->msatoshi_received = msatoshi_received; invoice->details->msatoshi_received = msatoshi_received;
invoice->details->paid_timestamp = paid_timestamp; invoice->details->paid_timestamp = paid_timestamp;
invoice->expiration_timer = tal_free(invoice->expiration_timer);
/* 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);

2
wallet/wallet.h

@ -397,8 +397,6 @@ struct invoice {
struct list_node list; struct list_node list;
/* Database ID */ /* Database ID */
u64 id; u64 id;
/* Any expiration timer in effect */
struct oneshot *expiration_timer;
/* The owning invoices object. */ /* The owning invoices object. */
struct invoices *owner; struct invoices *owner;
/* Loaded details. */ /* Loaded details. */

Loading…
Cancel
Save