From 299b280f788922524a6d3ff6de6a67be2ad648ba Mon Sep 17 00:00:00 2001 From: ZmnSCPxj Date: Sat, 20 Jan 2018 14:00:35 +0000 Subject: [PATCH] pay: Save nodes and channels used on route to payment. --- lightningd/pay.c | 8 ++++ wallet/db.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++ wallet/db.h | 10 +++++ wallet/wallet.c | 18 +++++++-- wallet/wallet.h | 3 ++ 5 files changed, 137 insertions(+), 4 deletions(-) diff --git a/lightningd/pay.c b/lightningd/pay.c index 44724f6d6..f9ad74716 100644 --- a/lightningd/pay.c +++ b/lightningd/pay.c @@ -131,6 +131,7 @@ static bool send_payment(struct command *cmd, struct pubkey *ids = tal_arr(tmpctx, struct pubkey, n_hops); struct wallet_payment *payment = NULL; struct htlc_out *hout; + struct short_channel_id *channels; /* Expiry for HTLCs is absolute. And add one to give some margin. */ base_expiry = get_block_height(cmd->ld->topology) + 1; @@ -213,6 +214,11 @@ static bool send_payment(struct command *cmd, return false; } + /* Copy channels used along the route. */ + channels = tal_arr(tmpctx, struct short_channel_id, n_hops); + for (i = 0; i < n_hops; ++i) + channels[i] = route[i].channel_id; + /* If hout fails, payment should be freed too. */ payment = tal(hout, struct wallet_payment); payment->id = 0; @@ -223,6 +229,8 @@ static bool send_payment(struct command *cmd, payment->timestamp = time_now().ts.tv_sec; payment->payment_preimage = NULL; payment->path_secrets = tal_steal(payment, path_secrets); + payment->route_nodes = tal_steal(payment, ids); + payment->route_channels = tal_steal(payment, channels); /* We write this into db when HTLC is actually sent. */ wallet_payment_setup(cmd->ld->wallet, payment); diff --git a/wallet/db.c b/wallet/db.c index d25e8ef15..183b2e549 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -183,6 +183,12 @@ char *dbmigrations[] = { "UPDATE invoices" " SET paid_timestamp = strftime('%s', 'now')" " WHERE state = 1;", + /* We need to keep the route node pubkeys and short channel ids to + * correctly mark routing failures. We separate short channel ids + * because we cannot safely save them as blobs due to byteorder + * concerns. */ + "ALTER TABLE payments ADD COLUMN route_nodes BLOB;", + "ALTER TABLE payments ADD COLUMN route_channels TEXT;", NULL, }; @@ -454,6 +460,56 @@ bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, size_t sourcelen = sqlite3_column_bytes(stmt, col); return short_channel_id_from_str(source, sourcelen, dest); } +bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, + const struct short_channel_id *id) +{ + u8 *ser; + size_t num; + size_t i; + + /* Handle nulls early. */ + if (!id) { + sqlite3_bind_null(stmt, col); + return true; + } + + ser = tal_arr(NULL, u8, 0); + num = tal_count(id); + + for (i = 0; i < num; ++i) + towire_short_channel_id(&ser, &id[i]); + + sqlite3_bind_blob(stmt, col, ser, tal_len(ser), SQLITE_TRANSIENT); + + tal_free(ser); + return true; +} +struct short_channel_id * +sqlite3_column_short_channel_id_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + const u8 *ser; + size_t len; + struct short_channel_id *ret; + size_t n; + + /* Handle nulls early. */ + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) + return NULL; + + ser = sqlite3_column_blob(stmt, col); + len = sqlite3_column_bytes(stmt, col); + ret = tal_arr(ctx, struct short_channel_id, 0); + n = 0; + + while (len != 0) { + tal_resize(&ret, n + 1); + fromwire_short_channel_id(&ser, &len, &ret[n]); + ++n; + } + + return ret; +} bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx) { @@ -503,6 +559,52 @@ bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk) return true; } +bool sqlite3_bind_pubkey_array(sqlite3_stmt *stmt, int col, + const struct pubkey *pks) +{ + size_t n; + size_t i; + u8 *ders; + + if (!pks) { + sqlite3_bind_null(stmt, col); + return true; + } + + n = tal_count(pks); + ders = tal_arr(NULL, u8, n * PUBKEY_DER_LEN); + + for (i = 0; i < n; ++i) + pubkey_to_der(&ders[i * PUBKEY_DER_LEN], &pks[i]); + sqlite3_bind_blob(stmt, col, ders, tal_len(ders), SQLITE_TRANSIENT); + + tal_free(ders); + return true; +} +struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + size_t i; + size_t n; + struct pubkey *ret; + const u8 *ders; + + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) + return NULL; + + n = sqlite3_column_bytes(stmt, col) / PUBKEY_DER_LEN; + assert(n * PUBKEY_DER_LEN == sqlite3_column_bytes(stmt, col)); + ret = tal_arr(ctx, struct pubkey, n); + ders = sqlite3_column_blob(stmt, col); + + for (i = 0; i < n; ++i) { + if (!pubkey_from_der(&ders[i * PUBKEY_DER_LEN], PUBKEY_DER_LEN, &ret[i])) + return tal_free(ret); + } + + return ret; +} + bool sqlite3_column_preimage(sqlite3_stmt *stmt, int col, struct preimage *dest) { assert(sqlite3_column_bytes(stmt, col) == sizeof(struct preimage)); diff --git a/wallet/db.h b/wallet/db.h index 21f135913..0c8151bfb 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -117,6 +117,11 @@ bool sqlite3_bind_short_channel_id(sqlite3_stmt *stmt, int col, const struct short_channel_id *id); bool sqlite3_column_short_channel_id(sqlite3_stmt *stmt, int col, struct short_channel_id *dest); +bool sqlite3_bind_short_channel_id_array(sqlite3_stmt *stmt, int col, + const struct short_channel_id *id); +struct short_channel_id * +sqlite3_column_short_channel_id_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col); bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx); struct bitcoin_tx *sqlite3_column_tx(const tal_t *ctx, sqlite3_stmt *stmt, int col); @@ -126,6 +131,11 @@ bool sqlite3_column_signature(sqlite3_stmt *stmt, int col, secp256k1_ecdsa_signa bool sqlite3_column_pubkey(sqlite3_stmt *stmt, int col, struct pubkey *dest); bool sqlite3_bind_pubkey(sqlite3_stmt *stmt, int col, const struct pubkey *pk); +bool sqlite3_bind_pubkey_array(sqlite3_stmt *stmt, int col, + const struct pubkey *pks); +struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col); + bool sqlite3_column_preimage(sqlite3_stmt *stmt, int col, struct preimage *dest); bool sqlite3_bind_preimage(sqlite3_stmt *stmt, int col, const struct preimage *p); diff --git a/wallet/wallet.c b/wallet/wallet.c index 220d80e02..c881728d8 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1295,8 +1295,10 @@ void wallet_payment_store(struct wallet *wallet, " destination," " msatoshi," " timestamp," - " path_secrets" - ") VALUES (?, ?, ?, ?, ?, ?);"); + " path_secrets," + " route_nodes," + " route_channels" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?);"); sqlite3_bind_int(stmt, 1, payment->status); sqlite3_bind_sha256(stmt, 2, &payment->payment_hash); @@ -1306,6 +1308,9 @@ void wallet_payment_store(struct wallet *wallet, sqlite3_bind_blob(stmt, 6, payment->path_secrets, tal_len(payment->path_secrets), SQLITE_TRANSIENT); + sqlite3_bind_pubkey_array(stmt, 7, payment->route_nodes); + sqlite3_bind_short_channel_id_array(stmt, 8, + payment->route_channels); db_exec_prepared(wallet->db, stmt); @@ -1353,6 +1358,11 @@ static struct wallet_payment *wallet_stmt2payment(const tal_t *ctx, /* Can be NULL for old db! */ payment->path_secrets = sqlite3_column_secrets(payment, stmt, 7); + + payment->route_nodes = sqlite3_column_pubkey_array(payment, stmt, 8); + payment->route_channels + = sqlite3_column_short_channel_id_array(payment, stmt, 9); + return payment; } @@ -1371,7 +1381,7 @@ wallet_payment_by_hash(const tal_t *ctx, struct wallet *wallet, stmt = db_prepare(wallet->db, "SELECT id, status, destination," "msatoshi, payment_hash, timestamp, payment_preimage, " - "path_secrets " + "path_secrets, route_nodes, route_channels " "FROM payments " "WHERE payment_hash = ?"); @@ -1463,7 +1473,7 @@ wallet_payment_list(const tal_t *ctx, wallet->db, "SELECT id, status, destination, " "msatoshi, payment_hash, timestamp, payment_preimage, " - "path_secrets " + "path_secrets, route_nodes, route_channels " "FROM payments;"); } diff --git a/wallet/wallet.h b/wallet/wallet.h index 1741b04db..837a776ce 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -93,7 +93,10 @@ struct wallet_payment { u64 msatoshi; /* If and only if PAYMENT_COMPLETE */ struct preimage *payment_preimage; + /* Needed for recovering from routing failures. */ struct secret *path_secrets; + struct pubkey *route_nodes; + struct short_channel_id *route_channels; }; /**