Skip to content

Commit

Permalink
routing: Refactoring IRC announcements.
Browse files Browse the repository at this point in the history
Moving all IRC related functionality from routing.c into its own module
so that we can replace it later.
  • Loading branch information
cdecker committed Sep 7, 2016
1 parent 6ca7802 commit 4f9c213
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 149 deletions.
1 change: 1 addition & 0 deletions daemon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ DAEMON_SRC := \
daemon/feechange.c \
daemon/htlc.c \
daemon/invoice.c \
daemon/irc_announce.c \
daemon/jsonrpc.c \
daemon/lightningd.c \
daemon/netaddr.c \
Expand Down
155 changes: 155 additions & 0 deletions daemon/irc_announce.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#include "bitcoin/privkey.h"
#include "bitcoin/signature.h"
#include "daemon/chaintopology.h"
#include "daemon/irc_announce.h"
#include "daemon/lightningd.h"
#include "daemon/log.h"
#include "daemon/peer.h"
#include "daemon/routing.h"
#include "daemon/secrets.h"
#include "daemon/timeout.h"
#include "utils.h"

#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)
{
char txid[65];
int siglen;
u8 der[72];
struct signature sig;
struct privmsg *msg = talz(ctx, struct privmsg);
struct txlocator *loc = locate_tx(ctx, state->dstate, &p->anchor.txid);

if (loc == NULL)
return false;

bitcoin_txid_to_hex(&p->anchor.txid, txid, sizeof(txid));
msg->channel = "#lightning-nodes";
msg->msg = tal_fmt(
msg, "CHAN %s %s %s %d %d %d %d %d",
pubkey_to_hexstr(msg, state->dstate->secpctx, &state->dstate->id),
pubkey_to_hexstr(msg, state->dstate->secpctx, p->id),
txid,
loc->blkheight,
loc->index,
state->dstate->config.fee_base,
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);

irc_send_msg(state, msg);
return true;
}

static void announce_channels(struct ircstate *state)
{
tal_t *ctx = tal(state, tal_t);
struct peer *p;

list_for_each(&state->dstate->peers, p, list) {

if (!state_is_normal(p->state))
continue;
announce_channel(ctx, state, p);
}
tal_free(ctx);

new_reltimer(state->dstate, state, time_from_sec(60), announce_channels, state);
}

/* Reconnect to IRC server upon disconnection. */
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)
{
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;
if (!signature_from_der(istate->dstate->secpctx, der, siglen, &sig))
return;

sha256_double(&hash, content, strlen(content));
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;
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);
blkheight = atoi(splits[4]);
index = atoi(splits[5]);
if (!ok || index < 0 || blkheight < 0) {
log_debug(istate->dstate->base_log, "Unable to parse channel announcent.");
return;
}

if (!check_signed_hash(istate->dstate->secpctx, &hash, &sig, pk1)) {
log_debug(istate->log,
"Ignoring announcement from %s, signature check failed.",
splits[1]);
return;
}

/*
* FIXME Check in topology that the tx is in the block and
* that the endpoints match.
*/

add_connection(istate->dstate, pk1, pk2, atoi(splits[6]),
atoi(splits[7]), atoi(splits[8]), 6);
}

void setup_irc_connection(struct lightningd_state *dstate)
{
// Register callback
irc_privmsg_cb = *handle_irc_privmsg;
irc_disconnect_cb = *handle_irc_disconnect;

struct ircstate *state = talz(dstate, struct ircstate);
state->dstate = dstate;
state->server = "irc.freenode.net";
state->reconnect_timeout = time_from_sec(15);
state->log = new_log(state, state->dstate->log_record, "%s:irc",
log_prefix(state->dstate->base_log));

/* Truncate nick at 13 bytes, would be imposed by freenode anyway */
state->nick = tal_fmt(
state,
"N%.12s",
pubkey_to_hexstr(state, dstate->secpctx, &dstate->id) + 1);

irc_connect(state);
announce_channels(state);
}
9 changes: 9 additions & 0 deletions daemon/irc_announce.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef LIGHTNING_DAEMON_IRC_ANNOUNCE_H
#define LIGHTNING_DAEMON_IRC_ANNOUNCE_H
#include "config.h"
#include "irc.h"

// Main entrypoint for the lightning daemon
void setup_irc_connection(struct lightningd_state *dstate);

