libplugin: allow display of default values.

This means we can see the values in listconfigs, even if we haven't set
them yet.

In particular, we now see the following:

* autoclean-cycle.value_int=3600
* bitcoin-rpcclienttimeout.value_int=60
* bitcoin-retry-timeout.value_int=60
* funder-max-their-funding.value_str=4294967295sat
* funder-per-channel-min.value_str=10000sat
* funder-reserve-tank.value_str=0sat
* funder-fund-probability.value_int=100

Changelog-Changed: plugins: libplugin now shows plugin option default values (where they're non-trivial)
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-05-14 12:54:01 +09:30 committed by Alex Myers
parent 6af32885fa
commit 757e6f8a9b
14 changed files with 206 additions and 52 deletions

View file

@ -581,21 +581,29 @@ static const char *init(struct plugin *p,
static char *cycle_seconds_option(struct plugin *plugin, const char *arg,
bool check_only,
void *unused)
u64 *cycle_seconds)
{
char *problem = u64_option(plugin, arg, check_only, &cycle_seconds);
char *problem = u64_option(plugin, arg, check_only, cycle_seconds);
if (problem || check_only)
return problem;
/* If timer is not running right now, reset it to new cycle_seconds */
if (cleantimer) {
tal_free(cleantimer);
cleantimer = plugin_timer(plugin, time_from_sec(cycle_seconds),
cleantimer = plugin_timer(plugin, time_from_sec(*cycle_seconds),
do_clean_timer, NULL);
}
return NULL;
}
static bool u64_jsonfmt_unless_zero(struct plugin *plugin,
struct json_stream *js, const char *fieldname, u64 *i)
{
if (!*i)
return false;
return u64_jsonfmt(plugin, js, fieldname, i);
}
static const struct plugin_command commands[] = { {
"autocleaninvoice",
"payment",
@ -628,30 +636,37 @@ int main(int argc, char *argv[])
"int",
"Perform cleanup every"
" given seconds",
cycle_seconds_option, NULL),
cycle_seconds_option, u64_jsonfmt,
&cycle_seconds),
plugin_option_dynamic("autoclean-succeededforwards-age",
"int",
"How old do successful forwards have to be before deletion (0 = never)",
u64_option, &timer_cinfo.subsystem_age[SUCCEEDEDFORWARDS]),
u64_option, u64_jsonfmt_unless_zero,
&timer_cinfo.subsystem_age[SUCCEEDEDFORWARDS]),
plugin_option_dynamic("autoclean-failedforwards-age",
"int",
"How old do failed forwards have to be before deletion (0 = never)",
u64_option, &timer_cinfo.subsystem_age[FAILEDFORWARDS]),
u64_option, u64_jsonfmt_unless_zero,
&timer_cinfo.subsystem_age[FAILEDFORWARDS]),
plugin_option_dynamic("autoclean-succeededpays-age",
"int",
"How old do successful pays have to be before deletion (0 = never)",
u64_option, &timer_cinfo.subsystem_age[SUCCEEDEDPAYS]),
u64_option, u64_jsonfmt_unless_zero,
&timer_cinfo.subsystem_age[SUCCEEDEDPAYS]),
plugin_option_dynamic("autoclean-failedpays-age",
"int",
"How old do failed pays have to be before deletion (0 = never)",
u64_option, &timer_cinfo.subsystem_age[FAILEDPAYS]),
u64_option, u64_jsonfmt_unless_zero,
&timer_cinfo.subsystem_age[FAILEDPAYS]),
plugin_option_dynamic("autoclean-paidinvoices-age",
"int",
"How old do paid invoices have to be before deletion (0 = never)",
u64_option, &timer_cinfo.subsystem_age[PAIDINVOICES]),
u64_option, u64_jsonfmt_unless_zero,
&timer_cinfo.subsystem_age[PAIDINVOICES]),
plugin_option_dynamic("autoclean-expiredinvoices-age",
"int",
"How old do expired invoices have to be before deletion (0 = never)",
u64_option, &timer_cinfo.subsystem_age[EXPIREDINVOICES]),
u64_option, u64_jsonfmt_unless_zero,
&timer_cinfo.subsystem_age[EXPIREDINVOICES]),
NULL);
}

