Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -1871,10 +1871,17 @@
},
"HelpHelp": {
"Help.help[].category": 2,
"Help.help[].clnrest": 5,
"Help.help[].command": 1,
"Help.help[].description": 3,
"Help.help[].verbose": 4
},
"HelpHelpClnrest": {
"Help.help[].clnrest.content_type": 3,
"Help.help[].clnrest.method": 2,
"Help.help[].clnrest.path": 1,
"Help.help[].clnrest.rune": 4
},
"HelpRequest": {
"Help.command": 1
},
Expand Down Expand Up @@ -7676,6 +7683,26 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"Help.help[].clnrest": {
"added": "v25.09",
"deprecated": null
},
"Help.help[].clnrest.content_type": {
"added": "v25.09",
"deprecated": null
},
"Help.help[].clnrest.method": {
"added": "v25.09",
"deprecated": null
},
"Help.help[].clnrest.path": {
"added": "v25.09",
"deprecated": null
},
"Help.help[].clnrest.rune": {
"added": "v25.09",
"deprecated": null
},
"Help.help[].command": {
"added": "pre-v0.10.1",
"deprecated": null
Expand Down
8 changes: 8 additions & 0 deletions cln-grpc/proto/node.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions cln-grpc/src/convert.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions cln-rpc/src/model.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -15298,6 +15298,39 @@
"description": [
"The command."
]
},
"clnrest": {
"type": "object",
"additionalProperties": false,
"required": [
"path",
"method",
"content_type",
"rune"
],
"added": "v25.09",
"properties": {
"path": {
"type": "string",
"description": "the path to the HTTP endpoint for this command",
"added": "v25.09"
},
"method": {
"type": "string",
"description": "the HTTP method for this command",
"added": "v25.09"
},
"content_type": {
"type": "string",
"description": "http content-type that clnrest should return",
"added": "v25.09"
},
"rune": {
"type": "boolean",
"description": "whether or not this command requires a rune for authentication",
"added": "v25.09"
}
}
}
}
}
Expand Down
31 changes: 23 additions & 8 deletions contrib/pyln-client/pyln/client/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from dataclasses import dataclass
from enum import Enum
from threading import RLock
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, TypedDict

from .lightning import LightningRpc, Millisatoshi

Expand All @@ -38,6 +38,13 @@ class RequestState(Enum):
FAILED = 'failed'


class CLNRestData(TypedDict):
path: str
method: str
content_type: str
rune: bool


class Method(object):
"""Description of methods that are registered with the plugin.

Expand All @@ -49,7 +56,8 @@ class Method(object):
def __init__(self, name: str, func: Callable[..., JSONType],
mtype: MethodType = MethodType.RPCMETHOD,
deprecated: Union[bool, List[str]] = None,
description: str = None):
description: str = None,
clnrest_data: CLNRestData = None):
self.name = name
self.func = func
self.mtype = mtype
Expand All @@ -58,6 +66,7 @@ def __init__(self, name: str, func: Callable[..., JSONType],
self.description = description
self.before: List[str] = []
self.after: List[str] = []
self.clnrest = clnrest_data

def get_usage(self):
# Handles out-of-order use of parameters like:
Expand Down Expand Up @@ -357,7 +366,8 @@ def convert_featurebits(
def add_method(self, name: str, func: Callable[..., Any],
background: bool = False,
deprecated: Optional[Union[bool, List[str]]] = None,
description: str = None) -> None:
description: str = None,
clnrest_data: CLNRestData = None) -> None:
"""Add a plugin method to the dispatch table.

The function will be expected at call time (see `_dispatch`)
Expand Down Expand Up @@ -394,7 +404,7 @@ def add_method(self, name: str, func: Callable[..., Any],
)

# Register the function with the name
method = Method(name, func, MethodType.RPCMETHOD, deprecated, description)
method = Method(name, func, MethodType.RPCMETHOD, deprecated, description, clnrest_data)
method.background = background
self.methods[name] = method

Expand Down Expand Up @@ -511,7 +521,8 @@ def get_option(self, name: str) -> Optional[Any]:
def async_method(self, method_name: str, category: Optional[str] = None,
desc: Optional[str] = None,
long_desc: Optional[str] = None,
deprecated: Optional[Union[bool, List[str]]] = None) -> NoneDecoratorType:
deprecated: Optional[Union[bool, List[str]]] = None,
clnrest_data: CLNRestData = None) -> NoneDecoratorType:
"""Decorator to add an async plugin method to the dispatch table.

Internally uses add_method.
Expand All @@ -520,15 +531,16 @@ def decorator(f: Callable[..., None]) -> Callable[..., None]:
for attr, attr_name in [(category, "Category"), (desc, "Description"), (long_desc, "Long description")]:
if attr is not None:
self.log("{} is deprecated but defined in method {}; it will be ignored by Core Lightning".format(attr_name, method_name), level="warn")
self.add_method(method_name, f, background=True, deprecated=deprecated)
self.add_method(method_name, f, background=True, deprecated=deprecated, clnrest_data=clnrest_data)
return f
return decorator

def method(self, method_name: str, category: Optional[str] = None,
desc: Optional[str] = None,
long_desc: Optional[str] = None,
deprecated: Union[bool, List[str]] = None,
description: str = None) -> JsonDecoratorType:
description: str = None,
clnrest_data: Optional[CLNRestData] = None) -> JsonDecoratorType:
"""Decorator to add a plugin method to the dispatch table.

