mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-23 06:55:13 +01:00
commando: add commando-rune command.
Can both mint new runes, and add one or more restrictions to existing ones. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
cf4374c4ed
commit
419cb60b1b
2 changed files with 182 additions and 2 deletions
|
@ -520,11 +520,152 @@ static struct command_result *json_commando(struct command *cmd,
|
|||
return send_more_cmd(cmd, NULL, NULL, outgoing);
|
||||
}
|
||||
|
||||
static struct command_result *param_rune(struct command *cmd, const char *name,
|
||||
const char * buffer, const jsmntok_t *tok,
|
||||
struct rune **rune)
|
||||
{
|
||||
*rune = rune_from_base64n(cmd, buffer + tok->start, tok->end - tok->start);
|
||||
if (!*rune)
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"should be base64 string");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct rune_restr **readonly_restrictions(const tal_t *ctx)
|
||||
{
|
||||
struct rune_restr **restrs = tal_arr(ctx, struct rune_restr *, 2);
|
||||
|
||||
/* Any list*, get*, or summary:
|
||||
* method^list|method^get|method=summary
|
||||
*/
|
||||
restrs[0] = rune_restr_new(restrs);
|
||||
rune_restr_add_altern(restrs[0],
|
||||
take(rune_altern_new(NULL,
|
||||
"method",
|
||||
RUNE_COND_BEGINS,
|
||||
"list")));
|
||||
rune_restr_add_altern(restrs[0],
|
||||
take(rune_altern_new(NULL,
|
||||
"method",
|
||||
RUNE_COND_BEGINS,
|
||||
"get")));
|
||||
rune_restr_add_altern(restrs[0],
|
||||
take(rune_altern_new(NULL,
|
||||
"method",
|
||||
RUNE_COND_EQUAL,
|
||||
"summary")));
|
||||
/* But not listdatastore!
|
||||
* method/listdatastore
|
||||
*/
|
||||
restrs[1] = rune_restr_new(restrs);
|
||||
rune_restr_add_altern(restrs[1],
|
||||
take(rune_altern_new(NULL,
|
||||
"method",
|
||||
RUNE_COND_NOT_EQUAL,
|
||||
"listdatastore")));
|
||||
|
||||
return restrs;
|
||||
}
|
||||
|
||||
static struct command_result *param_restrictions(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
struct rune_restr ***restrs)
|
||||
{
|
||||
if (json_tok_streq(buffer, tok, "readonly"))
|
||||
*restrs = readonly_restrictions(cmd);
|
||||
else if (tok->type == JSMN_ARRAY) {
|
||||
size_t i;
|
||||
const jsmntok_t *t;
|
||||
|
||||
*restrs = tal_arr(cmd, struct rune_restr *, tok->size);
|
||||
json_for_each_arr(i, t, tok) {
|
||||
(*restrs)[i] = rune_restr_from_string(*restrs,
|
||||
buffer + t->start,
|
||||
t->end - t->start);
|
||||
if (!(*restrs)[i])
|
||||
return command_fail_badparam(cmd, name, buffer, t,
|
||||
"not a valid restriction");
|
||||
}
|
||||
} else {
|
||||
*restrs = tal_arr(cmd, struct rune_restr *, 1);
|
||||
(*restrs)[0] = rune_restr_from_string(*restrs,
|
||||
buffer + tok->start,
|
||||
tok->end - tok->start);
|
||||
if (!(*restrs)[0])
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"not a valid restriction");
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct command_result *reply_with_rune(struct command *cmd,
|
||||
const char *buf UNUSED,
|
||||
const jsmntok_t *result UNUSED,
|
||||
struct rune *rune)
|
||||
{
|
||||
struct json_stream *js = jsonrpc_stream_success(cmd);
|
||||
|
||||
json_add_string(js, "rune", rune_to_base64(tmpctx, rune));
|
||||
json_add_string(js, "unique_id", rune->unique_id);
|
||||
return command_finished(cmd, js);
|
||||
}
|
||||
|
||||
static struct command_result *json_commando_rune(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct rune *rune;
|
||||
struct rune_restr **restrs;
|
||||
struct out_req *req;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_opt("rune", param_rune, &rune),
|
||||
p_opt("restrictions", param_restrictions, &restrs),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
if (rune) {
|
||||
for (size_t i = 0; i < tal_count(restrs); i++)
|
||||
rune_add_restr(rune, restrs[i]);
|
||||
return reply_with_rune(cmd, NULL, NULL, rune);
|
||||
}
|
||||
|
||||
rune = rune_derive_start(cmd, master_rune,
|
||||
tal_fmt(tmpctx, "%"PRIu64,
|
||||
rune_counter ? *rune_counter : 0));
|
||||
for (size_t i = 0; i < tal_count(restrs); i++)
|
||||
rune_add_restr(rune, restrs[i]);
|
||||
|
||||
/* Now update datastore, before returning rune */
|
||||
req = jsonrpc_request_start(plugin, cmd, "datastore",
|
||||
reply_with_rune, forward_error, rune);
|
||||
json_array_start(req->js, "key");
|
||||
json_add_string(req->js, NULL, "commando");
|
||||
json_add_string(req->js, NULL, "rune_counter");
|
||||
json_array_end(req->js);
|
||||
if (rune_counter) {
|
||||
(*rune_counter)++;
|
||||
json_add_string(req->js, "mode", "must-replace");
|
||||
} else {
|
||||
/* This used to say "🌩🤯🧨🔫!" but our log filters are too strict :( */
|
||||
plugin_log(plugin, LOG_INFORM, "Commando powers enabled: BOOM!");
|
||||
rune_counter = tal(plugin, u64);
|
||||
*rune_counter = 1;
|
||||
json_add_string(req->js, "mode", "must-create");
|
||||
}
|
||||
json_add_u64(req->js, "string", *rune_counter);
|
||||
return send_outreq(plugin, req);
|
||||
}
|
||||
|
||||
#if DEVELOPER
|
||||
static void memleak_mark_globals(struct plugin *p, struct htable *memtable)
|
||||
{
|
||||
memleak_remove_region(memtable, outgoing_commands, tal_bytelen(outgoing_commands));
|
||||
memleak_remove_region(memtable, incoming_commands, tal_bytelen(incoming_commands));
|
||||
memleak_remove_region(memtable, master_rune, sizeof(*master_rune));
|
||||
if (rune_counter)
|
||||
memleak_remove_region(memtable, rune_counter, sizeof(*rune_counter));
|
||||
}
|
||||
|
@ -578,7 +719,13 @@ static const struct plugin_command commands[] = { {
|
|||
"Send a commando message to a direct peer, wait for response",
|
||||
"Sends {peer_id} {method} with optional {params} and {rune}",
|
||||
json_commando,
|
||||
}
|
||||
}, {
|
||||
"commando-rune",
|
||||
"utility",
|
||||
"Create or restrict a rune",
|
||||
"Takes an optional {rune} with optional {restrictions} and returns {rune}",
|
||||
json_commando_rune,
|
||||
},
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
|
|
@ -2546,7 +2546,7 @@ def test_plugin_shutdown(node_factory):
|
|||
'test_libplugin: failed to self-terminate in time, killing.'])
|
||||
|
||||
|
||||
def test_commando(node_factory):
|
||||
def test_commando(node_factory, executor):
|
||||
l1, l2 = node_factory.line_graph(2, fundchannel=False)
|
||||
|
||||
# This works
|
||||
|
@ -2610,3 +2610,36 @@ def test_commando(node_factory):
|
|||
'channel': '1x2x3'}],
|
||||
'payment_hash': '00' * 32}})
|
||||
assert exc_info.value.error['data']['erring_index'] == 0
|
||||
|
||||
|
||||
def test_commando_rune(node_factory):
|
||||
l1, l2 = node_factory.line_graph(2, fundchannel=False)
|
||||
|
||||
# l1's commando secret is 1241faef85297127c2ac9bde95421b2c51e5218498ae4901dc670c974af4284b.
|
||||
# I put that into a test node's commando.py to generate these runes (modified readonly to match ours):
|
||||
# $ l1-cli commando-rune
|
||||
# "rune": "zKc2W88jopslgUBl0UE77aEe5PNCLn5WwqSusU_Ov3A9MA=="
|
||||
# $ l1-cli commando-rune restrictions=readonly
|
||||
# "rune": "1PJnoR9a7u4Bhglj2s7rVOWqRQnswIwUoZrDVMKcLTY9MSZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl"
|
||||
# $ l1-cli commando-rune restrictions='time>1656675211'
|
||||
# "rune": "RnlWC4lwBULFaObo6ZP8jfqYRyTbfWPqcMT3qW-Wmso9MiZ0aW1lPjE2NTY2NzUyMTE="
|
||||
# $ l1-cli commando-rune restrictions='["id^022d223620a359a47ff7","method=listpeers"]'
|
||||
# "rune": "lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz"
|
||||
# $ l1-cli commando-rune lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz 'pnamelevel!|pnamelevel/io'
|
||||
# "rune": "Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8="
|
||||
|
||||
rune1 = l1.rpc.commando_rune()
|
||||
assert rune1['rune'] == 'zKc2W88jopslgUBl0UE77aEe5PNCLn5WwqSusU_Ov3A9MA=='
|
||||
assert rune1['unique_id'] == '0'
|
||||
rune2 = l1.rpc.commando_rune(restrictions="readonly")
|
||||
assert rune2['rune'] == '1PJnoR9a7u4Bhglj2s7rVOWqRQnswIwUoZrDVMKcLTY9MSZtZXRob2RebGlzdHxtZXRob2ReZ2V0fG1ldGhvZD1zdW1tYXJ5Jm1ldGhvZC9saXN0ZGF0YXN0b3Jl'
|
||||
assert rune2['unique_id'] == '1'
|
||||
rune3 = l1.rpc.commando_rune(restrictions="time>1656675211")
|
||||
assert rune3['rune'] == 'RnlWC4lwBULFaObo6ZP8jfqYRyTbfWPqcMT3qW-Wmso9MiZ0aW1lPjE2NTY2NzUyMTE='
|
||||
assert rune3['unique_id'] == '2'
|
||||
rune4 = l1.rpc.commando_rune(restrictions=["id^022d223620a359a47ff7", "method=listpeers"])
|
||||
assert rune4['rune'] == 'lXFWzb51HjWxKV5TmfdiBgd74w0moeyChj3zbLoxmws9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJz'
|
||||
assert rune4['unique_id'] == '3'
|
||||
rune5 = l1.rpc.commando_rune(rune4['rune'], "pnamelevel!|pnamelevel/io")
|
||||
assert rune5['rune'] == 'Dw2tzGCoUojAyT0JUw7fkYJYqExpEpaDRNTkyvWKoJY9MyZpZF4wMjJkMjIzNjIwYTM1OWE0N2ZmNyZtZXRob2Q9bGlzdHBlZXJzJnBuYW1lbGV2ZWwhfHBuYW1lbGV2ZWwvaW8='
|
||||
assert rune5['unique_id'] == '3'
|
||||
|
|
Loading…
Add table
Reference in a new issue