wallet: Add pay_index column to database for paid invoice ordering.

This commit is contained in:
ZmnSCPxj 2017-12-19 11:52:03 +00:00 committed by Rusty Russell
parent d50fb131b6
commit 3dd50d6219
2 changed files with 75 additions and 6 deletions

View File

@ -135,6 +135,16 @@ char *dbmigrations[] = {
/* Add expiry field to invoices (effectively infinite). */
"ALTER TABLE invoices ADD expiry_time INTEGER;",
"UPDATE invoices SET expiry_time=9223372036854775807;",
/* Add pay_index field to paid invoices (initially, same order as id). */
"ALTER TABLE invoices ADD pay_index INTEGER;",
"CREATE UNIQUE INDEX invoices_pay_index"
" ON invoices(pay_index);",
"UPDATE invoices SET pay_index=id WHERE state=1;", /* only paid invoice */
/* Create next_pay_index variable (highest pay_index). */
"INSERT OR REPLACE INTO vars(name, val)"
" VALUES('next_pay_index', "
" COALESCE((SELECT MAX(pay_index) FROM invoices WHERE state=1), 0) + 1"
" );",
NULL,
};

View File

@ -3,6 +3,7 @@
#include <bitcoin/script.h>
#include <ccan/tal/str/str.h>
#include <inttypes.h>
#include <lightningd/invoice.h>
#include <lightningd/lightningd.h>
#include <common/wireaddr.h>
#include <lightningd/log.h>
@ -1124,15 +1125,51 @@ bool wallet_htlcs_reconnect(struct wallet *wallet,
return true;
}
/* Acquire the next pay_index. */
static s64 wallet_invoice_next_pay_index(struct db *db)
{
/* Equivalent to (next_pay_index++) */
s64 next_pay_index;
next_pay_index = db_get_intvar(db, "next_pay_index", 0);
/* Variable should exist. */
assert(next_pay_index > 0);
db_set_intvar(db, "next_pay_index", next_pay_index + 1);
return next_pay_index;
}
/* Determine the on-database state of an invoice. */
static enum invoice_status wallet_invoice_db_state(
struct db *db, const struct invoice *inv)
{
sqlite3_stmt *stmt;
int res;
int state;
stmt = db_prepare(db, "SELECT state FROM invoices WHERE id=?;");
sqlite3_bind_int64(stmt, 1, inv->id);
res = sqlite3_step(stmt);
assert(res == SQLITE_ROW);
state = sqlite3_column_int(stmt, 0);
res = sqlite3_step(stmt);
assert(res == SQLITE_DONE);
sqlite3_finalize(stmt);
return (enum invoice_status) state;
}
void wallet_invoice_save(struct wallet *wallet, struct invoice *inv)
{
sqlite3_stmt *stmt;
s64 pay_index;
bool unpaid_to_paid = false;
/* Need to use the lower level API of sqlite3 to bind
* label. Otherwise we'd need to implement sanitization of
* that string for sql injections... */
sqlite3_stmt *stmt;
if (!inv->id) {
stmt = db_prepare(wallet->db,
"INSERT INTO invoices (payment_hash, payment_key, state, msatoshi, label, expiry_time) VALUES (?, ?, ?, ?, ?, ?);");
"INSERT INTO invoices (payment_hash, payment_key, state, msatoshi, label, expiry_time, pay_index) VALUES (?, ?, ?, ?, ?, ?, ?);");
sqlite3_bind_blob(stmt, 1, &inv->rhash, sizeof(inv->rhash), SQLITE_TRANSIENT);
sqlite3_bind_blob(stmt, 2, &inv->r, sizeof(inv->r), SQLITE_TRANSIENT);
@ -1140,17 +1177,39 @@ void wallet_invoice_save(struct wallet *wallet, struct invoice *inv)
sqlite3_bind_int64(stmt, 4, inv->msatoshi);
sqlite3_bind_text(stmt, 5, inv->label, strlen(inv->label), SQLITE_TRANSIENT);
sqlite3_bind_int64(stmt, 6, inv->expiry_time);
if (inv->state == PAID) {
pay_index = wallet_invoice_next_pay_index(wallet->db);
sqlite3_bind_int64(stmt, 7, pay_index);
} else {
sqlite3_bind_null(stmt, 7);
}
db_exec_prepared(wallet->db, stmt);
inv->id = sqlite3_last_insert_rowid(wallet->db->sql);
} else {
stmt = db_prepare(wallet->db, "UPDATE invoices SET state=? WHERE id=?;");
if (inv->state == PAID) {
if (wallet_invoice_db_state(wallet->db, inv) == UNPAID) {
unpaid_to_paid = true;
}
}
if (unpaid_to_paid) {
stmt = db_prepare(wallet->db, "UPDATE invoices SET state=?, pay_index=? WHERE id=?;");
sqlite3_bind_int(stmt, 1, inv->state);
sqlite3_bind_int64(stmt, 2, inv->id);
sqlite3_bind_int(stmt, 1, inv->state);
pay_index = wallet_invoice_next_pay_index(wallet->db);
sqlite3_bind_int64(stmt, 2, pay_index);
sqlite3_bind_int64(stmt, 3, inv->id);
db_exec_prepared(wallet->db, stmt);
db_exec_prepared(wallet->db, stmt);
} else {
stmt = db_prepare(wallet->db, "UPDATE invoices SET state=? WHERE id=?;");
sqlite3_bind_int(stmt, 1, inv->state);
sqlite3_bind_int64(stmt, 2, inv->id);
db_exec_prepared(wallet->db, stmt);
}
}
}