diff --git a/lightningd/Makefile b/lightningd/Makefile index f87682fc8..8567f6220 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -84,6 +84,7 @@ LIGHTNINGD_HEADERS_NOGEN = \ lightningd/lightningd.h \ lightningd/pay.h \ lightningd/peer_control.h \ + lightningd/peer_state.h \ lightningd/subd.h \ $(LIGHTNINGD_OLD_LIB_HEADERS) \ $(LIGHTNINGD_LIB_HEADERS) \ @@ -95,6 +96,7 @@ LIGHTNINGD_HEADERS_NOGEN = \ # Generated headers LIGHTNINGD_HEADERS_GEN = \ + lightningd/gen_peer_state_names.h \ $(WIRE_GEN_HEADERS) \ $(GEN_HEADERS) @@ -118,6 +120,9 @@ include lightningd/channel/Makefile $(LIGHTNINGD_OBJS) $(LIGHTNINGD_LIB_OBJS): $(LIGHTNINGD_HEADERS) +lightningd/gen_peer_state_names.h: lightningd/peer_state.h ccan/ccan/cdump/tools/cdump-enumstr + ccan/ccan/cdump/tools/cdump-enumstr lightningd/peer_state.h > $@ + check-source: $(LIGHTNINGD_SRC:%=check-src-include-order/%) check-source: $(LIGHTNINGD_LIB_SRC:%=check-src-include-order/%) check-source: $(LIGHTNINGD_CLI_SRC:%=check-src-include-order/%) diff --git a/lightningd/dev_newhtlc.c b/lightningd/dev_newhtlc.c index 0e13b42ab..d9003a3d7 100644 --- a/lightningd/dev_newhtlc.c +++ b/lightningd/dev_newhtlc.c @@ -88,8 +88,9 @@ static void json_dev_newhtlc(struct command *cmd, return; } - if (!streq(peer->condition, "Normal operation")) { - command_fail(cmd, "Peer in condition %s", peer->condition); + if (!peer_can_add_htlc(peer)) { + command_fail(cmd, "Peer in state %s", + peer_state_name(peer->state)); return; } diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 7ae8250e3..911cc0477 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -89,8 +89,8 @@ static void peer_nongossip(struct subd *gossip, const u8 *msg, peer->fd = peer_fd; peer->gossip_client_fd = gossip_fd; - peer_set_condition(peer, "Gossip ended up receipt of %s", - wire_type_name(fromwire_peektype(inner))); + log_info(peer->log, "Gossip ended up receipt of %s", + wire_type_name(fromwire_peektype(inner))); peer_accept_open(peer, &cs, inner); } @@ -123,7 +123,7 @@ static void peer_ready(struct subd *gossip, const u8 *msg) peer->connect_cmd = NULL; } - peer_set_condition(peer, "Exchanging gossip"); + peer_set_condition(peer, GOSSIPING); } static int gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 4c1a47c79..d8a3212ae 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -39,8 +40,8 @@ static void destroy_peer(struct peer *peer) if (peer->fd >= 0) close(peer->fd); if (peer->connect_cmd) - command_fail(peer->connect_cmd, "Failed after %s", - peer->condition); + command_fail(peer->connect_cmd, "Failed in state %s", + peer_state_name(peer->state)); } void peer_fail(struct peer *peer, const char *fmt, ...) @@ -55,20 +56,15 @@ void peer_fail(struct peer *peer, const char *fmt, ...) tal_free(peer); } -void peer_set_condition(struct peer *peer, const char *fmt, ...) +void peer_set_condition(struct peer *peer, enum peer_state state) { - va_list ap; - - va_start(ap, fmt); - tal_free(peer->condition); - peer->condition = tal_vfmt(peer, fmt, ap); - va_end(ap); - log_info(peer->log, "condition: %s", peer->condition); + log_info(peer->log, "state: %s -> %s", + peer_state_name(peer->state), peer_state_name(state)); + peer->state = state; } static struct peer *new_peer(struct lightningd *ld, struct io_conn *conn, - const char *in_or_out, struct command *cmd) { static u64 id_counter; @@ -85,6 +81,7 @@ static struct peer *new_peer(struct lightningd *ld, peer->funding_txid = NULL; peer->seed = NULL; peer->balance = NULL; + peer->state = HANDSHAKING; /* Max 128k per peer. */ peer->log_book = new_log_book(peer, 128*1024, @@ -100,7 +97,6 @@ static struct peer *new_peer(struct lightningd *ld, return tal_free(peer); } netname = netaddr_name(peer, &peer->netaddr); - peer->condition = tal_fmt(peer, "%s %s", in_or_out, netname); tal_free(netname); list_add_tail(&ld->peers, &peer->list); tal_add_destructor(peer, destroy_peer); @@ -153,7 +149,7 @@ static bool handshake_succeeded(struct subd *hs, const u8 *msg, const int *fds, peer->owner = peer->ld->gossip; tal_steal(peer->owner, peer); - peer_set_condition(peer, "Beginning gossip"); + peer_set_condition(peer, INITIALIZING); /* Tell gossip to handle it now. */ msg = towire_gossipctl_new_peer(peer, peer->unique_id, &cs); @@ -205,11 +201,10 @@ static bool peer_got_handshake_hsmfd(struct subd *hsm, const u8 *msg, if (peer->id) { req = towire_handshake_initiator(peer, &peer->ld->dstate.id, peer->id); - peer_set_condition(peer, "Starting handshake as initiator"); } else { req = towire_handshake_responder(peer, &peer->ld->dstate.id); - peer_set_condition(peer, "Starting handshake as responder"); } + peer_set_condition(peer, HANDSHAKING); /* Now hand peer request to the handshake daemon: hands it * back on success */ @@ -224,7 +219,7 @@ error: /* FIXME: timeout handshake if taking too long? */ static struct io_plan *peer_in(struct io_conn *conn, struct lightningd *ld) { - struct peer *peer = new_peer(ld, conn, "Incoming from", NULL); + struct peer *peer = new_peer(ld, conn, NULL); if (!peer) return io_close(conn); @@ -352,7 +347,7 @@ static struct io_plan *peer_out(struct io_conn *conn, struct json_connecting *jc) { struct lightningd *ld = ld_from_dstate(jc->cmd->dstate); - struct peer *peer = new_peer(ld, conn, "Outgoing to", jc->cmd); + struct peer *peer = new_peer(ld, conn, jc->cmd); if (!peer) return io_close(conn); @@ -476,7 +471,7 @@ static void json_getpeers(struct command *cmd, list_for_each(&ld->peers, p, list) { json_object_start(response, NULL); json_add_u64(response, "unique_id", p->unique_id); - json_add_string(response, "condition", p->condition); + json_add_string(response, "state", peer_state_name(p->state)); json_add_string(response, "netaddr", netaddr_name(response, &p->netaddr)); if (p->id) @@ -585,7 +580,7 @@ static enum watch_result funding_depth_cb(struct peer *peer, peer->scid->outnum = peer->funding_outnum; tal_free(loc); - peer_set_condition(peer, "Funding tx reached depth %u", depth); + peer_set_condition(peer, OPENING_SENT_LOCKED); subd_send_msg(peer->owner, take(towire_channel_funding_locked(peer, peer->scid))); } @@ -615,7 +610,7 @@ static bool opening_got_hsm_funding_sig(struct subd *hsm, const u8 *resp, fatal("HSM gave %zu sigs, needed %zu", tal_count(sigs), tal_count(tx->input)); - peer_set_condition(fc->peer, "Waiting for our funding tx"); + peer_set_condition(fc->peer, OPENING_AWAITING_LOCKIN); /* Create input parts from signatures. */ for (i = 0; i < tal_count(tx->input); i++) { @@ -976,10 +971,9 @@ static void forward_htlc(struct htlc_end *hend, err = towire_unknown_next_peer(hend); goto fail; } - /* FIXME: These checks are horrible, use a peer flag to say it's - * ready to forward! */ - if (!next->owner || !streq(next->owner->name, "lightningd_channel") - || !streq(next->condition, "Normal operation")) { + + if (!peer_can_add_htlc(next)) { + log_info(next->log, "Attempt to forward HTLC but not ready"); err = towire_unknown_next_peer(hend); goto fail; } @@ -1340,10 +1334,10 @@ static int channel_msg(struct subd *sd, const u8 *msg, const int *unused) switch (t) { case WIRE_CHANNEL_RECEIVED_FUNDING_LOCKED: - peer_set_condition(sd->peer, "Received funding locked"); + peer_set_condition(sd->peer, OPENING_RCVD_LOCKED); break; case WIRE_CHANNEL_NORMAL_OPERATION: - peer_set_condition(sd->peer, "Normal operation"); + peer_set_condition(sd->peer, NORMAL); break; case WIRE_CHANNEL_ACCEPTED_HTLC: return peer_accepted_htlc(sd->peer, msg); @@ -1406,7 +1400,7 @@ static bool peer_start_channeld_hsmfd(struct subd *hsm, const u8 *resp, } cds->peer->fd = -1; - peer_set_condition(cds->peer, "Waiting for funding confirmations"); + log_debug(cds->peer->log, "Waiting for funding confirmations"); /* We don't expect a response: we are triggered by funding_depth_cb. */ subd_send_msg(cds->peer->owner, take(cds->initmsg)); tal_free(cds); @@ -1428,7 +1422,7 @@ static void peer_start_channeld(struct peer *peer, peer->owner = NULL; tal_steal(peer->ld, peer); - peer_set_condition(peer, "Waiting for HSM file descriptor"); + log_debug(peer->log, "Waiting for HSM file descriptor"); /* Now we can consider balance set. */ peer->balance = tal_arr(peer, u64, NUM_SIDES); @@ -1498,7 +1492,7 @@ static bool opening_release_tx(struct subd *opening, const u8 *resp, tal_free(fc->peer); return false; } - peer_set_condition(fc->peer, "Getting HSM to sign funding tx"); + log_debug(fc->peer->log, "Getting HSM to sign funding tx"); /* Get HSM to sign the funding tx. */ for (i = 0; i < tal_count(fc->utxomap); i++) @@ -1529,7 +1523,7 @@ static bool opening_gen_funding(struct subd *opening, const u8 *reply, u8 *msg; struct pubkey changekey; - peer_set_condition(fc->peer, "Created funding transaction for channel"); + log_debug(fc->peer->log, "Created funding transaction for channel"); if (!fromwire_opening_open_reply(reply, NULL, &fc->local_fundingkey, &fc->remote_fundingkey)) { @@ -1611,6 +1605,7 @@ static bool opening_accept_reply(struct subd *opening, const u8 *reply, return false; } + peer_set_condition(peer, OPENING_AWAITING_LOCKIN); log_debug(peer->log, "Watching funding tx %s", type_to_string(reply, struct sha256_double, peer->funding_txid)); @@ -1686,7 +1681,7 @@ void peer_accept_open(struct peer *peer, return; } - peer_set_condition(peer, "Starting opening daemon"); + peer_set_condition(peer, OPENING_NOT_LOCKED); peer->owner = new_subd(ld, ld, "lightningd_opening", peer, opening_wire_type_name, NULL, NULL, @@ -1758,7 +1753,7 @@ static bool gossip_peer_released(struct subd *gossip, fatal("Gossup daemon release gave %"PRIu64" not %"PRIu64, id, fc->peer->unique_id); - peer_set_condition(fc->peer, "Starting opening daemon"); + peer_set_condition(fc->peer, OPENING_NOT_LOCKED); opening = new_subd(fc->peer->ld, ld, "lightningd_opening", fc->peer, opening_wire_type_name, @@ -1856,3 +1851,13 @@ static const struct json_command fund_channel_command = { "Returns once channel established" }; AUTODATA(json_command, &fund_channel_command); + +const char *peer_state_name(enum peer_state state) +{ + size_t i; + + for (i = 0; enum_peer_state_names[i].name; i++) + if (enum_peer_state_names[i].v == state) + return enum_peer_state_names[i].name; + return "unknown"; +} diff --git a/lightningd/peer_control.h b/lightningd/peer_control.h index 856fd0681..bafca0397 100644 --- a/lightningd/peer_control.h +++ b/lightningd/peer_control.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #define ANNOUNCE_MIN_DEPTH 6 @@ -19,6 +20,9 @@ struct peer { /* Unique ID (works before we know their pubkey) */ u64 unique_id; + /* What's happening. */ + enum peer_state state; + /* Which side offered channel? */ enum side funder; @@ -28,9 +32,6 @@ struct peer { /* What stage is this in? NULL during first creation. */ struct subd *owner; - /* What's happening (doubles as error return for connect_cmd) */ - const char *condition; - /* History */ struct log_book *log_book; struct log *log; @@ -71,6 +72,34 @@ struct peer { int gossip_client_fd; }; +static inline bool peer_can_add_htlc(const struct peer *peer) +{ + return peer->state == NORMAL; +} + +static inline bool peer_can_remove_htlc(const struct peer *peer) +{ + return peer->state == NORMAL + || peer->state == SHUTDOWN_SENT + || peer->state == SHUTDOWN_RCVD + || peer->state == ONCHAIN_THEIR_UNILATERAL + || peer->state == ONCHAIN_OUR_UNILATERAL; +} + +static inline bool peer_on_chain(const struct peer *peer) +{ + return peer->state == ONCHAIN_CHEATED + || peer->state == ONCHAIN_THEIR_UNILATERAL + || peer->state == ONCHAIN_OUR_UNILATERAL + || peer->state == ONCHAIN_MUTUAL; +} + +/* Do we need to remember anything about this peer? */ +static inline bool peer_persists(const struct peer *peer) +{ + return peer->state > OPENING_NOT_LOCKED; +} + struct peer *peer_by_unique_id(struct lightningd *ld, u64 unique_id); struct peer *peer_by_id(struct lightningd *ld, const struct pubkey *id); struct peer *peer_from_json(struct lightningd *ld, @@ -83,6 +112,7 @@ void peer_accept_open(struct peer *peer, /* Peer has failed. */ PRINTF_FMT(2,3) void peer_fail(struct peer *peer, const char *fmt, ...); -PRINTF_FMT(2,3) void peer_set_condition(struct peer *peer, const char *fmt, ...); +const char *peer_state_name(enum peer_state state); +void peer_set_condition(struct peer *peer, enum peer_state state); void setup_listeners(struct lightningd *ld); #endif /* LIGHTNING_LIGHTNINGD_PEER_CONTROL_H */ diff --git a/lightningd/peer_state.h b/lightningd/peer_state.h new file mode 100644 index 000000000..fb0c0a6a6 --- /dev/null +++ b/lightningd/peer_state.h @@ -0,0 +1,38 @@ +#ifndef LIGHTNING_LIGHTNINGD_PEER_STATE_H +#define LIGHTNING_LIGHTNINGD_PEER_STATE_H +#include "config.h" + +enum peer_state { + /* Not important: we can forget about peers in these states. */ + HANDSHAKING, + INITIALIZING, + GOSSIPING, + + /* Negotiating channel opening */ + OPENING_NOT_LOCKED, + /* Waiting for funding tx to lock in. */ + OPENING_AWAITING_LOCKIN, + /* Opening, have received funding_locked (not sent). */ + OPENING_RCVD_LOCKED, + /* Opening, have sent funding_locked (not received). */ + OPENING_SENT_LOCKED, + + /* Normal operating state. */ + NORMAL, + + /* We are closing, pending HTLC resolution. */ + SHUTDOWN_SENT, + /* Both are closing, pending HTLC resolution. */ + SHUTDOWN_RCVD, + + /* Exchanging signatures on closing tx. */ + CLOSING_SIGEXCHANGE, + + /* Various onchain states. */ + ONCHAIN_CHEATED, + ONCHAIN_THEIR_UNILATERAL, + ONCHAIN_OUR_UNILATERAL, + ONCHAIN_MUTUAL +}; + +#endif /* LIGHTNING_LIGHTNINGD_PEER_STATE_H */ diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 7c841988f..ed2f09162 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -180,8 +180,8 @@ class LightningDTests(BaseLightningDTests): l1.bitcoin.rpc.generate(6) - l1.daemon.wait_for_log('Normal operation') - l2.daemon.wait_for_log('Normal operation') + l1.daemon.wait_for_log('-> NORMAL') + l2.daemon.wait_for_log('-> NORMAL') def test_connect(self): l1,l2 = self.connect() @@ -189,13 +189,13 @@ class LightningDTests(BaseLightningDTests): p1 = l1.rpc.getpeer(l2.info['id'], 'info') p2 = l2.rpc.getpeer(l1.info['id'], 'info') - assert p1['condition'] == 'Exchanging gossip' - assert p2['condition'] == 'Exchanging gossip' + assert p1['state'] == 'GOSSIPING' + assert p2['state'] == 'GOSSIPING' # It should have gone through these steps - assert 'condition: Starting handshake as initiator' in p1['log'] - assert 'condition: Beginning gossip' in p1['log'] - assert 'condition: Exchanging gossip' in p1['log'] + print(p1['log']) + assert 'state: HANDSHAKING -> INITIALIZING' in p1['log'] + assert 'state: INITIALIZING -> GOSSIPING' in p1['log'] # Both should still be owned by gossip assert p1['owner'] == 'lightningd_gossip' @@ -205,8 +205,8 @@ class LightningDTests(BaseLightningDTests): l1,l2 = self.connect() self.fund_channel(l1, l2, 10**6) - l1.daemon.wait_for_log('Normal operation') - l2.daemon.wait_for_log('Normal operation') + l1.daemon.wait_for_log('-> NORMAL') + l2.daemon.wait_for_log('-> NORMAL') secret = '1de08917a61cb2b62ed5937d38577f6a7bfe59c176781c6d8128018e8b5ccdfd' rhash = l1.rpc.dev_rhash(secret)['rhash'] diff --git a/tests/utils.py b/tests/utils.py index bcf0e5dcd..ed83ae710 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -282,5 +282,5 @@ class LightningNode(object): self.daemon.wait_for_log('sendrawtx exit 0, gave') time.sleep(1) self.bitcoin.rpc.generate(6) - self.daemon.wait_for_log('Normal operation') + self.daemon.wait_for_log('-> NORMAL')