Skip to content

Commit

Permalink
askrene: add expiration parameter to create-layer
Browse files Browse the repository at this point in the history
This could be useful if we want to create one-time use layers
on the flight and not worry about memory leaks (one time used
layers that are not removed constitute a memory leak in a sense).

Changelog-Added: askrene: add an optional self-destruction timer (expiration parameter) to new layers.

Signed-off-by: Lagrang3 <[email protected]>
  • Loading branch information
Lagrang3 committed Dec 23, 2024
1 parent 98679aa commit 2c2a4e6
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 0 deletions.
7 changes: 7 additions & 0 deletions doc/schemas/lightning-askrene-create-layer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@
"description": [
"True if askrene should save and restore this layer. As a side-effect, create-layer also succeeds if the layer already exists and persistent is true."
]
},
"expiration": {
"type": "u64",
"description": [
"Sets the number of seconds until this layer expires. If this number is specified the layer will be automatically deleted at expiration. A layer cannot be persistent and have an expiration time."
],
"added": "v25.02"
}
}
},
Expand Down
35 changes: 35 additions & 0 deletions plugins/askrene/askrene.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <common/gossmods_listpeerchannels.h>
#include <common/json_param.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/route.h>
#include <errno.h>
#include <math.h>
Expand Down Expand Up @@ -623,6 +624,7 @@ static struct command_result *do_getroutes(struct command *cmd,
*info->amount, *info->maxfee, *info->finalcltv,
info->layers, localmods, info->local_layer,
&routes, &amounts, info->additional_costs, &probability);

if (err)
return command_fail(cmd, PAY_ROUTE_NOT_FOUND, "%s", err);

Expand Down Expand Up @@ -1060,6 +1062,27 @@ static struct command_result *json_askrene_disable_node(struct command *cmd,
return command_finished(cmd, response);
}

static struct command_result *expire_layer_done(struct command *timer_cmd,
const char *method,
const char *buf,
const jsmntok_t *result,
void *unused)
{
return timer_complete(timer_cmd);
}

static struct command_result *expire_layer(struct command *timer_cmd,
struct layer *l)
{
struct out_req *req;
req = jsonrpc_request_start(timer_cmd, "askrene-remove-layer",
expire_layer_done, plugin_broken_cb, NULL);
json_add_string(req->js, "layer", layer_name(l));
plugin_log(timer_cmd->plugin, LOG_DBG, "removing expired layer '%s'",
layer_name(l));
return send_outreq(req);
}

static struct command_result *json_askrene_create_layer(struct command *cmd,
const char *buffer,
const jsmntok_t *params)
Expand All @@ -1069,10 +1092,12 @@ static struct command_result *json_askrene_create_layer(struct command *cmd,
const char *layername;
struct json_stream *response;
bool *persistent;
u64 *expire_seconds;

if (!param_check(cmd, buffer, params,
p_req("layer", param_string, &layername),
p_opt_def("persistent", param_bool, &persistent, false),
p_opt("expiration", param_u64, &expire_seconds),
NULL))
return command_param_failed();

Expand All @@ -1086,13 +1111,23 @@ static struct command_result *json_askrene_create_layer(struct command *cmd,
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Layer already exists");
}
if (persistent && expire_seconds && *persistent == true) {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"A persistent layer cannot have an expiration time.");
}

if (command_check_only(cmd))
return command_check_done(cmd);

if (!layer)
layer = new_layer(askrene, layername, *persistent);

if (expire_seconds)
notleak(global_timer(cmd->plugin,
time_from_sec(*expire_seconds),
expire_layer, layer));

response = jsonrpc_stream_success(cmd);
json_add_layers(response, askrene, "layers", layer);
return command_finished(cmd, response);
Expand Down
25 changes: 25 additions & 0 deletions tests/test_askrene.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,31 @@ def test_layer_persistence(node_factory):
assert l1.rpc.askrene_listlayers() == {'layers': []}


def test_layer_expiration(node_factory):
l1 = node_factory.get_nodes(1, opts={"disable-plugin": "cln-xpay"})[0]
expect = {
"layer": "tmp_layer",
"persistent": False,
"disabled_nodes": [],
"created_channels": [],
"channel_updates": [],
"constraints": [],
"biases": [],
}

assert l1.rpc.askrene_listlayers() == {"layers": []}
# Add a self-destructing layer
l1.rpc.askrene_create_layer(layer="tmp_layer", expiration=5)
assert l1.rpc.askrene_listlayers() == {"layers": [expect]}
time.sleep(6)
assert l1.rpc.askrene_listlayers() == {"layers": []}

with pytest.raises(
RpcError, match="A persistent layer cannot have an expiration time."
):
l1.rpc.askrene_create_layer(layer="pers_layer", persistent=True, expiration=5)


def check_route_as_expected(routes, paths):
"""Make sure all fields in paths are match those in routes"""
def dict_subset_eq(a, b):
Expand Down

0 comments on commit 2c2a4e6

Please sign in to comment.