Internally uses add_method.
Expand All @@ -537,7 +549,7 @@ def decorator(f: Callable[..., JSONType]) -> Callable[..., JSONType]:
for attr, attr_name in [(category, "Category"), (desc, "Description"), (long_desc, "Long description")]:
if attr is not None:
self.log("{} is deprecated but defined in method {}; it will be ignored by Core Lightning".format(attr_name, method_name), level="warn")
self.add_method(method_name, f, background=False, deprecated=deprecated, description=f.__doc__)
self.add_method(method_name, f, background=False, deprecated=deprecated, description=f.__doc__, clnrest_data=clnrest_data)
return f
return decorator

Expand Down Expand Up @@ -951,6 +963,9 @@ def _getmanifest(self, **kwargs) -> JSONType:
'usage': method.get_usage(),
'description': description,
})
if method.clnrest:
m = methods[len(methods) - 1]
m["clnrest"] = method.clnrest

manifest = {
'options': list(d.json() for d in self.options.values()),
Expand Down
470 changes: 236 additions & 234 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions contrib/pyln-testing/pyln/testing/grpc2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -2656,6 +2656,15 @@ def stop2py(m):
})


def help_help_clnrest2py(m):
return remove_default({
"content_type": m.content_type, # PrimitiveField in generate_composite
"method": m.method, # PrimitiveField in generate_composite
"path": m.path, # PrimitiveField in generate_composite
"rune": m.rune, # PrimitiveField in generate_composite
})


def help_help2py(m):
return remove_default({
"command": m.command, # PrimitiveField in generate_composite
Expand Down
33 changes: 33 additions & 0 deletions doc/schemas/help.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,39 @@
"description": [
"The command."
]
},
"clnrest": {
"type": "object",
"additionalProperties": false,
"required": [
"path",
"method",
"content_type",
"rune"
],
"added": "v25.09",
"properties": {
"path": {
"type": "string",
"description": "the path to the HTTP endpoint for this command",
"added": "v25.09"
},
"method": {
"type": "string",
"description": "the HTTP method for this command",
"added": "v25.09"
},
"content_type": {
"type": "string",
"description": "http content-type that clnrest should return",
"added": "v25.09"
},
"rune": {
"type": "boolean",
"description": "whether or not this command requires a rune for authentication",
"added": "v25.09"
}
}
}
}
}
Expand Down
54 changes: 48 additions & 6 deletions lightningd/jsonrpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,23 @@ static void json_add_help_command(struct command *cmd,
json_command->name));
json_object_start(response, NULL);
json_add_string(response, "command", usage);

if (json_command->clnrest) {
json_object_start(response, "clnrest");
if (json_command->clnrest->method) {
json_add_string(response, "method", json_command->clnrest->method);
}
if (json_command->clnrest->path) {
json_add_string(response, "path", json_command->clnrest->path);
}
if (json_command->clnrest->content_type) {
json_add_string(response, "content_type", json_command->clnrest->content_type);
}
if (json_command->clnrest->rune) {
json_add_bool(response, "rune", *json_command->clnrest->rune);
}
json_object_end(response);
}
json_object_end(response);
}

Expand Down Expand Up @@ -1360,14 +1377,39 @@ static void destroy_json_command(struct json_command *command, struct jsonrpc *r
abort();
}

static bool command_add(struct jsonrpc *rpc, struct json_command *command)
static bool command_add(struct jsonrpc *rpc, struct json_command *command,
char **collision_name)
{
size_t count = tal_count(rpc->commands);

/* Check that we don't clobber a method */
for (size_t i = 0; i < count; i++)
if (streq(rpc->commands[i]->name, command->name))
for (size_t i = 0; i < count; i++) {

if (streq(rpc->commands[i]->name, command->name)) {
if (collision_name) {
*collision_name =
tal_strdup(tmpctx, rpc->commands[i]->name);
}
return false;
}

/* Check for clnrest conflict */
if (command->clnrest && rpc->commands[i]->clnrest) {
bool method_match =
streq(command->clnrest->method,
rpc->commands[i]->clnrest->method);
bool path_match =
streq(command->clnrest->path,
rpc->commands[i]->clnrest->path);
if (method_match && path_match) {
if (collision_name) {
*collision_name = tal_strdup(
tmpctx, rpc->commands[i]->name);
}
return false;
}
}
}

tal_arr_expand(&rpc->commands, command);
return true;
Expand All @@ -1391,12 +1433,12 @@ static void setup_command_usage(struct lightningd *ld,
}

bool jsonrpc_command_add(struct jsonrpc *rpc, struct json_command *command,
const char *usage TAKES)
const char *usage TAKES, char **collision_name)
{
struct json_escape *esc;
const char *unescaped;

if (!command_add(rpc, command))
if (!command_add(rpc, command, collision_name))
return false;

esc = json_escape_string_(tmpctx, usage, strlen(usage));
Expand All @@ -1417,7 +1459,7 @@ static bool jsonrpc_command_add_perm(struct lightningd *ld,
struct jsonrpc *rpc,
struct json_command *command)
{
if (!command_add(rpc, command))
if (!command_add(rpc, command, NULL))
return false;
setup_command_usage(ld, command);
return true;
Expand Down
Loading
Loading