View file

@ -1257,39 +1257,39 @@ int main(int argc, char *argv[])
plugin_option("bitcoin-datadir",
"string",
"-datadir arg for bitcoin-cli",
charp_option, &bitcoind->datadir),
charp_option, NULL, &bitcoind->datadir),
plugin_option("bitcoin-cli",
"string",
"bitcoin-cli pathname",
charp_option, &bitcoind->cli),
charp_option, NULL, &bitcoind->cli),
plugin_option("bitcoin-rpcuser",
"string",
"bitcoind RPC username",
charp_option, &bitcoind->rpcuser),
charp_option, NULL, &bitcoind->rpcuser),
plugin_option("bitcoin-rpcpassword",
"string",
"bitcoind RPC password",
charp_option, &bitcoind->rpcpass),
charp_option, NULL, &bitcoind->rpcpass),
plugin_option("bitcoin-rpcconnect",
"string",
"bitcoind RPC host to connect to",
charp_option, &bitcoind->rpcconnect),
charp_option, NULL, &bitcoind->rpcconnect),
plugin_option("bitcoin-rpcport",
"int",
"bitcoind RPC host's port",
charp_option, &bitcoind->rpcport),
charp_option, NULL, &bitcoind->rpcport),
plugin_option("bitcoin-rpcclienttimeout",
"int",
"bitcoind RPC timeout in seconds during HTTP requests",
u64_option, &bitcoind->rpcclienttimeout),
u64_option, u64_jsonfmt, &bitcoind->rpcclienttimeout),
plugin_option("bitcoin-retry-timeout",
"int",
"how long to keep retrying to contact bitcoind"
" before fatally exiting",
u64_option, &bitcoind->retry_timeout),
u64_option, u64_jsonfmt, &bitcoind->retry_timeout),
plugin_option_dev("dev-no-fake-fees",
"bool",
"Suppress fee faking for regtest",
bool_option, &bitcoind->dev_no_fake_fees),
bool_option, NULL, &bitcoind->dev_no_fake_fees),
NULL);
}

View file

@ -1839,11 +1839,11 @@ int main(int argc, char *argv[])
plugin_option("bookkeeper-dir",
"string",
"Location for bookkeeper records.",
charp_option, &datadir),
charp_option, NULL, &datadir),
plugin_option("bookkeeper-db",
"string",
"Location of the bookkeeper database",
charp_option, &db_dsn),
charp_option, NULL, &db_dsn),
NULL);
return 0;

View file

@ -1500,6 +1500,6 @@ int main(int argc, char *argv[])
NULL, 0,
plugin_option("fetchinvoice-noconnect", "flag",
"Don't try to connect directly to fetch an invoice.",
flag_option, &disable_connect),
flag_option, flag_jsonfmt, &disable_connect),
NULL);
}

View file

