#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool deprecated_apis = true; /* Tal wrappers for opt. */ static void *opt_allocfn(size_t size) { return tal_arr_label(NULL, char, size, TAL_LABEL("opt_allocfn", "")); } static void *tal_reallocfn(void *ptr, size_t size) { if (!ptr) { /* realloc(NULL) call is to allocate opt_table */ static bool opt_table_alloced = false; if (!opt_table_alloced) { opt_table_alloced = true; return notleak(opt_allocfn(size)); } return opt_allocfn(size); } tal_resize_(&ptr, 1, size, false); return ptr; } static void tal_freefn(void *ptr) { tal_free(ptr); } /* FIXME: Put into ccan/time. */ #define TIME_FROM_SEC(sec) { { .tv_nsec = 0, .tv_sec = sec } } #define TIME_FROM_MSEC(msec) \ { { .tv_nsec = ((msec) % 1000) * 1000000, .tv_sec = (msec) / 1000 } } static char *opt_set_u64(const char *arg, u64 *u) { char *endp; unsigned long long l; assert(arg != NULL); /* This is how the manpage says to do it. Yech. */ errno = 0; l = strtoull(arg, &endp, 0); if (*endp || !arg[0]) return tal_fmt(NULL, "'%s' is not a number", arg); *u = l; if (errno || *u != l) return tal_fmt(NULL, "'%s' is out of range", arg); return NULL; } static char *opt_set_u32(const char *arg, u32 *u) { char *endp; unsigned long l; assert(arg != NULL); /* This is how the manpage says to do it. Yech. */ errno = 0; l = strtoul(arg, &endp, 0); if (*endp || !arg[0]) return tal_fmt(NULL, "'%s' is not a number", arg); *u = l; if (errno || *u != l) return tal_fmt(NULL, "'%s' is out of range", arg); return NULL; } static char *opt_set_s32(const char *arg, s32 *u) { char *endp; long l; assert(arg != NULL); /* This is how the manpage says to do it. Yech. */ errno = 0; l = strtol(arg, &endp, 0); if (*endp || !arg[0]) return tal_fmt(NULL, "'%s' is not a number", arg); *u = l; if (errno || *u != l) return tal_fmt(NULL, "'%s' is out of range", arg); return NULL; } static char *opt_add_addr_withtype(const char *arg, struct lightningd *ld, enum addr_listen_announce ala, bool wildcard_ok) { char const *err_msg; struct wireaddr_internal wi; assert(arg != NULL); tal_arr_expand(&ld->proposed_listen_announce, ala); if (!parse_wireaddr_internal(arg, &wi, ld->portnum, wildcard_ok, !ld->use_proxy_always, false, &err_msg)) { return tal_fmt(NULL, "Unable to parse address '%s': %s", arg, err_msg); } tal_arr_expand(&ld->proposed_wireaddr, wi); return NULL; } static char *opt_add_addr(const char *arg, struct lightningd *ld) { return opt_add_addr_withtype(arg, ld, ADDR_LISTEN_AND_ANNOUNCE, true); } static char *opt_add_bind_addr(const char *arg, struct lightningd *ld) { return opt_add_addr_withtype(arg, ld, ADDR_LISTEN, true); } static char *opt_add_announce_addr(const char *arg, struct lightningd *ld) { const struct wireaddr *wn; size_t n = tal_count(ld->proposed_wireaddr); char *err = opt_add_addr_withtype(arg, ld, ADDR_ANNOUNCE, false); if (err) return err; /* Can't announce anything that's not a normal wireaddr. */ if (ld->proposed_wireaddr[n].itype != ADDR_INTERNAL_WIREADDR) return tal_fmt(NULL, "address '%s' is not announcable", arg); /* gossipd will refuse to announce the second one, sure, but it's * better to check and fail now if they've explicitly asked for it. */ wn = &ld->proposed_wireaddr[n].u.wireaddr; for (size_t i = 0; i < n; i++) { const struct wireaddr *wi; if (ld->proposed_listen_announce[i] != ADDR_ANNOUNCE) continue; assert(ld->proposed_wireaddr[i].itype == ADDR_INTERNAL_WIREADDR); wi = &ld->proposed_wireaddr[i].u.wireaddr; if (wn->type != wi->type) continue; return tal_fmt(NULL, "Cannot announce address %s;" " already have %s which is the same type", type_to_string(tmpctx, struct wireaddr, wn), type_to_string(tmpctx, struct wireaddr, wi)); } return NULL; } static void opt_show_u64(char buf[OPT_SHOW_LEN], const u64 *u) { snprintf(buf, OPT_SHOW_LEN, "%"PRIu64, *u); } static void opt_show_u32(char buf[OPT_SHOW_LEN], const u32 *u) { snprintf(buf, OPT_SHOW_LEN, "%"PRIu32, *u); } static void opt_show_s32(char buf[OPT_SHOW_LEN], const s32 *u) { snprintf(buf, OPT_SHOW_LEN, "%"PRIi32, *u); } static char *opt_set_network(const char *arg, struct lightningd *ld) { assert(arg != NULL); ld->topology->bitcoind->chainparams = chainparams_for_network(arg); if (!ld->topology->bitcoind->chainparams) return tal_fmt(NULL, "Unknown network name '%s'", arg); return NULL; } static char *opt_set_testnet(struct lightningd *ld) { return opt_set_network("testnet", ld); } static char *opt_set_mainnet(struct lightningd *ld) { return opt_set_network("bitcoin", ld); } static void opt_show_network(char buf[OPT_SHOW_LEN], const struct lightningd *ld) { snprintf(buf, OPT_SHOW_LEN, "%s", get_chainparams(ld)->network_name); } static char *opt_set_rgb(const char *arg, struct lightningd *ld) { assert(arg != NULL); ld->rgb = tal_free(ld->rgb); /* BOLT #7: * * - Note: the first byte of `rgb_color` is the red value, the second * byte is the green value, and the last byte is the blue value. */ ld->rgb = tal_hexdata(ld, arg, strlen(arg)); if (!ld->rgb || tal_count(ld->rgb) != 3) return tal_fmt(NULL, "rgb '%s' is not six hex digits", arg); return NULL; } static char *opt_set_alias(const char *arg, struct lightningd *ld) { assert(arg != NULL); ld->alias = tal_free(ld->alias); /* BOLT #7: * * * [`32`:`alias`] *... * - MUST set `alias` to a valid UTF-8 string, with any * `alias` trailing-bytes equal to 0. */ if (strlen(arg) > 32) return tal_fmt(NULL, "Alias '%s' is over 32 characters", arg); ld->alias = tal_arrz(ld, u8, 33); strncpy((char*)ld->alias, arg, 32); return NULL; } static char *opt_set_offline(struct lightningd *ld) { ld->reconnect = false; ld->listen = false; return NULL; } static char *opt_add_proxy_addr(const char *arg, struct lightningd *ld) { bool needed_dns = false; tal_free(ld->proxyaddr); /* We use a tal_arr here, so we can marshal it to gossipd */ ld->proxyaddr = tal_arr(ld, struct wireaddr, 1); if (!parse_wireaddr(arg, ld->proxyaddr, 9050, ld->use_proxy_always ? &needed_dns : NULL, NULL)) { return tal_fmt(NULL, "Unable to parse Tor proxy address '%s' %s", arg, needed_dns ? " (needed dns)" : ""); } return NULL; } static char *opt_add_plugin(const char *arg, struct lightningd *ld) { plugin_register(ld->plugins, arg); return NULL; } static char *opt_disable_plugin(const char *arg, struct lightningd *ld) { if (plugin_remove(ld->plugins, arg)) return NULL; return tal_fmt(NULL, "Could not find plugin %s", arg); } static char *opt_add_plugin_dir(const char *arg, struct lightningd *ld) { return add_plugin_dir(ld->plugins, arg, false); } static char *opt_clear_plugins(struct lightningd *ld) { clear_plugins(ld->plugins); return NULL; } static void config_register_opts(struct lightningd *ld) { opt_register_early_arg("--conf=", opt_set_talstr, NULL, &ld->config_filename, "Specify configuration file. Relative paths will be prefixed by lightning-dir location. (default: config)"); /* Register plugins as an early args, so we can initialize them and have * them register more command line options */ opt_register_early_arg("--plugin", opt_add_plugin, NULL, ld, "Add a plugin to be run (can be used multiple times)"); opt_register_early_arg("--plugin-dir", opt_add_plugin_dir, NULL, ld, "Add a directory to load plugins from (can be used multiple times)"); opt_register_early_noarg("--clear-plugins", opt_clear_plugins, ld, "Remove all plugins added before this option"); opt_register_early_arg("--disable-plugin", opt_disable_plugin, NULL, ld, "Disable a particular plugin by filename/name"); opt_register_noarg("--daemon", opt_set_bool, &ld->daemon, "Run in the background, suppress stdout/stderr"); opt_register_arg("--ignore-fee-limits", opt_set_bool_arg, opt_show_bool, &ld->config.ignore_fee_limits, "(DANGEROUS) allow peer to set any feerate"); opt_register_arg("--watchtime-blocks", opt_set_u32, opt_show_u32, &ld->config.locktime_blocks, "Blocks before peer can unilaterally spend funds"); opt_register_arg("--max-locktime-blocks", opt_set_u32, opt_show_u32, &ld->config.locktime_max, "Maximum blocks funds may be locked for"); opt_register_arg("--funding-confirms", opt_set_u32, opt_show_u32, &ld->config.anchor_confirms, "Confirmations required for funding transaction"); opt_register_arg("--commit-fee-min=", opt_set_u32, opt_show_u32, &ld->config.commitment_fee_min_percent, "Minimum percentage of fee to accept for commitment"); opt_register_arg("--commit-fee-max=", opt_set_u32, opt_show_u32, &ld->config.commitment_fee_max_percent, "Maximum percentage of fee to accept for commitment (0 for unlimited)"); opt_register_arg("--commit-fee=", opt_set_u32, opt_show_u32, &ld->config.commitment_fee_percent, "Percentage of fee to request for their commitment"); opt_register_arg("--cltv-delta", opt_set_u32, opt_show_u32, &ld->config.cltv_expiry_delta, "Number of blocks for cltv_expiry_delta"); opt_register_arg("--cltv-final", opt_set_u32, opt_show_u32, &ld->config.cltv_final, "Number of blocks for final cltv_expiry"); opt_register_arg("--commit-time=", opt_set_u32, opt_show_u32, &ld->config.commit_time_ms, "Time after changes before sending out COMMIT"); opt_register_arg("--fee-base", opt_set_u32, opt_show_u32, &ld->config.fee_base, "Millisatoshi minimum to charge for HTLC"); opt_register_arg("--rescan", opt_set_s32, opt_show_s32, &ld->config.rescan, "Number of blocks to rescan from the current head, or " "absolute blockheight if negative"); opt_register_arg("--fee-per-satoshi", opt_set_u32, opt_show_u32, &ld->config.fee_per_satoshi, "Microsatoshi fee for every satoshi in HTLC"); opt_register_arg("--addr", opt_add_addr, NULL, ld, "Set an IP address (v4 or v6) to listen on and announce to the network for incoming connections"); opt_register_arg("--bind-addr", opt_add_bind_addr, NULL, ld, "Set an IP address (v4 or v6) to listen on, but not announce"); opt_register_arg("--announce-addr", opt_add_announce_addr, NULL, ld, "Set an IP address (v4 or v6) or .onion v2/v3 to announce, but not listen on"); opt_register_noarg("--offline", opt_set_offline, ld, "Start in offline-mode (do not automatically reconnect and do not accept incoming connections)"); opt_register_arg("--autolisten", opt_set_bool_arg, opt_show_bool, &ld->autolisten, "If true, listen on default port and announce if it seems to be a public interface"); opt_register_early_arg("--network", opt_set_network, opt_show_network, ld, "Select the network parameters (bitcoin, testnet," " regtest, litecoin or litecoin-testnet)"); opt_register_early_noarg("--testnet", opt_set_testnet, ld, "Alias for --network=testnet"); opt_register_early_noarg("--mainnet", opt_set_mainnet, ld, "Alias for --network=bitcoin"); opt_register_early_arg("--allow-deprecated-apis", opt_set_bool_arg, opt_show_bool, &deprecated_apis, "Enable deprecated options, JSONRPC commands, fields, etc."); opt_register_arg("--autocleaninvoice-cycle", opt_set_u64, opt_show_u64, &ld->ini_autocleaninvoice_cycle, "Perform cleanup of expired invoices every given seconds, or do not autoclean if 0"); opt_register_arg("--autocleaninvoice-expired-by", opt_set_u64, opt_show_u64, &ld->ini_autocleaninvoice_expiredby, "If expired invoice autoclean enabled, invoices that have expired for at least this given seconds are cleaned"); opt_register_arg("--proxy", opt_add_proxy_addr, NULL, ld,"Set a socks v5 proxy IP address and port"); opt_register_arg("--tor-service-password", opt_set_talstr, NULL, &ld->tor_service_password, "Set a Tor hidden service password"); /* Early, as it suppresses DNS lookups from cmdline too. */ opt_register_early_arg("--always-use-proxy", opt_set_bool_arg, opt_show_bool, &ld->use_proxy_always, "Use the proxy always"); opt_register_noarg("--disable-dns", opt_set_invbool, &ld->config.use_dns, "Disable DNS lookups of peers"); #if DEVELOPER opt_register_arg("--dev-max-funding-unconfirmed-blocks", opt_set_u32, opt_show_u32, &ld->max_funding_unconfirmed, "Maximum number of blocks we wait for a channel " "funding transaction to confirm, if we are the " "fundee."); #endif } #if DEVELOPER static char *opt_subprocess_debug(const char *optarg, struct lightningd *ld) { ld->dev_debug_subprocess = optarg; return NULL; } static void dev_register_opts(struct lightningd *ld) { opt_register_noarg("--dev-no-reconnect", opt_set_invbool, &ld->reconnect, "Disable automatic reconnect attempts"); opt_register_noarg("--dev-fail-on-subdaemon-fail", opt_set_bool, &ld->dev_subdaemon_fail, opt_hidden); opt_register_early_arg("--dev-debugger=", opt_subprocess_debug, NULL, ld, "Invoke gdb at start of "); opt_register_arg("--dev-broadcast-interval=", opt_set_uintval, opt_show_uintval, &ld->config.broadcast_interval_msec, "Time between gossip broadcasts in milliseconds"); opt_register_arg("--dev-disconnect=", opt_subd_dev_disconnect, NULL, ld, "File containing disconnection points"); opt_register_noarg("--dev-allow-localhost", opt_set_bool, &ld->dev_allow_localhost, "Announce and allow announcments for localhost address"); opt_register_arg("--dev-bitcoind-poll", opt_set_u32, opt_show_u32, &ld->topology->poll_seconds, "Time between polling for new transactions"); opt_register_arg("--dev-max-fee-multiplier", opt_set_u32, opt_show_u32, &ld->config.max_fee_multiplier, "Allow the fee proposed by the remote end to be up to " "multiplier times higher than our own. Small values " "will cause channels to be closed more often due to " "fee fluctuations, large values may result in large " "fees."); opt_register_arg( "--dev-channel-update-interval=", opt_set_u32, opt_show_u32, &ld->config.channel_update_interval, "Time in seconds between channel updates for our own channels."); } #endif static const struct config testnet_config = { /* 6 blocks to catch cheating attempts. */ .locktime_blocks = 6, /* They can have up to 14 days, maximumu value that lnd will ask for by default. */ /* FIXME Convince lnd to use more reasonable defaults... */ .locktime_max = 14 * 24 * 6, /* We're fairly trusting, under normal circumstances. */ .anchor_confirms = 1, /* Testnet fees are crazy, allow infinite feerange. */ .commitment_fee_min_percent = 0, .commitment_fee_max_percent = 0, /* We offer to pay 5 times 2-block fee */ .commitment_fee_percent = 500, /* Be aggressive on testnet. */ .cltv_expiry_delta = 6, .cltv_final = 10, /* Send commit 10msec after receiving; almost immediately. */ .commit_time_ms = 10, /* Allow dust payments */ .fee_base = 1, /* Take 0.001% */ .fee_per_satoshi = 10, /* BOLT #7: * * - SHOULD flush outgoing gossip messages once every 60 * seconds, independently of the arrival times of the messages. */ .broadcast_interval_msec = 60000, /* Send a keepalive update at least every week, prune every twice that */ .channel_update_interval = 1209600/2, /* Testnet sucks */ .ignore_fee_limits = true, /* Rescan 5 hours of blocks on testnet, it's reorg happy */ .rescan = 30, /* Fees may be in the range our_fee - 10*our_fee */ .max_fee_multiplier = 10, .use_dns = true, }; /* aka. "Dude, where's my coins?" */ static const struct config mainnet_config = { /* ~one day to catch cheating attempts. */ .locktime_blocks = 6 * 24, /* They can have up to 14 days, maximumu value that lnd will ask for by default. */ /* FIXME Convince lnd to use more reasonable defaults... */ .locktime_max = 14 * 24 * 6, /* We're fairly trusting, under normal circumstances. */ .anchor_confirms = 3, /* Insist between 2 and 20 times the 2-block fee. */ .commitment_fee_min_percent = 200, .commitment_fee_max_percent = 2000, /* We offer to pay 5 times 2-block fee */ .commitment_fee_percent = 500, /* BOLT #2: * * 1. the `cltv_expiry_delta` for channels, `3R+2G+2S`: if in doubt, a * `cltv_expiry_delta` of 12 is reasonable (R=2, G=1, S=2) */ /* R = 2, G = 1, S = 3 */ .cltv_expiry_delta = 14, /* BOLT #2: * * 4. the minimum `cltv_expiry` accepted for terminal payments: the * worst case for the terminal node C is `2R+G+S` blocks */ .cltv_final = 10, /* Send commit 10msec after receiving; almost immediately. */ .commit_time_ms = 10, /* Discourage dust payments */ .fee_base = 1000, /* Take 0.001% */ .fee_per_satoshi = 10, /* BOLT #7: * * - SHOULD flush outgoing gossip messages once every 60 * seconds, independently of the arrival times of the messages. */ .broadcast_interval_msec = 60000, /* Send a keepalive update at least every week, prune every twice that */ .channel_update_interval = 1209600/2, /* Mainnet should have more stable fees */ .ignore_fee_limits = false, /* Rescan 2.5 hours of blocks on startup, it's not so reorg happy */ .rescan = 15, /* Fees may be in the range our_fee - 10*our_fee */ .max_fee_multiplier = 10, .use_dns = true, }; static void check_config(struct lightningd *ld) { /* We do this by ensuring it's less than the minimum we would accept. */ if (ld->config.commitment_fee_max_percent != 0 && ld->config.commitment_fee_max_percent < ld->config.commitment_fee_min_percent) fatal("Commitment fee invalid min-max %u-%u", ld->config.commitment_fee_min_percent, ld->config.commitment_fee_max_percent); if (ld->config.anchor_confirms == 0) fatal("anchor-confirms must be greater than zero"); if (ld->use_proxy_always && !ld->proxyaddr) fatal("--always-use-proxy needs --proxy"); } static void setup_default_config(struct lightningd *ld) { if (get_chainparams(ld)->testnet) ld->config = testnet_config; else ld->config = mainnet_config; /* Set default PID file name to be per-network */ tal_free(ld->pidfile); ld->pidfile = tal_fmt(ld, "lightningd-%s.pid", get_chainparams(ld)->network_name); } /* FIXME: make this nicer! */ static int config_parse_line_number = 0; static void config_log_stderr_exit(const char *fmt, ...) { char *msg; va_list ap; va_start(ap, fmt); /* This is the format we expect:*/ if (streq(fmt, "%s: %.*s: %s")) { const char *argv0 = va_arg(ap, const char *); unsigned int len = va_arg(ap, unsigned int); const char *arg = va_arg(ap, const char *); const char *problem = va_arg(ap, const char *); assert(argv0 != NULL); assert(arg != NULL); assert(problem != NULL); /*mangle it to remove '--' and add the line number.*/ msg = tal_fmt(NULL, "%s line %d: %.*s: %s", argv0, config_parse_line_number, len-2, arg+2, problem); } else { msg = tal_vfmt(NULL, fmt, ap); } va_end(ap); fatal("%s", msg); } /** * We turn the config file into cmdline arguments. @early tells us * whether to parse early options only, or the non-early options. */ static void opt_parse_from_config(struct lightningd *ld, bool early) { char *contents, **lines; char **all_args; /*For each line: either argument string or NULL*/ char *argv[3]; int i, argc; char *filename; if (ld->config_filename != NULL) filename = ld->config_filename; else filename = path_join(tmpctx, ld->config_dir, "config"); contents = grab_file(ld, filename); /* The default config doesn't have to exist, but if the config was * specified on the command line it has to exist. */ if (!contents) { if ((errno != ENOENT) || (ld->config_filename != NULL)) fatal("Opening and reading %s: %s", filename, strerror(errno)); /* Now we can set up defaults, since no config file. */ setup_default_config(ld); return; } lines = tal_strsplit(contents, contents, "\r\n", STR_NO_EMPTY); /* We have to keep all_args around, since opt will point into it */ all_args = notleak(tal_arr(ld, char *, tal_count(lines) - 1)); for (i = 0; i < tal_count(lines) - 1; i++) { if (strstarts(lines[i], "#")) { all_args[i] = NULL; } else { /* Only valid forms are "foo" and "foo=bar" */ all_args[i] = tal_fmt(all_args, "--%s", lines[i]); } } /* For each line we construct a fake argc,argv commandline. argv[1] is the only element that changes between iterations. */ argc = 2; argv[0] = "lightning config file"; argv[argc] = NULL; if (early) { for (i = 0; i < tal_count(all_args); i++) { if (all_args[i] != NULL) { config_parse_line_number = i + 1; argv[1] = all_args[i]; opt_early_parse(argc, argv, config_log_stderr_exit); } } /* Now we can set up defaults, depending on whether testnet or * not */ setup_default_config(ld); } else { for (i = 0; i < tal_count(all_args); i++) { if (all_args[i] != NULL) { config_parse_line_number = i + 1; argv[1] = all_args[i]; opt_parse(&argc, argv, config_log_stderr_exit); argc = 2; /* opt_parse might have changed it */ } } } tal_free(contents); } static char *test_subdaemons_and_exit(struct lightningd *ld) { test_subdaemons(ld); exit(0); return NULL; } static char *opt_lightningd_usage(struct lightningd *ld) { /* Reload config so that --help has the correct network defaults * to display before it exits */ setup_default_config(ld); char *extra = tal_fmt(NULL, "\nA bitcoin lightning daemon (default " "values shown for network: %s).", get_chainparams(ld)->network_name); opt_usage_and_exit(extra); tal_free(extra); return NULL; } void register_opts(struct lightningd *ld) { opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn); opt_register_noarg("--help|-h", opt_lightningd_usage, ld, "Print this message."); opt_register_early_noarg("--test-daemons-only", test_subdaemons_and_exit, ld, opt_hidden); opt_register_arg("--bitcoin-datadir", opt_set_talstr, NULL, &ld->topology->bitcoind->datadir, "-datadir arg for bitcoin-cli"); opt_register_arg("--rgb", opt_set_rgb, NULL, ld, "RRGGBB hex color for node"); opt_register_arg("--alias", opt_set_alias, NULL, ld, "Up to 32-byte alias for node"); opt_register_arg("--bitcoin-cli", opt_set_talstr, NULL, &ld->topology->bitcoind->cli, "bitcoin-cli pathname"); opt_register_arg("--bitcoin-rpcuser", opt_set_talstr, NULL, &ld->topology->bitcoind->rpcuser, "bitcoind RPC username"); opt_register_arg("--bitcoin-rpcpassword", opt_set_talstr, NULL, &ld->topology->bitcoind->rpcpass, "bitcoind RPC password"); opt_register_arg("--bitcoin-rpcconnect", opt_set_talstr, NULL, &ld->topology->bitcoind->rpcconnect, "bitcoind RPC host to connect to"); opt_register_arg("--bitcoin-rpcport", opt_set_talstr, NULL, &ld->topology->bitcoind->rpcport, "bitcoind RPC port"); opt_register_arg("--pid-file=", opt_set_talstr, opt_show_charp, &ld->pidfile, "Specify pid file"); opt_register_logging(ld); opt_register_version(); 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 */ static const char *codename_adjective[] = { "LOUD", "RED", "BLUE", "GREEN", "YELLOW", "IRATE", "ANGRY", "PEEVED", "HAPPY", "SLIMY", "SLEEPY", "JUNIOR", "SLICKER", "UNITED", "SOMBER", "BIZARRE", "ODD", "WEIRD", "WRONG", "LATENT", "CHILLY", "STRANGE", "LOUD", "SILENT", "HOPPING", "ORANGE", "VIOLET", "VIOLENT", "LIGHTNING" }; static const char *codename_noun[] = { "WHISPER", "FELONY", "MOON", "SUCKER", "PENGUIN", "WAFFLE", "MAESTRO", "NIGHT", "TRINITY", "DEITY", "MONKEY", "ARK", "SQUIRREL", "IRON", "BOUNCE", "FARM", "CHEF", "TROUGH", "NET", "TRAWL", "GLEE", "WATER", "SPORK", "PLOW", "FEED", "SOUFFLE", "ROUTE", "BAGEL", "MONTANA", "ANALYST", "AUTO", "WATCH", "PHOTO", "YARD", "SOURCE", "MONKEY", "SEAGULL", "TOLL", "SPAWN", "GOPHER", "CHIPMUNK", "SET", "CALENDAR", "ARTIST", "CHASER", "SCAN", "TOTE", "BEAM", "ENTOURAGE", "GENESIS", "WALK", "SPATULA", "RAGE", "FIRE", "MASTER" }; void setup_color_and_alias(struct lightningd *ld) { u8 der[PUBKEY_DER_LEN]; pubkey_to_der(der, &ld->id); if (!ld->rgb) /* You can't get much red by default */ ld->rgb = tal_dup_arr(ld, u8, der, 3, 0); if (!ld->alias) { u64 adjective, noun; char *name; memcpy(&adjective, der+3, sizeof(adjective)); memcpy(&noun, der+3+sizeof(adjective), sizeof(noun)); noun %= ARRAY_SIZE(codename_noun); adjective %= ARRAY_SIZE(codename_adjective); /* Only use 32 characters */ name = tal_fmt(ld, "%s%s", codename_adjective[adjective], codename_noun[noun]); #if DEVELOPER assert(strlen(name) < 32); int taillen = 31 - strlen(name); if (taillen > strlen(version())) taillen = strlen(version()); /* Fit as much of end of version() as possible */ tal_append_fmt(&name, "-%s", version() + strlen(version()) - taillen); #endif assert(strlen(name) <= 32); ld->alias = tal_arrz(ld, u8, 33); strcpy((char*)ld->alias, name); tal_free(name); } } void handle_early_opts(struct lightningd *ld, int argc, char *argv[]) { /* Load defaults. The actual values loaded here will be overwritten * later by opt_parse_from_config. */ setup_default_config(ld); /* Get any configdir/testnet options first. */ opt_early_parse_incomplete(argc, argv, opt_log_stderr_exit); /* Now look for config file, but only handle the early * options, others may be added on-demand */ opt_parse_from_config(ld, true); } void handle_opts(struct lightningd *ld, int argc, char *argv[]) { /* Now look for config file, but only handle non-early * options, early ones have been parsed in * handle_early_opts */ opt_parse_from_config(ld, false); /* Move to config dir, to save ourselves the hassle of path manip. */ if (chdir(ld->config_dir) != 0) { log_unusual(ld->log, "Creating configuration directory %s", ld->config_dir); if (mkdir(ld->config_dir, 0700) != 0) fatal("Could not make directory %s: %s", ld->config_dir, strerror(errno)); if (chdir(ld->config_dir) != 0) fatal("Could not change directory %s: %s", ld->config_dir, strerror(errno)); } opt_parse(&argc, argv, opt_log_stderr_exit); if (argc != 1) errx(1, "no arguments accepted"); /* We keep a separate variable rather than overriding use_proxy_always, * so listconfigs shows the correct thing. */ if (tal_count(ld->proposed_wireaddr) != 0 && all_tor_addresses(ld->proposed_wireaddr)) { ld->pure_tor_setup = true; if (!ld->proxyaddr) log_info(ld->log, "Pure Tor setup with no --proxy:" " you won't be able to make connections out"); } check_config(ld); } /* FIXME: This is a hack! Expose somehow in ccan/opt.*/ /* Returns string after first '-'. */ static const char *first_name(const char *names, unsigned *len) { *len = strcspn(names + 1, "|= "); return names + 1; } static const char *next_name(const char *names, unsigned *len) { names += *len; if (names[0] == ' ' || names[0] == '=' || names[0] == '\0') return NULL; return first_name(names + 1, len); } static void json_add_opt_addrs(struct json_stream *response, const char *name0, const struct wireaddr_internal *wireaddrs, const enum addr_listen_announce *listen_announce, enum addr_listen_announce ala) { for (size_t i = 0; i < tal_count(wireaddrs); i++) { if (listen_announce[i] != ala) continue; json_add_string(response, name0, fmt_wireaddr_internal(name0, wireaddrs+i)); } } static void add_config(struct lightningd *ld, struct json_stream *response, const struct opt_table *opt, const char *name, size_t len) { char *name0 = tal_strndup(response, name, len); const char *answer = NULL; if (opt->type & OPT_NOARG) { if (opt->cb == (void *)opt_usage_and_exit || opt->cb == (void *)version_and_exit /* These two show up as --network= */ || opt->cb == (void *)opt_set_testnet || opt->cb == (void *)opt_set_mainnet || opt->cb == (void *)opt_lightningd_usage || opt->cb == (void *)test_subdaemons_and_exit /* FIXME: we can't recover this. */ || opt->cb == (void *)opt_clear_plugins) { /* These are not important */ } else if (opt->cb == (void *)opt_set_bool) { const bool *b = opt->u.carg; answer = tal_fmt(name0, "%s", *b ? "true" : "false"); } else if (opt->cb == (void *)opt_set_invbool) { const bool *b = opt->u.carg; answer = tal_fmt(name0, "%s", !*b ? "true" : "false"); } else if (opt->cb == (void *)opt_set_offline) { answer = tal_fmt(name0, "%s", (!ld->reconnect && !ld->listen) ? "true" : "false"); } else { /* Insert more decodes here! */ assert(!"A noarg option was added but was not handled"); } } else if (opt->type & OPT_HASARG) { if (opt->desc == opt_hidden) { /* Ignore hidden options (deprecated) */ } else if (opt->show) { char *buf = tal_arr(name0, char, OPT_SHOW_LEN+1); opt->show(buf, opt->u.carg); if (streq(buf, "true") || streq(buf, "false") || strspn(buf, "0123456789.") == strlen(buf)) { /* Let pure numbers and true/false through as * literals. */ json_add_literal(response, name0, buf, strlen(buf)); return; } /* opt_show_charp surrounds with "", strip them */ if (strstarts(buf, "\"")) { buf[strlen(buf)-1] = '\0'; answer = buf + 1; } else answer = buf; } else if (opt->cb_arg == (void *)opt_set_talstr || opt->cb_arg == (void *)opt_set_charp) { const char *arg = *(char **)opt->u.carg; if (arg) answer = tal_fmt(name0, "%s", arg); } else if (opt->cb_arg == (void *)opt_set_rgb) { if (ld->rgb) answer = tal_hexstr(name0, ld->rgb, 3); } else if (opt->cb_arg == (void *)opt_set_alias) { answer = (const char *)ld->alias; } else if (opt->cb_arg == (void *)arg_log_to_file) { answer = ld->logfile; } else if (opt->cb_arg == (void *)opt_add_addr) { json_add_opt_addrs(response, name0, ld->proposed_wireaddr, ld->proposed_listen_announce, ADDR_LISTEN_AND_ANNOUNCE); return; } else if (opt->cb_arg == (void *)opt_add_bind_addr) { json_add_opt_addrs(response, name0, ld->proposed_wireaddr, ld->proposed_listen_announce, ADDR_LISTEN); return; } else if (opt->cb_arg == (void *)opt_add_announce_addr) { json_add_opt_addrs(response, name0, ld->proposed_wireaddr, ld->proposed_listen_announce, ADDR_ANNOUNCE); return; } else if (opt->cb_arg == (void *)opt_add_proxy_addr) { if (ld->proxyaddr) answer = fmt_wireaddr(name0, ld->proxyaddr); } else if (opt->cb_arg == (void *)opt_add_plugin) { json_add_opt_plugins(response, ld->plugins); } else if (opt->cb_arg == (void *)opt_add_plugin_dir || opt->cb_arg == (void *)opt_disable_plugin || opt->cb_arg == (void *)plugin_opt_set) { /* FIXME: We actually treat it as if they specified * --plugin for each one, so ignore these */ #if DEVELOPER } else if (strstarts(name, "dev-")) { /* Ignore dev settings */ #endif } else { /* Insert more decodes here! */ abort(); } } if (answer) { struct json_escaped *esc = json_escape(NULL, answer); json_add_escaped_string(response, name0, take(esc)); } tal_free(name0); } static struct command_result *json_listconfigs(struct command *cmd, const char *buffer, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { size_t i; struct json_stream *response = NULL; const jsmntok_t *configtok; if (!param(cmd, buffer, params, p_opt("config", param_tok, &configtok), NULL)) return command_param_failed(); if (!configtok) { response = json_stream_success(cmd); json_object_start(response, NULL); json_add_string(response, "# version", version()); } for (i = 0; i < opt_count; i++) { unsigned int len; const char *name; /* FIXME: Print out comment somehow? */ if (opt_table[i].type == OPT_SUBTABLE) continue; for (name = first_name(opt_table[i].names, &len); name; name = next_name(name, &len)) { /* Skips over first -, so just need to look for one */ if (name[0] != '-') continue; if (configtok && !memeq(buffer + configtok->start, configtok->end - configtok->start, name + 1, len - 1)) continue; if (!response) { response = json_stream_success(cmd); json_object_start(response, NULL); } add_config(cmd->ld, response, &opt_table[i], name+1, len-1); } } if (configtok && !response) { return command_fail(cmd, JSONRPC2_INVALID_PARAMS, "Unknown config option '%.*s'", json_tok_full_len(configtok), json_tok_full(buffer, configtok)); } json_object_end(response); return command_success(cmd, response); } static const struct json_command listconfigs_command = { "listconfigs", json_listconfigs, "List all configuration options, or with [config], just that one.", .verbose = "listconfigs [config]\n" "Outputs an object, with each field a config options\n" "(Option names which start with # are comments)\n" "With [config], object only has that field" }; AUTODATA(json_command, &listconfigs_command);