From 68feb55dbfe4f5a2e2f9d14a20ad55402b040b12 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Sun, 24 Nov 2024 12:09:16 +1030 Subject: [PATCH] wallet: save last known address. If we connected out, remember that address. We always remember the last address, but that may be an incoming address. This is explicitly the last outgoing address which worked. Signed-off-by: Rusty Russell --- db/bindings.c | 12 ++++++++++++ db/bindings.h | 3 +++ lightningd/opening_control.c | 1 + lightningd/peer_control.c | 23 +++++++++++++++++++++-- lightningd/peer_control.h | 4 ++++ plugins/bkpr/test/run-bkpr_db.c | 3 +++ plugins/bkpr/test/run-recorder.c | 3 +++ wallet/db.c | 1 + wallet/test/run-db.c | 1 + wallet/test/run-wallet.c | 6 +++--- wallet/wallet.c | 28 +++++++++++++++++++++++++--- 11 files changed, 77 insertions(+), 8 deletions(-) diff --git a/db/bindings.c b/db/bindings.c index be1cf14a6..e8b83785b 100644 --- a/db/bindings.c +++ b/db/bindings.c @@ -601,6 +601,18 @@ struct secret *db_col_secret_arr(const tal_t *ctx, return db_col_arr(ctx, stmt, colname, struct secret); } +struct wireaddr *db_col_wireaddr(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname) +{ + struct wireaddr *waddr = tal(ctx, struct wireaddr); + const u8 *wire = db_col_arr(tmpctx, stmt, colname, u8); + size_t len = tal_bytelen(wire); + if (!fromwire_wireaddr(&wire, &len, waddr)) + return tal_free(waddr); + return waddr; +} + void db_col_txid(struct db_stmt *stmt, const char *colname, struct bitcoin_txid *t) { db_col_sha256d(stmt, colname, &t->shad); diff --git a/db/bindings.h b/db/bindings.h index e78e45868..c4cbe707d 100644 --- a/db/bindings.h +++ b/db/bindings.h @@ -103,6 +103,9 @@ struct bitcoin_tx *db_col_psbt_to_tx(const tal_t *ctx, struct db_stmt *stmt, con struct onionreply *db_col_onionreply(const tal_t *ctx, struct db_stmt *stmt, const char *colname); +struct wireaddr *db_col_wireaddr(const tal_t *ctx, + struct db_stmt *stmt, + const char *colname); #define db_col_arr(ctx, stmt, colname, type) \ ((type *)db_col_arr_((ctx), (stmt), (colname), \ diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index e8b6168fd..3dd2e028f 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -1496,6 +1496,7 @@ static struct channel *stub_chan(struct command *cmd, &nodeid, &wint, NULL, + NULL, false); } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index d80f98e50..07641ae83 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -91,6 +91,7 @@ void peer_set_dbid(struct peer *peer, u64 dbid) struct peer *new_peer(struct lightningd *ld, u64 dbid, const struct node_id *id, const struct wireaddr_internal *addr, + const struct wireaddr *last_known_addr, const u8 *their_features, bool connected_incoming) { @@ -102,6 +103,7 @@ struct peer *new_peer(struct lightningd *ld, u64 dbid, peer->id = *id; peer->uncommitted_channel = NULL; peer->addr = *addr; + peer->last_known_addr = tal_dup_or_null(peer, struct wireaddr, last_known_addr); peer->connected_incoming = connected_incoming; peer->remote_addr = NULL; list_head_init(&peer->channels); @@ -1673,6 +1675,7 @@ void peer_connected(struct lightningd *ld, const u8 *msg) struct peer_connected_hook_payload *hook_payload; u64 connectd_counter; const char *cmd_id; + struct wireaddr *last_known_addr; hook_payload = tal(NULL, struct peer_connected_hook_payload); hook_payload->ld = ld; @@ -1691,15 +1694,31 @@ void peer_connected(struct lightningd *ld, const u8 *msg) * now it's reconnected, we've gotta force them out. */ peer_channels_cleanup(ld, &id); + /* If we connected, and it's a normal address */ + if (!hook_payload->incoming + && hook_payload->addr.itype == ADDR_INTERNAL_WIREADDR + && !hook_payload->addr.u.wireaddr.is_websocket) { + last_known_addr = &hook_payload->addr.u.wireaddr.wireaddr; + } else { + last_known_addr = NULL; + } + /* If we're already dealing with this peer, hand off to correct * subdaemon. Otherwise, we'll hand to openingd to wait there. */ peer = peer_by_id(ld, &id); - if (!peer) + if (!peer) { + /* If we connected to them, we know this is a good address. */ peer = new_peer(ld, 0, &id, &hook_payload->addr, + last_known_addr, take(their_features), hook_payload->incoming); - else { + } else { tal_free(peer->their_features); peer->their_features = tal_steal(peer, their_features); + + /* Update known address. */ + tal_free(peer->last_known_addr); + peer->last_known_addr = tal_dup_or_null(peer, struct wireaddr, + last_known_addr); } /* We track this, because messages can race between connectd and us. diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index face71198..c8015cbad 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -53,6 +53,9 @@ struct peer { struct wireaddr_internal addr; bool connected_incoming; + /* If we ever successfully connected out to an address, this is non-NULL */ + struct wireaddr *last_known_addr; + /* They send what they see as our address as remote_addr */ struct wireaddr *remote_addr; @@ -71,6 +74,7 @@ struct peer *find_peer_by_dbid(struct lightningd *ld, u64 dbid); struct peer *new_peer(struct lightningd *ld, u64 dbid, const struct node_id *id, const struct wireaddr_internal *addr, + const struct wireaddr *last_known_addr, const u8 *their_features TAKES, bool connected_incoming); diff --git a/plugins/bkpr/test/run-bkpr_db.c b/plugins/bkpr/test/run-bkpr_db.c index 308110548..91d4e0271 100644 --- a/plugins/bkpr/test/run-bkpr_db.c +++ b/plugins/bkpr/test/run-bkpr_db.c @@ -92,6 +92,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for fromwire_wirestring */ char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_wirestring called!\n"); abort(); } diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c index 85c901200..25774f44b 100644 --- a/plugins/bkpr/test/run-recorder.c +++ b/plugins/bkpr/test/run-recorder.c @@ -98,6 +98,9 @@ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8_array */ void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for fromwire_wireaddr */ +bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); } /* Generated stub for fromwire_wirestring */ char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_wirestring called!\n"); abort(); } diff --git a/wallet/db.c b/wallet/db.c index 161d67c44..963e8c535 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1028,6 +1028,7 @@ static struct migration dbmigrations[] = { " addrtype INTEGER)"), NULL}, {NULL, insert_addrtype_to_addresses}, {SQL("ALTER TABLE channel_funding_inflights ADD remote_funding BLOB DEFAULT NULL;"), NULL}, + {SQL("ALTER TABLE peers ADD last_known_address BLOB DEFAULT NULL;"), NULL}, }; /** diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index cb346dba1..824b9197e 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -239,6 +239,7 @@ struct logger *new_logger(const tal_t *ctx UNNEEDED, struct log_book *record UNN struct peer *new_peer(struct lightningd *ld UNNEEDED, u64 dbid UNNEEDED, const struct node_id *id UNNEEDED, const struct wireaddr_internal *addr UNNEEDED, + const struct wireaddr *last_known_addr UNNEEDED, const u8 *their_features TAKES UNNEEDED, bool connected_incoming UNNEEDED) { fprintf(stderr, "new_peer called!\n"); abort(); } diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index e21585083..091b0d4e6 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1441,7 +1441,7 @@ static bool test_wallet_outputs(struct lightningd *ld, const tal_t *ctx) /* Add another utxo that's CSV-locked for 5 blocks */ assert(parse_wireaddr_internal(tmpctx, "localhost:1234", 0, false, &addr) == NULL); - channel.peer = new_peer(ld, 0, &id, &addr, NULL, false); + channel.peer = new_peer(ld, 0, &id, &addr, NULL, NULL, false); channel.dbid = 1; channel.type = channel_type_anchors_zero_fee_htlc(tmpctx); memset(&u.outpoint, 3, sizeof(u.outpoint)); @@ -1767,7 +1767,7 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx) c1.first_blocknum = 1; assert(parse_wireaddr_internal(tmpctx, "localhost:1234", 0, false, &addr) == NULL); c1.final_key_idx = 1337; - p = new_peer(ld, 0, &id, &addr, NULL, false); + p = new_peer(ld, 0, &id, &addr, NULL, NULL, false); c1.peer = p; c1.dbid = wallet_get_channel_dbid(w); c1.state = CHANNELD_NORMAL; @@ -1935,7 +1935,7 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) assert(parse_wireaddr_internal(tmpctx, "localhost:1234", 0, false, &addr) == NULL); /* new channel! */ - p = new_peer(ld, 0, &id, &addr, NULL, false); + p = new_peer(ld, 0, &id, &addr, NULL, NULL, false); funding_sats = AMOUNT_SAT(4444444); our_sats = AMOUNT_SAT(3333333); diff --git a/wallet/wallet.c b/wallet/wallet.c index cb79c32cc..bba5479f0 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1011,10 +1011,11 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) struct peer *peer = NULL; struct node_id id; struct wireaddr_internal addr; + struct wireaddr *last_known_addr; struct db_stmt *stmt; stmt = db_prepare_v2( - w->db, SQL("SELECT id, node_id, address, feature_bits FROM peers WHERE id=?;")); + w->db, SQL("SELECT id, node_id, address, feature_bits, last_known_address FROM peers WHERE id=?;")); db_bind_u64(stmt, dbid); db_query_prepared(stmt); @@ -1025,6 +1026,7 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) db_col_ignore(stmt, "address"); db_col_ignore(stmt, "id"); db_col_ignore(stmt, "feature_bits"); + db_col_ignore(stmt, "last_known_address"); goto done; } @@ -1041,8 +1043,18 @@ static struct peer *wallet_peer_load(struct wallet *w, const u64 dbid) assert(!err); } - /* FIXME: save incoming in db! */ - peer = new_peer(w->ld, db_col_u64(stmt, "id"), &id, &addr, db_col_arr(stmt, stmt, "feature_bits", u8), false); + if (db_col_is_null(stmt, "last_known_address")) { + last_known_addr = NULL; + } else { + last_known_addr = db_col_wireaddr(tmpctx, stmt, "last_known_address"); + if (!last_known_addr) { + log_broken(w->log, "Unparsable lastknown address %s: ignoring", + tal_hex(tmpctx, db_col_arr(tmpctx, stmt, "last_known_address", u8))); + } + } + + peer = new_peer(w->ld, db_col_u64(stmt, "id"), &id, &addr, last_known_addr, + db_col_arr(stmt, stmt, "feature_bits", u8), false); done: tal_free(stmt); @@ -2583,6 +2595,16 @@ static void wallet_peer_save(struct wallet *w, struct peer *peer) db_exec_prepared_v2(stmt); peer_set_dbid(peer, db_last_insert_id_v2(take(stmt))); } + + if (peer->last_known_addr) { + u8 *wire = tal_arr(tmpctx, u8, 0); + towire_wireaddr(&wire, peer->last_known_addr); + stmt = db_prepare_v2(w->db, + SQL("UPDATE peers SET last_known_address = ? WHERE id = ?;")); + db_bind_talarr(stmt, wire); + db_bind_u64(stmt, peer->dbid); + db_exec_prepared_v2(take(stmt)); + } } bool channel_exists_by_id(struct wallet *w, u64 dbid) {