mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
Merge remote-tracking branch 'origin/pr/44'
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
commit
d47d19ef2b
@ -13,12 +13,20 @@
|
||||
#include <ccan/list/list.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
|
||||
static bool announce_channel(const tal_t *ctx, struct ircstate *state, struct peer *p)
|
||||
/* Sign a privmsg by prepending the signature to the message */
|
||||
static void sign_privmsg(struct ircstate *state, struct privmsg *msg)
|
||||
{
|
||||
char txid[65];
|
||||
int siglen;
|
||||
u8 der[72];
|
||||
struct signature sig;
|
||||
privkey_sign(state->dstate, msg->msg, strlen(msg->msg), &sig);
|
||||
siglen = signature_to_der(state->dstate->secpctx, der, &sig);
|
||||
msg->msg = tal_fmt(msg, "%s %s", tal_hexstr(msg, der, siglen), msg->msg);
|
||||
}
|
||||
|
||||
static bool announce_channel(const tal_t *ctx, struct ircstate *state, struct peer *p)
|
||||
{
|
||||
char txid[65];
|
||||
struct privmsg *msg = talz(ctx, struct privmsg);
|
||||
struct txlocator *loc = locate_tx(ctx, state->dstate, &p->anchor.txid);
|
||||
|
||||
@ -38,20 +46,45 @@ static bool announce_channel(const tal_t *ctx, struct ircstate *state, struct pe
|
||||
state->dstate->config.fee_per_satoshi,
|
||||
p->remote.locktime.locktime
|
||||
);
|
||||
|
||||
privkey_sign(state->dstate, msg->msg, strlen(msg->msg), &sig);
|
||||
siglen = signature_to_der(state->dstate->secpctx, der, &sig);
|
||||
msg->msg = tal_fmt(msg, "%s %s", tal_hexstr(ctx, der, siglen), msg->msg);
|
||||
|
||||
sign_privmsg(state, msg);
|
||||
irc_send_msg(state, msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void announce_channels(struct ircstate *state)
|
||||
/* Send an announcement for this node to the channel, including its
|
||||
* hostname, port and ID */
|
||||
static void announce_node(const tal_t *ctx, struct ircstate *state)
|
||||
{
|
||||
char *hostname = state->dstate->external_ip;
|
||||
int port = state->dstate->portnum;
|
||||
struct privmsg *msg = talz(ctx, struct privmsg);
|
||||
|
||||
if (hostname == NULL) {
|
||||
//FIXME: log that we don't know our IP yet.
|
||||
return;
|
||||
}
|
||||
|
||||
msg->channel = "#lightning-nodes";
|
||||
msg->msg = tal_fmt(
|
||||
msg, "NODE %s %s %d",
|
||||
pubkey_to_hexstr(msg, state->dstate->secpctx, &state->dstate->id),
|
||||
hostname,
|
||||
port
|
||||
);
|
||||
|
||||
sign_privmsg(state, msg);
|
||||
irc_send_msg(state, msg);
|
||||
}
|
||||
|
||||
/* Announce the node's contact information and all of its channels */
|
||||
static void announce(struct ircstate *state)
|
||||
{
|
||||
|
||||
tal_t *ctx = tal(state, tal_t);
|
||||
struct peer *p;
|
||||
|
||||
announce_node(ctx, state);
|
||||
|
||||
list_for_each(&state->dstate->peers, p, list) {
|
||||
|
||||
if (!state_is_normal(p->state))
|
||||
@ -60,7 +93,7 @@ static void announce_channels(struct ircstate *state)
|
||||
}
|
||||
tal_free(ctx);
|
||||
|
||||
new_reltimer(state->dstate, state, time_from_sec(60), announce_channels, state);
|
||||
new_reltimer(state->dstate, state, time_from_sec(60), announce, state);
|
||||
}
|
||||
|
||||
/* Reconnect to IRC server upon disconnection. */
|
||||
@ -69,42 +102,41 @@ static void handle_irc_disconnect(struct ircstate *state)
|
||||
new_reltimer(state->dstate, state, state->reconnect_timeout, irc_connect, state);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an incoming message by checking if it is a channel
|
||||
* announcement, parse it and add the channel to the topology if yes.
|
||||
*
|
||||
* The format for a valid announcement is:
|
||||
* <sig> CHAN <pk1> <pk2> <anchor txid> <block height> <tx position> <base_fee>
|
||||
* <proportional_fee> <locktime>
|
||||
*/
|
||||
static void handle_irc_privmsg(struct ircstate *istate, const struct privmsg *msg)
|
||||
/* Verify a signed privmsg */
|
||||
static bool verify_signed_privmsg(
|
||||
struct ircstate *istate,
|
||||
const struct pubkey *pk,
|
||||
const struct privmsg *msg)
|
||||
{
|
||||
int blkheight;
|
||||
char **splits = tal_strsplit(msg, msg->msg + 1, " ", STR_NO_EMPTY);
|
||||
|
||||
if (tal_count(splits) != 11 || !streq(splits[1], "CHAN"))
|
||||
return;
|
||||
|
||||
int siglen = hex_data_size(strlen(splits[0]));
|
||||
u8 *der = tal_hexdata(msg, splits[0], strlen(splits[0]));
|
||||
if (der == NULL)
|
||||
return;
|
||||
|
||||
struct signature sig;
|
||||
struct sha256_double hash;
|
||||
char *content = strchr(msg->msg, ' ') + 1;
|
||||
const char *m = msg->msg + 1;
|
||||
int siglen = strchr(m, ' ') - m;
|
||||
const char *content = m + siglen + 1;
|
||||
u8 *der = tal_hexdata(msg, m, siglen);
|
||||
|
||||
siglen = hex_data_size(siglen);
|
||||
if (der == NULL)
|
||||
return false;
|
||||
|
||||
if (!signature_from_der(istate->dstate->secpctx, der, siglen, &sig))
|
||||
return;
|
||||
|
||||
return false;
|
||||
sha256_double(&hash, content, strlen(content));
|
||||
splits++;
|
||||
return check_signed_hash(istate->dstate->secpctx, &hash, &sig, pk);
|
||||
}
|
||||
|
||||
static void handle_channel_announcement(
|
||||
struct ircstate *istate,
|
||||
const struct privmsg *msg,
|
||||
char **splits)
|
||||
{
|
||||
struct pubkey *pk1 = talz(msg, struct pubkey);
|
||||
struct pubkey *pk2 = talz(msg, struct pubkey);
|
||||
struct sha256_double *txid = talz(msg, struct sha256_double);
|
||||
int index;
|
||||
|
||||
bool ok = true;
|
||||
int blkheight;
|
||||
|
||||
ok &= pubkey_from_hexstr(istate->dstate->secpctx, splits[1], strlen(splits[1]), pk1);
|
||||
ok &= pubkey_from_hexstr(istate->dstate->secpctx, splits[2], strlen(splits[2]), pk2);
|
||||
ok &= bitcoin_txid_from_hex(splits[3], strlen(splits[3]), txid);
|
||||
@ -115,7 +147,7 @@ static void handle_irc_privmsg(struct ircstate *istate, const struct privmsg *ms
|
||||
return;
|
||||
}
|
||||
|
||||
if (!check_signed_hash(istate->dstate->secpctx, &hash, &sig, pk1)) {
|
||||
if (!verify_signed_privmsg(istate, pk1, msg)) {
|
||||
log_debug(istate->log,
|
||||
"Ignoring announcement from %s, signature check failed.",
|
||||
splits[1]);
|
||||
@ -131,11 +163,80 @@ static void handle_irc_privmsg(struct ircstate *istate, const struct privmsg *ms
|
||||
atoi(splits[7]), atoi(splits[8]), 6);
|
||||
}
|
||||
|
||||
static void handle_node_announcement(
|
||||
struct ircstate *istate,
|
||||
const struct privmsg *msg,
|
||||
char **splits)
|
||||
{
|
||||
struct pubkey *pk = talz(msg, struct pubkey);
|
||||
char *hostname = tal_strdup(msg, splits[2]);
|
||||
int port = atoi(splits[3]);
|
||||
|
||||
if (!pubkey_from_hexstr(istate->dstate->secpctx, splits[1], strlen(splits[1]), pk) || port < 1)
|
||||
return;
|
||||
|
||||
if (!verify_signed_privmsg(istate, pk, msg)) {
|
||||
log_debug(istate->log, "Ignoring node announcement from %s, signature check failed.",
|
||||
splits[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
add_node(istate->dstate, pk, hostname, port);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an incoming message by checking if it is a channel
|
||||
* announcement, parse it and add the channel to the topology if yes.
|
||||
*
|
||||
* The format for a valid announcement is:
|
||||
* <sig> CHAN <pk1> <pk2> <anchor txid> <block height> <tx position> <base_fee>
|
||||
* <proportional_fee> <locktime>
|
||||
*/
|
||||
static void handle_irc_privmsg(struct ircstate *istate, const struct privmsg *msg)
|
||||
{
|
||||
char **splits = tal_strsplit(msg, msg->msg + 1, " ", STR_NO_EMPTY);
|
||||
int splitcount = tal_count(splits) - 1;
|
||||
|
||||
if (splitcount < 2)
|
||||
return;
|
||||
|
||||
char *type = splits[1];
|
||||
|
||||
if (splitcount == 10 && streq(type, "CHAN"))
|
||||
handle_channel_announcement(istate, msg, splits + 1);
|
||||
else if (splitcount == 5 && streq(type, "NODE"))
|
||||
handle_node_announcement(istate, msg, splits + 1);
|
||||
}
|
||||
|
||||
static void handle_irc_command(struct ircstate *istate, const struct irccommand *cmd)
|
||||
{
|
||||
struct lightningd_state *dstate = istate->dstate;
|
||||
char **params = tal_strsplit(cmd, cmd->params, " ", STR_NO_EMPTY);
|
||||
int numparams = tal_count(params) - 1;
|
||||
|
||||
if (streq(cmd->command, "378")) {
|
||||
dstate->external_ip = tal_strdup(
|
||||
istate->dstate, params[numparams - 1]);
|
||||
|
||||
// Add our node to the node_map for completeness
|
||||
add_node(istate->dstate, &dstate->id,
|
||||
dstate->external_ip, dstate->portnum);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_irc_connected(struct ircstate *istate)
|
||||
{
|
||||
irc_send(istate, "JOIN", "#lightning-nodes");
|
||||
irc_send(istate, "WHOIS", "%s", istate->nick);
|
||||
}
|
||||
|
||||
void setup_irc_connection(struct lightningd_state *dstate)
|
||||
{
|
||||
// Register callback
|
||||
irc_privmsg_cb = *handle_irc_privmsg;
|
||||
irc_connect_cb = *handle_irc_connected;
|
||||
irc_disconnect_cb = *handle_irc_disconnect;
|
||||
irc_command_cb = *handle_irc_command;
|
||||
|
||||
struct ircstate *state = talz(dstate, struct ircstate);
|
||||
state->dstate = dstate;
|
||||
@ -151,5 +252,5 @@ void setup_irc_connection(struct lightningd_state *dstate)
|
||||
pubkey_to_hexstr(state, dstate->secpctx, &dstate->id) + 1);
|
||||
|
||||
irc_connect(state);
|
||||
announce_channels(state);
|
||||
announce(state);
|
||||
}
|
||||
|
@ -287,6 +287,7 @@ static const struct json_command *cmdlist[] = {
|
||||
&getlog_command,
|
||||
&connect_command,
|
||||
&getpeers_command,
|
||||
&getnodes_command,
|
||||
&gethtlcs_command,
|
||||
&close_command,
|
||||
&newaddr_command,
|
||||
|
@ -62,6 +62,7 @@ extern const struct json_command connect_command;
|
||||
extern const struct json_command close_command;
|
||||
extern const struct json_command getchannels_command;
|
||||
extern const struct json_command getpeers_command;
|
||||
extern const struct json_command getnodes_command;
|
||||
|
||||
/* Invoice management. */
|
||||
extern const struct json_command invoice_command;
|
||||
|
@ -133,5 +133,8 @@ struct lightningd_state {
|
||||
|
||||
/* Re-exec hack for testing. */
|
||||
char **reexec;
|
||||
|
||||
/* IP/hostname to be announced for incoming connections */
|
||||
char *external_ip;
|
||||
};
|
||||
#endif /* LIGHTNING_DAEMON_LIGHTNING_H */
|
||||
|
@ -28,6 +28,7 @@ static bool node_eq(const struct node *n, const secp256k1_pubkey *key)
|
||||
{
|
||||
return structeq(&n->id.pubkey, key);
|
||||
}
|
||||
|
||||
HTABLE_DEFINE_TYPE(struct node, keyof_node, hash_key, node_eq, node_map);
|
||||
|
||||
struct node_map *empty_node_map(struct lightningd_state *dstate)
|
||||
@ -63,12 +64,33 @@ struct node *new_node(struct lightningd_state *dstate,
|
||||
n->id = *id;
|
||||
n->in = tal_arr(n, struct node_connection *, 0);
|
||||
n->out = tal_arr(n, struct node_connection *, 0);
|
||||
n->port = 0;
|
||||
node_map_add(dstate->nodes, n);
|
||||
tal_add_destructor(n, destroy_node);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
struct node *add_node(
|
||||
struct lightningd_state *dstate,
|
||||
const struct pubkey *pk,
|
||||
char *hostname,
|
||||
int port)
|
||||
{
|
||||
struct node *n = get_node(dstate, pk);
|
||||
if (!n) {
|
||||
n = new_node(dstate, pk);
|
||||
log_debug_struct(dstate->base_log, "Creating new node %s",
|
||||
struct pubkey, pk);
|
||||
} else {
|
||||
log_debug_struct(dstate->base_log, "Update existing node %s",
|
||||
struct pubkey, pk);
|
||||
}
|
||||
n->hostname = tal_steal(n, hostname);
|
||||
n->port = port;
|
||||
return n;
|
||||
}
|
||||
|
||||
static bool remove_conn_from_array(struct node_connection ***conns,
|
||||
struct node_connection *nc)
|
||||
{
|
||||
@ -526,4 +548,40 @@ const struct json_command dev_routefail_command = {
|
||||
"Returns an empty result on success"
|
||||
};
|
||||
|
||||
static void json_getnodes(struct command *cmd,
|
||||
const char *buffer, const jsmntok_t *params)
|
||||
{
|
||||
struct json_result *response = new_json_result(cmd);
|
||||
struct node *n;
|
||||
struct node_map_iter i;
|
||||
|
||||
n = node_map_first(cmd->dstate->nodes, &i);
|
||||
|
||||
json_object_start(response, NULL);
|
||||
json_array_start(response, "nodes");
|
||||
|
||||
while (n != NULL) {
|
||||
json_object_start(response, NULL);
|
||||
json_add_pubkey(response, cmd->dstate->secpctx,
|
||||
"nodeid", &n->id);
|
||||
json_add_num(response, "port", n->port);
|
||||
if (!n->port)
|
||||
json_add_null(response, "hostname");
|
||||
else
|
||||
json_add_string(response, "hostname", n->hostname);
|
||||
|
||||
json_object_end(response);
|
||||
n = node_map_next(cmd->dstate->nodes, &i);
|
||||
}
|
||||
|
||||
json_array_end(response);
|
||||
json_object_end(response);
|
||||
command_success(cmd, response);
|
||||
}
|
||||
|
||||
const struct json_command getnodes_command = {
|
||||
"getnodes",
|
||||
json_getnodes,
|
||||
"List all known nodes in the network.",
|
||||
"Returns a 'nodes' array"
|
||||
};
|
||||
|
@ -20,6 +20,11 @@ struct node_connection {
|
||||
|
||||
struct node {
|
||||
struct pubkey id;
|
||||
|
||||
/* IP/Hostname and port of this node */
|
||||
char *hostname;
|
||||
int port;
|
||||
|
||||
/* Routes connecting to us, from us. */
|
||||
struct node_connection **in, **out;
|
||||
|
||||
@ -46,6 +51,12 @@ struct node *get_node(struct lightningd_state *dstate,
|
||||
* If it returns more than msatoshi, it overflowed. */
|
||||
s64 connection_fee(const struct node_connection *c, u64 msatoshi);
|
||||
|
||||
/* Updates existing node, or creates a new one as required. */
|
||||
struct node *add_node(struct lightningd_state *dstate,
|
||||
const struct pubkey *pk,
|
||||
char *hostname,
|
||||
int port);
|
||||
|
||||
/* Updates existing connection, or creates new one as required. */
|
||||
struct node_connection *add_connection(struct lightningd_state *dstate,
|
||||
const struct pubkey *from,
|
||||
|
14
irc.c
14
irc.c
@ -3,6 +3,8 @@
|
||||
#include "irc.h"
|
||||
|
||||
void (*irc_privmsg_cb)(struct ircstate *, const struct privmsg *) = NULL;
|
||||
void (*irc_command_cb)(struct ircstate *, const struct irccommand *) = NULL;
|
||||
void (*irc_connect_cb)(struct ircstate *) = NULL;
|
||||
void (*irc_disconnect_cb)(struct ircstate *) = NULL;
|
||||
|
||||
static struct io_plan *irc_connected(struct io_conn *conn, struct lightningd_state *dstate, struct ircstate *state);
|
||||
@ -63,7 +65,7 @@ static struct io_plan *irc_write_loop(struct io_conn *conn, struct ircstate *sta
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Called by the read loop to handle individual lines. This splits the
|
||||
* line into a struct irccommand and passes it on to the specific
|
||||
* handlers for the irccommand type. It silently drops any irccommand
|
||||
@ -94,10 +96,14 @@ static void handle_irc_command(struct ircstate *state, const char *line)
|
||||
pm->msg = tal_strjoin(m, splits + 2, " ", STR_NO_TRAIL);
|
||||
irc_privmsg_cb(state, pm);
|
||||
}
|
||||
|
||||
if (irc_command_cb != NULL)
|
||||
irc_command_cb(state, m);
|
||||
|
||||
tal_free(m);
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Read incoming data and split it along the newline boundaries. Takes
|
||||
* care of buffering incomplete lines and passes the lines to the
|
||||
* handle_irc_command handler.
|
||||
@ -168,7 +174,9 @@ static struct io_plan *irc_connected(struct io_conn *conn, struct lightningd_sta
|
||||
state->connected = true;
|
||||
irc_send(state, "USER", "%s 0 * :A lightning node", state->nick);
|
||||
irc_send(state, "NICK", "%s", state->nick);
|
||||
irc_send(state, "JOIN", "#lightning-nodes");
|
||||
|
||||
if (irc_connect_cb != NULL)
|
||||
irc_connect_cb(state);
|
||||
|
||||
return io_duplex(conn,
|
||||
io_read_partial(conn,
|
||||
|
4
irc.h
4
irc.h
@ -53,8 +53,10 @@ struct ircstate {
|
||||
struct timerel reconnect_timeout;
|
||||
};
|
||||
|
||||
/* Callback to register for incoming messages */
|
||||
/* Callbacks to register for incoming messages, events and raw commands */
|
||||
extern void (*irc_privmsg_cb)(struct ircstate *, const struct privmsg *);
|
||||
extern void (*irc_command_cb)(struct ircstate *, const struct irccommand *);
|
||||
extern void (*irc_connect_cb)(struct ircstate *);
|
||||
extern void (*irc_disconnect_cb)(struct ircstate *);
|
||||
|
||||
/* Send messages to IRC */
|
||||
|
Loading…
Reference in New Issue
Block a user