diff --git a/common/Makefile b/common/Makefile index 8b63eb3ca118..0932b1d05b53 100644 --- a/common/Makefile +++ b/common/Makefile @@ -109,6 +109,7 @@ COMMON_HEADERS_NOGEN := $(COMMON_SRC_NOGEN:.c=.h) \ common/htlc.h \ common/json_command.h \ common/jsonrpc_errors.h \ + common/jsonrpc_paginator.h \ common/overflows.h COMMON_HEADERS_GEN := common/htlc_state_names_gen.h common/status_wiregen.h common/peer_status_wiregen.h common/scb_wiregen.h diff --git a/common/json_param.c b/common/json_param.c index 7aa2174645eb..066ca8acafd6 100644 --- a/common/json_param.c +++ b/common/json_param.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -14,6 +15,10 @@ #include #include #include +#include +#include +#include +#include struct param { const char *name; @@ -337,7 +342,7 @@ const char *param_subcommand(struct command *cmd, const char *buffer, } bool param(struct command *cmd, const char *buffer, - const jsmntok_t tokens[], ...) + const jsmntok_t *tokens, ...) { struct param *params = tal_arr(tmpctx, struct param, 0); const char *name; @@ -353,6 +358,7 @@ bool param(struct command *cmd, const char *buffer, allow_extra = true; continue; } + if (!param_add(¶ms, name, style, cbx, arg)) { /* We really do ignore this return! */ struct command_result *ignore; @@ -439,6 +445,37 @@ struct command_result *param_string(struct command *cmd, const char *name, return NULL; } +struct command_result *param_arr_str(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + const char ***arr) +{ + const jsmntok_t *curr; + size_t i; + + if (tok->type != JSMN_ARRAY) + return command_fail_badparam(cmd, name, buffer, tok, + "schould be an array of string"); + + *arr = tal_arr(cmd, const char *, 0); + json_for_each_arr(i, curr, tok) { + struct json_escape *esc; + const char *str; + + if (curr->type != JSMN_STRING) + return command_fail_badparam(cmd, name, buffer, tok, + "the item of the array should be a string"); + + esc = json_escape_string_(cmd, buffer + curr->start, + curr->end - curr->start); + str = json_escape_unescape(cmd, esc); + tal_arr_expand(arr, str); + } + + return NULL; +} + + + struct command_result *param_ignore(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, const void *unused) @@ -1066,3 +1103,40 @@ struct command_result *param_pubkey(struct command *cmd, const char *name, "should be a compressed pubkey"); } +struct command_result *param_paginator(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct jsonrpc_paginator **paginator) +{ + const jsmntok_t *batch_tok, *offset_tok, *limit_tok, *reverse_tok; + u64 *limit, *offset; + bool *reverse; + const char **batch; + + batch = NULL; + batch_tok = json_get_member(buffer, tok, "batch"); + if (batch_tok) + json_to_strarr(cmd, buffer, batch_tok, &batch); + + offset = tal(cmd, uint64_t); + offset_tok = json_get_member(buffer, tok, "offset"); + if (offset_tok) + json_to_u64(buffer, offset_tok, offset); + + limit = tal(cmd, uint64_t); + limit_tok = json_get_member(buffer, tok, "limit"); + if (limit_tok) + json_to_u64(buffer, limit_tok, limit); + + reverse = tal(cmd, bool); + reverse_tok = json_get_member(buffer, tok, "reverse"); + if (reverse_tok) + json_to_bool(buffer, reverse_tok, reverse); + + if (batch || (limit && offset)) { + *paginator = new_paginator(cmd, batch, limit, offset, reverse); + assert(paginator); + return NULL; + } + return command_fail_badparam(cmd, name, buffer, tok, + "paginator request format in the wrong way!"); +} diff --git a/common/json_param.h b/common/json_param.h index 19c924b66b92..5a780ef792ee 100644 --- a/common/json_param.h +++ b/common/json_param.h @@ -2,9 +2,11 @@ #ifndef LIGHTNING_COMMON_JSON_PARAM_H #define LIGHTNING_COMMON_JSON_PARAM_H #include "config.h" +#include #include #include #include +#include #include #include #include @@ -49,6 +51,10 @@ struct command_result; bool param(struct command *cmd, const char *buffer, const jsmntok_t params[], ...) LAST_ARG_NULL; +bool param_partial_par(struct command *cmd, const char *buffer, + const jsmntok_t tokens[], ...) LAST_ARG_NULL; + + /* * The callback signature. * @@ -80,6 +86,11 @@ enum param_style { PARAM_OPTIONAL_WITH_DEFAULT, }; +/** Check if this is a valid paginator input */ +struct command_result *param_paginator(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct jsonrpc_paginator **paginator); + /* * Add a required parameter. */ @@ -139,6 +150,18 @@ enum param_style { /* Special flag for 'check' which allows any parameters. */ #define p_opt_any() "", PARAM_OPTIONAL, NULL, NULL +#define p_paginator(arg) \ + "paginator", \ + PARAM_OPTIONAL, \ + (param_cbx)(param_paginator), \ + ({ *arg = NULL; \ + (arg) + 0*sizeof((param_paginator)((struct command *)NULL, \ + (const char *)NULL, \ + (const char *)NULL, \ + (const jsmntok_t *)NULL, \ + (arg)) == (struct command_result *)NULL); }) + + /* All the helper routines. */ struct amount_msat; struct amount_sat; @@ -181,6 +204,11 @@ struct command_result *param_string(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, const char **str); +/* Extract an array of strings */ +struct command_result *param_arr_str(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + const char ***arr); + /* Extract a label. It is either an escaped string or a number. */ struct command_result *param_label(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, @@ -340,5 +368,4 @@ struct command_result *param_lease_hex(struct command *cmd, struct command_result *param_pubkey(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct pubkey **pubkey); - #endif /* LIGHTNING_COMMON_JSON_PARAM_H */ diff --git a/common/json_parse.c b/common/json_parse.c index 14e5d6102c60..ffc9fdfd7848 100644 --- a/common/json_parse.c +++ b/common/json_parse.c @@ -711,3 +711,28 @@ json_tok_channel_id(const char *buffer, const jsmntok_t *tok, return hex_decode(buffer + tok->start, tok->end - tok->start, cid, sizeof(*cid)); } + +bool json_to_strarr(const tal_t *ctx, const char *buffer, + const jsmntok_t *tok, const char ***arr) +{ + const jsmntok_t *curr; + size_t i; + + if (tok->type != JSMN_ARRAY) + return false; + + *arr = tal_arr(ctx, const char *, 0); + json_for_each_arr(i, curr, tok) { + struct json_escape *esc; + const char *str; + + if (curr->type != JSMN_STRING) + return false; + esc = json_escape_string_(ctx, buffer + curr->start, + curr->end - curr->start); + str = json_escape_unescape(ctx, esc); + tal_arr_expand(arr, str); + } + + return NULL; +} diff --git a/common/json_parse.h b/common/json_parse.h index fef86b7b3e8b..e5cd5e9296fa 100644 --- a/common/json_parse.h +++ b/common/json_parse.h @@ -124,6 +124,9 @@ json_to_blinded_path(const tal_t *ctx, const char *buffer, const jsmntok_t *tok) bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok, struct channel_id *cid); +bool json_to_strarr(const tal_t *ctx, const char *buffer, + const jsmntok_t *tok, const char ***arr); + /* Guide is % for a token: each must be followed by JSON_SCAN(). * Returns NULL on error (asserts() on bad guide). */ const char *json_scan(const tal_t *ctx, diff --git a/common/jsonrpc_paginator.h b/common/jsonrpc_paginator.h new file mode 100644 index 000000000000..5cc12eb3e70d --- /dev/null +++ b/common/jsonrpc_paginator.h @@ -0,0 +1,82 @@ +/** + * Welcome in this wanderful experience of writing + * a paginator for the core lightning JSON RPC 2.0 in C! + * + * I will try to keep the code as simple as possible, + * so if you had any dout on what I'm trying to do, blame me + * that I was not able to do my job. + * + * In short, the goal of this paginator is offer a struct + * that it is used to grep the paginator information by + * pre processin the JSON RPC request. + * + * So let immagine a normal listnode RPC call, where + * cli | rest | grpc -> json_listnodes -> json response {....} + * + * if the paginator will be enbaled on the listnodes, the + * call will be + * + * cli | rest | grpc -> json_paginator_listnodes -> json_listnodes + struct jsonrpc_paginator -> jon response { .... } + * + * Done, this should be all! + */ +#ifndef JSONRPC_PAGINATOR_H +#define JSONRPC_PAGINATOR_H + +#include +#include +#include + +/** + * jsonrpc_paginator - core struct where all the paginator information + * are stored, + * + * A developer that want to extend the functionality of the + * paginator must not abuse of this struct to avoid to made the logic + * messy, thanks! + * + * @batch: array string to be able to query a list of things, useful also + * when you deal with reading stuff from file to avoid reaccess to the file + * to search another item. + * + * @offset: a position (u64) that determines the number of element (in SQL row) + * returned by request. + * @limit: a position (u64) that give the number of element in [0,..,offset - 1] to skip skip. + * @reverse: a boolean that reverse the order of the result. + */ +struct jsonrpc_paginator { + /** reall usefult for access to gossip map */ + const char **batch; + /** query the database, for more complexy + * query please use the sql plugins */ + const u64 *offset; + const u64 *limit; + const bool *reverse; + /* FIXME: more smarter one? like sort_by = "json key" + * but this required to have a mapping between json_keys and sql keys + * maybe we had already somethings in the sql plugin? */ +}; + +/** + * new_paginator - helper function to create a new paginator from a list of parameter. + * This is simple enought to be avoided, but write simple code inside the C macros + * is frustating, so let use this insteand. + * + * BTW: I love C Macros, really! + */ +static inline struct jsonrpc_paginator * +new_paginator(const tal_t *ctx, const char **batch, const u64 *limit, + const u64 *offset, const bool *reverse) +{ + struct jsonrpc_paginator *paginator = NULL; + if (batch || (limit && offset)) { + paginator = tal(ctx, struct jsonrpc_paginator); + paginator->batch = batch; + paginator->limit = limit; + paginator->offset = offset; + paginator->reverse = reverse; + return paginator; + } + return NULL; +} +#endif // JSONRPC_PAGINATOR_H diff --git a/common/test/run-json_filter.c b/common/test/run-json_filter.c index 594eb00f7ef7..d61f42705417 100644 --- a/common/test/run-json_filter.c +++ b/common/test/run-json_filter.c @@ -72,6 +72,8 @@ const char *json_scan(const tal_t *ctx UNNEEDED, bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct channel_id *cid UNNEEDED) { fprintf(stderr, "json_to_channel_id called!\n"); abort(); } +bool json_to_strarr(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char ***arr UNNEEDED) +{ fprintf(stderr, "json_to_strarr called"); abort();} /* Generated stub for json_to_millionths */ bool json_to_millionths(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *millionths UNNEEDED) diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c index 99ae0122a938..c639bde481a2 100644 --- a/common/test/run-json_remove.c +++ b/common/test/run-json_remove.c @@ -133,6 +133,9 @@ bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint16_t *num UNNEEDED) { fprintf(stderr, "json_to_u16 called!\n"); abort(); } +bool json_to_strarr(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, const char ***arr UNNEEDED) +{ fprintf(stderr, "json_to_strarr called"); abort();} /* Generated stub for json_tok_bin_from_hex */ u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } diff --git a/lightningd/jsonrpc.h b/lightningd/jsonrpc.h index a3f6cd5d6777..568d97b6961e 100644 --- a/lightningd/jsonrpc.h +++ b/lightningd/jsonrpc.h @@ -4,6 +4,7 @@ #include #include #include +#include #include struct jsonrpc; @@ -43,6 +44,8 @@ struct command { struct json_stream *json_stream; /* Optional output field filter. */ struct json_filter *filter; + /* Option filtering option */ + struct jsonrpc_paginator *paginator; }; /** @@ -223,6 +226,7 @@ struct jsonrpc_notification *jsonrpc_notification_start(const tal_t *ctx, const */ void jsonrpc_notification_end(struct jsonrpc_notification *n); + /** * start a JSONRPC request; id_prefix is non-NULL if this was triggered by * another JSONRPC request. @@ -273,4 +277,21 @@ void jsonrpc_request_end(struct jsonrpc_request *request); AUTODATA_TYPE(json_command, struct json_command); +#define PAGINATOR(callback) \ + static struct command_result* callback##_paginator(struct command *cmd, \ + const char *buffer, \ + const jsmntok_t *obj UNNEEDED, \ + const jsmntok_t *params) \ + { \ + const char **batch; \ + u64 *limit, *offset; \ + if (!param_partial_par(cmd, buffer, params, \ + p_opt("batch", param_arr_str, &batch), \ + p_opt("limit", param_u64, &limit), \ + p_opt("offset", param_u64, &offset), \ + NULL)) \ + return command_param_failed(); \ + cmd->paginator = new_paginator(cmd, batch, limit, offset); \ + return callback(cmd, buffer, obj, params); \ + } #endif /* LIGHTNING_LIGHTNINGD_JSONRPC_H */ diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index 6c4c1c1697fd..398bbfb9fa6a 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -2927,15 +2927,16 @@ void json_add_forwarding_object(struct json_stream *response, json_object_end(response); } -static void listforwardings_add_forwardings(struct json_stream *response, - struct wallet *wallet, +static void listforwardings_add_forwardings(struct command *cmd, + struct json_stream *response, enum forward_status status, const struct short_channel_id *chan_in, const struct short_channel_id *chan_out) { const struct forwarding *forwardings; - forwardings = wallet_forwarded_payments_get(wallet, tmpctx, status, chan_in, chan_out); + forwardings = wallet_forwarded_payments_get(cmd->ld->wallet, tmpctx, status, + chan_in, chan_out, cmd->paginator); json_array_start(response, "forwards"); for (size_t i=0; ipaginator), NULL)) return command_param_failed(); response = json_stream_success(cmd); - listforwardings_add_forwardings(response, cmd->ld->wallet, *status, chan_in, chan_out); + listforwardings_add_forwardings(cmd, response, *status, chan_in, chan_out); return command_success(cmd, response); } diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 1c09a78004a5..acafc381c7f0 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ struct command { struct plugin *plugin; /* Optional output field filter. */ struct json_filter *filter; + /* Option filtering option */ + struct jsonrpc_paginator *paginator; }; /* Create an array of these, one for each command you support. */ @@ -484,5 +487,4 @@ const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx, struct plugin *plugin, const char *method, const struct json_out *params TAKES, const char **resp); - #endif /* LIGHTNING_PLUGINS_LIBPLUGIN_H */ diff --git a/plugins/topology.c b/plugins/topology.c index c6def06ced1a..4313853aed51 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -15,6 +16,8 @@ #include #include #include +#include +#include /* Access via get_gossmap() */ static struct gossmap *global_gossmap; @@ -408,15 +411,16 @@ static struct command_result *json_listchannels(struct command *cmd, return send_outreq(cmd->plugin, req); } -static void json_add_node(struct json_stream *js, +static void json_add_node(struct command *cmd, + struct json_stream *js, const struct gossmap *gossmap, const struct gossmap_node *n) { struct node_id node_id; u8 *nannounce; - json_object_start(js, NULL); gossmap_node_get_id(gossmap, n, &node_id); + json_object_start(js, NULL); json_add_node_id(js, "nodeid", &node_id); nannounce = gossmap_node_get_announce(tmpctx, gossmap, n); if (nannounce) { @@ -489,6 +493,7 @@ static struct command_result *json_listnodes(struct command *cmd, if (!param(cmd, buffer, params, p_opt("id", param_node_id, &id), + p_paginator(&cmd->paginator), NULL)) return command_param_failed(); @@ -498,12 +503,23 @@ static struct command_result *json_listnodes(struct command *cmd, if (id) { struct gossmap_node *n = gossmap_find_node(gossmap, id); if (n) - json_add_node(js, gossmap, n); + json_add_node(cmd, js, gossmap, n); + } else if (cmd->paginator) { + const char **batch = cmd->paginator->batch; + for (size_t i = 0; i < tal_count(batch); i++) { + const char *idstr = batch[i]; + id = tal(tmpctx, struct node_id); +; node_id_from_hexstr(idstr, sizeof(idstr), id); + struct gossmap_node *n = gossmap_find_node(gossmap, id); + if (n) + json_add_node(cmd, js, gossmap, n); + tal_free(id); + } } else { for (struct gossmap_node *n = gossmap_first_node(gossmap); n; n = gossmap_next_node(gossmap, n)) { - json_add_node(js, gossmap, n); + json_add_node(cmd, js, gossmap, n); } } json_array_end(js); diff --git a/tests/test_paginator.py b/tests/test_paginator.py new file mode 100644 index 000000000000..b351ccc96f00 --- /dev/null +++ b/tests/test_paginator.py @@ -0,0 +1,41 @@ +""" +I think I'm getting to excited for this paginator API +that I think this deserve a own python test file! +""" +import unittest +from fixtures import * # noqua: F401,F403 +from utils import COMPAT + +def test_listnodes_paginator(node_factory): + """ + We run 4 nodes and then we query the + list node by batch for the other two nodes. + """ + l1, l2, l3, _ = node_factory.line_graph(4, fundchannel=True, wait_for_announce=True) + l1.rpc.jsonschemas = {} + + nodes = l1.rpc.call("listnodes", { "paginator": { "batch": [l3.info["id"], l2.info["id"]] } }) + nodes_nobatch = l1.rpc.listnodes() + print(nodes) + print(nodes_nobatch) + assert len(nodes["nodes"]) == 2 + assert len(nodes_nobatch["nodes"]) == 4 + + +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Canned db used") +@unittest.skipIf(not COMPAT, "needs COMPAT to convert obsolete db") +@unittest.skipIf(TEST_NETWORK != 'regtest', "The DB migration is network specific due to the chain var.") +def test_filter_listforwards_from_db(bitcoind, node_factory): + """This test is taken from the test_db and adapt to support the + paginator API. This allow to have a static way to assert over the + forwards without doing crazy things.""" + bitcoind.generate_block(113) + l1 = node_factory.get_node(dbfile='v0.12.1-forward.sqlite3.xz', + options={'database-upgrade': True}) + + assert l1.rpc.getinfo()['fees_collected_msat'] == 4 + assert len(l1.rpc.listforwards()['forwards']) == 4 + filter_forwards = l1.rpc.call("listforwards", { "paginator": { "limit": 2, "offset": 0 } })['forwards'] + assert len(filter_forwards) == 2 + + diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index 63eb6c608dac..b0cd7a1b3798 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -1,6 +1,5 @@ #include "config.h" - #include - +#include #include "test_utils.h" #include #include @@ -617,7 +616,11 @@ struct command_result *param_bool(struct command *cmd UNNEEDED, const char *name const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool **b UNNEEDED) { fprintf(stderr, "param_bool called!\n"); abort(); } -/* Generated stub for param_channel_id */ +/* Generated stub for param_bool */ +struct command_result *param_paginator(struct command *cmd UNNEEDED, const char *name UNNEEDED, + const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct jsonrpc_paginator **b UNNEEDED) +{ fprintf(stderr, "param_paginator called!\n"); abort(); }/* Generated stub for param_channel_id */ struct command_result *param_channel_id(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char *buffer UNNEEDED, diff --git a/wallet/wallet.c b/wallet/wallet.c index e2a34b18d171..1e0d94c985c5 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1,4 +1,5 @@ #include "config.h" +#include #include #include #include @@ -4528,7 +4529,8 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, const tal_t *ctx, enum forward_status status, const struct short_channel_id *chan_in, - const struct short_channel_id *chan_out) + const struct short_channel_id *chan_out, + const struct jsonrpc_paginator *paginator) { struct forwarding *results = tal_arr(ctx, struct forwarding, 0); size_t count = 0; @@ -4537,24 +4539,66 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, // placeholder for any parameter, the value doesn't matter because it's discarded by sql const int any = -1; - stmt = db_prepare_v2( - w->db, - SQL("SELECT" - " state" - ", in_msatoshi" - ", out_msatoshi" - ", in_channel_scid" - ", out_channel_scid" - ", in_htlc_id" - ", out_htlc_id" - ", received_time" - ", resolved_time" - ", failcode " - ", forward_style " - "FROM forwards " - "WHERE (1 = ? OR state = ?) AND " - "(1 = ? OR in_channel_scid = ?) AND " - "(1 = ? OR out_channel_scid = ?)")); + if (paginator && *paginator->reverse == true) { + stmt = db_prepare_v2( + w->db, + SQL("SELECT" + " state" + ", in_msatoshi" + ", out_msatoshi" + ", in_channel_scid" + ", out_channel_scid" + ", in_htlc_id" + ", out_htlc_id" + ", received_time" + ", resolved_time" + ", failcode " + ", forward_style " + "FROM forwards " + "ORDER BY ROWID DESC " + "WHERE (1 = ? OR state = ?) AND " + "(1 = ? OR in_channel_scid = ?) AND " + "(1 = ? OR out_channel_scid = ?)")); + } else if (paginator && *paginator->reverse == false) { + stmt = db_prepare_v2( + w->db, + SQL("SELECT" + " state" + ", in_msatoshi" + ", out_msatoshi" + ", in_channel_scid" + ", out_channel_scid" + ", in_htlc_id" + ", out_htlc_id" + ", received_time" + ", resolved_time" + ", failcode " + ", forward_style " + "FROM forwards " + "WHERE (1 = ? OR state = ?) AND " + "(1 = ? OR in_channel_scid = ?) AND " + "(1 = ? OR out_channel_scid = ?)")); + } + else { + stmt = db_prepare_v2( + w->db, + SQL("SELECT" + " state" + ", in_msatoshi" + ", out_msatoshi" + ", in_channel_scid" + ", out_channel_scid" + ", in_htlc_id" + ", out_htlc_id" + ", received_time" + ", resolved_time" + ", failcode " + ", forward_style " + "FROM forwards " + "WHERE (1 = ? OR state = ?) AND " + "(1 = ? OR in_channel_scid = ?) AND " + "(1 = ? OR out_channel_scid = ?)")); + } if (status == FORWARD_ANY) { // any status @@ -4586,6 +4630,12 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, db_bind_int(stmt, 5, any); } + if (paginator) { + assert(paginator->limit && paginator->offset); + db_bind_int(stmt, 6, *paginator->limit); + db_bind_int(stmt, 7, *paginator->offset); + } + db_query_prepared(stmt); for (count=0; db_step(stmt); count++) { diff --git a/wallet/wallet.h b/wallet/wallet.h index 759e99fdb7a5..f105ebaf0114 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -1359,7 +1360,8 @@ const struct forwarding *wallet_forwarded_payments_get(struct wallet *w, const tal_t *ctx, enum forward_status state, const struct short_channel_id *chan_in, - const struct short_channel_id *chan_out); + const struct short_channel_id *chan_out, + const struct jsonrpc_paginator *paginator); /** * Delete a particular forward entry