diff --git a/bitcoin/signature.c b/bitcoin/signature.c index 001ec2b54f4e..ca3bf40a5279 100644 --- a/bitcoin/signature.c +++ b/bitcoin/signature.c @@ -251,6 +251,12 @@ size_t signature_to_der(secp256k1_context *secpctx, return len; } +bool signature_from_der(secp256k1_context *secpctx, + const u8 *der, size_t len, struct signature *sig) +{ + return secp256k1_ecdsa_signature_parse_der(secpctx, &sig->sig, der, len); +} + /* Signature must have low S value. */ bool sig_valid(secp256k1_context *secpctx, const struct signature *sig) { diff --git a/bitcoin/signature.h b/bitcoin/signature.h index d2251ca94c10..534040c24751 100644 --- a/bitcoin/signature.h +++ b/bitcoin/signature.h @@ -58,4 +58,8 @@ bool sig_valid(secp256k1_context *secpctx, const struct signature *sig); size_t signature_to_der(secp256k1_context *secpctx, u8 der[72], const struct signature *s); +/* Parse DER encoding into signature sig */ +bool signature_from_der(secp256k1_context *secpctx, + const u8 *der, size_t len, struct signature *sig); + #endif /* LIGHTNING_BITCOIN_SIGNATURE_H */ diff --git a/daemon/chaintopology.c b/daemon/chaintopology.c index 87a6880aa02a..684d362de2c3 100644 --- a/daemon/chaintopology.c +++ b/daemon/chaintopology.c @@ -493,6 +493,27 @@ u64 get_feerate(struct lightningd_state *dstate) return dstate->topology->feerate; } +struct txlocator *locate_tx(const void *ctx, struct lightningd_state *dstate, + const struct sha256_double *txid) +{ + struct block *block = block_for_tx(dstate, txid); + if (block == NULL) { + return NULL; + } + + struct txlocator *loc = talz(ctx, struct txlocator); + loc->blkheight = block->height; + + size_t i, n = tal_count(block->txids); + for (i = 0; i < n; i++) { + if (structeq(&block->txids[i], txid)){ + loc->index = i; + break; + } + } + return loc; +} + void setup_topology(struct lightningd_state *dstate) { dstate->topology = tal(dstate, struct topology); diff --git a/daemon/chaintopology.h b/daemon/chaintopology.h index 815c8a110a4f..ac9e9f154479 100644 --- a/daemon/chaintopology.h +++ b/daemon/chaintopology.h @@ -10,6 +10,16 @@ struct peer; struct sha256_double; struct txwatch; +/* Information relevant to locating a TX in a blockchain. */ +struct txlocator { + + /* The height of the block that includes this transaction */ + u32 blkheight; + + /* Position of the transaction in the transactions list */ + u32 index; +}; + /* This is the number of blocks which would have to be mined to invalidate * the tx. */ size_t get_tx_depth(struct lightningd_state *dstate, @@ -33,4 +43,6 @@ void broadcast_tx(struct peer *peer, const struct bitcoin_tx *tx); void setup_topology(struct lightningd_state *dstate); +struct txlocator *locate_tx(const void *ctx, struct lightningd_state *dstate, const struct sha256_double *txid); + #endif /* LIGHTNING_DAEMON_CRYPTOPKT_H */ diff --git a/daemon/irc_announce.c b/daemon/irc_announce.c new file mode 100644 index 000000000000..1b2255aae731 --- /dev/null +++ b/daemon/irc_announce.c @@ -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 +#include + +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: + * CHAN + * + */ +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); +} diff --git a/daemon/irc_announce.h b/daemon/irc_announce.h new file mode 100644 index 000000000000..bce649771a5f --- /dev/null +++ b/daemon/irc_announce.h @@ -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 */ diff --git a/utils.c b/utils.c index 86d9790d4316..4058a2bbfbdf 100644 --- a/utils.c +++ b/utils.c @@ -7,3 +7,11 @@ char *tal_hexstr(const tal_t *ctx, const void *data, size_t len) hex_encode(data, len, str, hex_str_size(len)); return str; } + +u8 *tal_hexdata(const tal_t *ctx, const void *str, size_t len) +{ + u8 *data = tal_arr(ctx, u8, hex_data_size(len)); + if (!hex_decode(str, len, data, hex_data_size(len))) + return NULL; + return data; +} diff --git a/utils.h b/utils.h index f89af8c957d8..0fac3c7c8bc9 100644 --- a/utils.h +++ b/utils.h @@ -1,9 +1,13 @@ #ifndef LIGHTNING_UTILS_H #define LIGHTNING_UTILS_H #include "config.h" +#include #include /* Allocate and fill in a hex-encoded string of this data. */ char *tal_hexstr(const tal_t *ctx, const void *data, size_t len); +/* Allocate and fill a buffer with the data of this hex string. */ +u8 *tal_hexdata(const tal_t *ctx, const void *str, size_t len); + #endif /* LIGHTNING_UTILS_H */