#endif /* LIGHTNING_DAEMON_IRC_ANNOUNCE_H */
1 change: 1 addition & 0 deletions daemon/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "configdir.h"
#include "controlled_time.h"
#include "db.h"
#include "irc_announce.h"
#include "jsonrpc.h"
#include "lightningd.h"
#include "log.h"
Expand Down
1 change: 0 additions & 1 deletion daemon/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define LIGHTNING_DAEMON_LIGHTNING_H
#include "config.h"
#include "bitcoin/pubkey.h"
#include "routing.h"
#include "watch.h"
#include <ccan/list/list.h>
#include <ccan/short_types/short_types.h>
Expand Down
145 changes: 1 addition & 144 deletions daemon/routing.c
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
#include "jsonrpc.h"
#include "bitcoin/block.h"
#include "chaintopology.h"
#include "daemon/secrets.h"
#include "lightningd.h"
#include "log.h"
#include "overflows.h"
#include "peer.h"
#include "pseudorand.h"
#include "routing.h"
#include "timeout.h"
#include "utils.h"
#include <ccan/array_size/array_size.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/htable/htable_type.h>
#include <ccan/str/hex/hex.h>
#include <ccan/structeq/structeq.h>
#include <ccan/tal/str/str.h>
#include <inttypes.h>

/* 365.25 * 24 * 60 / 10 */
Expand Down Expand Up @@ -490,146 +483,10 @@ static void json_routefail(struct command *cmd,

command_success(cmd, null_response(cmd));
}

const struct json_command dev_routefail_command = {
"dev-routefail",
json_routefail,
"FAIL htlcs that we can't route if {enable}",
"Returns an empty result on success"
};

static void announce_channels(struct ircstate *state)
{
tal_t *ctx = tal(state, tal_t);
char txid[65];
struct peer *p;

list_for_each(&state->dstate->peers, p, list) {

if (!state_is_normal(p->state))
continue;

struct txlocator *loc = locate_tx(ctx, state->dstate, &p->anchor.txid);
if (loc == NULL)
continue;

bitcoin_txid_to_hex(&p->anchor.txid, txid, sizeof(txid));
struct privmsg *msg = talz(state, struct privmsg);
msg->channel = "#lightning-nodes";
msg->msg = tal_fmt(
msg, "CHAN %s %s %s %d %d %d %d %d",
pubkey_to_hexstr(msg, state->dstate->secpctx, &state->dstate->id),
pubkey_to_hexstr(msg, state->dstate->secpctx, p->id),
txid,
loc->blkheight,
loc->index,
state->dstate->config.fee_base,
state->dstate->config.fee_per_satoshi,
p->remote.locktime.locktime
);

struct signature sig;
u8 der[72];

memset(der, 0, sizeof(der));
privkey_sign(state->dstate, msg->msg, strlen(msg->msg), &sig);
int numbytes = signature_to_der(state->dstate->secpctx, der, &sig);
msg->msg = tal_fmt(msg, "%s %s", tal_hexstr(ctx, der, numbytes), msg->msg);

irc_send_msg(state, msg);
tal_free(loc);
}
tal_free(ctx);

new_reltimer(state->dstate, state, time_from_sec(60), announce_channels, state);
}

/* Reconnect to IRC server upon disconnection. */
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)
{
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;
if (!signature_from_der(istate->dstate->secpctx, der, siglen, &sig))
return;

sha256_double(&hash, content, strlen(content));
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;
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);
blkheight = atoi(splits[4]);
index = atoi(splits[5]);
if (!ok || index < 0 || blkheight < 0) {
log_debug(istate->dstate->base_log, "Unable to parse channel announcent.");
return;
}

if(!check_signed_hash(istate->dstate->secpctx, &hash, &sig, pk1)){
log_debug(istate->log,
"Ignoring announcement from %s, signature check failed.",
splits[1]);
return;
}

/*
* FIXME Check in topology that the tx is in the block and
* that the endpoints match.
*/

add_connection(istate->dstate, pk1, pk2, atoi(splits[6]),
atoi(splits[7]), atoi(splits[8]), 6);
}

void setup_irc_connection(struct lightningd_state *dstate)
{
// Register callback
irc_privmsg_cb = *handle_irc_privmsg;
irc_disconnect_cb = *handle_irc_disconnect;

struct ircstate *state = talz(dstate, struct ircstate);
state->dstate = dstate;
state->server = "irc.freenode.net";
state->reconnect_timeout = time_from_sec(15);
state->log = new_log(state, state->dstate->log_record, "%s:irc",
log_prefix(state->dstate->base_log));

/* Truncate nick at 13 bytes, would be imposed by freenode anyway */
state->nick = tal_fmt(
state,
"N%.12s",
pubkey_to_hexstr(state, dstate->secpctx, &dstate->id) + 1);

irc_connect(state);
announce_channels(state);
}
4 changes: 0 additions & 4 deletions daemon/routing.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#define LIGHTNING_DAEMON_ROUTING_H
#include "config.h"
#include "bitcoin/pubkey.h"
#include "irc.h"

#define ROUTING_MAX_HOPS 20

Expand Down Expand Up @@ -68,7 +67,4 @@ struct node_map *empty_node_map(struct lightningd_state *dstate);

char *opt_add_route(const char *arg, struct lightningd_state *dstate);

// Main entrypoint for the lightning daemon
void setup_irc_connection(struct lightningd_state *dstate);

#endif /* LIGHTNING_DAEMON_ROUTING_H */

0 comments on commit 4f9c213

Please sign in to comment.