diff --git a/.travis.yml b/.travis.yml index 44e87fec6..721cbc62b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,13 +3,14 @@ dist: trusty sudo: true env: - - NO_VALGRIND=1 ARCH=32 - - NO_VALGRIND=1 ARCH=64 - - NO_VALGRIND=0 ARCH=64 + - NO_VALGRIND=1 ARCH=32 DEVELOPER=1 + - NO_VALGRIND=1 ARCH=64 DEVELOPER=1 + - NO_VALGRIND=0 ARCH=64 DEVELOPER=1 + - NO_VALGRIND=0 ARCH=64 DEVELOPER=0 # Trusty (aka 14.04) is way way too old, so run in docker... script: - docker pull cdecker/lightning-ci:${ARCH}bit > /dev/null - - docker run --rm=true -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make -j3 - - docker run --rm=true -e NO_VALGRIND=${NO_VALGRIND:-0} -e TEST_DEBUG=${TEST_DEBUG:-0} -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make check + - docker run --rm=true -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make -j3 DEVELOPER=${DEVELOPER} + - docker run --rm=true -e NO_VALGRIND=${NO_VALGRIND:-0} -e TEST_DEBUG=${TEST_DEBUG:-0} -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make check DEVELOPER=${DEVELOPER} - docker run --rm=true -v "${TRAVIS_BUILD_DIR}":/build -t cdecker/lightning-ci:${ARCH}bit make check-source diff --git a/Makefile b/Makefile index c1987dd7a..de8a91776 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,15 @@ VALGRIND=valgrind -q --error-exitcode=7 VALGRIND_TEST_ARGS = --track-origins=yes --leak-check=full --show-reachable=yes --errors-for-leak-kinds=all endif +# By default, we are in DEVELOPER mode, use DEVELOPER= on cmdline to override. +DEVELOPER := 1 + +ifeq ($(DEVELOPER),1) +DEV_CFLAGS=-DDEVELOPER=1 +else +DEV_CFLAGS=-DDEVELOPER=0 +endif + ifeq ($(COVERAGE),1) COVFLAGS = --coverage endif @@ -132,7 +141,7 @@ ALL_PROGRAMS = CWARNFLAGS := -Werror -Wall -Wundef -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes -Wold-style-definition CDEBUGFLAGS := -std=gnu11 -g -fstack-protector -CFLAGS = $(CWARNFLAGS) $(CDEBUGFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . $(FEATURES) $(COVFLAGS) -DSHACHAIN_BITS=48 -DCCAN_TAKE_DEBUG=1 +CFLAGS = $(CWARNFLAGS) $(CDEBUGFLAGS) -I $(CCANDIR) $(EXTERNAL_INCLUDE_FLAGS) -I . $(FEATURES) $(COVFLAGS) $(DEV_CFLAGS) -DSHACHAIN_BITS=48 -DCCAN_TAKE_DEBUG=1 LDLIBS = -lgmp -lsqlite3 $(COVFLAGS) diff --git a/channeld/channel.c b/channeld/channel.c index b55e08615..648b6570c 100644 --- a/channeld/channel.c +++ b/channeld/channel.c @@ -615,12 +615,14 @@ static void send_commit(struct peer *peer) u8 *msg; const struct htlc **changed_htlcs; +#if DEVELOPER /* Hack to suppress all commit sends if dev_disconnect says to */ if (dev_suppress_commit) { peer->commit_timer = NULL; tal_free(tmpctx); return; } +#endif /* FIXME: Document this requirement in BOLT 2! */ /* We can't send two commits in a row. */ @@ -1910,6 +1912,7 @@ static void handle_shutdown_cmd(struct peer *peer, const u8 *inmsg) start_commit_timer(peer); } +#if DEVELOPER static void handle_dev_reenable_commit(struct peer *peer) { dev_suppress_commit = false; @@ -1918,6 +1921,7 @@ static void handle_dev_reenable_commit(struct peer *peer) wire_sync_write(MASTER_FD, take(towire_channel_dev_reenable_commit_reply(peer))); } +#endif static void req_in(struct peer *peer, const u8 *msg) { @@ -1946,8 +1950,10 @@ static void req_in(struct peer *peer, const u8 *msg) handle_shutdown_cmd(peer, msg); goto out; case WIRE_CHANNEL_DEV_REENABLE_COMMIT: +#if DEVELOPER handle_dev_reenable_commit(peer); goto out; +#endif /* DEVELOPER */ case WIRE_CHANNEL_NORMAL_OPERATION: case WIRE_CHANNEL_INIT: case WIRE_CHANNEL_OFFER_HTLC_REPLY: diff --git a/common/crypto_sync.c b/common/crypto_sync.c index e3b53f54a..69e4e9744 100644 --- a/common/crypto_sync.c +++ b/common/crypto_sync.c @@ -11,11 +11,14 @@ bool sync_crypto_write(struct crypto_state *cs, int fd, const void *msg TAKES) { +#if DEVELOPER + bool post_sabotage = false; int type = fromwire_peektype(msg); +#endif u8 *enc = cryptomsg_encrypt_msg(NULL, cs, msg); bool ret; - bool post_sabotage = false; +#if DEVELOPER switch (dev_disconnect(type)) { case DEV_DISCONNECT_BEFORE: dev_sabotage_fd(fd); @@ -31,11 +34,14 @@ bool sync_crypto_write(struct crypto_state *cs, int fd, const void *msg TAKES) case DEV_DISCONNECT_NORMAL: break; } +#endif ret = write_all(fd, enc, tal_len(enc)); tal_free(enc); +#if DEVELOPER if (post_sabotage) dev_sabotage_fd(fd); +#endif return ret; } diff --git a/common/cryptomsg.c b/common/cryptomsg.c index c29254e4e..976e380ba 100644 --- a/common/cryptomsg.c +++ b/common/cryptomsg.c @@ -326,6 +326,7 @@ u8 *cryptomsg_encrypt_msg(const tal_t *ctx, return out; } +#if DEVELOPER static struct io_plan *peer_write_postclose(struct io_conn *conn, struct peer_crypto_state *pcs) { @@ -333,6 +334,7 @@ static struct io_plan *peer_write_postclose(struct io_conn *conn, dev_sabotage_fd(io_conn_fd(conn)); return pcs->next_out(conn, pcs->peer); } +#endif struct io_plan *peer_write_message(struct io_conn *conn, struct peer_crypto_state *pcs, @@ -341,7 +343,10 @@ struct io_plan *peer_write_message(struct io_conn *conn, struct peer *)) { struct io_plan *(*post)(struct io_conn *, struct peer_crypto_state *); +#if DEVELOPER int type = fromwire_peektype(msg); +#endif + assert(!pcs->out); pcs->out = cryptomsg_encrypt_msg(conn, &pcs->cs, msg); @@ -349,6 +354,7 @@ struct io_plan *peer_write_message(struct io_conn *conn, post = peer_write_done; +#if DEVELOPER switch (dev_disconnect(type)) { case DEV_DISCONNECT_BEFORE: dev_sabotage_fd(io_conn_fd(conn)); @@ -364,6 +370,7 @@ struct io_plan *peer_write_message(struct io_conn *conn, case DEV_DISCONNECT_NORMAL: break; } +#endif /* DEVELOPER */ /* BOLT #8: * * Send `lc || c` over the network buffer. diff --git a/common/debug.c b/common/debug.c index dc6e70ecf..99a2e2ba1 100644 --- a/common/debug.c +++ b/common/debug.c @@ -50,13 +50,16 @@ static void crashlog_activate(void) void subdaemon_debug(int argc, char *argv[]) { +#if DEVELOPER int i; bool printed = false; +#endif err_set_progname(argv[0]); backtrace_state = backtrace_create_state(argv[0], 0, NULL, NULL); crashlog_activate(); +#if DEVELOPER for (i = 1; i < argc; i++) { if (strstarts(argv[i], "--dev-disconnect=")) { dev_disconnect_init(atoi(argv[i] @@ -73,4 +76,5 @@ void subdaemon_debug(int argc, char *argv[]) printed = true; } } +#endif } diff --git a/common/dev_disconnect.c b/common/dev_disconnect.c index ae0e15961..0b278a13d 100644 --- a/common/dev_disconnect.c +++ b/common/dev_disconnect.c @@ -11,6 +11,7 @@ #include #include +#if DEVELOPER /* We move the fd IFF we do a disconnect. */ static int dev_disconnect_fd = -1; static char dev_disconnect_line[200]; @@ -132,3 +133,4 @@ void dev_blackhole_fd(int fd) dup2(fds[1], fd); close(fds[1]); } +#endif diff --git a/common/dev_disconnect.h b/common/dev_disconnect.h index 026c672f0..8760f35ee 100644 --- a/common/dev_disconnect.h +++ b/common/dev_disconnect.h @@ -3,6 +3,7 @@ #include "config.h" #include +#if DEVELOPER enum dev_disconnect { /* Do nothing. */ DEV_DISCONNECT_NORMAL = '=', @@ -30,5 +31,6 @@ void dev_disconnect_init(int fd); /* Hack for channeld to do DEV_DISCONNECT_SUPPRESS_COMMIT. */ extern bool dev_suppress_commit; +#endif /* DEVELOPER */ #endif /* LIGHTNING_COMMON_DEV_DISCONNECT_H */ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index f4c7fe4b5..fa5a0b273 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -188,8 +188,10 @@ static void rebroadcast_txs(struct chain_topology *topo, struct command *cmd) struct txs_to_broadcast *txs; struct outgoing_tx *otx; +#if DEVELOPER if (topo->dev_no_broadcast) return; +#endif /* DEVELOPER */ txs = tal(topo, struct txs_to_broadcast); txs->cmd = cmd; @@ -263,11 +265,13 @@ void broadcast_tx(struct chain_topology *topo, log_add(topo->log, " (tx %s)", type_to_string(ltmp, struct sha256_double, &otx->txid)); - if (topo->dev_no_broadcast) +#if DEVELOPER + if (topo->dev_no_broadcast) { broadcast_done(topo->bitcoind, 0, "dev_no_broadcast", otx); - else - bitcoind_sendrawtx(topo->bitcoind, otx->hextx, - broadcast_done, otx); + return; + } +#endif + bitcoind_sendrawtx(topo->bitcoind, otx->hextx, broadcast_done, otx); } static void free_blocks(struct chain_topology *topo, struct block *b) @@ -484,6 +488,7 @@ struct txlocator *locate_tx(const void *ctx, const struct chain_topology *topo, return tal_free(loc); } +#if DEVELOPER void json_dev_broadcast(struct command *cmd, struct chain_topology *topo, const char *buffer, const jsmntok_t *params) @@ -534,6 +539,7 @@ static const struct json_command dev_blockheight = { "Returns { blockheight: u32 } on success" }; AUTODATA(json_command, &dev_blockheight); +#endif /* DEVELOPER */ /* On shutdown, peers get deleted last. That frees from our list, so * do it now instead. */ @@ -556,8 +562,10 @@ struct chain_topology *new_topology(const tal_t *ctx, struct log *log) topo->log = log; topo->default_fee_rate = 40000; topo->override_fee_rate = 0; - topo->dev_no_broadcast = false; topo->bitcoind = new_bitcoind(topo, log); +#if DEVELOPER + topo->dev_no_broadcast = false; +#endif return topo; } diff --git a/lightningd/chaintopology.h b/lightningd/chaintopology.h index 4847093fb..5e69ce734 100644 --- a/lightningd/chaintopology.h +++ b/lightningd/chaintopology.h @@ -113,8 +113,10 @@ struct chain_topology { struct txwatch_hash txwatches; struct txowatch_hash txowatches; +#if DEVELOPER /* Suppress broadcast (for testing) */ bool dev_no_broadcast; +#endif }; /* Information relevant to locating a TX in a blockchain. */ @@ -156,8 +158,9 @@ struct txlocator *locate_tx(const void *ctx, const struct chain_topology *topo, void notify_new_block(struct chain_topology *topo, unsigned int height); +#if DEVELOPER void json_dev_broadcast(struct command *cmd, struct chain_topology *topo, const char *buffer, const jsmntok_t *params); - +#endif #endif /* LIGHTNING_LIGHTNINGD_CHAINTOPOLOGY_H */ diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index ec6647e2c..596c495c2 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -186,6 +186,7 @@ static const struct json_command getlog_command = { }; AUTODATA(json_command, &getlog_command); +#if DEVELOPER static void json_rhash(struct command *cmd, const char *buffer, const jsmntok_t *params) { @@ -238,6 +239,7 @@ static const struct json_command dev_crash_command = { "Simple crash test for developers" }; AUTODATA(json_command, &dev_crash_command); +#endif /* DEVELOPER */ static void json_getinfo(struct command *cmd, const char *buffer, const jsmntok_t *params) diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index 97a7cd62d..42305dd8d 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -71,11 +71,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx, struct lightningd *ld = tal(ctx, struct lightningd); list_head_init(&ld->peers); - ld->dev_debug_subdaemon = NULL; htlc_in_map_init(&ld->htlcs_in); htlc_out_map_init(&ld->htlcs_out); - ld->dev_disconnect_fd = -1; - ld->dev_hsm_seed = NULL; ld->log_book = log_book; ld->log = new_log(log_book, log_book, "lightningd(%u):", (int)getpid()); ld->alias = NULL; @@ -88,6 +85,13 @@ static struct lightningd *new_lightningd(const tal_t *ctx, /* FIXME: Move into invoice daemon. */ ld->invoices = invoices_init(ld); + +#if DEVELOPER + ld->dev_debug_subdaemon = NULL; + ld->dev_disconnect_fd = -1; + ld->dev_hsm_seed = NULL; +#endif + return ld; } diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h index 9303e982b..4bd37cd4b 100644 --- a/lightningd/lightningd.h +++ b/lightningd/lightningd.h @@ -114,18 +114,6 @@ struct lightningd { /* Our chain topology. */ struct chain_topology *topology; - /* If we want to debug a subdaemon. */ - const char *dev_debug_subdaemon; - - /* If we want to set a specific non-random HSM seed. */ - const u8 *dev_hsm_seed; - - /* If we have a --dev-disconnect file */ - int dev_disconnect_fd; - - /* If we have --dev-fail-on-subdaemon-fail */ - bool dev_subdaemon_fail; - /* HTLCs in flight. */ struct htlc_in_map htlcs_in; struct htlc_out_map htlcs_out; @@ -139,6 +127,20 @@ struct lightningd { /* Any outstanding "pay" commands. */ struct list_head pay_commands; + +#if DEVELOPER + /* If we want to debug a subdaemon. */ + const char *dev_debug_subdaemon; + + /* If we want to set a specific non-random HSM seed. */ + const u8 *dev_hsm_seed; + + /* If we have a --dev-disconnect file */ + int dev_disconnect_fd; + + /* If we have --dev-fail-on-subdaemon-fail */ + bool dev_subdaemon_fail; +#endif /* DEVELOPER */ }; /** diff --git a/lightningd/options.c b/lightningd/options.c index 9f2e39696..92d44b17c 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -285,6 +285,7 @@ static void config_register_opts(struct lightningd *ld) " regtest, or litecoin)"); } +#if DEVELOPER static char *opt_set_hsm_seed(const char *arg, struct lightningd *ld) { ld->dev_hsm_seed = tal_hexdata(ld, arg, strlen(arg)); @@ -310,6 +311,7 @@ static void dev_register_opts(struct lightningd *ld) opt_register_arg("--dev-hsm-seed=", opt_set_hsm_seed, NULL, ld, "Hex-encoded seed for HSM"); } +#endif static const struct config testnet_config = { /* 6 blocks to catch cheating attempts. */ @@ -560,7 +562,9 @@ void register_opts(struct lightningd *ld) configdir_register_opts(ld, &ld->config_dir, &ld->rpc_filename); config_register_opts(ld); +#if DEVELOPER dev_register_opts(ld); +#endif } /* Names stolen from https://github.com/ternus/nsaproductgenerator/blob/master/nsa.js */ @@ -634,6 +638,7 @@ bool handle_opts(struct lightningd *ld, int argc, char *argv[]) check_config(ld); +#if DEVELOPER if (ld->dev_hsm_seed) { int fd; unlink("hsm_secret"); @@ -645,6 +650,7 @@ bool handle_opts(struct lightningd *ld, int argc, char *argv[]) strerror(errno)); close(fd); } +#endif return newdir; } diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 2540d55ee..a0bcb8163 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -204,10 +204,12 @@ void peer_fail_transient(struct peer *peer, const char *fmt, ...) logv_add(peer->log, fmt, ap); va_end(ap); +#if DEVELOPER if (dev_disconnect_permanent(peer->ld)) { peer_internal_error(peer, "dev_disconnect permfail"); return; } +#endif peer_set_owner(peer, NULL); @@ -759,88 +761,6 @@ static const struct json_command connect_command = { }; AUTODATA(json_command, &connect_command); -static void json_dev_fail(struct command *cmd, - const char *buffer, const jsmntok_t *params) -{ - jsmntok_t *peertok; - struct peer *peer; - - if (!json_get_params(buffer, params, - "id", &peertok, - NULL)) { - command_fail(cmd, "Need id"); - return; - } - - peer = peer_from_json(cmd->ld, buffer, peertok); - if (!peer) { - command_fail(cmd, "Could not find peer with that id"); - return; - } - - peer_internal_error(peer, "Failing due to dev-fail command"); - command_success(cmd, null_response(cmd)); -} - -static const struct json_command dev_fail_command = { - "dev-fail", - json_dev_fail, - "Fail with peer {id}", - "Returns {} on success" -}; -AUTODATA(json_command, &dev_fail_command); - -static void dev_reenable_commit_finished(struct subd *channeld, - const u8 *resp, - const int *fds, - struct command *cmd) -{ - command_success(cmd, null_response(cmd)); -} - -static void json_dev_reenable_commit(struct command *cmd, - const char *buffer, const jsmntok_t *params) -{ - jsmntok_t *peertok; - struct peer *peer; - u8 *msg; - - if (!json_get_params(buffer, params, - "id", &peertok, - NULL)) { - command_fail(cmd, "Need id"); - return; - } - - peer = peer_from_json(cmd->ld, buffer, peertok); - if (!peer) { - command_fail(cmd, "Could not find peer with that id"); - return; - } - - if (!peer->owner) { - command_fail(cmd, "Peer has no owner"); - return; - } - - if (!streq(peer->owner->name, "lightning_channeld")) { - command_fail(cmd, "Peer owned by %s", peer->owner->name); - return; - } - - msg = towire_channel_dev_reenable_commit(peer); - subd_req(peer, peer->owner, take(msg), -1, 0, - dev_reenable_commit_finished, cmd); -} - -static const struct json_command dev_reenable_commit = { - "dev-reenable-commit", - json_dev_reenable_commit, - "Reenable the commit timer on peer {id}", - "Returns {} on success" -}; -AUTODATA(json_command, &dev_reenable_commit); - struct log_info { enum log_level level; struct json_result *response; @@ -1700,52 +1620,6 @@ void peer_last_tx(struct peer *peer, struct bitcoin_tx *tx, peer->last_tx = tal_steal(peer, tx); } -/* FIXME: Guard with heavy dev-only #ifdefs! */ -static void json_sign_last_tx(struct command *cmd, - const char *buffer, const jsmntok_t *params) -{ - jsmntok_t *peertok; - struct peer *peer; - struct json_result *response = new_json_result(cmd); - u8 *linear; - - if (!json_get_params(buffer, params, - "id", &peertok, - NULL)) { - command_fail(cmd, "Need id"); - return; - } - - peer = peer_from_json(cmd->ld, buffer, peertok); - if (!peer) { - command_fail(cmd, "Could not find peer with that id"); - return; - } - if (!peer->last_tx) { - command_fail(cmd, "Peer has no final transaction"); - return; - } - - log_debug(peer->log, "dev-sign-last-tx: signing tx with %zu outputs", - tal_count(peer->last_tx->output)); - sign_last_tx(peer); - linear = linearize_tx(cmd, peer->last_tx); - - json_object_start(response, NULL); - json_add_hex(response, "tx", linear, tal_len(linear)); - json_object_end(response); - command_success(cmd, response); -} - -static const struct json_command dev_sign_last_tx = { - "dev-sign-last-tx", - json_sign_last_tx, - "Sign and return the last commitment transaction", - "Sign last transaction with peer @id, return as @tx." - " This should never be called outside testing!" -}; -AUTODATA(json_command, &dev_sign_last_tx); - /* Is this better than the last tx we were holding? */ static bool better_closing_fee(struct peer *peer, const struct bitcoin_tx *tx) { @@ -2673,3 +2547,132 @@ const char *peer_state_name(enum peer_state state) return enum_peer_state_names[i].name; return "unknown"; } + +#if DEVELOPER +static void json_sign_last_tx(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + jsmntok_t *peertok; + struct peer *peer; + struct json_result *response = new_json_result(cmd); + u8 *linear; + + if (!json_get_params(buffer, params, + "id", &peertok, + NULL)) { + command_fail(cmd, "Need id"); + return; + } + + peer = peer_from_json(cmd->ld, buffer, peertok); + if (!peer) { + command_fail(cmd, "Could not find peer with that id"); + return; + } + if (!peer->last_tx) { + command_fail(cmd, "Peer has no final transaction"); + return; + } + + log_debug(peer->log, "dev-sign-last-tx: signing tx with %zu outputs", + tal_count(peer->last_tx->output)); + sign_last_tx(peer); + linear = linearize_tx(cmd, peer->last_tx); + + json_object_start(response, NULL); + json_add_hex(response, "tx", linear, tal_len(linear)); + json_object_end(response); + command_success(cmd, response); +} + +static const struct json_command dev_sign_last_tx = { + "dev-sign-last-tx", + json_sign_last_tx, + "Sign and return the last commitment transaction", + "Sign last transaction with peer @id, return as @tx." + " This should never be called outside testing!" +}; +AUTODATA(json_command, &dev_sign_last_tx); + +static void json_dev_fail(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + jsmntok_t *peertok; + struct peer *peer; + + if (!json_get_params(buffer, params, + "id", &peertok, + NULL)) { + command_fail(cmd, "Need id"); + return; + } + + peer = peer_from_json(cmd->ld, buffer, peertok); + if (!peer) { + command_fail(cmd, "Could not find peer with that id"); + return; + } + + peer_internal_error(peer, "Failing due to dev-fail command"); + command_success(cmd, null_response(cmd)); +} + +static const struct json_command dev_fail_command = { + "dev-fail", + json_dev_fail, + "Fail with peer {id}", + "Returns {} on success" +}; +AUTODATA(json_command, &dev_fail_command); + +static void dev_reenable_commit_finished(struct subd *channeld, + const u8 *resp, + const int *fds, + struct command *cmd) +{ + command_success(cmd, null_response(cmd)); +} + +static void json_dev_reenable_commit(struct command *cmd, + const char *buffer, const jsmntok_t *params) +{ + jsmntok_t *peertok; + struct peer *peer; + u8 *msg; + + if (!json_get_params(buffer, params, + "id", &peertok, + NULL)) { + command_fail(cmd, "Need id"); + return; + } + + peer = peer_from_json(cmd->ld, buffer, peertok); + if (!peer) { + command_fail(cmd, "Could not find peer with that id"); + return; + } + + if (!peer->owner) { + command_fail(cmd, "Peer has no owner"); + return; + } + + if (!streq(peer->owner->name, "lightning_channeld")) { + command_fail(cmd, "Peer owned by %s", peer->owner->name); + return; + } + + msg = towire_channel_dev_reenable_commit(peer); + subd_req(peer, peer->owner, take(msg), -1, 0, + dev_reenable_commit_finished, cmd); +} + +static const struct json_command dev_reenable_commit = { + "dev-reenable-commit", + json_dev_reenable_commit, + "Reenable the commit timer on peer {id}", + "Returns {} on success" +}; +AUTODATA(json_command, &dev_reenable_commit); +#endif /* DEVELOPER */ diff --git a/lightningd/subd.c b/lightningd/subd.c index 16f2030e4..09c659f0b 100644 --- a/lightningd/subd.c +++ b/lightningd/subd.c @@ -134,7 +134,6 @@ static int subd(const char *dir, const char *name, const char *debug_subdaemon, int childmsg[2], execfail[2]; pid_t childpid; int err, *fd; - bool debug = debug_subdaemon && strends(name, debug_subdaemon); if (socketpair(AF_LOCAL, SOCK_STREAM, 0, childmsg) != 0) goto fail; @@ -188,10 +187,12 @@ static int subd(const char *dir, const char *name, const char *debug_subdaemon, if (i != dev_disconnect_fd) close(i); +#if DEVELOPER if (dev_disconnect_fd != -1) debug_arg[0] = tal_fmt(NULL, "--dev-disconnect=%i", dev_disconnect_fd); - if (debug) + if (debug_subdaemon && strends(name, debug_subdaemon)) debug_arg[debug_arg[0] ? 1 : 0] = "--debugger"; +#endif execl(path_join(NULL, dir, name), name, debug_arg[0], debug_arg[1], NULL); child_errno_fail: @@ -236,9 +237,16 @@ int subd_raw(struct lightningd *ld, const char *name) { pid_t pid; int msg_fd; + const char *debug_subd = NULL; + int disconnect_fd = -1; - pid = subd(ld->daemon_dir, name, ld->dev_debug_subdaemon, - &msg_fd, ld->dev_disconnect_fd, NULL); +#if DEVELOPER + debug_subd = ld->dev_debug_subdaemon; + disconnect_fd = ld->dev_disconnect_fd; +#endif /* DEVELOPER */ + + pid = subd(ld->daemon_dir, name, debug_subd, &msg_fd, disconnect_fd, + NULL); if (pid == (pid_t)-1) { log_unusual(ld->log, "subd %s failed: %s", name, strerror(errno)); @@ -342,8 +350,10 @@ static void subdaemon_malformed_msg(struct subd *sd, const u8 *msg) msg + sizeof(be16), tal_count(msg) - sizeof(be16))); +#if DEVELOPER if (sd->ld->dev_subdaemon_fail) fatal("Subdaemon %s sent malformed message", sd->name); +#endif } /* Returns true if logged, false if malformed. */ @@ -388,8 +398,10 @@ log_str_peer: log_str_broken: log_broken(sd->log, "%s: %.*s", name, str_len, str); +#if DEVELOPER if (sd->ld->dev_subdaemon_fail) fatal("Subdaemon %s hit error", sd->name); +#endif return true; } @@ -491,7 +503,11 @@ next: static void destroy_subd(struct subd *sd) { int status; - bool fail_if_subd_fails = sd->ld->dev_subdaemon_fail; + bool fail_if_subd_fails = false; + +#if DEVELOPER + fail_if_subd_fails = sd->ld->dev_subdaemon_fail; +#endif switch (waitpid(sd->pid, &status, WNOHANG)) { case 0: @@ -568,9 +584,16 @@ static struct subd *new_subd(struct lightningd *ld, { struct subd *sd = tal(ld, struct subd); int msg_fd; + const char *debug_subd = NULL; + int disconnect_fd = -1; - sd->pid = subd(ld->daemon_dir, name, ld->dev_debug_subdaemon, - &msg_fd, ld->dev_disconnect_fd, ap); +#if DEVELOPER + debug_subd = ld->dev_debug_subdaemon; + disconnect_fd = ld->dev_disconnect_fd; +#endif /* DEVELOPER */ + + sd->pid = subd(ld->daemon_dir, name, debug_subd, &msg_fd, disconnect_fd, + ap); if (sd->pid == (pid_t)-1) { log_unusual(ld->log, "subd %s failed: %s", name, strerror(errno)); @@ -696,6 +719,7 @@ void subd_release_peer(struct subd *owner, struct peer *peer) } } +#if DEVELOPER char *opt_subd_debug(const char *optarg, struct lightningd *ld) { ld->dev_debug_subdaemon = optarg; @@ -731,3 +755,4 @@ bool dev_disconnect_permanent(struct lightningd *ld) lseek(ld->dev_disconnect_fd, -r, SEEK_CUR); return false; } +#endif /* DEVELOPER */ diff --git a/lightningd/subd.h b/lightningd/subd.h index 62cee7dc2..ac24353c2 100644 --- a/lightningd/subd.h +++ b/lightningd/subd.h @@ -163,8 +163,10 @@ void subd_release_peer(struct subd *owner, struct peer *peer); */ void subd_shutdown(struct subd *subd, unsigned int seconds); +#if DEVELOPER char *opt_subd_debug(const char *optarg, struct lightningd *ld); char *opt_subd_dev_disconnect(const char *optarg, struct lightningd *ld); bool dev_disconnect_permanent(struct lightningd *ld); +#endif /* DEVELOPER */ #endif /* LIGHTNING_LIGHTNINGD_SUBD_H */ diff --git a/lightningd/test/run-cryptomsg.c b/lightningd/test/run-cryptomsg.c index 15b0de13d..35126ede9 100644 --- a/lightningd/test/run-cryptomsg.c +++ b/lightningd/test/run-cryptomsg.c @@ -39,6 +39,7 @@ static void do_write(const void *buf, size_t len) #define status_trace(fmt, ...) \ printf(fmt "\n", __VA_ARGS__) +#if DEVELOPER /* AUTOGENERATED MOCKS START */ /* Generated stub for dev_blackhole_fd */ void dev_blackhole_fd(int fd UNNEEDED) @@ -52,6 +53,7 @@ enum dev_disconnect dev_disconnect(int pkt_type) { return DEV_DISCONNECT_NORMAL; } +#endif /* DEVELOPER */ /* We test what look like unknown messages. */ #define is_unknown_msg_discardable(x) 0 diff --git a/lightningd/test/run-find_my_path.c b/lightningd/test/run-find_my_path.c index 364091dbb..64c6ebc1c 100644 --- a/lightningd/test/run-find_my_path.c +++ b/lightningd/test/run-find_my_path.c @@ -88,6 +88,16 @@ struct wallet *wallet_new(const tal_t *ctx UNNEEDED, struct log *log UNNEEDED) { fprintf(stderr, "wallet_new called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ +/* We only need these in developer mode */ +#if DEVELOPER +/* Generated stub for opt_subd_debug */ +char *opt_subd_debug(const char *optarg UNNEEDED, struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "opt_subd_debug called!\n"); abort(); } +/* Generated stub for opt_subd_dev_disconnect */ +char *opt_subd_dev_disconnect(const char *optarg UNNEEDED, struct lightningd *ld UNNEEDED) +{ fprintf(stderr, "opt_subd_dev_disconnect called!\n"); abort(); } +#endif + #undef main int main(int argc, char *argv[]) { diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index ec13486d8..15fef25de 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -23,6 +23,7 @@ import utils bitcoind = None TEST_DIR = tempfile.mkdtemp(prefix='lightning-') VALGRIND = os.getenv("NO_VALGRIND", "0") == "0" +DEVELOPER = os.getenv("DEVELOPER", "0") == "1" TEST_DEBUG = os.getenv("TEST_DEBUG", "0") == "1" print("Testing results are in {}".format(TEST_DIR)) @@ -104,7 +105,8 @@ class NodeFactory(object): with open(os.path.join(lightning_dir, "dev_disconnect"), "w") as f: f.write("\n".join(disconnect)) daemon.cmd_line.append("--dev-disconnect=dev_disconnect") - daemon.cmd_line.append("--dev-fail-on-subdaemon-fail") + if DEVELOPER: + daemon.cmd_line.append("--dev-fail-on-subdaemon-fail") opts = [] if options is None else options for opt in opts: daemon.cmd_line.append(opt) @@ -441,6 +443,7 @@ class LightningDTests(BaseLightningDTests): l2.daemon.wait_for_log('sendrawtx exit 0') assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_permfail(self): l1,l2 = self.connect() @@ -485,6 +488,7 @@ class LightningDTests(BaseLightningDTests): bitcoind.rpc.generate(6) l2.daemon.wait_for_log('onchaind complete, forgetting peer') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_onchain_first_commit(self): """Onchain handling where funder immediately drops to chain""" @@ -527,6 +531,7 @@ class LightningDTests(BaseLightningDTests): bitcoind.rpc.generate(6) l1.daemon.wait_for_log('onchaind complete, forgetting peer') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_onchain_dust_out(self): """Onchain handling of outgoing dust htlcs (they should fail)""" # HTLC 1->2, 1 fails after it's irrevocably committed @@ -579,6 +584,7 @@ class LightningDTests(BaseLightningDTests): # Payment failed, BTW assert l2.rpc.listinvoice('onchain_dust_out')[0]['complete'] == False + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_onchain_timeout(self): """Onchain handling of outgoing failed htlcs""" # HTLC 1->2, 1 fails just after it's irrevocably committed @@ -634,6 +640,7 @@ class LightningDTests(BaseLightningDTests): # Payment failed, BTW assert l2.rpc.listinvoice('onchain_timeout')[0]['complete'] == False + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_onchain_middleman(self): # HTLC 1->2->3, 1->2 goes down after 2 gets preimage from 3. disconnects = ['-WIRE_UPDATE_FULFILL_HTLC', 'permfail'] @@ -708,6 +715,7 @@ class LightningDTests(BaseLightningDTests): l1.bitcoin.rpc.generate(100) l2.daemon.wait_for_log('onchaind complete, forgetting peer') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_penalty_inhtlc(self): """Test penalty transaction with an incoming HTLC""" # We suppress each one after first commit; HTLC gets added not fulfilled. @@ -829,6 +837,7 @@ class LightningDTests(BaseLightningDTests): # FIXME: Test wallet balance... l2.daemon.wait_for_log('onchaind complete, forgetting peer') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_permfail_new_commit(self): # Test case where we have two possible commits: it will use new one. disconnects = ['-WIRE_REVOKE_AND_ACK', 'permfail'] @@ -864,6 +873,7 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_permfail_htlc_in(self): # Test case where we fail with unsettled incoming HTLC. disconnects = ['-WIRE_UPDATE_FULFILL_HTLC', 'permfail'] @@ -905,6 +915,7 @@ class LightningDTests(BaseLightningDTests): bitcoind.rpc.generate(6) l2.daemon.wait_for_log('onchaind complete, forgetting peer') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_permfail_htlc_out(self): # Test case where we fail with unsettled outgoing HTLC. disconnects = ['+WIRE_REVOKE_AND_ACK', 'permfail'] @@ -1003,6 +1014,7 @@ class LightningDTests(BaseLightningDTests): ret = l1.rpc.dev_ping(l2.info['id'], 1000, s) assert ret['totlen'] == 0 + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_ping(self): l1,l2 = self.connect() @@ -1014,6 +1026,7 @@ class LightningDTests(BaseLightningDTests): # channeld pinging self.ping_tests(l1, l2) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_routing_gossip_reconnect(self): # Connect two peers, reconnect and then see if we resume the # gossip. @@ -1042,6 +1055,7 @@ class LightningDTests(BaseLightningDTests): self.fund_channel(l1, l2, 10**6) self.fund_channel(l1, l3, 10**6) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1 for --dev-broadcast-interval") def test_routing_gossip(self): nodes = [self.node_factory.get_node() for _ in range(5)] l1 = nodes[0] @@ -1142,6 +1156,7 @@ class LightningDTests(BaseLightningDTests): route = copy.deepcopy(baseroute) l1.rpc.sendpay(to_json(route), rhash) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_disconnect(self): # These should all make us fail, and retry. # FIXME: Configure short timeout for reconnect! @@ -1157,6 +1172,7 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('Failed connected out for {}, will try again' .format(l2.info['id'])) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_disconnect_funder(self): # Now error on funder side duringchannel open. disconnects = ['-WIRE_OPEN_CHANNEL', @@ -1177,6 +1193,7 @@ class LightningDTests(BaseLightningDTests): self.assertRaises(ValueError, l1.rpc.fundchannel, l2.info['id'], 20000) assert l1.rpc.getpeer(l2.info['id']) == None + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_disconnect_fundee(self): # Now error on fundee side during channel open. disconnects = ['-WIRE_ACCEPT_CHANNEL', @@ -1195,6 +1212,7 @@ class LightningDTests(BaseLightningDTests): self.assertRaises(ValueError, l1.rpc.fundchannel, l2.info['id'], 20000) assert l1.rpc.getpeer(l2.info['id']) == None + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_disconnect_half_signed(self): # Now, these are the corner cases. Fundee sends funding_signed, # but funder doesn't receive it. @@ -1214,6 +1232,7 @@ class LightningDTests(BaseLightningDTests): assert l1.rpc.getpeer(l2.info['id']) == None assert l2.rpc.getpeer(l1.info['id'])['peerid'] == l1.info['id'] + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_signed(self): # This will fail *after* both sides consider channel opening. disconnects = ['+WIRE_FUNDING_SIGNED'] @@ -1243,6 +1262,7 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('-> CHANNELD_NORMAL') l2.daemon.wait_for_log('-> CHANNELD_NORMAL') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_openingd(self): # Openingd thinks we're still opening; funder reconnects.. disconnects = ['0WIRE_ACCEPT_CHANNEL'] @@ -1272,6 +1292,7 @@ class LightningDTests(BaseLightningDTests): # Just to be sure, second openingd hand over to channeld. l2.daemon.wait_for_log('lightning_openingd.*REPLY WIRE_OPENING_FUNDEE_REPLY with 2 fds') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_normal(self): # Should reconnect fine even if locked message gets lost. disconnects = ['-WIRE_FUNDING_LOCKED', @@ -1283,6 +1304,7 @@ class LightningDTests(BaseLightningDTests): self.fund_channel(l1, l2, 10**6) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_sender_add1(self): # Fail after add is OK, will cause payment failure though. disconnects = ['-WIRE_UPDATE_ADD_HTLC-nocommit', @@ -1309,6 +1331,7 @@ class LightningDTests(BaseLightningDTests): # This will send commit, so will reconnect as required. l1.rpc.sendpay(to_json(route), rhash) + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_sender_add(self): disconnects = ['-WIRE_COMMITMENT_SIGNED', '@WIRE_COMMITMENT_SIGNED', @@ -1334,6 +1357,7 @@ class LightningDTests(BaseLightningDTests): for i in range(0,len(disconnects)): l1.daemon.wait_for_log('Already have funding locked in') + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_receiver_add(self): disconnects = ['-WIRE_COMMITMENT_SIGNED', '@WIRE_COMMITMENT_SIGNED', @@ -1357,6 +1381,7 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('Already have funding locked in') assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_reconnect_receiver_fulfill(self): # Ordering matters: after +WIRE_UPDATE_FULFILL_HTLC, channeld # will continue and try to send WIRE_COMMITMENT_SIGNED: if @@ -1386,6 +1411,7 @@ class LightningDTests(BaseLightningDTests): l1.daemon.wait_for_log('Already have funding locked in') assert l2.rpc.listinvoice('testpayment2')[0]['complete'] == True + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_shutdown_reconnect(self): disconnects = ['-WIRE_SHUTDOWN', '@WIRE_SHUTDOWN', @@ -1413,6 +1439,7 @@ class LightningDTests(BaseLightningDTests): l2.daemon.wait_for_logs(['sendrawtx exit 0', '-> CLOSINGD_COMPLETE']) assert l1.bitcoin.rpc.getmempoolinfo()['size'] == 1 + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_closing_negotiation_reconnect(self): disconnects = ['-WIRE_CLOSING_SIGNED', '@WIRE_CLOSING_SIGNED', @@ -1511,6 +1538,7 @@ class LightningDTests(BaseLightningDTests): assert outputs[0] > 8990000 assert outputs[2] == 10000000 + @unittest.skipIf(not DEVELOPER, "needs DEVELOPER=1") def test_channel_persistence(self): # Start two nodes and open a channel (to remember). l2 will # mysteriously die while committing the first HTLC so we can diff --git a/tests/utils.py b/tests/utils.py index bd4ec1914..690e898b5 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -25,6 +25,7 @@ LIGHTNINGD_CONFIG = { "locktime-blocks": 6, } +DEVELOPER = os.getenv("DEVELOPER", "0") == "1" def write_config(filename, opts): with open(filename, 'w') as f: @@ -237,11 +238,11 @@ class LightningD(TailableProc): '--bitcoin-datadir={}'.format(bitcoin_dir), '--lightning-dir={}'.format(lightning_dir), '--port={}'.format(port), - '--network=regtest', - '--dev-broadcast-interval=1000', - '--dev-hsm-seed={}'.format(seed.hex()) + '--network=regtest' ] - + if DEVELOPER: + self.cmd_line += ['--dev-broadcast-interval=1000', + '--dev-hsm-seed={}'.format(seed.hex())] self.cmd_line += ["--{}={}".format(k, v) for k, v in LIGHTNINGD_CONFIG.items()] self.prefix = 'lightningd(%d)' % (port)