mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 06:41:44 +01:00
lightningd: setconfig command.
Currently only implemented for min-capacity-sat. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: JSON-RPC: new command `setconfig` allows a limited number of configuration settings to be changed without restart.
This commit is contained in:
parent
ecc030f12d
commit
6546be9757
8 changed files with 387 additions and 9 deletions
|
@ -89,6 +89,7 @@ MANPAGES := doc/lightning-cli.1 \
|
||||||
doc/lightning-sendonionmessage.7 \
|
doc/lightning-sendonionmessage.7 \
|
||||||
doc/lightning-sendpay.7 \
|
doc/lightning-sendpay.7 \
|
||||||
doc/lightning-setchannel.7 \
|
doc/lightning-setchannel.7 \
|
||||||
|
doc/lightning-setconfig.7 \
|
||||||
doc/lightning-setpsbtversion.7 \
|
doc/lightning-setpsbtversion.7 \
|
||||||
doc/lightning-sendcustommsg.7 \
|
doc/lightning-sendcustommsg.7 \
|
||||||
doc/lightning-signinvoice.7 \
|
doc/lightning-signinvoice.7 \
|
||||||
|
|
|
@ -122,6 +122,7 @@ Core Lightning Documentation
|
||||||
lightning-sendpay <lightning-sendpay.7.md>
|
lightning-sendpay <lightning-sendpay.7.md>
|
||||||
lightning-sendpsbt <lightning-sendpsbt.7.md>
|
lightning-sendpsbt <lightning-sendpsbt.7.md>
|
||||||
lightning-setchannel <lightning-setchannel.7.md>
|
lightning-setchannel <lightning-setchannel.7.md>
|
||||||
|
lightning-setconfig <lightning-setconfig.7.md>
|
||||||
lightning-setpsbtversion <lightning-setpsbtversion.7.md>
|
lightning-setpsbtversion <lightning-setpsbtversion.7.md>
|
||||||
lightning-signinvoice <lightning-signinvoice.7.md>
|
lightning-signinvoice <lightning-signinvoice.7.md>
|
||||||
lightning-signmessage <lightning-signmessage.7.md>
|
lightning-signmessage <lightning-signmessage.7.md>
|
||||||
|
|
59
doc/lightning-setconfig.7.md
Normal file
59
doc/lightning-setconfig.7.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
lightning-setconfig -- Dynamically change some config options
|
||||||
|
=============================================================
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
--------
|
||||||
|
|
||||||
|
**setconfig** *config* [*val*]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
-----------
|
||||||
|
|
||||||
|
The **setconfig** RPC command allows you set the (dynamic) configuration option named by `config`: options which take a value (as separate from simple flag options) also need a `val` parameter.
|
||||||
|
|
||||||
|
This new value will *also* be written at the end of the config file, for persistence across restarts.
|
||||||
|
|
||||||
|
You can see what options are dynamically adjustable using lightning-listconfigs(7). Note that you can also adjust existing options for stopped plugins; they will have an effect when the plugin is restarted.
|
||||||
|
|
||||||
|
|
||||||
|
RETURN VALUE
|
||||||
|
------------
|
||||||
|
|
||||||
|
[comment]: # (GENERATE-FROM-SCHEMA-START)
|
||||||
|
On success, an object containing **config** is returned. It is an object containing:
|
||||||
|
|
||||||
|
- **config** (string): name of the config variable which was set
|
||||||
|
- **source** (string): source of configuration setting (`file`:`linenum`)
|
||||||
|
- **dynamic** (boolean): whether this option is settable via setconfig (always *true*)
|
||||||
|
- **plugin** (string, optional): the plugin this configuration setting is for
|
||||||
|
- **set** (boolean, optional): for simple flag options
|
||||||
|
- **value\_str** (string, optional): for string options
|
||||||
|
- **value\_msat** (msat, optional): for msat options
|
||||||
|
- **value\_int** (integer, optional): for integer options
|
||||||
|
- **value\_bool** (boolean, optional): for boolean options
|
||||||
|
|
||||||
|
[comment]: # (GENERATE-FROM-SCHEMA-END)
|
||||||
|
|
||||||
|
ERRORS
|
||||||
|
------
|
||||||
|
|
||||||
|
The following error codes may occur:
|
||||||
|
- -32602: JSONRPC2\_INVALID\_PARAMS, i.e. the parameter is not dynamic, or the val was invalid.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
------
|
||||||
|
|
||||||
|
Rusty Russell <<rusty@rustcorp.com.au>> is mainly responsible for this
|
||||||
|
feature.
|
||||||
|
|
||||||
|
SEE ALSO
|
||||||
|
--------
|
||||||
|
|
||||||
|
lightningd-config(5), lightning-listconfigs(7)
|
||||||
|
|
||||||
|
RESOURCES
|
||||||
|
---------
|
||||||
|
|
||||||
|
Main web site: <https://github.com/ElementsProject/lightning>
|
||||||
|
|
||||||
|
[comment]: # ( SHA256STAMP:d61e4e6eea7b8c214644334ee194b273aef2a8a26465adfcd685be0d70653966)
|
|
@ -14,6 +14,8 @@ file (default: **$HOME/.lightning/config**) then a network-specific
|
||||||
configuration file (default: **$HOME/.lightning/testnet/config**). This can
|
configuration file (default: **$HOME/.lightning/testnet/config**). This can
|
||||||
be changed: see *--conf* and *--lightning-dir*.
|
be changed: see *--conf* and *--lightning-dir*.
|
||||||
|
|
||||||
|
Note that some configuration options, marked *dynamic*m can be changed at runtime: see lightning-setconfig(7).
|
||||||
|
|
||||||
General configuration files are processed first, then network-specific
|
General configuration files are processed first, then network-specific
|
||||||
ones, then command line options: later options override earlier ones
|
ones, then command line options: later options override earlier ones
|
||||||
except *addr* options and *log-level* with subsystems, which
|
except *addr* options and *log-level* with subsystems, which
|
||||||
|
@ -316,7 +318,7 @@ millionths, so 10000 is 1%, 1000 is 0.1%. Changing this value will only
|
||||||
affect new channels and not existing ones. If you want to change fees
|
affect new channels and not existing ones. If you want to change fees
|
||||||
for existing channels, use the RPC call lightning-setchannel(7).
|
for existing channels, use the RPC call lightning-setchannel(7).
|
||||||
|
|
||||||
* **min-capacity-sat**=*SATOSHI*
|
* **min-capacity-sat**=*SATOSHI* [*dynamic*]
|
||||||
|
|
||||||
Default: 10000. This value defines the minimal effective channel
|
Default: 10000. This value defines the minimal effective channel
|
||||||
capacity in satoshi to accept for channel opening requests. This will
|
capacity in satoshi to accept for channel opening requests. This will
|
||||||
|
|
15
doc/schemas/setconfig.request.json
Normal file
15
doc/schemas/setconfig.request.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"config"
|
||||||
|
],
|
||||||
|
"added": "v23.08",
|
||||||
|
"properties": {
|
||||||
|
"config": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"val": {}
|
||||||
|
}
|
||||||
|
}
|
62
doc/schemas/setconfig.schema.json
Normal file
62
doc/schemas/setconfig.schema.json
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"added": "v23.08",
|
||||||
|
"required": [
|
||||||
|
"config"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"config": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "config settings after completion",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"config",
|
||||||
|
"source",
|
||||||
|
"dynamic"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"config": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the config variable which was set"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "source of configuration setting (`file`:`linenum`)"
|
||||||
|
},
|
||||||
|
"plugin": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "the plugin this configuration setting is for"
|
||||||
|
},
|
||||||
|
"dynamic": {
|
||||||
|
"type": "boolean",
|
||||||
|
"enum": [
|
||||||
|
true
|
||||||
|
],
|
||||||
|
"description": "whether this option is settable via setconfig"
|
||||||
|
},
|
||||||
|
"set": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "for simple flag options"
|
||||||
|
},
|
||||||
|
"value_str": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "for string options"
|
||||||
|
},
|
||||||
|
"value_msat": {
|
||||||
|
"type": "msat",
|
||||||
|
"description": "for msat options"
|
||||||
|
},
|
||||||
|
"value_int": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "for integer options"
|
||||||
|
},
|
||||||
|
"value_bool": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "for boolean options"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,20 @@
|
||||||
#include <ccan/err/err.h>
|
#include <ccan/err/err.h>
|
||||||
#include <ccan/opt/opt.h>
|
#include <ccan/opt/opt.h>
|
||||||
#include <ccan/opt/private.h>
|
#include <ccan/opt/private.h>
|
||||||
|
#include <ccan/tal/grab_file/grab_file.h>
|
||||||
|
#include <ccan/tal/path/path.h>
|
||||||
#include <ccan/tal/str/str.h>
|
#include <ccan/tal/str/str.h>
|
||||||
#include <common/configdir.h>
|
#include <common/configdir.h>
|
||||||
#include <common/configvar.h>
|
#include <common/configvar.h>
|
||||||
#include <common/json_command.h>
|
#include <common/json_command.h>
|
||||||
#include <common/json_param.h>
|
#include <common/json_param.h>
|
||||||
#include <common/version.h>
|
#include <common/version.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <lightningd/jsonrpc.h>
|
#include <lightningd/jsonrpc.h>
|
||||||
#include <lightningd/options.h>
|
#include <lightningd/options.h>
|
||||||
#include <lightningd/plugin.h>
|
#include <lightningd/plugin.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
static void json_add_source(struct json_stream *result,
|
static void json_add_source(struct json_stream *result,
|
||||||
const char *fieldname,
|
const char *fieldname,
|
||||||
|
@ -113,10 +118,14 @@ static void json_add_configval(struct json_stream *result,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Config vars can have multiple names ("--large-channels|--wumbo"), but first
|
/* Config vars can have multiple names ("--large-channels|--wumbo"), but first
|
||||||
* is preferred */
|
* is preferred.
|
||||||
|
* wrap_object means we wrap json in an object of that name, otherwise outputs
|
||||||
|
* raw fields.
|
||||||
|
*/
|
||||||
static void json_add_config(struct lightningd *ld,
|
static void json_add_config(struct lightningd *ld,
|
||||||
struct json_stream *response,
|
struct json_stream *response,
|
||||||
bool always_include,
|
bool always_include,
|
||||||
|
bool wrap_object,
|
||||||
const struct opt_table *ot,
|
const struct opt_table *ot,
|
||||||
const char **names)
|
const char **names)
|
||||||
{
|
{
|
||||||
|
@ -139,18 +148,21 @@ static void json_add_config(struct lightningd *ld,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ot->type & OPT_NOARG) {
|
if (ot->type & OPT_NOARG) {
|
||||||
|
if (wrap_object)
|
||||||
json_object_start(response, names[0]);
|
json_object_start(response, names[0]);
|
||||||
json_add_bool(response, "set", cv != NULL);
|
json_add_bool(response, "set", cv != NULL);
|
||||||
json_add_source(response, "source", cv);
|
json_add_source(response, "source", cv);
|
||||||
json_add_config_plugin(response, ld->plugins, "plugin", ot);
|
json_add_config_plugin(response, ld->plugins, "plugin", ot);
|
||||||
if (ot->type & OPT_DYNAMIC)
|
if (ot->type & OPT_DYNAMIC)
|
||||||
json_add_bool(response, "dynamic", true);
|
json_add_bool(response, "dynamic", true);
|
||||||
|
if (wrap_object)
|
||||||
json_object_end(response);
|
json_object_end(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(ot->type & OPT_HASARG);
|
assert(ot->type & OPT_HASARG);
|
||||||
if (ot->type & OPT_MULTI) {
|
if (ot->type & OPT_MULTI) {
|
||||||
|
if (wrap_object)
|
||||||
json_object_start(response, names[0]);
|
json_object_start(response, names[0]);
|
||||||
json_array_start(response, configval_fieldname(ot));
|
json_array_start(response, configval_fieldname(ot));
|
||||||
while (cv) {
|
while (cv) {
|
||||||
|
@ -171,6 +183,7 @@ static void json_add_config(struct lightningd *ld,
|
||||||
json_add_config_plugin(response, ld->plugins, "plugin", ot);
|
json_add_config_plugin(response, ld->plugins, "plugin", ot);
|
||||||
if (ot->type & OPT_DYNAMIC)
|
if (ot->type & OPT_DYNAMIC)
|
||||||
json_add_bool(response, "dynamic", true);
|
json_add_bool(response, "dynamic", true);
|
||||||
|
if (wrap_object)
|
||||||
json_object_end(response);
|
json_object_end(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -180,12 +193,14 @@ static void json_add_config(struct lightningd *ld,
|
||||||
if (!val)
|
if (!val)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (wrap_object)
|
||||||
json_object_start(response, names[0]);
|
json_object_start(response, names[0]);
|
||||||
json_add_configval(response, configval_fieldname(ot), ot, val);
|
json_add_configval(response, configval_fieldname(ot), ot, val);
|
||||||
json_add_source(response, "source", cv);
|
json_add_source(response, "source", cv);
|
||||||
json_add_config_plugin(response, ld->plugins, "plugin", ot);
|
json_add_config_plugin(response, ld->plugins, "plugin", ot);
|
||||||
if (ot->type & OPT_DYNAMIC)
|
if (ot->type & OPT_DYNAMIC)
|
||||||
json_add_bool(response, "dynamic", true);
|
json_add_bool(response, "dynamic", true);
|
||||||
|
if (wrap_object)
|
||||||
json_object_end(response);
|
json_object_end(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +307,7 @@ modern:
|
||||||
}
|
}
|
||||||
/* We don't usually print dev or deprecated options, unless
|
/* We don't usually print dev or deprecated options, unless
|
||||||
* they explicitly ask, or they're set. */
|
* they explicitly ask, or they're set. */
|
||||||
json_add_config(cmd->ld, response, config != NULL,
|
json_add_config(cmd->ld, response, config != NULL, true,
|
||||||
&opt_table[i], names);
|
&opt_table[i], names);
|
||||||
}
|
}
|
||||||
json_object_end(response);
|
json_object_end(response);
|
||||||
|
@ -311,3 +326,172 @@ static const struct json_command listconfigs_command = {
|
||||||
"With [config], object only has that field"
|
"With [config], object only has that field"
|
||||||
};
|
};
|
||||||
AUTODATA(json_command, &listconfigs_command);
|
AUTODATA(json_command, &listconfigs_command);
|
||||||
|
|
||||||
|
static struct command_result *param_opt_dynamic_config(struct command *cmd,
|
||||||
|
const char *name,
|
||||||
|
const char *buffer,
|
||||||
|
const jsmntok_t *tok,
|
||||||
|
const struct opt_table **config)
|
||||||
|
{
|
||||||
|
struct command_result *ret;
|
||||||
|
|
||||||
|
ret = param_opt_config(cmd, name, buffer, tok, config);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (!((*config)->type & OPT_DYNAMIC))
|
||||||
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
|
"Not a dynamic config option");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: put in ccan/mem! */
|
||||||
|
static size_t memcount(const void *mem, size_t len, char c)
|
||||||
|
{
|
||||||
|
size_t count = 0;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
if (((char *)mem)[i] == c)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void configvar_append_file(struct lightningd *ld,
|
||||||
|
const char *fname,
|
||||||
|
enum configvar_src src,
|
||||||
|
const char *confline,
|
||||||
|
bool must_exist)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
size_t num_lines;
|
||||||
|
const char *buffer, *insert;
|
||||||
|
bool needs_term;
|
||||||
|
struct configvar *cv;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
|
||||||
|
fd = open(fname, O_RDWR|O_APPEND);
|
||||||
|
if (fd < 0) {
|
||||||
|
if (errno != ENOENT || must_exist)
|
||||||
|
fatal("Could not write to config %s: %s",
|
||||||
|
fname, strerror(errno));
|
||||||
|
fd = open(fname, O_RDWR|O_APPEND|O_CREAT, 0644);
|
||||||
|
if (fd < 0)
|
||||||
|
fatal("Could not create config file %s: %s",
|
||||||
|
fname, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: always nul terminates */
|
||||||
|
buffer = grab_fd(tmpctx, fd);
|
||||||
|
if (!buffer)
|
||||||
|
fatal("Error reading %s: %s", fname, strerror(errno));
|
||||||
|
|
||||||
|
num_lines = memcount(buffer, tal_bytelen(buffer)-1, '\n');
|
||||||
|
|
||||||
|
/* If there's a last character and it's not \n, add one */
|
||||||
|
if (tal_bytelen(buffer) == 1)
|
||||||
|
needs_term = false;
|
||||||
|
else
|
||||||
|
needs_term = (buffer[tal_bytelen(buffer)-2] != '\n');
|
||||||
|
|
||||||
|
/* Note: ctime() contains a \n! */
|
||||||
|
insert = tal_fmt(tmpctx, "%s# Inserted by setconfig %s%s\n",
|
||||||
|
needs_term ? "\n": "",
|
||||||
|
ctime(&now), confline);
|
||||||
|
if (write(fd, insert, strlen(insert)) != strlen(insert))
|
||||||
|
fatal("Could not write to config file %s: %s",
|
||||||
|
fname, strerror(errno));
|
||||||
|
|
||||||
|
cv = configvar_new(ld->configvars, src, fname, num_lines+2, confline);
|
||||||
|
configvar_unparsed(cv);
|
||||||
|
|
||||||
|
log_info(ld->log, "setconfig: %s %s (updated %s:%u)",
|
||||||
|
cv->optvar, cv->optarg ? cv->optarg : "SET",
|
||||||
|
cv->file, cv->linenum);
|
||||||
|
|
||||||
|
tal_arr_expand(&ld->configvars, cv);
|
||||||
|
configvar_finalize_overrides(ld->configvars);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void configvar_save(struct lightningd *ld, const char *confline)
|
||||||
|
{
|
||||||
|
/* If they used --conf then append to that */
|
||||||
|
if (ld->config_filename)
|
||||||
|
configvar_append_file(ld,
|
||||||
|
ld->config_filename,
|
||||||
|
CONFIGVAR_EXPLICIT_CONF,
|
||||||
|
confline, true);
|
||||||
|
else {
|
||||||
|
const char *fname;
|
||||||
|
|
||||||
|
fname = path_join(tmpctx, ld->config_netdir, "config");
|
||||||
|
configvar_append_file(ld,
|
||||||
|
fname,
|
||||||
|
CONFIGVAR_NETWORK_CONF,
|
||||||
|
confline,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct command_result *json_setconfig(struct command *cmd,
|
||||||
|
const char *buffer,
|
||||||
|
const jsmntok_t *obj UNNEEDED,
|
||||||
|
const jsmntok_t *params)
|
||||||
|
{
|
||||||
|
struct json_stream *response;
|
||||||
|
const struct opt_table *ot;
|
||||||
|
const char *val, **names, *confline;
|
||||||
|
unsigned int len;
|
||||||
|
char *err;
|
||||||
|
|
||||||
|
if (!param(cmd, buffer, params,
|
||||||
|
p_req("config", param_opt_dynamic_config, &ot),
|
||||||
|
p_opt("val", param_string, &val),
|
||||||
|
NULL))
|
||||||
|
return command_param_failed();
|
||||||
|
|
||||||
|
/* We don't handle DYNAMIC MULTI, at least yet! */
|
||||||
|
assert(!(ot->type & OPT_MULTI));
|
||||||
|
|
||||||
|
names = tal_arr(tmpctx, const char *, 1);
|
||||||
|
/* This includes leading -! */
|
||||||
|
names[0] = first_name(ot->names, &len) + 1;
|
||||||
|
|
||||||
|
if (ot->type & OPT_NOARG) {
|
||||||
|
if (val)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"%s does not take a value",
|
||||||
|
ot->names + 2);
|
||||||
|
confline = tal_strdup(tmpctx, names[0]);
|
||||||
|
err = ot->cb(ot->u.arg);
|
||||||
|
} else {
|
||||||
|
assert(ot->type & OPT_HASARG);
|
||||||
|
if (!val)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"%s requires a value",
|
||||||
|
ot->names + 2);
|
||||||
|
confline = tal_fmt(tmpctx, "%s=%s", names[0], val);
|
||||||
|
err = ot->cb_arg(val, ot->u.arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"Error setting %s: %s", ot->names + 2, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
configvar_save(cmd->ld, confline);
|
||||||
|
|
||||||
|
response = json_stream_success(cmd);
|
||||||
|
json_object_start(response, "config");
|
||||||
|
json_add_string(response, "config", names[0]);
|
||||||
|
json_add_config(cmd->ld, response, true, false, ot, names);
|
||||||
|
json_object_end(response);
|
||||||
|
return command_success(cmd, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct json_command setconfig_command = {
|
||||||
|
"setconfig",
|
||||||
|
"utility",
|
||||||
|
json_setconfig,
|
||||||
|
"Set a dynamically-adjustable config."
|
||||||
|
};
|
||||||
|
AUTODATA(json_command, &setconfig_command);
|
||||||
|
|
|
@ -3396,3 +3396,57 @@ def test_fast_shutdown(node_factory):
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
|
def test_setconfig(node_factory):
|
||||||
|
l1, l2 = node_factory.line_graph(2, fundchannel=False)
|
||||||
|
configfile = os.path.join(l2.daemon.opts.get("lightning-dir"), TEST_NETWORK, 'config')
|
||||||
|
|
||||||
|
assert (l2.rpc.listconfigs('min-capacity-sat')['configs']
|
||||||
|
== {'min-capacity-sat':
|
||||||
|
{'source': 'default',
|
||||||
|
'value_int': 10000,
|
||||||
|
'dynamic': True}})
|
||||||
|
|
||||||
|
with pytest.raises(RpcError, match='requires a value'):
|
||||||
|
l2.rpc.setconfig('min-capacity-sat')
|
||||||
|
|
||||||
|
with pytest.raises(RpcError, match='requires a value'):
|
||||||
|
l2.rpc.setconfig(config='min-capacity-sat')
|
||||||
|
|
||||||
|
with pytest.raises(RpcError, match='is not a number'):
|
||||||
|
l2.rpc.setconfig(config='min-capacity-sat', val="abcd")
|
||||||
|
|
||||||
|
ret = l2.rpc.setconfig(config='min-capacity-sat', val=500000)
|
||||||
|
assert ret == {'config':
|
||||||
|
{'config': 'min-capacity-sat',
|
||||||
|
'source': '{}:2'.format(configfile),
|
||||||
|
'value_int': 500000,
|
||||||
|
'dynamic': True}}
|
||||||
|
|
||||||
|
with open(configfile, 'r') as f:
|
||||||
|
lines = f.read().splitlines()
|
||||||
|
assert lines[0].startswith('# Inserted by setconfig ')
|
||||||
|
assert lines[1] == 'min-capacity-sat=500000'
|
||||||
|
assert len(lines) == 2
|
||||||
|
|
||||||
|
# Now we need to meet minumum
|
||||||
|
with pytest.raises(RpcError, match='which is below 500000sat'):
|
||||||
|
l1.fundchannel(l2, 400000)
|
||||||
|
|
||||||
|
l1.fundchannel(l2, 10**6)
|
||||||
|
l1.rpc.close(l2.info['id'])
|
||||||
|
|
||||||
|
# It's persistent!
|
||||||
|
l2.restart()
|
||||||
|
|
||||||
|
assert (l2.rpc.listconfigs('min-capacity-sat')['configs']
|
||||||
|
== {'min-capacity-sat':
|
||||||
|
{'source': '{}:2'.format(configfile),
|
||||||
|
'value_int': 500000,
|
||||||
|
'dynamic': True}})
|
||||||
|
|
||||||
|
# Still need to meet minumum
|
||||||
|
l1.connect(l2)
|
||||||
|
with pytest.raises(RpcError, match='which is below 500000sat'):
|
||||||
|
l1.fundchannel(l2, 400000)
|
||||||
|
|
Loading…
Add table
Reference in a new issue