2021-12-04 12:23:56 +01:00
|
|
|
#include "config.h"
|
2022-05-08 18:04:29 +02:00
|
|
|
#include <bitcoin/chainparams.h>
|
2018-07-24 08:18:58 +02:00
|
|
|
#include <ccan/err/err.h>
|
2018-02-20 21:59:09 +01:00
|
|
|
#include <ccan/tal/str/str.h>
|
2021-05-20 17:45:27 +02:00
|
|
|
#include <common/configdir.h>
|
2018-12-08 01:39:28 +01:00
|
|
|
#include <common/json_command.h>
|
2022-07-04 05:49:38 +02:00
|
|
|
#include <common/json_param.h>
|
2018-08-09 02:25:29 +02:00
|
|
|
#include <common/memleak.h>
|
|
|
|
#include <common/timeout.h>
|
2021-09-16 07:00:42 +02:00
|
|
|
#include <common/type_to_string.h>
|
2020-08-25 04:16:22 +02:00
|
|
|
#include <connectd/connectd_wiregen.h>
|
2022-01-11 02:15:48 +01:00
|
|
|
#include <gossipd/gossipd_wiregen.h>
|
2018-07-24 08:18:58 +02:00
|
|
|
#include <hsmd/capabilities.h>
|
|
|
|
#include <lightningd/channel.h>
|
2018-02-20 21:59:09 +01:00
|
|
|
#include <lightningd/connect_control.h>
|
2021-01-22 01:55:23 +01:00
|
|
|
#include <lightningd/dual_open_control.h>
|
2018-09-20 05:07:41 +02:00
|
|
|
#include <lightningd/hsm_control.h>
|
2018-02-20 21:59:09 +01:00
|
|
|
#include <lightningd/lightningd.h>
|
2022-01-29 04:32:32 +01:00
|
|
|
#include <lightningd/onion_message.h>
|
2020-09-09 09:20:53 +02:00
|
|
|
#include <lightningd/opening_common.h>
|
2022-01-29 04:33:05 +01:00
|
|
|
#include <lightningd/opening_control.h>
|
2018-07-24 08:18:58 +02:00
|
|
|
#include <lightningd/peer_control.h>
|
2022-01-29 04:33:05 +01:00
|
|
|
#include <lightningd/plugin_hook.h>
|
2018-02-20 21:59:09 +01:00
|
|
|
|
|
|
|
struct connect {
|
|
|
|
struct list_node list;
|
2019-04-08 11:58:32 +02:00
|
|
|
struct node_id id;
|
2018-02-20 21:59:09 +01:00
|
|
|
struct command *cmd;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void destroy_connect(struct connect *c)
|
|
|
|
{
|
|
|
|
list_del(&c->list);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct connect *new_connect(struct lightningd *ld,
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *id,
|
2018-02-20 21:59:09 +01:00
|
|
|
struct command *cmd)
|
|
|
|
{
|
|
|
|
struct connect *c = tal(cmd, struct connect);
|
|
|
|
c->id = *id;
|
|
|
|
c->cmd = cmd;
|
2018-04-26 06:50:58 +02:00
|
|
|
list_add_tail(&ld->connects, &c->list);
|
2018-02-20 21:59:09 +01:00
|
|
|
tal_add_destructor(c, destroy_connect);
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2018-04-26 06:50:58 +02:00
|
|
|
/* Finds first command which matches. */
|
|
|
|
static struct connect *find_connect(struct lightningd *ld,
|
2019-04-08 11:58:32 +02:00
|
|
|
const struct node_id *id)
|
2018-02-20 21:59:09 +01:00
|
|
|
{
|
2018-04-26 06:50:58 +02:00
|
|
|
struct connect *i;
|
2018-02-20 21:59:09 +01:00
|
|
|
|
2018-04-26 06:50:58 +02:00
|
|
|
list_for_each(&ld->connects, i, list) {
|
2019-04-08 11:58:32 +02:00
|
|
|
if (node_id_eq(&i->id, id))
|
2018-04-26 06:50:58 +02:00
|
|
|
return i;
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
2018-04-26 06:50:58 +02:00
|
|
|
return NULL;
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
|
|
|
|
2018-12-16 05:52:06 +01:00
|
|
|
static struct command_result *connect_cmd_succeed(struct command *cmd,
|
2021-03-16 04:44:36 +01:00
|
|
|
const struct peer *peer,
|
2021-03-25 04:53:31 +01:00
|
|
|
bool incoming,
|
2021-03-16 04:44:36 +01:00
|
|
|
const struct wireaddr_internal *addr)
|
2018-08-09 02:25:29 +02:00
|
|
|
{
|
2018-10-19 03:17:49 +02:00
|
|
|
struct json_stream *response = json_stream_success(cmd);
|
2020-04-02 06:07:47 +02:00
|
|
|
json_add_node_id(response, "id", &peer->id);
|
2020-04-03 02:03:59 +02:00
|
|
|
json_add_hex_talarr(response, "features", peer->their_features);
|
2021-03-25 04:53:31 +01:00
|
|
|
json_add_string(response, "direction", incoming ? "in" : "out");
|
2021-03-16 04:44:36 +01:00
|
|
|
json_add_address_internal(response, "address", addr);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_success(cmd, response);
|
2018-08-09 02:25:29 +02:00
|
|
|
}
|
|
|
|
|
2022-01-11 02:15:48 +01:00
|
|
|
/* FIXME: Reorder! */
|
|
|
|
static void try_connect(const tal_t *ctx,
|
|
|
|
struct lightningd *ld,
|
|
|
|
const struct node_id *id,
|
|
|
|
u32 seconds_delay,
|
|
|
|
const struct wireaddr_internal *addrhint);
|
|
|
|
|
2022-07-04 03:25:56 +02:00
|
|
|
struct id_and_addr {
|
2019-04-08 11:58:32 +02:00
|
|
|
struct node_id id;
|
2022-07-04 03:25:56 +02:00
|
|
|
const char *host;
|
|
|
|
const u16 *port;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct command_result *param_id_maybe_addr(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
|
|
|
struct id_and_addr *id_addr)
|
|
|
|
{
|
2018-02-20 21:59:09 +01:00
|
|
|
char *id_str;
|
|
|
|
char *atptr;
|
2022-07-04 03:25:56 +02:00
|
|
|
char *ataddr = NULL, *host;
|
|
|
|
u16 port;
|
|
|
|
jsmntok_t idtok = *tok;
|
2018-02-20 21:59:09 +01:00
|
|
|
|
|
|
|
/* Check for id@addrport form */
|
2022-07-04 03:25:56 +02:00
|
|
|
id_str = json_strdup(cmd, buffer, &idtok);
|
2018-02-20 21:59:09 +01:00
|
|
|
atptr = strchr(id_str, '@');
|
|
|
|
if (atptr) {
|
2018-03-25 21:51:11 +02:00
|
|
|
int atidx = atptr - id_str;
|
2018-02-20 21:59:09 +01:00
|
|
|
ataddr = tal_strdup(cmd, atptr + 1);
|
|
|
|
/* Cut id. */
|
2022-07-04 03:25:56 +02:00
|
|
|
idtok.end = idtok.start + atidx;
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
|
|
|
|
2022-07-04 03:25:56 +02:00
|
|
|
if (!json_to_node_id(buffer, &idtok, &id_addr->id))
|
|
|
|
return command_fail_badparam(cmd, name, buffer, tok,
|
|
|
|
"should be a node id");
|
|
|
|
|
|
|
|
if (!atptr)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* We could parse port/host in any order, using keyword params. */
|
|
|
|
if (id_addr->host) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
2022-07-04 03:25:56 +02:00
|
|
|
"Can't specify host as both xxx@yyy "
|
|
|
|
"and separate argument");
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
|
|
|
|
2022-07-04 03:25:56 +02:00
|
|
|
port = 0;
|
|
|
|
if (!separate_address_and_port(cmd, ataddr, &host, &port))
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"malformed host @part");
|
|
|
|
|
|
|
|
id_addr->host = host;
|
|
|
|
if (port) {
|
|
|
|
if (id_addr->port) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can't specify port as both xxx@yyy:port "
|
|
|
|
"and separate argument");
|
|
|
|
}
|
|
|
|
id_addr->port = tal_dup(cmd, u16, &port);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *param_id_addr_string(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
|
|
|
const char **addr)
|
|
|
|
{
|
|
|
|
if (*addr) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can't specify host as both xxx@yyy "
|
|
|
|
"and separate argument");
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
2022-07-04 03:25:56 +02:00
|
|
|
return param_string(cmd, name, buffer, tok, addr);
|
|
|
|
}
|
2018-02-20 21:59:09 +01:00
|
|
|
|
2022-07-04 03:25:56 +02:00
|
|
|
static struct command_result *param_id_addr_u16(struct command *cmd,
|
|
|
|
const char *name,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *tok,
|
|
|
|
const u16 **port)
|
|
|
|
{
|
|
|
|
u16 val;
|
|
|
|
if (*port) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
2022-07-04 03:25:56 +02:00
|
|
|
"Can't specify port as both xxx@yyy:port "
|
|
|
|
"and separate argument");
|
|
|
|
}
|
|
|
|
if (json_to_u16(buffer, tok, &val)) {
|
|
|
|
if (val == 0)
|
|
|
|
return command_fail_badparam(cmd, name, buffer, tok,
|
|
|
|
"should be non-zero");
|
|
|
|
*port = tal_dup(cmd, u16, &val);
|
|
|
|
return NULL;
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
|
|
|
|
2022-07-04 03:25:56 +02:00
|
|
|
return command_fail_badparam(cmd, name, buffer, tok,
|
|
|
|
"should be a 16-bit integer");
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct command_result *json_connect(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
struct wireaddr_internal *addr;
|
|
|
|
const char *err_msg;
|
|
|
|
struct id_and_addr id_addr;
|
2022-07-16 06:49:30 +02:00
|
|
|
struct peer *peer;
|
2022-07-04 03:25:56 +02:00
|
|
|
|
|
|
|
id_addr.host = NULL;
|
|
|
|
id_addr.port = NULL;
|
|
|
|
if (!param(cmd, buffer, params,
|
|
|
|
p_req("id", param_id_maybe_addr, &id_addr),
|
|
|
|
p_opt("host", param_id_addr_string, &id_addr.host),
|
|
|
|
p_opt("port", param_id_addr_u16, &id_addr.port),
|
|
|
|
NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
|
|
|
/* If we have a host, convert */
|
|
|
|
if (id_addr.host) {
|
|
|
|
u16 port = id_addr.port ? *id_addr.port : chainparams_get_ln_port(chainparams);
|
2018-08-09 02:25:29 +02:00
|
|
|
addr = tal(cmd, struct wireaddr_internal);
|
2022-07-04 03:25:56 +02:00
|
|
|
if (!parse_wireaddr_internal(id_addr.host, addr, port, false,
|
2021-08-18 12:52:21 +02:00
|
|
|
!cmd->ld->always_use_proxy
|
2018-05-10 01:18:24 +02:00
|
|
|
&& !cmd->ld->pure_tor_setup,
|
2021-05-20 17:45:27 +02:00
|
|
|
true, deprecated_apis,
|
2018-05-07 06:29:22 +02:00
|
|
|
&err_msg)) {
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_fail(cmd, LIGHTNINGD,
|
|
|
|
"Host %s:%u not valid: %s",
|
2022-07-04 03:25:56 +02:00
|
|
|
id_addr.host, port, err_msg);
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
2022-07-04 03:25:56 +02:00
|
|
|
} else {
|
2018-08-09 02:25:29 +02:00
|
|
|
addr = NULL;
|
2022-07-04 03:25:56 +02:00
|
|
|
/* Port without host name? */
|
|
|
|
if (id_addr.port)
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
|
|
|
"Can't specify port without host");
|
|
|
|
}
|
2018-02-20 21:59:09 +01:00
|
|
|
|
2022-07-16 06:49:30 +02:00
|
|
|
/* If we know about peer, see if it's already connected. */
|
|
|
|
peer = peer_by_id(cmd->ld, &id_addr.id);
|
2022-07-16 06:49:31 +02:00
|
|
|
if (peer && peer->connected == PEER_CONNECTED) {
|
2022-07-16 06:49:30 +02:00
|
|
|
log_debug(cmd->ld->log, "Already connected via %s",
|
|
|
|
type_to_string(tmpctx, struct wireaddr_internal,
|
|
|
|
&peer->addr));
|
|
|
|
return connect_cmd_succeed(cmd, peer,
|
|
|
|
peer->connected_incoming,
|
|
|
|
&peer->addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
try_connect(cmd, cmd->ld, &id_addr.id, 0, addr);
|
2018-08-09 02:25:29 +02:00
|
|
|
|
2022-07-16 06:49:31 +02:00
|
|
|
/* Leave this here for peer_connected, connect_failed or peer_disconnect_done. */
|
2022-07-04 03:25:56 +02:00
|
|
|
new_connect(cmd->ld, &id_addr.id, cmd);
|
2018-12-16 05:52:06 +01:00
|
|
|
return command_still_pending(cmd);
|
2018-02-20 21:59:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command connect_command = {
|
|
|
|
"connect",
|
2019-05-22 16:08:16 +02:00
|
|
|
"network",
|
2018-02-20 21:59:09 +01:00
|
|
|
json_connect,
|
|
|
|
"Connect to {id} at {host} (which can end in ':port' if not default). "
|
|
|
|
"{id} can also be of the form id@host"
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &connect_command);
|
2018-07-24 08:18:58 +02:00
|
|
|
|
2022-01-11 02:15:48 +01:00
|
|
|
/* We actually use this even if we don't need a delay, while we talk to
|
|
|
|
* gossipd to get the addresses. */
|
2018-08-09 02:25:29 +02:00
|
|
|
struct delayed_reconnect {
|
2022-01-11 02:15:48 +01:00
|
|
|
struct lightningd *ld;
|
|
|
|
struct node_id id;
|
2018-08-09 02:25:29 +02:00
|
|
|
u32 seconds_delayed;
|
2018-08-09 02:25:29 +02:00
|
|
|
struct wireaddr_internal *addrhint;
|
2018-08-09 02:25:29 +02:00
|
|
|
};
|
|
|
|
|
2022-01-11 02:15:48 +01:00
|
|
|
static void gossipd_got_addrs(struct subd *subd,
|
|
|
|
const u8 *msg,
|
|
|
|
const int *fds,
|
|
|
|
struct delayed_reconnect *d)
|
2018-08-09 02:25:29 +02:00
|
|
|
{
|
2022-01-11 02:15:48 +01:00
|
|
|
struct wireaddr *addrs;
|
|
|
|
u8 *connectmsg;
|
|
|
|
|
|
|
|
if (!fromwire_gossipd_get_addrs_reply(tmpctx, msg, &addrs))
|
|
|
|
fatal("Gossipd gave bad GOSSIPD_GET_ADDRS_REPLY %s",
|
|
|
|
tal_hex(msg, msg));
|
|
|
|
|
|
|
|
connectmsg = towire_connectd_connect_to_peer(NULL,
|
|
|
|
&d->id,
|
|
|
|
d->seconds_delayed,
|
|
|
|
addrs,
|
|
|
|
d->addrhint);
|
|
|
|
subd_send_msg(d->ld->connectd, take(connectmsg));
|
2018-08-09 02:25:29 +02:00
|
|
|
tal_free(d);
|
|
|
|
}
|
|
|
|
|
2022-01-11 02:15:48 +01:00
|
|
|
/* We might be off a delay timer. Now ask gossipd about public addresses. */
|
|
|
|
static void do_connect(struct delayed_reconnect *d)
|
2018-08-09 02:25:29 +02:00
|
|
|
{
|
2022-01-11 02:15:48 +01:00
|
|
|
u8 *msg = towire_gossipd_get_addrs(NULL, &d->id);
|
2018-08-09 02:25:29 +02:00
|
|
|
|
2022-01-11 02:15:48 +01:00
|
|
|
subd_req(d, d->ld->gossip, take(msg), -1, 0, gossipd_got_addrs, d);
|
|
|
|
}
|
2018-08-09 02:25:29 +02:00
|
|
|
|
2022-03-22 21:30:59 +01:00
|
|
|
/* peer may be NULL here */
|
2022-01-11 02:15:48 +01:00
|
|
|
static void try_connect(const tal_t *ctx,
|
|
|
|
struct lightningd *ld,
|
|
|
|
const struct node_id *id,
|
|
|
|
u32 seconds_delay,
|
|
|
|
const struct wireaddr_internal *addrhint)
|
|
|
|
{
|
|
|
|
struct delayed_reconnect *d;
|
2022-03-22 21:30:59 +01:00
|
|
|
struct peer *peer;
|
2022-01-11 02:15:48 +01:00
|
|
|
|
|
|
|
d = tal(ctx, struct delayed_reconnect);
|
|
|
|
d->ld = ld;
|
|
|
|
d->id = *id;
|
2018-08-09 02:25:29 +02:00
|
|
|
d->seconds_delayed = seconds_delay;
|
2021-12-28 00:21:09 +01:00
|
|
|
d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, addrhint);
|
2018-08-09 02:25:29 +02:00
|
|
|
|
2022-01-11 02:15:48 +01:00
|
|
|
if (!seconds_delay) {
|
|
|
|
do_connect(d);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-22 21:30:59 +01:00
|
|
|
log_peer_debug(ld->log, id, "Will try reconnect in %u seconds",
|
|
|
|
seconds_delay);
|
|
|
|
/* Update any channel billboards */
|
|
|
|
peer = peer_by_id(ld, id);
|
|
|
|
if (peer) {
|
|
|
|
struct channel *channel;
|
|
|
|
list_for_each(&peer->channels, channel, list) {
|
|
|
|
if (!channel_active(channel))
|
|
|
|
continue;
|
|
|
|
channel_set_billboard(channel, false,
|
|
|
|
tal_fmt(tmpctx,
|
|
|
|
"Will attempt reconnect "
|
|
|
|
"in %u seconds",
|
|
|
|
seconds_delay));
|
|
|
|
}
|
|
|
|
}
|
2018-08-09 02:28:30 +02:00
|
|
|
|
|
|
|
/* We fuzz the timer by up to 1 second, to avoid getting into
|
|
|
|
* simultanous-reconnect deadlocks with peer. */
|
2019-06-14 05:49:23 +02:00
|
|
|
notleak(new_reltimer(ld->timers, d,
|
2018-08-09 02:28:30 +02:00
|
|
|
timerel_add(time_from_sec(seconds_delay),
|
|
|
|
time_from_usec(pseudorand(1000000))),
|
2022-01-11 02:15:48 +01:00
|
|
|
do_connect, d));
|
|
|
|
}
|
|
|
|
|
2022-03-22 21:30:59 +01:00
|
|
|
void try_reconnect(const tal_t *ctx,
|
|
|
|
struct peer *peer,
|
2022-01-11 02:15:48 +01:00
|
|
|
u32 seconds_delay,
|
|
|
|
const struct wireaddr_internal *addrhint)
|
|
|
|
{
|
2022-03-22 21:30:59 +01:00
|
|
|
if (!peer->ld->reconnect)
|
2022-01-11 02:15:48 +01:00
|
|
|
return;
|
|
|
|
|
2022-03-22 21:30:59 +01:00
|
|
|
try_connect(ctx,
|
|
|
|
peer->ld,
|
|
|
|
&peer->id,
|
2022-01-11 02:15:48 +01:00
|
|
|
seconds_delay,
|
|
|
|
addrhint);
|
2018-08-09 02:25:29 +02:00
|
|
|
}
|
|
|
|
|
2022-07-16 06:49:31 +02:00
|
|
|
/* We were trying to connect, but they disconnected. */
|
|
|
|
static void connect_failed(struct lightningd *ld,
|
|
|
|
const struct node_id *id,
|
|
|
|
errcode_t errcode,
|
|
|
|
const char *errmsg,
|
2022-07-18 14:12:28 +02:00
|
|
|
const u32 *seconds_to_delay,
|
2022-07-16 06:49:31 +02:00
|
|
|
const struct wireaddr_internal *addrhint)
|
2018-08-09 02:25:29 +02:00
|
|
|
{
|
2022-03-22 21:30:59 +01:00
|
|
|
struct peer *peer;
|
2022-07-16 06:49:31 +02:00
|
|
|
struct connect *c;
|
2018-08-09 02:25:29 +02:00
|
|
|
|
|
|
|
/* We can have multiple connect commands: fail them all */
|
2022-07-16 06:49:31 +02:00
|
|
|
while ((c = find_connect(ld, id)) != NULL) {
|
2018-08-09 02:25:29 +02:00
|
|
|
/* They delete themselves from list */
|
2020-01-12 15:33:59 +01:00
|
|
|
was_pending(command_fail(c->cmd, errcode, "%s", errmsg));
|
2018-08-09 02:25:29 +02:00
|
|
|
}
|
2018-08-09 02:25:29 +02:00
|
|
|
|
|
|
|
/* If we have an active channel, then reconnect. */
|
2022-07-16 06:49:31 +02:00
|
|
|
peer = peer_by_id(ld, id);
|
2022-07-18 14:12:28 +02:00
|
|
|
if (peer && peer_any_active_channel(peer, NULL)) {
|
|
|
|
u32 delay;
|
|
|
|
if (seconds_to_delay)
|
|
|
|
delay = *seconds_to_delay;
|
2022-07-23 18:21:31 +02:00
|
|
|
else if (peer->delay_reconnect)
|
|
|
|
delay = DEV_FAST_RECONNECT(ld->dev_fast_reconnect, 3, 60);
|
2022-07-18 14:12:28 +02:00
|
|
|
else
|
2022-07-23 18:21:31 +02:00
|
|
|
delay = 1;
|
2022-07-18 14:12:28 +02:00
|
|
|
log_peer_debug(ld->log, id, "Reconnecting in %u seconds", delay);
|
|
|
|
try_reconnect(peer, peer, delay, addrhint);
|
|
|
|
} else
|
|
|
|
log_peer_debug(ld->log, id, "Not reconnecting: %s", peer ? "no active channel" : "no channels");
|
2018-08-09 02:25:29 +02:00
|
|
|
}
|
|
|
|
|
2022-07-18 14:12:28 +02:00
|
|
|
void connect_failed_disconnect(struct lightningd *ld,
|
|
|
|
const struct node_id *id,
|
|
|
|
const struct wireaddr_internal *addrhint)
|
2022-07-16 06:49:31 +02:00
|
|
|
{
|
|
|
|
connect_failed(ld, id, CONNECT_DISCONNECTED_DURING,
|
2022-07-18 14:12:28 +02:00
|
|
|
"disconnected during connection", NULL, addrhint);
|
2022-07-16 06:49:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void handle_connect_failed(struct lightningd *ld, const u8 *msg)
|
|
|
|
{
|
|
|
|
struct node_id id;
|
|
|
|
errcode_t errcode;
|
|
|
|
char *errmsg;
|
|
|
|
u32 seconds_to_delay;
|
|
|
|
struct wireaddr_internal *addrhint;
|
|
|
|
|
|
|
|
if (!fromwire_connectd_connect_failed(tmpctx, msg, &id, &errcode, &errmsg,
|
|
|
|
&seconds_to_delay, &addrhint))
|
|
|
|
fatal("Connect gave bad CONNECTD_CONNECT_FAILED message %s",
|
|
|
|
tal_hex(msg, msg));
|
|
|
|
|
2022-07-18 14:12:28 +02:00
|
|
|
connect_failed(ld, &id, errcode, errmsg, &seconds_to_delay, addrhint);
|
2022-07-16 06:49:31 +02:00
|
|
|
}
|
|
|
|
|
2021-03-16 04:44:36 +01:00
|
|
|
void connect_succeeded(struct lightningd *ld, const struct peer *peer,
|
2021-03-25 04:53:31 +01:00
|
|
|
bool incoming,
|
2021-03-16 04:44:36 +01:00
|
|
|
const struct wireaddr_internal *addr)
|
2018-08-09 02:25:29 +02:00
|
|
|
{
|
|
|
|
struct connect *c;
|
|
|
|
|
|
|
|
/* We can have multiple connect commands: fail them all */
|
2020-04-02 06:07:47 +02:00
|
|
|
while ((c = find_connect(ld, &peer->id)) != NULL) {
|
2018-08-09 02:25:29 +02:00
|
|
|
/* They delete themselves from list */
|
2021-03-25 04:53:31 +01:00
|
|
|
connect_cmd_succeed(c->cmd, peer, incoming, addr);
|
2018-08-09 02:25:29 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-29 04:33:05 +01:00
|
|
|
struct custommsg_payload {
|
|
|
|
struct node_id peer_id;
|
|
|
|
u8 *msg;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool custommsg_cb(struct custommsg_payload *payload,
|
|
|
|
const char *buffer, const jsmntok_t *toks)
|
|
|
|
{
|
|
|
|
const jsmntok_t *t_res;
|
|
|
|
|
|
|
|
if (!toks || !buffer)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
t_res = json_get_member(buffer, toks, "result");
|
|
|
|
|
|
|
|
/* fail */
|
|
|
|
if (!t_res || !json_tok_streq(buffer, t_res, "continue"))
|
|
|
|
fatal("Plugin returned an invalid response to the "
|
|
|
|
"custommsg hook: %s", buffer);
|
|
|
|
|
|
|
|
/* call next hook */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void custommsg_final(struct custommsg_payload *payload STEALS)
|
|
|
|
{
|
|
|
|
tal_steal(tmpctx, payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void custommsg_payload_serialize(struct custommsg_payload *payload,
|
|
|
|
struct json_stream *stream,
|
|
|
|
struct plugin *plugin)
|
|
|
|
{
|
|
|
|
json_add_hex_talarr(stream, "payload", payload->msg);
|
|
|
|
json_add_node_id(stream, "peer_id", &payload->peer_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_PLUGIN_HOOK(custommsg,
|
|
|
|
custommsg_cb,
|
|
|
|
custommsg_final,
|
|
|
|
custommsg_payload_serialize,
|
|
|
|
struct custommsg_payload *);
|
|
|
|
|
|
|
|
static void handle_custommsg_in(struct lightningd *ld, const u8 *msg)
|
|
|
|
{
|
|
|
|
struct custommsg_payload *p = tal(NULL, struct custommsg_payload);
|
|
|
|
|
|
|
|
if (!fromwire_connectd_custommsg_in(p, msg, &p->peer_id, &p->msg)) {
|
|
|
|
log_broken(ld->log, "Malformed custommsg: %s",
|
|
|
|
tal_hex(tmpctx, msg));
|
|
|
|
tal_free(p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
plugin_hook_call_custommsg(ld, p);
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:18:58 +02:00
|
|
|
static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fds)
|
|
|
|
{
|
2020-08-25 04:16:22 +02:00
|
|
|
enum connectd_wire t = fromwire_peektype(msg);
|
2018-07-24 08:18:58 +02:00
|
|
|
|
|
|
|
switch (t) {
|
|
|
|
/* These are messages we send, not them. */
|
2020-08-25 04:16:22 +02:00
|
|
|
case WIRE_CONNECTD_INIT:
|
|
|
|
case WIRE_CONNECTD_ACTIVATE:
|
|
|
|
case WIRE_CONNECTD_CONNECT_TO_PEER:
|
2022-03-22 21:26:16 +01:00
|
|
|
case WIRE_CONNECTD_DISCARD_PEER:
|
2020-08-25 04:16:22 +02:00
|
|
|
case WIRE_CONNECTD_DEV_MEMLEAK:
|
2022-03-31 07:56:22 +02:00
|
|
|
case WIRE_CONNECTD_DEV_SUPPRESS_GOSSIP:
|
2021-06-03 04:01:16 +02:00
|
|
|
case WIRE_CONNECTD_PEER_FINAL_MSG:
|
2022-07-18 14:12:18 +02:00
|
|
|
case WIRE_CONNECTD_PEER_CONNECT_SUBD:
|
2022-01-29 04:31:32 +01:00
|
|
|
case WIRE_CONNECTD_PING:
|
2022-01-29 04:32:32 +01:00
|
|
|
case WIRE_CONNECTD_SEND_ONIONMSG:
|
2022-01-29 04:33:05 +01:00
|
|
|
case WIRE_CONNECTD_CUSTOMMSG_OUT:
|
2018-07-24 08:18:58 +02:00
|
|
|
/* This is a reply, so never gets through to here. */
|
2020-08-25 04:16:22 +02:00
|
|
|
case WIRE_CONNECTD_INIT_REPLY:
|
|
|
|
case WIRE_CONNECTD_ACTIVATE_REPLY:
|
|
|
|
case WIRE_CONNECTD_DEV_MEMLEAK_REPLY:
|
2022-01-29 04:31:32 +01:00
|
|
|
case WIRE_CONNECTD_PING_REPLY:
|
2018-07-24 08:18:58 +02:00
|
|
|
break;
|
|
|
|
|
2020-08-25 04:16:22 +02:00
|
|
|
case WIRE_CONNECTD_PEER_CONNECTED:
|
2022-03-22 21:27:29 +01:00
|
|
|
peer_connected(connectd->ld, msg);
|
|
|
|
break;
|
|
|
|
|
2022-07-18 14:12:18 +02:00
|
|
|
case WIRE_CONNECTD_PEER_SPOKE:
|
|
|
|
peer_spoke(connectd->ld, msg);
|
2018-07-24 08:18:58 +02:00
|
|
|
break;
|
openingd: take peer before we're opening, wait for explicit funding msg.
Prior to this, lightningd would hand uninteresting peers back to connectd,
which would then return it to lightningd if it sent a non-gossip msg,
or if lightningd asked it to release the peer.
Now connectd hands the peer to lightningd once we've done the init
handshake, which hands it off to openingd.
This is a deep structural change, so we do the minimum here and cleanup
in the following patches.
Lightningd:
1. Remove peer_nongossip handling from connect_control and peer_control.
2. Remove list of outstanding fundchannel command; it was only needed to
find the race between us asking connectd to release the peer and it
reconnecting.
3. We can no longer tell if the remote end has started trying to fund a
channel (until it has succeeded): it's very transitory anyway so not
worth fixing.
4. We now always have a struct peer, and allocate an uncommitted_channel
for it, though it may never be used if neither end funds a channel.
5. We start funding on messages for openingd: we can get a funder_reply
or a fundee, or an error in response to our request to fund a channel.
so we handle all of them.
6. A new peer_start_openingd() is called after connectd hands us a peer.
7. json_fund_channel just looks through local peers; there are none
hidden in connectd any more.
8. We sometimes start a new openingd just to send an error message.
Openingd:
1. We always have information we need to accept them funding a channel (in
the init message).
2. We have to listen for three fds: peer, gossip and master, so we opencode
the poll.
3. We have an explicit message to start trying to fund a channel.
4. We can be told to send a message in our init message.
Testing:
1. We don't handle some things gracefully yet, so two tests are disabled.
2. 'hand_back_peer .*: now local again' from connectd is no longer a message,
openingd says 'Handed peer, entering loop' once its managing it.
3. peer['state'] used to be set to 'GOSSIPING' (otherwise this field doesn't
exist; 'state' is now per-channel. It doesn't exist at all now.
4. Some tests now need to turn on IO logging in openingd, not connectd.
5. There's a gap between connecting on one node and having connectd on
the peer hand over the connection to openingd. Our tests sometimes
checked getpeers() on the peer, and didn't see anything, so line_graph
needed updating.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2018-08-08 16:10:58 +02:00
|
|
|
|
2022-03-22 21:26:30 +01:00
|
|
|
case WIRE_CONNECTD_PEER_DISCONNECT_DONE:
|
|
|
|
peer_disconnect_done(connectd->ld, msg);
|
|
|
|
break;
|
|
|
|
|
2020-08-25 04:16:22 +02:00
|
|
|
case WIRE_CONNECTD_CONNECT_FAILED:
|
2022-07-16 06:49:31 +02:00
|
|
|
handle_connect_failed(connectd->ld, msg);
|
2018-07-24 08:18:58 +02:00
|
|
|
break;
|
2022-01-29 04:32:32 +01:00
|
|
|
|
|
|
|
case WIRE_CONNECTD_GOT_ONIONMSG_TO_US:
|
|
|
|
handle_onionmsg_to_us(connectd->ld, msg);
|
|
|
|
break;
|
2022-01-29 04:33:05 +01:00
|
|
|
|
|
|
|
case WIRE_CONNECTD_CUSTOMMSG_IN:
|
|
|
|
handle_custommsg_in(connectd->ld, msg);
|
|
|
|
break;
|
2018-07-24 08:18:58 +02:00
|
|
|
}
|
2018-07-24 08:18:58 +02:00
|
|
|
return 0;
|
2018-07-24 08:18:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void connect_init_done(struct subd *connectd,
|
|
|
|
const u8 *reply,
|
|
|
|
const int *fds UNUSED,
|
|
|
|
void *unused UNUSED)
|
|
|
|
{
|
|
|
|
struct lightningd *ld = connectd->ld;
|
2022-03-03 11:24:11 +01:00
|
|
|
char *errmsg;
|
2018-07-24 08:18:58 +02:00
|
|
|
|
2020-08-25 04:16:22 +02:00
|
|
|
if (!fromwire_connectd_init_reply(ld, reply,
|
2022-03-03 11:24:11 +01:00
|
|
|
&ld->binding,
|
2022-02-23 17:58:41 +01:00
|
|
|
&ld->announceable,
|
2022-03-03 11:24:11 +01:00
|
|
|
&errmsg))
|
2022-03-04 07:10:57 +01:00
|
|
|
fatal("Bad connectd_init_reply: %s",
|
2018-07-24 08:18:58 +02:00
|
|
|
tal_hex(reply, reply));
|
|
|
|
|
2022-03-03 11:24:11 +01:00
|
|
|
/* connectd can fail in *informative* ways: don't use fatal() here and
|
|
|
|
* confuse things with a backtrace! */
|
|
|
|
if (errmsg) {
|
|
|
|
log_broken(connectd->log, "%s", errmsg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:18:58 +02:00
|
|
|
/* Break out of loop, so we can begin */
|
2022-06-26 06:51:01 +02:00
|
|
|
log_debug(connectd->ld->log, "io_break: %s", __func__);
|
2018-07-24 08:18:58 +02:00
|
|
|
io_break(connectd);
|
|
|
|
}
|
|
|
|
|
2022-01-29 04:33:05 +01:00
|
|
|
int connectd_init(struct lightningd *ld)
|
2018-07-24 08:18:58 +02:00
|
|
|
{
|
2022-01-29 04:33:05 +01:00
|
|
|
int fds[2];
|
2020-03-31 00:39:01 +02:00
|
|
|
u8 *msg;
|
2018-07-24 08:18:58 +02:00
|
|
|
int hsmfd;
|
|
|
|
struct wireaddr_internal *wireaddrs = ld->proposed_wireaddr;
|
|
|
|
enum addr_listen_announce *listen_announce = ld->proposed_listen_announce;
|
2021-10-18 02:13:33 +02:00
|
|
|
const char *websocket_helper_path;
|
2022-06-26 06:51:01 +02:00
|
|
|
void *ret;
|
2021-10-18 02:13:33 +02:00
|
|
|
|
|
|
|
websocket_helper_path = subdaemon_path(tmpctx, ld,
|
|
|
|
"lightning_websocketd");
|
2018-07-24 08:18:58 +02:00
|
|
|
|
|
|
|
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fds) != 0)
|
|
|
|
fatal("Could not socketpair for connectd<->gossipd");
|
|
|
|
|
2018-09-20 05:07:41 +02:00
|
|
|
hsmfd = hsm_get_global_fd(ld, HSM_CAP_ECDH);
|
2018-07-24 08:18:58 +02:00
|
|
|
|
|
|
|
ld->connectd = new_global_subd(ld, "lightning_connectd",
|
2020-08-25 04:16:22 +02:00
|
|
|
connectd_wire_name, connectd_msg,
|
2022-01-29 04:33:05 +01:00
|
|
|
take(&hsmfd), take(&fds[1]),
|
2022-01-08 14:25:29 +01:00
|
|
|
#if DEVELOPER
|
|
|
|
/* Not take(): we share it */
|
|
|
|
ld->dev_disconnect_fd >= 0 ?
|
|
|
|
&ld->dev_disconnect_fd : NULL,
|
|
|
|
#endif
|
|
|
|
NULL);
|
2018-07-24 08:18:58 +02:00
|
|
|
if (!ld->connectd)
|
|
|
|
err(1, "Could not subdaemon connectd");
|
|
|
|
|
|
|
|
/* If no addr specified, hand wildcard to connectd */
|
|
|
|
if (tal_count(wireaddrs) == 0 && ld->autolisten) {
|
|
|
|
wireaddrs = tal_arrz(tmpctx, struct wireaddr_internal, 1);
|
|
|
|
listen_announce = tal_arr(tmpctx, enum addr_listen_announce, 1);
|
|
|
|
wireaddrs->itype = ADDR_INTERNAL_ALLPROTO;
|
|
|
|
wireaddrs->u.port = ld->portnum;
|
|
|
|
*listen_announce = ADDR_LISTEN_AND_ANNOUNCE;
|
|
|
|
}
|
|
|
|
|
2020-08-25 04:16:22 +02:00
|
|
|
msg = towire_connectd_init(
|
2019-11-28 15:11:07 +01:00
|
|
|
tmpctx, chainparams,
|
2020-04-03 02:03:59 +02:00
|
|
|
ld->our_features,
|
2019-11-28 15:11:07 +01:00
|
|
|
&ld->id,
|
2018-12-03 00:06:06 +01:00
|
|
|
wireaddrs,
|
2018-08-09 02:25:29 +02:00
|
|
|
listen_announce,
|
2021-08-18 12:52:21 +02:00
|
|
|
ld->proxyaddr, ld->always_use_proxy || ld->pure_tor_setup,
|
2019-09-12 02:24:00 +02:00
|
|
|
IFDEV(ld->dev_allow_localhost, false), ld->config.use_dns,
|
2019-09-25 13:05:14 +02:00
|
|
|
ld->tor_service_password ? ld->tor_service_password : "",
|
2021-10-15 07:49:05 +02:00
|
|
|
ld->config.connection_timeout_secs,
|
|
|
|
websocket_helper_path,
|
2022-01-08 14:25:29 +01:00
|
|
|
ld->websocket_port,
|
2022-04-20 13:02:04 +02:00
|
|
|
!deprecated_apis,
|
2022-01-08 14:28:29 +01:00
|
|
|
IFDEV(ld->dev_fast_gossip, false),
|
2022-03-25 04:11:55 +01:00
|
|
|
IFDEV(ld->dev_disconnect_fd >= 0, false),
|
|
|
|
IFDEV(ld->dev_no_ping_timer, false));
|
2018-07-24 08:18:58 +02:00
|
|
|
|
|
|
|
subd_req(ld->connectd, ld->connectd, take(msg), -1, 0,
|
|
|
|
connect_init_done, NULL);
|
|
|
|
|
|
|
|
/* Wait for init_reply */
|
2022-06-26 06:51:01 +02:00
|
|
|
ret = io_loop(NULL, NULL);
|
|
|
|
log_debug(ld->log, "io_loop: %s", __func__);
|
|
|
|
assert(ret == ld->connectd);
|
2018-07-24 08:18:58 +02:00
|
|
|
|
2018-07-24 08:18:58 +02:00
|
|
|
return fds[0];
|
|
|
|
}
|
2018-07-24 08:18:58 +02:00
|
|
|
|
|
|
|
static void connect_activate_done(struct subd *connectd,
|
2022-03-04 07:10:57 +01:00
|
|
|
const u8 *reply,
|
2018-07-24 08:18:58 +02:00
|
|
|
const int *fds UNUSED,
|
|
|
|
void *unused UNUSED)
|
|
|
|
{
|
2022-03-04 07:10:57 +01:00
|
|
|
char *errmsg;
|
|
|
|
if (!fromwire_connectd_activate_reply(reply, reply, &errmsg))
|
|
|
|
fatal("Bad connectd_activate_reply: %s",
|
|
|
|
tal_hex(reply, reply));
|
|
|
|
|
|
|
|
/* connectd can fail in *informative* ways: don't use fatal() here and
|
|
|
|
* confuse things with a backtrace! */
|
|
|
|
if (errmsg) {
|
|
|
|
log_broken(connectd->log, "%s", errmsg);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2018-07-24 08:18:58 +02:00
|
|
|
/* Break out of loop, so we can begin */
|
2022-06-26 06:51:01 +02:00
|
|
|
log_debug(connectd->ld->log, "io_break: %s", __func__);
|
2018-07-24 08:18:58 +02:00
|
|
|
io_break(connectd);
|
|
|
|
}
|
|
|
|
|
|
|
|
void connectd_activate(struct lightningd *ld)
|
|
|
|
{
|
2022-06-26 06:51:01 +02:00
|
|
|
void *ret;
|
2020-08-25 04:16:22 +02:00
|
|
|
const u8 *msg = towire_connectd_activate(NULL, ld->listen);
|
2018-07-24 08:18:58 +02:00
|
|
|
|
|
|
|
subd_req(ld->connectd, ld->connectd, take(msg), -1, 0,
|
|
|
|
connect_activate_done, NULL);
|
|
|
|
|
|
|
|
/* Wait for activate_reply */
|
2022-06-26 06:51:01 +02:00
|
|
|
ret = io_loop(NULL, NULL);
|
|
|
|
log_debug(ld->log, "io_loop: %s", __func__);
|
|
|
|
assert(ret == ld->connectd);
|
2018-07-24 08:18:58 +02:00
|
|
|
}
|
|
|
|
|
2022-01-29 04:33:05 +01:00
|
|
|
static struct command_result *json_sendcustommsg(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
struct json_stream *response;
|
|
|
|
struct node_id *dest;
|
|
|
|
struct peer *peer;
|
|
|
|
u8 *msg;
|
|
|
|
int type;
|
|
|
|
|
|
|
|
if (!param(cmd, buffer, params,
|
|
|
|
p_req("node_id", param_node_id, &dest),
|
|
|
|
p_req("msg", param_bin_from_hex, &msg),
|
|
|
|
NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
|
|
|
type = fromwire_peektype(msg);
|
|
|
|
if (peer_wire_is_defined(type)) {
|
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_REQUEST,
|
|
|
|
"Cannot send messages of type %d (%s). It is not possible "
|
|
|
|
"to send messages that have a type managed internally "
|
|
|
|
"since that might cause issues with the internal state "
|
|
|
|
"tracking.",
|
|
|
|
type, peer_wire_name(type));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type % 2 == 0) {
|
|
|
|
return command_fail(
|
|
|
|
cmd, JSONRPC2_INVALID_REQUEST,
|
|
|
|
"Cannot send even-typed %d custom message. Currently "
|
|
|
|
"custom messages are limited to odd-numbered message "
|
|
|
|
"types, as even-numbered types might result in "
|
|
|
|
"disconnections.",
|
|
|
|
type);
|
|
|
|
}
|
|
|
|
|
|
|
|
peer = peer_by_id(cmd->ld, dest);
|
|
|
|
if (!peer) {
|
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_REQUEST,
|
|
|
|
"No such peer: %s",
|
|
|
|
type_to_string(cmd, struct node_id, dest));
|
|
|
|
}
|
|
|
|
|
2022-07-16 06:49:31 +02:00
|
|
|
if (peer->connected != PEER_CONNECTED)
|
2022-01-29 04:33:05 +01:00
|
|
|
return command_fail(cmd, JSONRPC2_INVALID_REQUEST,
|
2022-07-16 06:49:31 +02:00
|
|
|
"Peer is %s",
|
|
|
|
peer->connected == PEER_DISCONNECTED
|
|
|
|
? "not connected" : "still connecting");
|
2022-01-29 04:33:05 +01:00
|
|
|
|
|
|
|
subd_send_msg(cmd->ld->connectd,
|
|
|
|
take(towire_connectd_custommsg_out(cmd, dest, msg)));
|
|
|
|
|
|
|
|
response = json_stream_success(cmd);
|
|
|
|
json_add_string(response, "status",
|
|
|
|
"Message sent to connectd for delivery");
|
|
|
|
|
|
|
|
return command_success(cmd, response);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command sendcustommsg_command = {
|
|
|
|
"sendcustommsg",
|
|
|
|
"utility",
|
|
|
|
json_sendcustommsg,
|
|
|
|
"Send a custom message to the peer with the given {node_id}",
|
|
|
|
.verbose = "sendcustommsg node_id hexcustommsg",
|
|
|
|
};
|
|
|
|
|
|
|
|
AUTODATA(json_command, &sendcustommsg_command);
|
|
|
|
|
|
|
|
#ifdef COMPAT_V0100
|
|
|
|
#ifdef DEVELOPER
|
|
|
|
static const struct json_command dev_sendcustommsg_command = {
|
|
|
|
"dev-sendcustommsg",
|
|
|
|
"utility",
|
|
|
|
json_sendcustommsg,
|
|
|
|
"Send a custom message to the peer with the given {node_id}",
|
|
|
|
.verbose = "dev-sendcustommsg node_id hexcustommsg",
|
|
|
|
};
|
|
|
|
|
|
|
|
AUTODATA(json_command, &dev_sendcustommsg_command);
|
|
|
|
#endif /* DEVELOPER */
|
|
|
|
#endif /* COMPAT_V0100 */
|
2022-03-31 07:56:22 +02:00
|
|
|
|
|
|
|
#if DEVELOPER
|
|
|
|
static struct command_result *json_dev_suppress_gossip(struct command *cmd,
|
|
|
|
const char *buffer,
|
|
|
|
const jsmntok_t *obj UNNEEDED,
|
|
|
|
const jsmntok_t *params)
|
|
|
|
{
|
|
|
|
if (!param(cmd, buffer, params, NULL))
|
|
|
|
return command_param_failed();
|
|
|
|
|
|
|
|
subd_send_msg(cmd->ld->connectd,
|
|
|
|
take(towire_connectd_dev_suppress_gossip(NULL)));
|
|
|
|
|
|
|
|
return command_success(cmd, json_stream_success(cmd));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct json_command dev_suppress_gossip = {
|
|
|
|
"dev-suppress-gossip",
|
|
|
|
"developer",
|
|
|
|
json_dev_suppress_gossip,
|
|
|
|
"Stop this node from sending any more gossip."
|
|
|
|
};
|
|
|
|
AUTODATA(json_command, &dev_suppress_gossip);
|
|
|
|
#endif /* DEVELOPER */
|