Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add restriction per for lightningd runes #6617

Merged
Merged
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
6 changes: 3 additions & 3 deletions common/json_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,11 +370,11 @@ void json_add_timeabs(struct json_stream *result, const char *fieldname,
struct timeabs t)
{
json_add_primitive_fmt(result, fieldname,
"%" PRIu64 ".%03" PRIu64,
ShahanaFarooqui marked this conversation as resolved.
Show resolved Hide resolved
(u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000);
"%" PRIu64 ".%09" PRIu64,
(u64)t.ts.tv_sec, (u64)t.ts.tv_nsec);
}

void json_add_time(struct json_stream *result, const char *fieldname,
void json_add_timestr(struct json_stream *result, const char *fieldname,
struct timespec ts)
{
char timebuf[100];
Expand Down
2 changes: 1 addition & 1 deletion common/json_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ void json_add_timeabs(struct json_stream *result, const char *fieldname,
struct timeabs t);

/* used in log.c and notification.c*/
void json_add_time(struct json_stream *result, const char *fieldname,
void json_add_timestr(struct json_stream *result, const char *fieldname,
struct timespec ts);

/* Add ISO_8601 timestamp string, i.e. "2019-09-07T15:50+01:00" */
Expand Down
3 changes: 2 additions & 1 deletion doc/lightning-commando-listrunes.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ On success, an object containing **runes** is returned. It is an array of objec
- **restrictions\_as\_english** (string): English readable description of the restrictions array above
- **stored** (boolean, optional): This is false if the rune does not appear in our datastore (only possible when `rune` is specified) (always *false*)
- **blacklisted** (boolean, optional): The rune has been blacklisted; see commando-blacklist(7) (always *true*)
- **last\_used** (number, optional): The last time this rune was successfully used *(added 23.11)*
- **our\_rune** (boolean, optional): This is not a rune for this node (only possible when `rune` is specified) (always *false*)

[comment]: # (GENERATE-FROM-SCHEMA-END)
Expand All @@ -50,4 +51,4 @@ RESOURCES

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:cd0e75bbeef3d5824448f67485de4679b0c163e97f405673b2ba9495f970d498)
[comment]: # ( SHA256STAMP:900e91777cd1e181c87a78913ab6f914585fcd99cd0dba16da19a81159f98aea)
1 change: 1 addition & 0 deletions doc/lightning-createrune.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ being run:
* id: the node\_id of the peer, e.g. "id=024b9a1fa8e006f1e3937f65f66c408e6da8e1ca728ea43222a7381df1cc449605".
* method: the command being run, e.g. "method=withdraw".
* rate: the rate limit, per minute, e.g. "rate=60".
* per: how often the rune can be used, with suffix "sec" (default), "min", "hour", "day" or "msec", "usec" or "nsec". e.g. "per=5sec".
* pnum: the number of parameters. e.g. "pnum<2".
* pnameX: the parameter named X (with any punctuation like `_` removed). e.g. "pnamedestination=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T".
* parrN: the N'th parameter. e.g. "parr0=1RustyRX2oai4EYYDpQGWvEL62BBGqN9T".
Expand Down
3 changes: 2 additions & 1 deletion doc/lightning-showrunes.7.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ On success, an object containing **runes** is returned. It is an array of objec
- **restrictions\_as\_english** (string): English readable description of the restrictions array above
- **stored** (boolean, optional): This is false if the rune does not appear in our datastore (only possible when `rune` is specified) (always *false*)
- **blacklisted** (boolean, optional): The rune has been blacklisted; see commando-blacklist(7) (always *true*)
- **last\_used** (number, optional): The last time this rune was successfully used *(added 23.11)*
- **our\_rune** (boolean, optional): This is not a rune for this node (only possible when `rune` is specified) (always *false*)

[comment]: # (GENERATE-FROM-SCHEMA-END)
Expand All @@ -48,4 +49,4 @@ RESOURCES

Main web site: <https://github.com/ElementsProject/lightning>

[comment]: # ( SHA256STAMP:cd0e75bbeef3d5824448f67485de4679b0c163e97f405673b2ba9495f970d498)
[comment]: # ( SHA256STAMP:900e91777cd1e181c87a78913ab6f914585fcd99cd0dba16da19a81159f98aea)
5 changes: 5 additions & 0 deletions doc/schemas/commando-listrunes.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
],
"description": "The rune has been blacklisted; see commando-blacklist(7)"
},
"last_used": {
"type": "number",
"description": "The last time this rune was successfully used",
"added": "23.11"
},
"our_rune": {
"type": "boolean",
"enum": [
Expand Down
5 changes: 5 additions & 0 deletions doc/schemas/showrunes.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@
],
"description": "The rune has been blacklisted; see commando-blacklist(7)"
},
"last_used": {
"type": "number",
"description": "The last time this rune was successfully used",
"added": "23.11"
},
"our_rune": {
"type": "boolean",
"enum": [
Expand Down
4 changes: 2 additions & 2 deletions lightningd/log.c
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,7 @@ static void log_to_json(unsigned int skipped,
: level == LOG_IO_IN ? "IO_IN"
: level == LOG_IO_OUT ? "IO_OUT"
: "UNKNOWN");
json_add_time(info->response, "time", diff.ts);
json_add_timestr(info->response, "time", diff.ts);
if (node_id)
json_add_node_id(info->response, "node_id", node_id);
json_add_string(info->response, "source", prefix);
Expand Down Expand Up @@ -1148,7 +1148,7 @@ static struct command_result *json_getlog(struct command *cmd,
response = json_stream_success(cmd);
/* Suppress logging for this stream, to not bloat io logs */
json_stream_log_suppress_for_cmd(response, cmd);
json_add_time(response, "created_at", log_book->init_time.ts);
json_add_timestr(response, "created_at", log_book->init_time.ts);
json_add_num(response, "bytes_used", (unsigned int)log_book->mem_used);
json_add_num(response, "bytes_max", (unsigned int)log_book->max_mem);
json_add_log(response, log_book, NULL, *minlevel);
Expand Down
2 changes: 1 addition & 1 deletion lightningd/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ static void warning_notification_serialize(struct json_stream *stream,
: "warn");
/* unsuaul/broken event is rare, plugin pay more attentions on
* the absolute time, like when channels failed. */
json_add_time(stream, "time", l->time.ts);
json_add_timestr(stream, "time", l->time.ts);
json_add_timeiso(stream, "timestamp", &l->time);
json_add_string(stream, "source", l->prefix->prefix);
json_add_string(stream, "log", l->log);
Expand Down
85 changes: 79 additions & 6 deletions lightningd/runes.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <common/json_param.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/overflows.h>
#include <common/timeout.h>
#include <common/type_to_string.h>
#include <db/exec.h>
Expand Down Expand Up @@ -45,6 +46,7 @@ struct cond_info {
const struct node_id *peer;
const char *buf;
const char *method;
struct timeabs now;
const jsmntok_t *params;
STRMAP(const jsmntok_t *) cached_params;
struct usage *usage;
Expand Down Expand Up @@ -114,6 +116,58 @@ u64 rune_unique_id(const struct rune *rune)
return num;
}

static const char *last_time_check(const tal_t *ctx,
const struct runes *runes,
const struct rune *rune,
const struct rune_altern *alt,
struct cond_info *cinfo)
{
u64 r, multiplier, diff;
struct timeabs last_used;
char *endp;
const u64 sec_per_nsec = 1000000000;

if (alt->condition != '=')
return "per operator must be =";

r = strtoul(alt->value, &endp, 10);
if (endp == alt->value || r == 0 || r >= UINT32_MAX)
return "malformed per";
if (streq(endp, "") || streq(endp, "sec")) {
multiplier = sec_per_nsec;
} else if (streq(endp, "nsec")) {
multiplier = 1;
} else if (streq(endp, "usec")) {
multiplier = 1000;
} else if (streq(endp, "msec")) {
multiplier = 1000000;
} else if (streq(endp, "min")) {
multiplier = 60 * sec_per_nsec;
} else if (streq(endp, "hour")) {
multiplier = 60 * 60 * sec_per_nsec;
} else if (streq(endp, "day")) {
multiplier = 24 * 60 * 60 * sec_per_nsec;
} else {
return "malformed suffix";
}
if (mul_overflows_u64(r, multiplier)) {
return "per overflow";
}
if (!wallet_get_rune(tmpctx, cinfo->runes->ld->wallet, atol(rune->unique_id), &last_used)) {
/* FIXME: If we do not know the rune, per does not work */
return NULL;
}
if (time_before(cinfo->now, last_used)) {
last_used = cinfo->now;
}
diff = time_to_nsec(time_between(cinfo->now, last_used));

if (diff < (r * multiplier)) {
return "too soon";
}
return NULL;
}

static const char *rate_limit_check(const tal_t *ctx,
const struct runes *runes,
const struct rune *rune,
Expand Down Expand Up @@ -317,7 +371,8 @@ static struct command_result *json_add_rune(struct lightningd *ld,
const char *fieldname,
const char *runestr,
const struct rune *rune,
bool stored)
bool stored,
struct timeabs last_used)
{
char *rune_english;
rune_english = "";
Expand All @@ -332,6 +387,9 @@ static struct command_result *json_add_rune(struct lightningd *ld,
if (rune_is_ours(ld, rune) != NULL) {
json_add_bool(js, "our_rune", false);
}
if (last_used.ts.tv_sec != 0) {
json_add_timeabs(js, "last_used", last_used);
}
json_add_string(js, "unique_id", rune->unique_id);
json_array_start(js, "restrictions");
for (size_t i = 0; i < tal_count(rune->restrs); i++) {
Expand Down Expand Up @@ -374,16 +432,21 @@ static struct command_result *json_showrunes(struct command *cmd,
json_array_start(response, "runes");
if (ras) {
u64 uid = rune_unique_id(ras->rune);
const char *from_db = wallet_get_rune(tmpctx, cmd->ld->wallet, uid);
struct timeabs last_used;
const char *from_db = wallet_get_rune(tmpctx, cmd->ld->wallet, uid, &last_used);

/* This is how we indicate no timestamp: */
if (!from_db)
last_used.ts.tv_sec = 0;
/* We consider it stored iff this is exactly stored */
json_add_rune(cmd->ld, response, NULL, ras->runestr, ras->rune,
from_db && streq(from_db, ras->runestr));
from_db && streq(from_db, ras->runestr), last_used);
} else {
const char **strs = wallet_get_runes(cmd, cmd->ld->wallet);
struct timeabs *last_used;
const char **strs = wallet_get_runes(cmd, cmd->ld->wallet, &last_used);
for (size_t i = 0; i < tal_count(strs); i++) {
const struct rune *r = rune_from_base64(cmd, strs[i]);
json_add_rune(cmd->ld, response, NULL, strs[i], r, true);
json_add_rune(cmd->ld, response, NULL, strs[i], r, true, last_used[i]);
}
}
json_array_end(response);
Expand Down Expand Up @@ -716,7 +779,7 @@ static const char *check_condition(const tal_t *ctx,
const jsmntok_t *ptok;

if (streq(alt->fieldname, "time")) {
return rune_alt_single_int(ctx, alt, time_now().ts.tv_sec);
return rune_alt_single_int(ctx, alt, cinfo->now.ts.tv_sec);
} else if (streq(alt->fieldname, "id")) {
if (cinfo->peer) {
const char *id = node_id_to_hexstr(tmpctx, cinfo->peer);
Expand All @@ -733,6 +796,8 @@ static const char *check_condition(const tal_t *ctx,
return rune_alt_single_int(ctx, alt, (cinfo && cinfo->params) ? cinfo->params->size : 0);
} else if (streq(alt->fieldname, "rate")) {
return rate_limit_check(ctx, cinfo->runes, rune, alt, cinfo);
} else if (streq(alt->fieldname, "per")) {
return last_time_check(ctx, cinfo->runes, rune, alt, cinfo);
}

/* Rest are params looksup: generate this once! */
Expand Down Expand Up @@ -784,6 +849,12 @@ static const char *check_condition(const tal_t *ctx,
ptok->end - ptok->start);
}

static void update_rune_usage_time(struct runes *runes,
struct rune *rune, struct timeabs now)
{
wallet_rune_update_last_used(runes->ld->wallet, rune, now);
}

static struct command_result *json_checkrune(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
Expand Down Expand Up @@ -812,6 +883,7 @@ static struct command_result *json_checkrune(struct command *cmd,
cinfo.buf = buffer;
cinfo.method = method;
cinfo.params = methodparams;
cinfo.now = time_now();
/* We will populate it in rate_limit_check if required. */
cinfo.usage = NULL;
strmap_init(&cinfo.cached_params);
Expand All @@ -833,6 +905,7 @@ static struct command_result *json_checkrune(struct command *cmd,
/* If it succeeded, *now* we increment any associated usage counter. */
if (cinfo.usage)
cinfo.usage->counter++;
update_rune_usage_time(cmd->ld->runes, ras->rune, cinfo.now);

js = json_stream_success(cmd);
json_add_bool(js, "valid", true);
Expand Down
6 changes: 3 additions & 3 deletions lightningd/test/run-log-pruning.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ 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 json_add_time */
void json_add_time(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
/* Generated stub for json_add_timestr */
void json_add_timestr(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
struct timespec ts UNNEEDED)
{ fprintf(stderr, "json_add_time called!\n"); abort(); }
{ fprintf(stderr, "json_add_timestr called!\n"); abort(); }
/* Generated stub for json_array_end */
void json_array_end(struct json_stream *js UNNEEDED)
{ fprintf(stderr, "json_array_end called!\n"); abort(); }
Expand Down
6 changes: 3 additions & 3 deletions lightningd/test/run-log_filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ 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 json_add_time */
void json_add_time(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
/* Generated stub for json_add_timestr */
void json_add_timestr(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
struct timespec ts UNNEEDED)
{ fprintf(stderr, "json_add_time called!\n"); abort(); }
{ fprintf(stderr, "json_add_timestr called!\n"); abort(); }
/* Generated stub for json_array_end */
void json_array_end(struct json_stream *js UNNEEDED)
{ fprintf(stderr, "json_array_end called!\n"); abort(); }
Expand Down
Loading
Loading