Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct inject update #4361

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions contrib/pyln-client/pyln/client/lightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,15 @@ def __init__(self, socket_path, executor=None, logger=logging,
if patch_json:
monkey_patch_json(patch=True)

def addgossip(self, message):
"""
Inject this (hex-encoded) gossip message.
"""
payload = {
"message": message,
}
return self.call("addgossip", payload)

def autocleaninvoice(self, cycle_seconds=None, expired_by=None):
"""
Sets up automatic cleaning of expired invoices. {cycle_seconds} sets
Expand Down
1 change: 1 addition & 0 deletions doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ doc-wrongdir:
MANPAGES := doc/lightning-cli.1 \
doc/lightningd.8 \
doc/lightningd-config.5 \
doc/lightning-addgossip.7 \
doc/lightning-autocleaninvoice.7 \
doc/lightning-check.7 \
doc/lightning-checkmessage.7 \
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ c-lightning Documentation
:maxdepth: 1
:caption: Manpages

lightning-addgossip <lightning-addgossip.7.md>
lightning-autocleaninvoice <lightning-autocleaninvoice.7.md>
lightning-check <lightning-check.7.md>
lightning-checkmessage <lightning-checkmessage.7.md>
Expand Down
38 changes: 38 additions & 0 deletions doc/lightning-addgossip.7

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 41 additions & 0 deletions doc/lightning-addgossip.7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
lightning-addgossip -- Command for injecting a gossip message (low-level)
===============================================================

SYNOPSIS
--------

**addgossip** *message*

DESCRIPTION
-----------

The **addgossip** RPC command injects a hex-encoded gossip message into
the gossip daemon. It may return an error if it is malformed, or may
update its internal state using the gossip message.

Note that currently some paths will still silently reject the gossip: it
is best effort.

This is particularly used by plugins which may receive channel_update
messages within error replies.

RETURN VALUE
------------

On success, an empty object is returned.

AUTHOR
------

Rusty Russell <<[email protected]>> is mainly responsible.

SEE ALSO
--------

lightning-pay(7)

RESOURCES
---------

Main web site: <https://github.com/ElementsProject/lightning>

2 changes: 1 addition & 1 deletion gossipd/gossip_generation.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ static void update_local_channel(struct local_cupdate *lc /* frees! */)
msg = handle_channel_update(daemon->rstate, update,
find_peer(daemon,
&chan->nodes[!direction]->id),
NULL);
NULL, true);
if (msg)
status_failed(STATUS_FAIL_INTERNAL_ERROR,
"%s: rejected local channel update %s: %s",
Expand Down
2 changes: 1 addition & 1 deletion gossipd/gossip_store.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ u32 gossip_store_load(struct routing_state *rstate, struct gossip_store *gs)
case WIRE_CHANNEL_UPDATE:
if (!routing_add_channel_update(rstate,
take(msg), gs->len,
NULL)) {
NULL, false)) {
bad = "Bad channel_update";
goto badmsg;
}
Expand Down
145 changes: 54 additions & 91 deletions gossipd/gossipd.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ static bool get_node_announcement_by_id(const tal_t *ctx,
* message, and puts the announcemnt on an internal 'pending'
* queue. We'll send a request to lightningd to look it up, and continue
* processing in `handle_txout_reply`. */
static const u8 *handle_channel_announcement_msg(struct peer *peer,
static const u8 *handle_channel_announcement_msg(struct daemon *daemon,
struct peer *peer,
const u8 *msg)
{
const struct short_channel_id *scid;
Expand All @@ -239,20 +240,20 @@ static const u8 *handle_channel_announcement_msg(struct peer *peer,
/* If it's OK, tells us the short_channel_id to lookup; it notes
* if this is the unknown channel the peer was looking for (in
* which case, it frees and NULLs that ptr) */
err = handle_channel_announcement(peer->daemon->rstate, msg,
peer->daemon->current_blockheight,
err = handle_channel_announcement(daemon->rstate, msg,
daemon->current_blockheight,
&scid, peer);
if (err)
return err;
else if (scid) {
/* We give them some grace period, in case we don't know about
* block yet. */
if (peer->daemon->current_blockheight == 0
if (daemon->current_blockheight == 0
|| !is_scid_depth_announceable(scid,
peer->daemon->current_blockheight)) {
tal_arr_expand(&peer->daemon->deferred_txouts, *scid);
daemon->current_blockheight)) {
tal_arr_expand(&daemon->deferred_txouts, *scid);
} else {
daemon_conn_send(peer->daemon->master,
daemon_conn_send(daemon->master,
take(towire_gossipd_get_txout(NULL,
scid)));
}
Expand All @@ -268,7 +269,7 @@ static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg)

unknown_scid.u64 = 0;
err = handle_channel_update(peer->daemon->rstate, msg, peer,
&unknown_scid);
&unknown_scid, false);
if (err) {
if (unknown_scid.u64 != 0)
query_unknown_channel(peer->daemon, peer, &unknown_scid);
Expand Down Expand Up @@ -420,7 +421,7 @@ static bool handle_local_channel_announcement(struct daemon *daemon,
return false;
}

err = handle_channel_announcement_msg(peer, cannouncement);
err = handle_channel_announcement_msg(daemon, peer, cannouncement);
if (err) {
status_broken("peer %s invalid local_channel_announcement %s (%s)",
type_to_string(tmpctx, struct node_id, &peer->id),
Expand Down Expand Up @@ -705,7 +706,7 @@ static struct io_plan *peer_msg_in(struct io_conn *conn,
/* These are messages relayed from peer */
switch ((enum peer_wire)fromwire_peektype(msg)) {
case WIRE_CHANNEL_ANNOUNCEMENT:
err = handle_channel_announcement_msg(peer, msg);
err = handle_channel_announcement_msg(peer->daemon, peer, msg);
goto handled_relay;
case WIRE_CHANNEL_UPDATE:
err = handle_channel_update_msg(peer, msg);
Expand Down Expand Up @@ -1723,89 +1724,50 @@ static struct io_plan *handle_txout_reply(struct io_conn *conn,
return daemon_conn_read_next(conn, daemon->master);
}

/* Fix up the channel_update to include the type if it doesn't currently have
* one. See ElementsProject/lightning#1730 and lightningnetwork/lnd#1599 for the
* in-depth discussion on why we break message parsing here... */
static u8 *patch_channel_update(const tal_t *ctx, u8 *channel_update TAKES)
/*~ lightningd tells us when about a gossip message directly, when told to by
* the addgossip RPC call. That's usually used when a plugin gets an update
* returned in an payment error. */
static struct io_plan *inject_gossip(struct io_conn *conn,
struct daemon *daemon,
const u8 *msg)
{
u8 *fixed;
if (channel_update != NULL &&
fromwire_peektype(channel_update) != WIRE_CHANNEL_UPDATE) {
/* This should be a channel_update, prefix with the
* WIRE_CHANNEL_UPDATE type, but isn't. Let's prefix it. */
fixed = tal_arr(ctx, u8, 0);
towire_u16(&fixed, WIRE_CHANNEL_UPDATE);
towire(&fixed, channel_update, tal_bytelen(channel_update));
if (taken(channel_update))
tal_free(channel_update);
return fixed;
} else {
return tal_dup_talarr(ctx, u8, channel_update);
}
}
u8 *goss;
const u8 *errmsg;
const char *err;

/* Return NULL if the wrapped onion error message has no channel_update field,
* or return the embedded channel_update message otherwise. */
static u8 *channel_update_from_onion_error(const tal_t *ctx,
const u8 *onion_message)
{
u8 *channel_update = NULL;
struct amount_msat unused_msat;
u32 unused32;
if (!fromwire_gossipd_addgossip(msg, msg, &goss))
master_badmsg(WIRE_GOSSIPD_ADDGOSSIP, msg);

/* Identify failcodes that have some channel_update.
*
* TODO > BOLT 1.0: Add new failcodes when updating to a
* new BOLT version. */
if (!fromwire_temporary_channel_failure(ctx,
onion_message,
&channel_update) &&
!fromwire_amount_below_minimum(ctx,
onion_message, &unused_msat,
&channel_update) &&
!fromwire_fee_insufficient(ctx,
onion_message, &unused_msat,
&channel_update) &&
!fromwire_incorrect_cltv_expiry(ctx,
onion_message, &unused32,
&channel_update) &&
!fromwire_expiry_too_soon(ctx,
onion_message,
&channel_update))
/* No channel update. */
return NULL;

return patch_channel_update(ctx, take(channel_update));
}

/*~ lightningd tells us when a payment has failed; we mark the channel (or
* node) unusable here if it's a permanent failure, and unpack any
* channel_update contained in the error. */
static struct io_plan *handle_payment_failure(struct io_conn *conn,
struct daemon *daemon,
const u8 *msg)
{
u8 *error;
u8 *channel_update;

if (!fromwire_gossipd_payment_failure(msg, msg, &error))
master_badmsg(WIRE_GOSSIPD_PAYMENT_FAILURE, msg);

channel_update = channel_update_from_onion_error(tmpctx, error);
if (channel_update) {
status_debug("Extracted channel_update %s from onionreply %s",
tal_hex(tmpctx, channel_update),
tal_hex(tmpctx, error));
u8 *err = handle_channel_update(daemon->rstate, channel_update,
NULL, NULL);
if (err) {
status_info("extracted bad channel_update %s from onionreply %s",
sanitize_error(err, err, NULL),
error);
tal_free(err);
}
switch (fromwire_peektype(goss)) {
case WIRE_CHANNEL_ANNOUNCEMENT:
errmsg = handle_channel_announcement_msg(daemon, NULL, goss);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to verify that this still performs the chain_hash check, which is good, otherwise we could end up with some botched experiments :-)

break;
case WIRE_NODE_ANNOUNCEMENT:
errmsg = handle_node_announcement(daemon->rstate, goss,
NULL, NULL);
break;
case WIRE_CHANNEL_UPDATE:
errmsg = handle_channel_update(daemon->rstate, goss,
NULL, NULL, true);
break;
default:
err = tal_fmt(tmpctx, "unknown gossip type %i",
fromwire_peektype(goss));
goto err_extracted;
}

/* The APIs above are designed to send error messages back to peers:
* we extract the raw string instead. */
if (errmsg) {
err = sanitize_error(tmpctx, errmsg, NULL);
tal_free(errmsg);
} else
/* Send empty string if no error. */
err = "";

err_extracted:
daemon_conn_send(daemon->master,
take(towire_gossipd_addgossip_reply(NULL, err)));
return daemon_conn_read_next(conn, daemon->master);
}

Expand Down Expand Up @@ -1890,9 +1852,6 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_GOSSIPD_GET_TXOUT_REPLY:
return handle_txout_reply(conn, daemon, msg);

case WIRE_GOSSIPD_PAYMENT_FAILURE:
return handle_payment_failure(conn, daemon, msg);

case WIRE_GOSSIPD_OUTPOINT_SPENT:
return handle_outpoint_spent(conn, daemon, msg);

Expand All @@ -1908,6 +1867,9 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_GOSSIPD_NEW_BLOCKHEIGHT:
return new_blockheight(conn, daemon, msg);

case WIRE_GOSSIPD_ADDGOSSIP:
return inject_gossip(conn, daemon, msg);

#if DEVELOPER
case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE:
return dev_set_max_scids_encode_size(conn, daemon, msg);
Expand Down Expand Up @@ -1942,6 +1904,7 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_GOSSIPD_DEV_COMPACT_STORE_REPLY:
case WIRE_GOSSIPD_GOT_ONIONMSG_TO_US:
case WIRE_GOSSIPD_GOT_ONIONMSG_FORWARD:
case WIRE_GOSSIPD_ADDGOSSIP_REPLY:
break;
}

Expand Down
15 changes: 10 additions & 5 deletions gossipd/gossipd_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,6 @@ msgdata,gossipd_get_txout_reply,satoshis,amount_sat,
msgdata,gossipd_get_txout_reply,len,u16,
msgdata,gossipd_get_txout_reply,outscript,u8,len

# master->gossipd an htlc failed with this onion error.
msgtype,gossipd_payment_failure,3021
msgdata,gossipd_payment_failure,len,u16,
msgdata,gossipd_payment_failure,error,u8,len

# master -> gossipd: a potential funding outpoint was spent, please forget the eventual channel
msgtype,gossipd_outpoint_spent,3024
msgdata,gossipd_outpoint_spent,short_channel_id,short_channel_id,
Expand Down Expand Up @@ -161,3 +156,13 @@ msgdata,gossipd_send_onionmsg,id,node_id,
msgdata,gossipd_send_onionmsg,onion_len,u16,
msgdata,gossipd_send_onionmsg,onion,u8,onion_len
msgdata,gossipd_send_onionmsg,blinding,?pubkey,

# Lightningd tells us to inject a gossip message (for addgossip RPC)
msgtype,gossipd_addgossip,3044
msgdata,gossipd_addgossip,len,u16,
msgdata,gossipd_addgossip,msg,u8,len

# Empty string means no problem.
msgtype,gossipd_addgossip_reply,3144
msgdata,gossipd_addgossip_reply,err,wirestring,

Loading