@ -1593,6 +1593,17 @@ static char *amount_option(struct plugin *plugin, const char *arg, bool check_on
return NULL;
}
static bool jsonfmt_amount_sat(struct plugin *plugin,
struct json_stream *js,
const char *fieldname,
struct amount_sat *sats)
{
/* We do not expose raw numbers for sats fields: raw numbers
* in our interface means MSAT! */
json_add_str_fmt(js, fieldname, "%"PRIu64"sat", sats->satoshis /* Raw: fmt */);
return true;
}
static char *option_lease_fee_base(struct plugin *plugin, const char *arg,
bool check_only,
struct funder_policy *policy)
@ -1673,6 +1684,15 @@ static char *amount_sat_or_u64_option(struct plugin *plugin,
return NULL;
}
static bool jsonfmt_policy_mod(struct plugin *plugin,
struct json_stream *js,
const char *fieldname,
u64 *amt)
{
json_add_u64(js, fieldname, *amt);
return true;
}
int main(int argc, char **argv)
{
setup_locale();
@ -1690,93 +1710,111 @@ int main(int argc, char **argv)
"string",
"Policy to use for dual-funding requests."
" [match, available, fixed]",
funding_option, &current_policy->opt),
funding_option,
jsonfmt_funding_option,
&current_policy->opt),
plugin_option("funder-policy-mod",
"string",
"Percent to apply policy at"
" (match/available); or amount to fund"
" (fixed)",
amount_sat_or_u64_option,
jsonfmt_policy_mod,
&current_policy->mod),
plugin_option("funder-min-their-funding",
"string",
"Minimum funding peer must open with"
" to activate our policy",
amount_option,
jsonfmt_amount_sat,
&current_policy->min_their_funding),
plugin_option("funder-max-their-funding",
"string",
"Maximum funding peer may open with"
" to activate our policy",
amount_option,
jsonfmt_amount_sat,
&current_policy->max_their_funding),
plugin_option("funder-per-channel-min",
"string",
"Minimum funding we'll add to a channel."
" If we can't meet this, we don't fund",
amount_option,
jsonfmt_amount_sat,
&current_policy->per_channel_min),
plugin_option("funder-per-channel-max",
"string",
"Maximum funding we'll add to a channel."
" We cap all contributions to this",
amount_option,
jsonfmt_amount_sat,
&current_policy->per_channel_max),
plugin_option("funder-reserve-tank",
"string",
"Amount of funds we'll always leave"
" available.",
amount_option,
jsonfmt_amount_sat,
&current_policy->reserve_tank),
plugin_option("funder-fuzz-percent",
"int",
"Percent to fuzz the policy contribution by."
" Defaults to 0%. Max is 100%",
u32_option,
u32_option, u32_jsonfmt,
&current_policy->fuzz_factor),
plugin_option("funder-fund-probability",
"int",
"Percent of requests to consider."
" Defaults to 100%. Setting to 0% will"
" disable dual-funding",
u32_option,
u32_option, u32_jsonfmt,
&current_policy->fund_probability),
plugin_option("funder-lease-requests-only",
"bool",
"Only fund lease requests. Defaults to"
" true if channel lease rates are"
" being advertised",
bool_option,
bool_option, bool_jsonfmt,
&current_policy->leases_only),
plugin_option("lease-fee-base-sat",
"string",
"Channel lease rates, base fee for leased"
" funds, in satoshi.",
option_lease_fee_base, current_policy),
option_lease_fee_base,
NULL,
current_policy),
plugin_option("lease-fee-basis",
"int",
"Channel lease rates, basis charged"
" for leased funds (per 10,000 satoshi.)",
option_lease_fee_basis, current_policy),
option_lease_fee_basis,
NULL,
current_policy),
plugin_option("lease-funding-weight",
"int",
"Channel lease rates, weight"
" we'll ask opening peer to pay for in"
" funding transaction",
option_lease_weight_max, current_policy),
option_lease_weight_max,
NULL,
current_policy),
plugin_option("channel-fee-max-base-msat",
"string",
"Channel lease rates, maximum channel"
" fee base we'll charge for funds"
" routed through a leased channel.",
option_channel_base, current_policy),
option_channel_base,
NULL,
current_policy),
plugin_option("channel-fee-max-proportional-thousandths",
"int",
"Channel lease rates, maximum"
" proportional fee (in thousandths, or ppt)"
" we'll charge for funds routed through a"
" leased channel. Note: 1ppt = 1,000ppm",
option_channel_fee_proportional_thousandths_max, current_policy),
option_channel_fee_proportional_thousandths_max,
NULL,
current_policy),
NULL);
tal_free(current_policy);

View file

