lightningd: introduce peer_state enum.

The actual state names are place holders for now, really.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2017-05-22 20:54:59 +09:30
parent 93849b1e02
commit 34e6e56471
8 changed files with 130 additions and 51 deletions

View File

@ -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/%)

View File

@ -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;
}

View File

@ -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)

View File

@ -19,6 +19,7 @@
#include <lightningd/channel.h>
#include <lightningd/channel/gen_channel_wire.h>
#include <lightningd/funding_tx.h>
#include <lightningd/gen_peer_state_names.h>
#include <lightningd/gossip/gen_gossip_wire.h>
#include <lightningd/handshake/gen_handshake_wire.h>
#include <lightningd/hsm/gen_hsm_wire.h>
@ -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";
}

View File

@ -7,6 +7,7 @@
#include <daemon/json.h>
#include <daemon/netaddr.h>
#include <lightningd/channel_config.h>
#include <lightningd/peer_state.h>
#include <stdbool.h>
#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 */

38
lightningd/peer_state.h Normal file
View File

@ -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 */

View File

@ -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']

View File

@ -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')