diff --git a/lightningd/pay.c b/lightningd/pay.c index 129e1a9b8764..2f4081f1f648 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 d25e8ef15f4e..98ac489845d7 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,86 @@ 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) +{ + const tal_t *tmpctx; + char *allser; + char *ser; + size_t num; + size_t i; + bool res; + + /* Handle nulls early. */ + if (!id) { + sqlite3_bind_null(stmt, col); + return true; + } + + tmpctx = tal_tmpctx(id); + allser = tal_strdup(tmpctx, ""); + num = tal_count(id); + + for (i = 0; i < num; ++i) { + ser = short_channel_id_to_str(tmpctx, id); + if (i == 0) + res = tal_append_fmt(&allser, "%s", ser); + else + res = tal_append_fmt(&allser, ",%s", ser); + if (!res) { + tal_free(tmpctx); + return false; + } + } + + sqlite3_bind_blob(stmt, col, allser, strlen(allser), SQLITE_TRANSIENT); + + tal_free(tmpctx); + return true; +} +struct short_channel_id * +sqlite3_column_short_channel_id_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + const tal_t *tmpctx; + const char *scan; + const char *start; + const char *end; + struct short_channel_id *ret; + size_t n; + + /* Handle nulls early. */ + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) + return NULL; + + tmpctx = tal_tmpctx(ctx); + scan = sqlite3_column_blob(stmt, col); + end = scan + sqlite3_column_bytes(stmt, col); + ret = NULL; + n = 0; + + /* Scan for comma-separated scid strings. */ + while (scan != end) { + ret = tal_dup_arr (tmpctx, struct short_channel_id, + take(ret), n, 1); + if (!ret) { + tal_free(tmpctx); + return NULL; + } + start = scan; + while (scan != end && *scan != ',') + ++scan; + if (!short_channel_id_from_str(start, scan - start, &ret[n])) { + tal_free(tmpctx); + return NULL; + } + ++n; + } + + ret = tal_steal(ctx, ret); + tal_free(tmpctx); + return ret; +} bool sqlite3_bind_tx(sqlite3_stmt *stmt, int col, const struct bitcoin_tx *tx) { @@ -503,6 +589,60 @@ 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) +{ + const tal_t *tmpctx; + size_t n; + size_t i; + u8 *ders; + + if (!pks) { + sqlite3_bind_null(stmt, col); + return true; + } + + tmpctx = tal_tmpctx(pks); + n = tal_count(pks); + ders = tal_arr(tmpctx, 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(tmpctx); + return true; +} +struct pubkey *sqlite3_column_pubkey_array(const tal_t *ctx, + sqlite3_stmt *stmt, int col) +{ + const tal_t *tmpctx; + size_t i; + size_t n; + struct pubkey *ret; + const u8 *ders; + + if (sqlite3_column_type(stmt, col) == SQLITE_NULL) + return NULL; + + tmpctx = tal_tmpctx(ctx); + n = sqlite3_column_bytes(stmt, col) / PUBKEY_DER_LEN; + assert(n * PUBKEY_DER_LEN == sqlite3_column_bytes(stmt, col)); + ret = tal_arr(tmpctx, 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])) { + tal_free(tmpctx); + return NULL; + } + } + + ret = tal_steal(ctx, ret); + tal_free(tmpctx); + 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 21f135913628..0c8151bfb09f 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 9634dd6ecb6a..b1f00544ca7c 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1288,8 +1288,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); @@ -1299,6 +1301,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); @@ -1346,6 +1351,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; } @@ -1364,7 +1374,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 = ?"); @@ -1456,7 +1466,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 0f26a468cb0e..775c3c2ee86a 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -93,7 +93,10 @@ struct wallet_payment { u64 msatoshi; /* Iff 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; }; /**