@ -2,6 +2,7 @@
#include <assert.h>
#include <bitcoin/script.h>
#include <ccan/tal/str/str.h>
#include <common/json_stream.h>
#include <common/lease_rates.h>
#include <common/pseudorand.h>
#include <inttypes.h>
@ -39,6 +40,15 @@ char *funding_option(struct plugin *plugin, const char *arg, bool check_only, en
return NULL;
}
bool jsonfmt_funding_option(struct plugin *plugin,
struct json_stream *js,
const char *fieldname,
enum funder_opt *opt)
{
json_add_string(js, fieldname, funder_opt_name(*opt));
return true;
}
const char *funder_policy_desc(const tal_t *ctx,
const struct funder_policy *policy)
{

View file

@ -97,6 +97,12 @@ const char *funder_policy_desc(const tal_t *ctx,
char *funding_option(struct plugin *plugin, const char *arg, bool check_only,
enum funder_opt *opt);
/* Convert to JSON field */
bool jsonfmt_funding_option(struct plugin *plugin,
struct json_stream *js,
const char *fieldname,
enum funder_opt *opt);
/* Check policy settings, return error if fails */
char *funder_check_policy(const struct funder_policy *policy);
#endif /* LIGHTNING_PLUGINS_FUNDER_POLICY_H */

View file

@ -48,6 +48,10 @@ struct plugin_option {
* for validity (but must not make any changes!) */
char *(*handle)(struct plugin *plugin, const char *str, bool check_only,
void *arg);
/* Print an option (used to show the default value, if returns true) */
bool (*jsonfmt)(struct plugin *plugin, struct json_stream *js, const char *fieldname,
void *arg);
/* Arg for handle and jsonfmt */
void *arg;
/* If true, this option requires --developer to be enabled */
bool dev_only;
@ -1049,6 +1053,8 @@ handle_getmanifest(struct command *getmanifest_cmd,
json_add_string(params, "description", p->opts[i].description);
json_add_deprecated(params, "deprecated", p->opts[i].depr_start, p->opts[i].depr_end);
json_add_bool(params, "dynamic", p->opts[i].dynamic);
if (p->opts[i].jsonfmt)
p->opts[i].jsonfmt(p, params, "default", p->opts[i].arg);
json_object_end(params);
}
json_array_end(params);
@ -1463,6 +1469,46 @@ char *charp_option(struct plugin *plugin, const char *arg, bool check_only, char
return NULL;
}
bool u64_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname, u64 *i)
{
json_add_u64(js, fieldname, *i);
return true;
}
bool u32_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname, u32 *i)
{
json_add_u32(js, fieldname, *i);
return true;
}
bool u16_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname, u16 *i)
{
json_add_u32(js, fieldname, *i);
return true;
}
bool bool_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname, bool *i)
{
json_add_bool(js, fieldname, *i);
return true;
}
bool charp_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname, char **p)
{
if (!*p)
return false;
json_add_string(js, fieldname, *p);
return true;
}
bool flag_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname, bool *i)
{
/* Don't print if the default (false) */
if (!*i)
return false;
return bool_jsonfmt(plugin, js, fieldname, i);
}
static void setup_command_usage(struct plugin *p)
{
struct command *usage_cmd = tal(tmpctx, struct command);
@ -2099,6 +2145,7 @@ static struct plugin *new_plugin(const tal_t *ctx,
o.type = va_arg(ap, const char *);
o.description = va_arg(ap, const char *);
o.handle = va_arg(ap, char *(*)(struct plugin *, const char *str, bool check_only, void *arg));
o.jsonfmt = va_arg(ap, bool (*)(struct plugin *, struct json_stream *, const char *, void *arg));
o.arg = va_arg(ap, void *);
o.dev_only = va_arg(ap, int); /* bool gets promoted! */
o.depr_start = va_arg(ap, const char *);

View file

@ -416,11 +416,20 @@ static inline void *plugin_option_cb_check(char *(*set)(struct plugin *plugin,
return set;
}
/* Simply exists to check that `jsonfmt` to plugin_option* is correct type */
static inline void *plugin_option_jsonfmt_check(bool (*jsonfmt)(struct plugin *,
struct json_stream *,
const char *,
void *))
{
return jsonfmt;
}
/* Is --developer enabled? */
bool plugin_developer_mode(const struct plugin *plugin);
/* Macro to define arguments */
#define plugin_option_(name, type, description, set, arg, dev_only, depr_start, depr_end, dynamic) \
#define plugin_option_(name, type, description, set, jsonfmt, arg, dev_only, depr_start, depr_end, dynamic) \
(name), \
(type), \
(description), \
@ -428,23 +437,29 @@ bool plugin_developer_mode(const struct plugin *plugin);
(set), (arg), \
struct plugin *, \
const char *, bool)),\
plugin_option_jsonfmt_check(typesafe_cb_preargs(bool, void *, \
(jsonfmt), (arg), \
struct plugin *, \
struct json_stream *, \
const char *)), \
(arg), \
(dev_only), \
(depr_start), \
(depr_end), \
(dynamic)
#define plugin_option(name, type, description, set, arg) \
plugin_option_((name), (type), (description), (set), (arg), false, NULL, NULL, false)
/* jsonfmt can be NULL, but then default won't be printed */
#define plugin_option(name, type, description, set, jsonfmt, arg) \
plugin_option_((name), (type), (description), (set), (jsonfmt), (arg), false, NULL, NULL, false)
#define plugin_option_dev(name, type, description, set, arg) \
plugin_option_((name), (type), (description), (set), (arg), true, NULL, NULL, false)
#define plugin_option_dev(name, type, description, set, jsonfmt, arg) \
plugin_option_((name), (type), (description), (set), (jsonfmt), (arg), true, NULL, NULL, false)
#define plugin_option_dynamic(name, type, description, set, arg) \
plugin_option_((name), (type), (description), (set), (arg), false, NULL, NULL, true)
#define plugin_option_dynamic(name, type, description, set, jsonfmt, arg) \
plugin_option_((name), (type), (description), (set), (jsonfmt), (arg), false, NULL, NULL, true)
#define plugin_option_deprecated(name, type, description, depr_start, depr_end, set, arg) \
plugin_option_((name), (type), (description), (set), (arg), false, (depr_start), (depr_end), false)
#define plugin_option_deprecated(name, type, description, depr_start, depr_end, set, jsonfmt, arg) \
plugin_option_((name), (type), (description), (set), (jsonfmt), (arg), false, (depr_start), (depr_end), false)
/* Standard helpers */
char *u64_option(struct plugin *plugin, const char *arg, bool check_only, u64 *i);
@ -454,6 +469,21 @@ char *bool_option(struct plugin *plugin, const char *arg, bool check_only, bool
char *charp_option(struct plugin *plugin, const char *arg, bool check_only, char **p);
char *flag_option(struct plugin *plugin, const char *arg, bool check_only, bool *i);
bool u64_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname,
u64 *i);
bool u32_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname,
u32 *i);
bool u16_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname,
u16 *i);
bool bool_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname,
bool *i);
bool charp_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname,
char **p);
/* Usually equivalent to NULL, since flag must default to false be useful! */
bool flag_jsonfmt(struct plugin *plugin, struct json_stream *js, const char *fieldname,
bool *i);
/* The main plugin runner: append with 0 or more plugin_option(), then NULL. */
void NORETURN LAST_ARG_NULL plugin_main(char *argv[],
const char *(*init)(struct plugin *p,

View file

@ -1367,7 +1367,7 @@ int main(int argc, char *argv[])
notification_topics, ARRAY_SIZE(notification_topics),
plugin_option("disable-mpp", "flag",
"Disable multi-part payments.",
flag_option, &disablempp),
flag_option, flag_jsonfmt, &disablempp),
NULL);
io_poll_override(libplugin_pay_poll);
}

