Add DEVELOPER flag, set by default.

This is a bit messier than I'd like, but we want to clearly remove all
dev code (not just have it uncalled), so we remove fields and functions
altogether rather than stub them out.  This means we put #ifdefs in callers
in some places, but at least it's explicit.

We still run tests, but only a subset, and we run with NO_VALGRIND under
Travis to avoid increasing test times too much.

See-also: #176
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2017-10-24 12:36:14 +10:30 committed by Christian Decker
parent 8d9818ff9c
commit 3c6eec87e3
21 changed files with 301 additions and 168 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -11,6 +11,7 @@
#include <unistd.h>
#include <wire/gen_peer_wire.h>
#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

View File

@ -3,6 +3,7 @@
#include "config.h"
#include <stdbool.h>
#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 */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[])
{

View File

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

View File

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