diff --git a/daemon/peer.c b/daemon/peer.c index a643d3326..799eca45f 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -1,10 +1,15 @@ +#include "bitcoind.h" #include "cryptopkt.h" #include "dns.h" #include "jsonrpc.h" #include "lightningd.h" #include "log.h" +#include "names.h" #include "peer.h" +#include "secrets.h" #include "state.h" +#include +#include #include #include #include @@ -22,42 +27,186 @@ struct json_connecting { /* This owns us, so we're freed after command_fail or command_success */ struct command *cmd; const char *name, *port; + u64 satoshis; }; -/* Send and receive (encrypted) hello message. */ -static struct io_plan *peer_test_check(struct io_conn *conn, struct peer *peer) +static void queue_output_pkt(struct peer *peer, Pkt *pkt) { - if (peer->inpkt->pkt_case != PKT__PKT_ERROR) - fatal("Bad packet type %u", peer->inpkt->pkt_case); - if (!peer->inpkt->error->problem - || strcmp(peer->inpkt->error->problem, "hello") != 0) - fatal("Bad packet '%.6s'", peer->inpkt->error->problem); - log_info(peer->log, "Successful hello!"); + peer->outpkt[peer->num_outpkt++] = pkt; + assert(peer->num_outpkt < ARRAY_SIZE(peer->outpkt)); - /* Sleep forever... */ - return io_wait(conn, peer, io_close_cb, NULL); + /* In case it was waiting for output. */ + io_wake(peer); } -static struct io_plan *peer_test_read(struct io_conn *conn, struct peer *peer) +static struct json_result *null_response(const tal_t *ctx) { - return peer_read_packet(conn, peer, peer_test_check); + struct json_result *response; + + response = new_json_result(ctx); + json_object_start(response, NULL); + json_object_end(response); + return response; +} + +static void peer_cmd_complete(struct peer *peer, enum command_status status) +{ + assert(peer->cmd != INPUT_NONE); + + if (peer->jsoncmd) { + if (status == CMD_FAIL) + /* FIXME: y'know, details. */ + command_fail(peer->jsoncmd, "Failed"); + else { + assert(status == CMD_SUCCESS); + command_success(peer->jsoncmd, + null_response(peer->jsoncmd)); + } + peer->jsoncmd = NULL; + } + peer->cmd = INPUT_NONE; +} + +static void update_state(struct peer *peer, + const enum state_input input, + const union input *idata) +{ + enum command_status status; + Pkt *outpkt; + const struct bitcoin_tx *broadcast; + + status = state(peer, peer, input, idata, &outpkt, &broadcast); + log_debug(peer->log, "%s => %s", + input_name(input), state_name(peer->state)); + switch (status) { + case CMD_NONE: + break; + case CMD_SUCCESS: + log_add(peer->log, " (command success)"); + peer_cmd_complete(peer, CMD_SUCCESS); + break; + case CMD_FAIL: + log_add(peer->log, " (command FAIL)"); + peer_cmd_complete(peer, CMD_FAIL); + break; + case CMD_REQUEUE: + log_add(peer->log, " (Command requeue)"); + break; + } + + if (outpkt) { + log_add(peer->log, " (out %s)", input_name(outpkt->pkt_case)); + queue_output_pkt(peer, outpkt); + } + if (broadcast) { + struct sha256_double txid; + + bitcoin_txid(broadcast, &txid); + /* FIXME: log_struct */ + log_add(peer->log, " (tx %02x%02x%02x%02x...)", + txid.sha.u.u8[0], txid.sha.u.u8[1], + txid.sha.u.u8[2], txid.sha.u.u8[3]); + bitcoind_send_tx(peer->dstate, broadcast); + } +} + +static struct io_plan *pkt_out(struct io_conn *conn, struct peer *peer) +{ + Pkt *out; + + if (peer->num_outpkt == 0) + return io_out_wait(conn, peer, pkt_out, peer); + + out = peer->outpkt[--peer->num_outpkt]; + return peer_write_packet(conn, peer, out, pkt_out); } -static struct io_plan *peer_test(struct io_conn *conn, struct peer *peer) +static void try_command(struct peer *peer) +{ + while (peer->cond == PEER_CMD_OK && peer->cmd != INPUT_NONE) + update_state(peer, peer->cmd, &peer->cmddata); + + if (peer->cond == PEER_CLOSED) + io_close(peer->conn); +} + +static struct io_plan *pkt_in(struct io_conn *conn, struct peer *peer) { - Error err = ERROR__INIT; - Pkt pkt = PKT__INIT; - pkt.pkt_case = PKT__PKT_ERROR; - pkt.error = &err; - err.problem = "hello"; - return peer_write_packet(conn, peer, &pkt, peer_test_read); + union input idata; + const tal_t *ctx = tal(peer, char); + + idata.pkt = tal_steal(ctx, peer->inpkt); + update_state(peer, peer->inpkt->pkt_case, &idata); + + /* Free peer->inpkt unless stolen above. */ + tal_free(ctx); + + if (peer->cond == PEER_CLOSED) + return io_close(conn); + + /* Ready for command? */ + if (peer->cond == PEER_CMD_OK) + try_command(peer); + + return peer_read_packet(conn, peer, pkt_in); +} + +/* Crypto is on, we are live. */ +static struct io_plan *peer_crypto_on(struct io_conn *conn, struct peer *peer) +{ + peer_secrets_init(peer); + peer_get_revocation_hash(peer, 0, &peer->us.revocation_hash); + + assert(peer->state == STATE_INIT); + peer->cmd = peer->us.offer_anchor; + try_command(peer); + + return io_duplex(conn, + peer_read_packet(conn, peer, pkt_in), + pkt_out(conn, peer)); } static void destroy_peer(struct peer *peer) { + if (peer->conn) + io_close(peer->conn); list_del_from(&peer->dstate->peers, &peer->list); } +static void peer_disconnect(struct io_conn *conn, struct peer *peer) +{ + Pkt *outpkt; + const struct bitcoin_tx *broadcast; + + log_info(peer->log, "Disconnected"); + + /* No longer connected. */ + peer->conn = NULL; + + /* Not even set up yet? Simply free.*/ + if (peer->state == STATE_INIT) { + tal_free(peer); + return; + } + + /* FIXME: Try to reconnect. */ + + state(peer, peer, CMD_CLOSE, NULL, &outpkt, &broadcast); + /* Can't send packet, so ignore it. */ + tal_free(outpkt); + + if (broadcast) { + struct sha256_double txid; + + bitcoin_txid(broadcast, &txid); + /* FIXME: log_struct */ + log_debug(peer->log, "CMD_CLOSE: tx %02x%02x%02x%02x...", + txid.sha.u.u8[0], txid.sha.u.u8[1], + txid.sha.u.u8[2], txid.sha.u.u8[3]); + bitcoind_send_tx(peer->dstate, broadcast); + } +} + static struct peer *new_peer(struct lightningd_state *dstate, struct io_conn *conn, int addr_type, int addr_protocol, @@ -80,7 +229,14 @@ static struct peer *new_peer(struct lightningd_state *dstate, peer->io_data = NULL; peer->secrets = NULL; list_head_init(&peer->watches); + peer->num_outpkt = 0; + peer->cmd = INPUT_NONE; + /* If we free peer, conn should be closed, but can't be freed + * immediately so don't make peer a parent. */ + peer->conn = conn; + io_set_finish(conn, peer_disconnect, peer); + peer->us.offer_anchor = offer_anchor; if (!seconds_to_rel_locktime(dstate->config.rel_locktime, &peer->us.locktime)) @@ -112,7 +268,7 @@ static struct io_plan *peer_connected_out(struct io_conn *conn, struct lightningd_state *dstate, struct json_connecting *connect) { - struct json_result *response; + /* Initiator currently funds channel */ struct peer *peer = new_peer(dstate, conn, SOCK_STREAM, IPPROTO_TCP, CMD_OPEN_WITH_ANCHOR, "out"); if (!peer) { @@ -123,12 +279,9 @@ static struct io_plan *peer_connected_out(struct io_conn *conn, log_info(peer->log, "Connected out to %s:%s", connect->name, connect->port); - response = new_json_result(connect); - json_object_start(response, NULL); - json_object_end(response); - command_success(connect->cmd, response); - - return peer_crypto_setup(conn, peer, peer_test); + peer->jsoncmd = NULL; + command_success(connect->cmd, null_response(connect)); + return peer_crypto_setup(conn, peer, peer_crypto_on); } static struct io_plan *peer_connected_in(struct io_conn *conn, @@ -140,7 +293,8 @@ static struct io_plan *peer_connected_in(struct io_conn *conn, return io_close(conn); log_info(peer->log, "Peer connected in"); - return peer_crypto_setup(conn, peer, peer_test); + peer->jsoncmd = NULL; + return peer_crypto_setup(conn, peer, peer_crypto_on); } static int make_listen_fd(struct lightningd_state *dstate, @@ -240,12 +394,16 @@ static void json_connect(struct command *cmd, const char *buffer, const jsmntok_t *params) { struct json_connecting *connect; - jsmntok_t *host, *port; + jsmntok_t *host, *port, *satoshis; - json_get_params(buffer, params, "host", &host, "port", &port, NULL); + json_get_params(buffer, params, + "host", &host, + "port", &port, + "satoshis", &satoshis, + NULL); - if (!host || !port) { - command_fail(cmd, "Need host and port"); + if (!host || !port || !satoshis) { + command_fail(cmd, "Need host, port and satoshis"); return; } @@ -255,6 +413,10 @@ static void json_connect(struct command *cmd, host->end - host->start); connect->port = tal_strndup(connect, buffer + port->start, port->end - port->start); + if (!json_tok_u64(buffer, satoshis, &connect->satoshis)) + command_fail(cmd, "'%.*s' is not a valid number", + (int)(satoshis->end - satoshis->start), + buffer + satoshis->start); if (!dns_resolve_and_connect(cmd->dstate, connect->name, connect->port, peer_connected_out, peer_failed, connect)) { command_fail(cmd, "DNS failed"); @@ -265,7 +427,7 @@ static void json_connect(struct command *cmd, const struct json_command connect_command = { "connect", json_connect, - "Connect to a {host} at {port}", + "Connect to a {host} at {port} offering anchor of {satoshis}", "Returns an empty result on success" }; @@ -393,7 +555,8 @@ const struct htlc *peer_tx_revealed_r_value(struct peer *peer, bool committed_to_htlcs(const struct peer *peer) { - FIXME_STUB(peer); + /* FIXME */ + return false; } /* Create a bitcoin close tx. */ @@ -518,7 +681,7 @@ const struct bitcoin_tx *bitcoin_htlc_spend(const tal_t *ctx, /* Start creation of the bitcoin anchor tx. */ void bitcoin_create_anchor(struct peer *peer, enum state_input done) { - FIXME_STUB(peer); + /* FIXME */ } /* We didn't end up broadcasting the anchor: release the utxos. @@ -547,7 +710,14 @@ static void json_getpeers(struct command *cmd, list_for_each(&cmd->dstate->peers, p, list) { json_object_start(response, NULL); json_add_string(response, "name", log_prefix(p->log)); - json_add_hex(response, "id", p->id.der, pubkey_derlen(&p->id)); + json_add_string(response, "state", state_name(p->state)); + json_add_string(response, "cmd", input_name(p->cmd)); + + /* This is only valid after crypto setup. */ + if (p->state != STATE_INIT) + json_add_hex(response, "id", + p->id.der, pubkey_derlen(&p->id)); + json_object_end(response); } json_array_end(response); diff --git a/daemon/peer.h b/daemon/peer.h index c6837fcbf..2a94c0143 100644 --- a/daemon/peer.h +++ b/daemon/peer.h @@ -5,7 +5,7 @@ #include "bitcoin/pubkey.h" #include "lightning.pb-c.h" #include "netaddr.h" -#include "state_types.h" +#include "state.h" #include #include @@ -33,7 +33,15 @@ struct peer { /* Condition of communications */ enum state_peercond cond; - + + /* Network connection. */ + struct io_conn *conn; + + /* Current command (if any) */ + enum state_input cmd; + union input cmddata; + struct command *jsoncmd; + /* Global state. */ struct lightningd_state *dstate; @@ -45,6 +53,10 @@ struct peer { /* Current received packet. */ Pkt *inpkt; + + /* Queue of output packets. */ + Pkt *outpkt[5]; + size_t num_outpkt; /* Current ongoing packetflow */ struct io_data *io_data;