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
5 changes: 5 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -3887,6 +3887,7 @@
"Xpay.maxdelay": 7,
"Xpay.maxfee": 3,
"Xpay.partial_msat": 6,
"Xpay.payer_note": 8,
"Xpay.retry_for": 5
},
"XpayResponse": {
Expand Down Expand Up @@ -13422,6 +13423,10 @@
"added": "v24.11",
"deprecated": null
},
"Xpay.payer_note": {
"added": "v25.12",
"deprecated": null
},
"Xpay.payment_preimage": {
"added": "v24.11",
"deprecated": null
Expand Down
1 change: 1 addition & 0 deletions cln-grpc/proto/node.proto

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

2 changes: 2 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.

2 changes: 2 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.

7 changes: 7 additions & 0 deletions contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -36058,6 +36058,13 @@
"A payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case."
],
"default": "2016"
},
"payer_note": {
"type": "string",
"added": "v25.12",
"description": [
"To ask the issuer to include in the fetched invoice (only if invstring is a BIP353 address or offer)"
]
}
}
},
Expand Down
84 changes: 42 additions & 42 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions doc/schemas/xpay.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@
"A payment may be delayed for up to `maxdelay` blocks by another node; clients should be prepared for this worst case."
],
"default": "2016"
},
"payer_note": {
"type": "string",
"added": "v25.12",
"description": [
"To ask the issuer to include in the fetched invoice (only if invstring is a BIP353 address or offer)"
]
}
}
},
Expand Down
48 changes: 46 additions & 2 deletions plugins/libplugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,27 @@ static void destroy_out_req(struct out_req *out_req, struct plugin *plugin)
strmap_del(&plugin->out_reqs, out_req->id, NULL);
}

void forward_notified(struct command *cmd,
const char *method,
const char *buf,
const jsmntok_t *params,
void *arg)
{
const jsmntok_t *msgtok;
struct json_stream *js;

/* FIXME: There are also progress indicators, how to forward them? */
msgtok = json_get_member(buf, params, "message");
if (!msgtok)
return;

js = plugin_notify_start(cmd, "message");
json_add_tok(js, "level", json_get_member(buf, params, "level"), buf);
json_add_str_fmt(js, "message", "%s: %s",
method, json_strdup(tmpctx, buf, msgtok));
plugin_notify_end(cmd, js);
}

/* FIXME: Move lightningd/jsonrpc to common/ ? */