View file

@ -427,10 +427,10 @@ int main(int argc, char *argv[])
/* notification topics */ NULL, 0,
plugin_option("renepay-debug-mcf", "flag",
"Enable renepay MCF debug info.",
flag_option, &pay_plugin->debug_mcf),
flag_option, NULL, &pay_plugin->debug_mcf),
plugin_option("renepay-debug-payflow", "flag",
"Enable renepay payment flows debug info.",
flag_option, &pay_plugin->debug_payflow),
flag_option, NULL, &pay_plugin->debug_payflow),
NULL);
return 0;

View file

@ -15,6 +15,11 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
/* Generated stub for fromwire_node_id */
void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED)
{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); }
/* Generated stub for json_add_string */
void json_add_string(struct json_stream *js UNNEEDED,
const char *fieldname UNNEEDED,
const char *str TAKES UNNEEDED)
{ fprintf(stderr, "json_add_string called!\n"); abort(); }
/* Generated stub for pubkey_from_node_id */
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)
{ fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); }

View file

@ -9,7 +9,7 @@
static char *somearg;
static bool self_disable = false;
static bool dont_shutdown = false;
static int dynamic_opt = 7;
static u32 dynamic_opt = 7;
static struct command_result *get_ds_done(struct command *cmd,
const char *val,
@ -181,7 +181,7 @@ static struct command_result *json_checkthis(struct command *cmd,
static char *set_dynamic(struct plugin *plugin,
const char *arg,
bool check_only,
int *dynamic_opt)
u32 *dynamic_opt)
{
int val = atol(arg);
@ -298,23 +298,23 @@ int main(int argc, char *argv[])
plugin_option("somearg",
"string",
"Argument to print at init.",
charp_option, &somearg),
charp_option, charp_jsonfmt, &somearg),
plugin_option_deprecated("somearg-deprecated",
"string",
"Deprecated arg for init.",
CLN_NEXT_VERSION, NULL,
charp_option, &somearg),
charp_option, charp_jsonfmt, &somearg),
plugin_option("selfdisable",
"flag",
"Whether to disable.",
flag_option, &self_disable),
flag_option, flag_jsonfmt, &self_disable),
plugin_option("dont_shutdown",
"flag",
"Whether to timeout when asked to shutdown.",
flag_option, &dont_shutdown),
flag_option, flag_jsonfmt, &dont_shutdown),
plugin_option_dynamic("dynamicopt",
"int",
"Set me!",
set_dynamic, &dynamic_opt),
set_dynamic, u32_jsonfmt, &dynamic_opt),
NULL);
}

View file

@ -1561,6 +1561,8 @@ def test_libplugin(node_factory):
l1.daemon.wait_for_log("get_ds_bin_done: NOT FOUND")
# Check dynamic!
assert l1.rpc.listconfigs('dynamicopt')['configs']['dynamicopt']['value_int'] == 7
with pytest.raises(RpcError) as err:
l1.rpc.setconfig('dynamicopt', 4)
assert err.value.error['message'] == 'I don\'t like \\"even\\" numbers (valid JSON? Try {})!'
@ -1570,8 +1572,9 @@ def test_libplugin(node_factory):
assert err.value.error['message'] == 'I don\'t like \\"even\\" numbers (valid JSON? Try {})!'
l1.rpc.check(command_to_check='setconfig', config='dynamicopt', val=9)
# FIXME: libplugin doesn't populate defaults!
assert 'dynamicopt' not in l1.rpc.listconfigs()['configs']
# Unchanged!
assert l1.rpc.listconfigs('dynamicopt')['configs']['dynamicopt']['value_int'] == 7
l1.rpc.setconfig('dynamicopt', 9)
conf = l1.rpc.listconfigs('dynamicopt')['configs']['dynamicopt']