From 8e897746e2b0dd0d7c73071a5bccd02d240d1e31 Mon Sep 17 00:00:00 2001 From: Alex Myers Date: Thu, 7 Dec 2023 06:44:05 +1030 Subject: [PATCH] gossipd: pass remote private channel update to ld and stash in the database. Rusty: I added the bad gossip message so we would see unknown updates in CI, and made sure we don't send our own generated updates to lightningd. Signed-off-by: Rusty Russell --- gossipd/gossip_generation.c | 2 +- gossipd/gossipd.c | 1 + gossipd/gossipd_wire.csv | 13 +++++ gossipd/routing.c | 43 +++++++++++++- gossipd/test/run-check_channel_announcement.c | 6 ++ gossipd/test/run-txout_failure.c | 6 ++ lightningd/channel.c | 6 +- lightningd/channel.h | 9 ++- lightningd/gossip_control.c | 57 +++++++++++++++++++ lightningd/opening_control.c | 6 +- plugins/Makefile | 2 +- wallet/test/run-db.c | 4 +- wallet/test/run-wallet.c | 3 +- wallet/wallet.c | 52 ++++++++++++++++- 14 files changed, 198 insertions(+), 12 deletions(-) diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 2223d7be2..84532bc19 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -600,7 +600,7 @@ static void apply_update(struct daemon *daemon, take(update); } - msg = handle_channel_update(daemon->rstate, update, &chan->nodes[direction]->id, NULL, true); + msg = handle_channel_update(daemon->rstate, update, NULL, NULL, true); if (msg) status_failed(STATUS_FAIL_INTERNAL_ERROR, "%s: rejected local channel update %s: %s", diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 33bf8cda7..35015ec49 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1206,6 +1206,7 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_NEW_BLOCKHEIGHT_REPLY: case WIRE_GOSSIPD_GET_ADDRS_REPLY: case WIRE_GOSSIPD_GOT_LOCAL_CHANNEL_UPDATE: + case WIRE_GOSSIPD_REMOTE_CHANNEL_UPDATE: break; } diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index a94afb1bb..9fd669dc1 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -136,3 +136,16 @@ msgdata,gossipd_used_local_channel_update,scid,short_channel_id, # Tell gossipd we have verified a new public IP by the remote_addr feature msgtype,gossipd_discovered_ip,3009 msgdata,gossipd_discovered_ip,discovered_ip,wireaddr, + +subtype,remote_priv_update +subtypedata,remote_priv_update,source_node,node_id, +subtypedata,remote_priv_update,scid,short_channel_id, +subtypedata,remote_priv_update,fee_base,u32, +subtypedata,remote_priv_update,fee_ppm,u32, +subtypedata,remote_priv_update,cltv_delta,u16, +subtypedata,remote_priv_update,htlc_minimum_msat,amount_msat, +subtypedata,remote_priv_update,htlc_maximum_msat,amount_msat, + +# Tell lightningd we received channel update info for a local channel +msgtype,gossipd_remote_channel_update,3010 +msgdata,gossipd_remote_channel_update,update,remote_priv_update, diff --git a/gossipd/routing.c b/gossipd/routing.c index adedcb6c8..2948298d2 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -1347,9 +1348,32 @@ static bool is_chan_dying(struct routing_state *rstate, return false; } +static void tell_lightningd_private_update(struct routing_state *rstate, + const struct node_id *source_peer, + struct short_channel_id scid, + u32 fee_base_msat, + u32 fee_ppm, + u16 cltv_delta, + struct amount_msat htlc_minimum, + struct amount_msat htlc_maximum) +{ + struct remote_priv_update remote_update; + u8* msg; + remote_update.source_node = *source_peer; + remote_update.scid = scid; + remote_update.fee_base = fee_base_msat; + remote_update.fee_ppm = fee_ppm; + remote_update.cltv_delta = cltv_delta; + remote_update.htlc_minimum_msat = htlc_minimum; + remote_update.htlc_maximum_msat = htlc_maximum; + msg = towire_gossipd_remote_channel_update(NULL, &remote_update); + daemon_conn_send(rstate->daemon->master, take(msg)); +} + bool routing_add_channel_update(struct routing_state *rstate, const u8 *update TAKES, u32 index, + /* NULL if it's us */ const struct node_id *source_peer, bool ignore_timestamp, bool force_spam_flag, @@ -1398,6 +1422,16 @@ bool routing_add_channel_update(struct routing_state *rstate, /* Maybe announcement was waiting for this update? */ uc = get_unupdated_channel(rstate, &short_channel_id); if (!uc) { + if (index) + return false; + /* Allow ld to process a private channel update */ + if (source_peer) { + tell_lightningd_private_update(rstate, source_peer, + short_channel_id, fee_base_msat, + fee_proportional_millionths, + expiry, htlc_minimum, + htlc_maximum); + } return false; } sat = uc->sat; @@ -1535,6 +1569,14 @@ bool routing_add_channel_update(struct routing_state *rstate, hc->bcast.index = index; hc->rgraph.index = index; } + if (source_peer) { + /* give lightningd the channel's inbound info to store to db */ + tell_lightningd_private_update(rstate, source_peer, + short_channel_id, fee_base_msat, + fee_proportional_millionths, + expiry, htlc_minimum, + htlc_maximum); + } return true; } @@ -2340,4 +2382,3 @@ void routing_channel_spent(struct routing_state *rstate, type_to_string(msg, struct short_channel_id, &chan->scid)); remember_chan_dying(rstate, &chan->scid, deadline, index); } - diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index 2a307ef36..cf92664b9 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -59,6 +59,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, bool dying UNNEEDED, @@ -133,6 +136,9 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_remote_channel_update */ +u8 *towire_gossipd_remote_channel_update(const tal_t *ctx UNNEEDED, const struct remote_priv_update *update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_remote_channel_update called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ int main(int argc, char *argv[]) diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 1fb627199..fd62f10bd 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -30,6 +30,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED, const struct half_chan *hc UNNEEDED, const u8 *cupdate UNNEEDED) { fprintf(stderr, "cupdate_different called!\n"); abort(); } +/* Generated stub for daemon_conn_send */ +void daemon_conn_send(struct daemon_conn *dc UNNEEDED, const u8 *msg UNNEEDED) +{ fprintf(stderr, "daemon_conn_send called!\n"); abort(); } /* Generated stub for gossip_store_add */ u64 gossip_store_add(struct gossip_store *gs UNNEEDED, const u8 *gossip_msg UNNEEDED, u32 timestamp UNNEEDED, bool zombie UNNEEDED, bool spam UNNEEDED, bool dying UNNEEDED, @@ -99,6 +102,9 @@ void status_fmt(enum log_level level UNNEEDED, const char *fmt UNNEEDED, ...) { fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_gossipd_remote_channel_update */ +u8 *towire_gossipd_remote_channel_update(const tal_t *ctx UNNEEDED, const struct remote_priv_update *update UNNEEDED) +{ fprintf(stderr, "towire_gossipd_remote_channel_update called!\n"); abort(); } /* Generated stub for towire_warningfmt */ u8 *towire_warningfmt(const tal_t *ctx UNNEEDED, const struct channel_id *channel UNNEEDED, diff --git a/lightningd/channel.c b/lightningd/channel.c index a20b3f538..562b7e767 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -264,6 +264,7 @@ struct channel *new_unsaved_channel(struct peer *peer, = CLOSING_FEE_NEGOTIATION_STEP_UNIT_PERCENTAGE; channel->shutdown_wrong_funding = NULL; channel->closing_feerate_range = NULL; + channel->private_update = NULL; channel->channel_update = NULL; channel->alias[LOCAL] = channel->alias[REMOTE] = NULL; @@ -427,7 +428,9 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u16 lease_chan_max_ppt, struct amount_msat htlc_minimum_msat, struct amount_msat htlc_maximum_msat, - bool ignore_fee_limits) + bool ignore_fee_limits, + /* NULL or stolen */ + struct remote_priv_update *private_update STEALS) { struct channel *channel = tal(peer->ld, struct channel); struct amount_msat htlc_min, htlc_max; @@ -553,6 +556,7 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->lease_commit_sig = tal_steal(channel, lease_commit_sig); channel->lease_chan_max_msat = lease_chan_max_msat; channel->lease_chan_max_ppt = lease_chan_max_ppt; + channel->private_update = tal_steal(channel, private_update); channel->blockheight_states = dup_height_states(channel, height_states); channel->channel_update = NULL; diff --git a/lightningd/channel.h b/lightningd/channel.h index 1df9bd032..7da224273 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -297,6 +298,10 @@ struct channel { /* Lease commited max part per thousandth channel fee (ppm * 1000) */ u16 lease_chan_max_ppt; + /* Private channel incoming fee rates, cltv delta min/max htlc from + * peer. Used to generate route hints, blinded paths. */ + struct remote_priv_update *private_update; + /* Latest channel_update, for use in error messages. */ u8 *channel_update; @@ -383,7 +388,9 @@ struct channel *new_channel(struct peer *peer, u64 dbid, u16 lease_chan_max_ppt, struct amount_msat htlc_minimum_msat, struct amount_msat htlc_maximum_msat, - bool ignore_fee_limits); + bool ignore_fee_limits, + /* NULL or stolen */ + struct remote_priv_update *private_update STEALS); /* new_inflight - Create a new channel_inflight for a channel */ struct channel_inflight *new_inflight(struct channel *channel, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index f9f462d39..e96dcf34f 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -172,6 +173,57 @@ const u8 *get_channel_update(struct channel *channel) return channel->channel_update; } +static void set_channel_remote_update(struct lightningd *ld, + struct channel *channel, + struct remote_priv_update* update TAKES) +{ + struct short_channel_id *scid; + + scid = channel->scid; + if (!scid) + scid = channel->alias[LOCAL]; + + if (!node_id_eq(&update->source_node, &channel->peer->id)) { + log_unusual(ld->log, "Bad gossip order: %s sent us a channel update for a " + "channel owned by %s (%s)", + type_to_string(tmpctx, struct node_id, + &update->source_node), + type_to_string(tmpctx, struct node_id, + &channel->peer->id), + type_to_string(tmpctx, struct short_channel_id, scid)); + if (taken(update)) + tal_free(update); + return; + } + log_debug(ld->log, "updating channel %s with private inbound settings", + type_to_string(tmpctx, struct short_channel_id, scid)); + tal_free(channel->private_update); + channel->private_update = tal_dup(channel, + struct remote_priv_update, update); + if (taken(update)) + tal_free(update); + wallet_channel_save(ld->wallet, channel); +} + +static void handle_private_update_data(struct lightningd *ld, const u8 *msg) +{ + struct channel *channel; + struct remote_priv_update *update; + + update = tal(tmpctx, struct remote_priv_update); + if (!fromwire_gossipd_remote_channel_update(msg, update)) + fatal("Gossip gave bad GOSSIPD_REMOTE_CHANNEL_UPDATE %s", + tal_hex(msg, msg)); + channel = any_channel_by_scid(ld, &update->scid, true); + if (!channel) { + log_unusual(ld->log, "could not find channel for peer's " + "private channel update"); + return; + } + + set_channel_remote_update(ld, channel, update); +} + static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) { enum gossipd_wire t = fromwire_peektype(msg); @@ -213,6 +265,11 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_GOT_LOCAL_CHANNEL_UPDATE: handle_local_channel_update(gossip->ld, msg); break; + case WIRE_GOSSIPD_REMOTE_CHANNEL_UPDATE: + /* Please stash in database for us! */ + handle_private_update_data(gossip->ld, msg); + tal_free(msg); + break; } return 0; } diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 0acc6f1b2..8a043184c 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -232,7 +232,8 @@ wallet_commit_channel(struct lightningd *ld, 0, NULL, 0, 0, /* No leases on v1s */ ld->config.htlc_minimum_msat, ld->config.htlc_maximum_msat, - ld->config.ignore_fee_limits); + ld->config.ignore_fee_limits, + NULL); /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); @@ -1486,7 +1487,8 @@ static struct channel *stub_chan(struct command *cmd, 0, NULL, 0, 0, /* No leases on v1s */ ld->config.htlc_minimum_msat, ld->config.htlc_maximum_msat, - false); + false, + NULL); return channel; } diff --git a/plugins/Makefile b/plugins/Makefile index 852b93cbd..edc29a0f7 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -198,7 +198,7 @@ plugins/commando: $(PLUGIN_COMMANDO_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJ # Topology wants to decode node_announcement, and peer_wiregen which # pulls in some of bitcoin/. -plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o wire/peer_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) +plugins/topology: common/route.o common/dijkstra.o common/gossmap.o common/fp16.o wire/peer_wiregen.o wire/channel_type_wiregen.o bitcoin/block.o bitcoin/preimage.o common/gossmods_listpeerchannels.o $(PLUGIN_TOPOLOGY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) plugins/txprepare: $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index ddabea496..f3e7a51f6 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -176,7 +176,9 @@ struct channel *new_channel(struct peer *peer UNNEEDED, u64 dbid UNNEEDED, u16 lease_chan_max_ppt UNNEEDED, struct amount_msat htlc_minimum_msat UNNEEDED, struct amount_msat htlc_maximum_msat UNNEEDED, - bool ignore_fee_limits UNNEEDED) + bool ignore_fee_limits UNNEEDED, + /* NULL or stolen */ + struct remote_priv_update* remote_update STEALS UNNEEDED) { fprintf(stderr, "new_channel called!\n"); abort(); } /* Generated stub for new_coin_wallet_deposit */ struct chain_coin_mvt *new_coin_wallet_deposit(const tal_t *ctx UNNEEDED, diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 42cec47f1..29652fe48 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1904,7 +1904,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) 7777, 22, AMOUNT_MSAT(0), AMOUNT_MSAT(-1ULL), - false); + false, + NULL); db_begin_transaction(w->db); CHECK(!wallet_err); wallet_channel_insert(w, chan); diff --git a/wallet/wallet.c b/wallet/wallet.c index e2e798659..a740193b5 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1514,6 +1514,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm u32 lease_chan_max_msat; u16 lease_chan_max_ppt; bool ignore_fee_limits; + struct remote_priv_update *remote_update; peer_dbid = db_col_u64(stmt, "peer_id"); peer = find_peer_by_dbid(w->ld, peer_dbid); @@ -1678,6 +1679,27 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm last_sig = NULL; } + if (!db_col_is_null(stmt, "remote_cltv_expiry_delta")) { + remote_update = tal(NULL, struct remote_priv_update); + remote_update->source_node = peer->id; + if (scid) + remote_update->scid = *scid; + else + remote_update->scid = *alias[LOCAL]; + remote_update->fee_base = db_col_int(stmt, "remote_feerate_base"); + remote_update->fee_ppm = db_col_int(stmt, "remote_feerate_ppm"); + remote_update->cltv_delta = db_col_int(stmt, "remote_cltv_expiry_delta"); + remote_update->htlc_minimum_msat = db_col_amount_msat(stmt, "remote_htlc_minimum_msat"); + remote_update->htlc_maximum_msat = db_col_amount_msat(stmt, "remote_htlc_maximum_msat"); + } else { + remote_update = NULL; + db_col_ignore(stmt, "remote_feerate_base"); + db_col_ignore(stmt, "remote_feerate_ppm"); + db_col_ignore(stmt, "remote_cltv_expiry_delta"); + db_col_ignore(stmt, "remote_htlc_minimum_msat"); + db_col_ignore(stmt, "remote_htlc_maximum_msat"); + } + chan = new_channel(peer, db_col_u64(stmt, "id"), &wshachain, channel_state_in_db(db_col_int(stmt, "state")), @@ -1737,7 +1759,8 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm lease_chan_max_ppt, htlc_minimum_msat, htlc_maximum_msat, - ignore_fee_limits); + ignore_fee_limits, + remote_update); if (!wallet_channel_load_inflights(w, chan)) { tal_free(chan); @@ -1925,6 +1948,11 @@ static bool wallet_channels_load_active(struct wallet *w) ", alias_local" ", alias_remote" ", ignore_fee_limits" + ", remote_feerate_base" + ", remote_feerate_ppm" + ", remote_cltv_expiry_delta" + ", remote_htlc_minimum_msat" + ", remote_htlc_maximum_msat" " FROM channels" " WHERE state != ?;")); //? 0 db_bind_int(stmt, CLOSED); @@ -2238,8 +2266,13 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " htlc_maximum_msat=?," // 43 " alias_local=?," // 44 " alias_remote=?," // 45 - " ignore_fee_limits=?" // 46 - " WHERE id=?")); // 47 + " ignore_fee_limits=?," // 46 + " remote_feerate_base=?," // 47 + " remote_feerate_ppm=?," // 48 + " remote_cltv_expiry_delta=?," // 49 + " remote_htlc_minimum_msat=?," // 50 + " remote_htlc_maximum_msat=?" // 51 + " WHERE id=?")); // 52 db_bind_u64(stmt, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, chan->scid); @@ -2321,6 +2354,19 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt); db_bind_int(stmt, chan->ignore_fee_limits); + if (chan->private_update) { + db_bind_int(stmt, chan->private_update->fee_base); + db_bind_int(stmt, chan->private_update->fee_ppm); + db_bind_int(stmt, chan->private_update->cltv_delta); + db_bind_amount_msat(stmt, &chan->private_update->htlc_minimum_msat); + db_bind_amount_msat(stmt, &chan->private_update->htlc_maximum_msat); + } else { + db_bind_null(stmt); + db_bind_null(stmt); + db_bind_null(stmt); + db_bind_null(stmt); + db_bind_null(stmt); + } db_bind_u64(stmt, chan->dbid); db_exec_prepared_v2(take(stmt));