struct out_req *
Expand All @@ -409,6 +430,11 @@ jsonrpc_request_start_(struct command *cmd,
const char *buf,
const jsmntok_t *result,
void *arg),
void (*notified)(struct command *command,
const char *method,
const char *buf,
const jsmntok_t *params,
void *arg),
void *arg)
{
struct out_req *out;
Expand All @@ -420,6 +446,7 @@ jsonrpc_request_start_(struct command *cmd,
out->cmd = cmd;
out->cb = cb;
out->errcb = errcb;
out->notified = notified;
out->arg = arg;
strmap_add(&cmd->plugin->out_reqs, out->id, out);
tal_add_destructor2(out, destroy_out_req, cmd->plugin);
Expand Down Expand Up @@ -1085,9 +1112,19 @@ static void handle_rpc_reply(struct plugin *plugin, const char *buf, const jsmnt
bool cmd_freed;

idtok = json_get_member(buf, toks, "id");
if (!idtok)
/* FIXME: Don't simply ignore notifications! */
if (!idtok) {
const jsmntok_t *paramstok;
/* It's a notification about a command! */
paramstok = json_get_member(buf, toks, "params");
idtok = json_get_member(buf, paramstok, "id");
out = strmap_getn(&plugin->out_reqs,
json_tok_full(buf, idtok),
json_tok_full_len(idtok));
if (!out || !out->notified)
return;
out->notified(out->cmd, out->method, buf, paramstok, out->arg);
return;
}

out = strmap_getn(&plugin->out_reqs,
json_tok_full(buf, idtok),
Expand Down Expand Up @@ -1654,6 +1691,13 @@ static struct command_result *handle_init(struct command *cmd,
struct command *aux_cmd = aux_command(cmd);

io_new_conn(p, p->rpc_conn->fd, rpc_conn_init, p);

/* Enable notifications on all commands */
req = jsonrpc_request_start(aux_cmd, "notifications",
ignore_and_complete, plugin_broken_cb, NULL);
json_add_bool(req->js, "enable", true);
send_outreq(req);

/* In case they intercept rpc_command, we can't do this sync. */
req = jsonrpc_request_start(aux_cmd, "listconfigs",
get_beglist, plugin_broken_cb, NULL);
Expand Down
44 changes: 44 additions & 0 deletions plugins/libplugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ struct out_req {
const char *buf,
const jsmntok_t *error,
void *arg);
/* The callback when we a notification about this command. */
void (*notified)(struct command *command,
const char *method,
const char *buf,
const jsmntok_t *params,
void *arg);
void *arg;
};

Expand Down Expand Up @@ -135,6 +141,13 @@ struct command_result *plugin_broken_cb(struct command *cmd,
const jsmntok_t *result,
void *arg);

/* Helper to forward notifications */
void forward_notified(struct command *cmd,
const char *method,
const char *buf,
const jsmntok_t *params,
void *arg);

/* Helper to create a JSONRPC2 request stream. Send it with `send_outreq`. */
struct out_req *jsonrpc_request_start_(struct command *cmd,
const char *method,
Expand All @@ -150,6 +163,11 @@ struct out_req *jsonrpc_request_start_(struct command *cmd,
const char *buf,
const jsmntok_t *result,
void *arg),
void (*notified)(struct command *command,
const char *method,
const char *buf,
const jsmntok_t *params,
void *arg),
void *arg)
NON_NULL_ARGS(1, 2, 5);

Expand All @@ -167,6 +185,7 @@ struct out_req *jsonrpc_request_start_(struct command *cmd,
const char *mthod, \
const char *buf, \
const jsmntok_t *result), \
NULL, \
(arg))

#define jsonrpc_request_with_filter_start(cmd, method, filter, cb, errcb, arg) \
Expand All @@ -183,6 +202,7 @@ struct out_req *jsonrpc_request_start_(struct command *cmd,
const char *mthod, \
const char *buf, \
const jsmntok_t *result), \
NULL, \
(arg))

/* This variant has callbacks received whole obj, not "result" or
Expand All @@ -196,8 +216,32 @@ struct out_req *jsonrpc_request_start_(struct command *cmd,
const char *buf, \
const jsmntok_t *result), \
NULL, \
NULL, \
(arg))

/* This one handles notifications as well */
#define jsonrpc_request_notified_start(cmd, method, cb, errcb, notcb, arg) \
jsonrpc_request_start_((cmd), (method), NULL, NULL, \
typesafe_cb_preargs(struct command_result *, void *, \
(cb), (arg), \
struct command *command, \
const char *mthod, \
const char *buf, \
const jsmntok_t *result), \
typesafe_cb_preargs(struct command_result *, void *, \
(errcb), (arg), \
struct command *command, \
const char *mthod, \
const char *buf, \
const jsmntok_t *result), \
typesafe_cb_preargs(void, void *, \
(notcb), (arg), \
struct command *command, \
const char *mthod, \
const char *buf, \
const jsmntok_t *parms), \
(arg))

/* Batch of requests: cb and errcb are optional, finalcb is called when all complete. */
struct request_batch *request_batch_new_(const tal_t *ctx,
struct command_result *(*cb)(struct command *,
Expand Down
26 changes: 22 additions & 4 deletions plugins/xpay/xpay.c
Original file line number Diff line number Diff line change
Expand Up @@ -1348,10 +1348,11 @@ static struct command_result *getroutes_for(struct command *aux_cmd,
maxfee = AMOUNT_MSAT(0);
}

req = jsonrpc_request_start(aux_cmd, "getroutes",
getroutes_done,
getroutes_done_err,
payment);
req = jsonrpc_request_notified_start(aux_cmd, "getroutes",
getroutes_done,
getroutes_done_err,
forward_notified,
payment);

json_add_pubkey(req->js, "source", &xpay->local_id);
json_add_pubkey(req->js, "destination", dst);
Expand Down Expand Up @@ -1695,6 +1696,7 @@ struct xpay_params {
unsigned int retryfor;
u32 maxdelay, dev_maxparts;
const char *bip353;
const char *payer_note;
};

static struct command_result *
Expand All @@ -1718,6 +1720,8 @@ do_fetchinvoice(struct command *cmd, const char *offerstr, struct xpay_params *x
{
struct out_req *req;

plugin_notify_message(cmd, LOG_INFORM, "Fetching invoice for offer");
plugin_notify_message(cmd, LOG_DBG, "offer is %s", offerstr);
req = jsonrpc_request_start(cmd, "fetchinvoice",
invoice_fetched,
forward_error,
Expand All @@ -1727,6 +1731,8 @@ do_fetchinvoice(struct command *cmd, const char *offerstr, struct xpay_params *x
json_add_amount_msat(req->js, "amount_msat", *xparams->msat);
if (xparams->bip353)
json_add_string(req->js, "bip353", xparams->bip353);
if (xparams->payer_note)
json_add_string(req->js, "payer_note", xparams->payer_note);
return send_outreq(req);
}

Expand Down Expand Up @@ -1775,6 +1781,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
unsigned int *retryfor;
struct out_req *req;
struct xpay_params *xparams;
const char *payer_note;

if (!param_check(cmd, buffer, params,
p_req("invstring", param_invstring, &invstring),
Expand All @@ -1784,6 +1791,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
p_opt_def("retry_for", param_number, &retryfor, 60),
p_opt("partial_msat", param_msat, &partial),
p_opt_def("maxdelay", param_u32, &maxdelay, 2016),
p_opt("payer_note", param_string, &payer_note),
p_opt_dev("dev_maxparts", param_u32, &maxparts, 100),
NULL))
return command_param_failed();
Expand All @@ -1810,6 +1818,7 @@ static struct command_result *json_xpay_params(struct command *cmd,
xparams->layers = layers;
xparams->retryfor = *retryfor;
xparams->maxdelay = *maxdelay;
xparams->payer_note = tal_steal(xparams, payer_note);
xparams->dev_maxparts = *maxparts;
xparams->bip353 = NULL;

Expand All @@ -1825,16 +1834,25 @@ static struct command_result *json_xpay_params(struct command *cmd,
xparams->layers = layers;
xparams->retryfor = *retryfor;
xparams->maxdelay = *maxdelay;
xparams->payer_note = tal_steal(xparams, payer_note);
xparams->dev_maxparts = *maxparts;
xparams->bip353 = invstring;

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

plugin_notify_message(cmd, LOG_INFORM, "DNS lookup for %s", invstring);
req = jsonrpc_request_start(cmd, "fetchbip353",
bip353_fetched,
forward_error, xparams);
json_add_string(req->js, "address", invstring);
return send_outreq(req);
}

if (payer_note)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"payer_note only valid when paying an offer or BIP353 address");

return xpay_core(cmd, invstring,
msat, maxfee, layers, *retryfor, partial, *maxdelay, *maxparts,
as_pay);
Expand Down
17 changes: 17 additions & 0 deletions tests/test_askrene.py
Original file line number Diff line number Diff line change
Expand Up @@ -1536,3 +1536,20 @@ def test_simple_dummy_channel(node_factory):
final_cltv=5,
layers=["mylayer"],
)


def test_askrene_notifications(node_factory):
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True)

out = subprocess.check_output(['cli/lightning-cli',
'--network={}'.format(TEST_NETWORK),
'--lightning-dir={}'
.format(l1.daemon.lightning_dir),
'getroutes',
l1.info['id'],
l3.info['id'],
"10000msat",
"[]",
"1000msat",
"11"]).decode('utf-8').splitlines()
assert out == []
Loading
Loading