mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-20 13:54:36 +01:00
cli: add -c/--commando support.
It's easier to type: ``` lightning-cli --commando=03ce2d830369fc903ffec52ca1d7aba095c3cf5d17175b6c9a3ff058f6aece37bc:V08OylkJ2ZZPClAXbTaxrXJ9YpKnmucJxcQI-wvIGiE9MA== invoice any "Invoice Label" "Invoice Description" lightning-cli --commando=03ce2d830369fc903ffec52ca1d7aba095c3cf5d17175b6c9a3ff058f6aece37bc:V08OylkJ2ZZPClAXbTaxrXJ9YpKnmucJxcQI-wvIGiE9MA== commando amount_msat=100000 label="invoice label" description="invoice description" ``` Than: ``` commando 03ce2d830369fc903ffec52ca1d7aba095c3cf5d17175b6c9a3ff058f6aece37bc invoice '["any", "Invoice Label", "Invoice Description"]' V08OylkJ2ZZPClAXbTaxrXJ9YpKnmucJxcQI-wvIGiE9MA== commando 03ce2d830369fc903ffec52ca1d7aba095c3cf5d17175b6c9a3ff058f6aece37bc invoice '{"amount_msat": "100000", "label": "invoice label", "description": "invoice description"}' V08OylkJ2ZZPClAXbTaxrXJ9YpKnmucJxcQI-wvIGiE9MA== ``` Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: cli: `--commando=peerid:rune` (or `-c peerid:rune`) as convenient shortcut for running commando commands.
This commit is contained in:
parent
3f0c5b985b
commit
404e961bad
3 changed files with 183 additions and 6 deletions
|
@ -12,6 +12,7 @@
|
|||
#include <ccan/tal/str/str.h>
|
||||
#include <common/configdir.h>
|
||||
#include <common/json_command.h>
|
||||
#include <common/node_id.h>
|
||||
#include <common/status_levels.h>
|
||||
#include <common/utils.h>
|
||||
#include <common/version.h>
|
||||
|
@ -613,6 +614,29 @@ static void opt_log_stderr_exit_usage(const char *fmt, ...)
|
|||
exit(ERROR_USAGE);
|
||||
}
|
||||
|
||||
struct commando {
|
||||
const char *peer_id;
|
||||
const char *rune;
|
||||
};
|
||||
|
||||
static char *opt_set_commando(const char *arg, struct commando **commando)
|
||||
{
|
||||
size_t idlen = strcspn(arg, ":");
|
||||
*commando = tal(NULL, struct commando);
|
||||
|
||||
/* We don't use common/node_id.c here, to keep dependencies minimal */
|
||||
if (idlen != PUBKEY_CMPR_LEN * 2)
|
||||
return "Invalid peer id";
|
||||
(*commando)->peer_id = tal_strndup(*commando, arg, idlen);
|
||||
|
||||
if (arg[idlen] == '\0')
|
||||
(*commando)->rune = NULL;
|
||||
else
|
||||
(*commando)->rune = tal_strdup(*commando, arg + idlen + 1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
setup_locale();
|
||||
|
@ -633,6 +657,7 @@ int main(int argc, char *argv[])
|
|||
enum log_level notification_level = LOG_INFORM;
|
||||
bool last_was_progress = false;
|
||||
char *command = NULL, *filter = NULL;
|
||||
struct commando *commando = NULL;
|
||||
|
||||
err_set_progname(argv[0]);
|
||||
jsmn_init(&parser);
|
||||
|
@ -665,12 +690,18 @@ int main(int argc, char *argv[])
|
|||
opt_register_arg("-l|--filter", opt_set_charp,
|
||||
opt_show_charp, &filter,
|
||||
"Set JSON reply filter");
|
||||
opt_register_arg("-c|--commando", opt_set_commando,
|
||||
NULL, &commando,
|
||||
"Send this as a commando command to nodeid:rune");
|
||||
|
||||
opt_register_version();
|
||||
|
||||
opt_early_parse(argc, argv, opt_log_stderr_exit_usage);
|
||||
opt_parse(&argc, argv, opt_log_stderr_exit_usage);
|
||||
|
||||
/* Make sure this is parented correctly if set! */
|
||||
tal_steal(ctx, commando);
|
||||
|
||||
method = argv[1];
|
||||
if (!method) {
|
||||
char *usage = opt_usage(argv[0], NULL);
|
||||
|
@ -682,7 +713,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
/* Launch a manpage if we have a help command with an argument. We do
|
||||
* not need to have lightningd running in this case. */
|
||||
if (streq(method, "help") && format == DEFAULT_FORMAT && argc >= 3) {
|
||||
if (streq(method, "help") && format == DEFAULT_FORMAT && argc >= 3 && !commando) {
|
||||
command = argv[2];
|
||||
char *page = tal_fmt(ctx, "lightning-%s", command);
|
||||
|
||||
|
@ -724,16 +755,34 @@ int main(int argc, char *argv[])
|
|||
else
|
||||
idstr = tal_fmt(ctx, "cli:%s#%i", method, getpid());
|
||||
|
||||
if (notification_level <= LOG_LEVEL_MAX)
|
||||
/* FIXME: commando should support notifications! */
|
||||
if (notification_level <= LOG_LEVEL_MAX && !commando)
|
||||
enable_notifications(fd);
|
||||
|
||||
cmd = tal_fmt(ctx,
|
||||
"{ \"jsonrpc\" : \"2.0\", \"method\" : \"%s\", \"id\" : \"%s\",",
|
||||
json_escape(ctx, method)->s, idstr);
|
||||
if (filter)
|
||||
commando ? "commando" : json_escape(ctx, method)->s,
|
||||
idstr);
|
||||
if (filter && !commando)
|
||||
tal_append_fmt(&cmd, "\"filter\": %s,", filter);
|
||||
tal_append_fmt(&cmd, " \"params\" :");
|
||||
|
||||
if (commando) {
|
||||
tal_append_fmt(&cmd, "{"
|
||||
" \"peer_id\": \"%s\","
|
||||
" \"method\": \"%s\",",
|
||||
commando->peer_id,
|
||||
json_escape(ctx, method)->s);
|
||||
if (filter) {
|
||||
tal_append_fmt(&cmd, "\"filter\": %s,", filter);
|
||||
}
|
||||
if (commando->rune) {
|
||||
tal_append_fmt(&cmd, " \"rune\": \"%s\",",
|
||||
commando->rune);
|
||||
}
|
||||
tal_append_fmt(&cmd, " \"params\": ");
|
||||
}
|
||||
|
||||
if (input == DEFAULT_INPUT) {
|
||||
/* Hacky autodetect; only matters if more than single arg */
|
||||
if (argc > 2 && strchr(argv[2], '='))
|
||||
|
@ -764,6 +813,10 @@ int main(int argc, char *argv[])
|
|||
tal_append_fmt(&cmd, "] }");
|
||||
}
|
||||
|
||||
/* For commando, "params" we just populated is inside real "params" */
|
||||
if (commando)
|
||||
tal_append_fmt(&cmd, "}");
|
||||
|
||||
toks = json_parse_simple(ctx, cmd, strlen(cmd));
|
||||
if (toks == NULL)
|
||||
errx(ERROR_USAGE,
|
||||
|
|
|
@ -69,7 +69,7 @@ field without parsing JSON.
|
|||
If *LEVEL* is 'none', then never print out notifications. Otherwise,
|
||||
print out notifications of *LEVEL* or above (one of `io`, `debug`,
|
||||
`info` (the default), `unusual` or `broken`: they are prefixed with `#
|
||||
`.
|
||||
`. (Note: currently not supported with `--commando`).
|
||||
|
||||
* **--filter**/**-l**=*JSON*
|
||||
|
||||
|
@ -84,12 +84,21 @@ be changed using `-F`, `-R`, `-J`, `-H` etc.
|
|||
|
||||
Print version number to standard output and exit.
|
||||
|
||||
* **allow-deprecated-apis**=*BOOL*
|
||||
* **--allow-deprecated-apis**=*BOOL*
|
||||
|
||||
Enable deprecated options. It defaults to *true*, but you should set
|
||||
it to *false* when testing to ensure that an upgrade won't break your
|
||||
configuration.
|
||||
|
||||
* **--commando**/**-c**=**peerid**:**rune**
|
||||
|
||||
Convenience option to indicate that this command should be wrapped
|
||||
in a `commando` command to be sent to the connected peer with id
|
||||
`peerid`, using rune `rune`. This also means that any `--filter` is
|
||||
handed via commando to the remote peer to reduce its output (which it
|
||||
will do it it is v23.02 or newer), rather than trying to do so
|
||||
locally. Note that currently `-N` is not supported by commando.
|
||||
|
||||
COMMANDS
|
||||
--------
|
||||
|
||||
|
|
|
@ -1044,6 +1044,121 @@ def test_cli(node_factory):
|
|||
assert [l for l in lines if not re.search(r'^help\[[0-9]*\].', l)] == ['format-hint=simple']
|
||||
|
||||
|
||||
def test_cli_commando(node_factory):
|
||||
l1, l2 = node_factory.line_graph(2, fundchannel=False,
|
||||
opts={'log-level': 'io'})
|
||||
rune = l2.rpc.commando_rune()['rune']
|
||||
|
||||
# Invalid peer id.
|
||||
val = subprocess.run(['cli/lightning-cli',
|
||||
'--commando=00',
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'help'])
|
||||
assert val.returncode == 3
|
||||
|
||||
# Valid peer id, but needs rune!
|
||||
val = subprocess.run(['cli/lightning-cli',
|
||||
'--commando={}'.format(l2.info['id']),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'help'])
|
||||
assert val.returncode == 1
|
||||
|
||||
# This works!
|
||||
out = subprocess.check_output(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'help']).decode('utf-8')
|
||||
# Test some known output.
|
||||
assert 'help [command]\n List available commands, or give verbose help on one {command}' in out
|
||||
|
||||
# Check JSON id is as expected
|
||||
l1.daemon.wait_for_log(r'jsonrpc#[0-9]*: "cli:help#[0-9]*"\[IN\]')
|
||||
|
||||
# And through l2...
|
||||
l2.daemon.wait_for_log(r'jsonrpc#[0-9]*: "cli:help#[0-9]*/cln:commando#[0-9]*/commando:help#[0-9]*"\[IN\]')
|
||||
|
||||
# Test keyword input (forced)
|
||||
out = subprocess.check_output(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'-J', '-k',
|
||||
'help', 'command=help']).decode('utf-8')
|
||||
j, _ = json.JSONDecoder().raw_decode(out)
|
||||
assert 'help [command]' in j['help'][0]['verbose']
|
||||
|
||||
# Test ordered input (forced)
|
||||
out = subprocess.check_output(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'-J', '-o',
|
||||
'help', 'help']).decode('utf-8')
|
||||
j, _ = json.JSONDecoder().raw_decode(out)
|
||||
assert 'help [command]' in j['help'][0]['verbose']
|
||||
|
||||
# Test filtering
|
||||
out = subprocess.check_output(['cli/lightning-cli',
|
||||
'-c', '{}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'-J', '--filter={"help":[{"command":true}]}',
|
||||
'help', 'help']).decode('utf-8')
|
||||
j, _ = json.JSONDecoder().raw_decode(out)
|
||||
assert j == {'help': [{'command': 'help [command]'}]}
|
||||
|
||||
# Test missing parameters.
|
||||
try:
|
||||
# This will error due to missing parameters.
|
||||
# We want to check if lightningd will crash.
|
||||
out = subprocess.check_output(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'-J', '-o',
|
||||
'sendpay']).decode('utf-8')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Test it escapes JSON completely in both method and params.
|
||||
# cli turns " into \", reply turns that into \\\".
|
||||
out = subprocess.run(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'x"[]{}'],
|
||||
stdout=subprocess.PIPE)
|
||||
assert 'Unknown command' in out.stdout.decode('utf-8')
|
||||
|
||||
subprocess.check_output(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'invoice', '123000', 'l"[]{}', 'd"[]{}']).decode('utf-8')
|
||||
# Check label is correct, and also that cli's keyword parsing works.
|
||||
out = subprocess.check_output(['cli/lightning-cli',
|
||||
'--commando={}:{}'.format(l2.info['id'], rune),
|
||||
'--network={}'.format(TEST_NETWORK),
|
||||
'--lightning-dir={}'
|
||||
.format(l1.daemon.lightning_dir),
|
||||
'-k',
|
||||
'listinvoices', 'label=l"[]{}']).decode('utf-8')
|
||||
j = json.loads(out)
|
||||
assert only_one(j['invoices'])['label'] == 'l"[]{}'
|
||||
|
||||
|
||||
def test_daemon_option(node_factory):
|
||||
"""
|
||||
Make sure --daemon at least vaguely works!
|
||||
|
|
Loading…
Add table
Reference in a new issue