cln-plugin 0.1.9

A CLN plugin library. Write your plugin in Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
#include "config.h"
#include <bitcoin/preimage.h>
#include <bitcoin/privkey.h>
#include <ccan/tal/str/str.h>
#include <ccan/tal/tal.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <plugins/renepay/json.h>
#include <plugins/renepay/payment.h>
#include <plugins/renepay/payplugin.h>
#include <plugins/renepay/routetracker.h>

static struct command_result *payment_finish(struct payment *p);

struct payment *payment_new(
	    const tal_t *ctx,
	    const struct sha256 *payment_hash,
	    const char *invstr TAKES,
	    const char *label TAKES,
	    const char *description TAKES,
	    const struct secret *payment_secret TAKES,
	    const u8 *payment_metadata TAKES,
	    const struct route_info **routehints TAKES,
	    const struct node_id *destination,
	    struct amount_msat amount,
	    struct amount_msat maxfee,
	    unsigned int maxdelay,
	    u64 retryfor,
	    u16 final_cltv,
	    /* Tweakable in --developer mode */
	    u64 base_fee_penalty_millionths,
	    u64 prob_cost_factor_millionths,
	    u64 riskfactor_millionths,
	    u64 min_prob_success_millionths,
	    bool use_shadow)
{
	struct payment *p = tal(ctx, struct payment);
	struct payment_info *pinfo = &p->payment_info;

	/* === Unique properties === */
	assert(payment_hash);
	pinfo->payment_hash = *payment_hash;

	assert(invstr);
	pinfo->invstr = tal_strdup(p, invstr);

	pinfo->label = tal_strdup_or_null(p, label);
	pinfo->description = tal_strdup_or_null(p, description);
	pinfo->payment_secret = tal_dup_or_null(p, struct secret, payment_secret);
	pinfo->payment_metadata = tal_dup_talarr(p, u8, payment_metadata);

	if (taken(routehints))
		pinfo->routehints = tal_steal(p, routehints);
	else {
		/* Deep copy */
		pinfo->routehints =
		    tal_dup_talarr(p, const struct route_info *, routehints);
		for (size_t i = 0; i < tal_count(pinfo->routehints); i++)
			pinfo->routehints[i] =
			    tal_steal(pinfo->routehints, pinfo->routehints[i]);
	}

	assert(destination);
	pinfo->destination = *destination;
	pinfo->amount = amount;


	/* === Payment attempt parameters === */
	if (!amount_msat_add(&pinfo->maxspend, amount, maxfee))
		pinfo->maxspend = AMOUNT_MSAT(UINT64_MAX);
	pinfo->maxdelay = maxdelay;

	pinfo->start_time = time_now();
	pinfo->stop_time = timeabs_add(pinfo->start_time, time_from_sec(retryfor));

	pinfo->final_cltv = final_cltv;

	/* === Developer options === */
	pinfo->base_fee_penalty = base_fee_penalty_millionths / 1e6;
	pinfo->prob_cost_factor = prob_cost_factor_millionths / 1e6;
	pinfo->delay_feefactor = riskfactor_millionths / 1e6;
	pinfo->min_prob_success = min_prob_success_millionths / 1e6;
	pinfo->use_shadow = use_shadow;


	/* === Public State === */
	p->status = PAYMENT_PENDING;
	p->preimage = NULL;
	p->error_code = LIGHTNINGD;
	p->error_msg = NULL;
	p->total_sent = AMOUNT_MSAT(0);
	p->total_delivering = AMOUNT_MSAT(0);
	p->paynotes = tal_arr(p, const char *, 0);
	p->groupid = 1;


	/* === Hidden State === */
	p->exec_state = INVALID_STATE;
	p->next_partid = 1;
	p->cmd_array = tal_arr(p, struct command *, 0);
	p->local_gossmods = NULL;
	p->disabledmap = disabledmap_new(p);

	p->have_results = false;
	p->retry = false;
	p->waitresult_timer = NULL;

	p->routes_computed = NULL;
	p->routetracker = new_routetracker(p, p);
	return p;
}

/* A payment that finishes execution must clean its hidden state. */
static void payment_cleanup(struct payment *p)
{
	p->exec_state = INVALID_STATE;
	tal_resize(&p->cmd_array, 0);
	p->local_gossmods = tal_free(p->local_gossmods);

	/* FIXME: for optimization, a cleanup should prune all the data that has
	 * no use after a payent is completed. The entire disablemap structure
	 * is no longer needed, hence I guess we should free it not just reset
	 * it. */
	disabledmap_reset(p->disabledmap);
	p->waitresult_timer = tal_free(p->waitresult_timer);

	p->routes_computed = tal_free(p->routes_computed);
	routetracker_cleanup(p->routetracker);
}

bool payment_update(
		struct payment *p,
		struct amount_msat maxfee,
		unsigned int maxdelay,
		u64 retryfor,
		u16 final_cltv,
		    /* Tweakable in --developer mode */
		u64 base_fee_penalty_millionths,
		u64 prob_cost_factor_millionths,
		u64 riskfactor_millionths,
		u64 min_prob_success_millionths,
		bool use_shadow)
{
	assert(p);
	struct payment_info *pinfo = &p->payment_info;

	/* === Unique properties === */
	// unchanged

	/* === Payment attempt parameters === */
	if (!amount_msat_add(&pinfo->maxspend, pinfo->amount, maxfee))
		pinfo->maxspend = AMOUNT_MSAT(UINT64_MAX);
	pinfo->maxdelay = maxdelay;

	pinfo->start_time = time_now();
	pinfo->stop_time = timeabs_add(pinfo->start_time, time_from_sec(retryfor));

	pinfo->final_cltv = final_cltv;

	/* === Developer options === */
	pinfo->base_fee_penalty = base_fee_penalty_millionths / 1e6;
	pinfo->prob_cost_factor = prob_cost_factor_millionths / 1e6;
	pinfo->delay_feefactor = riskfactor_millionths / 1e6;
	pinfo->min_prob_success = min_prob_success_millionths / 1e6;
	pinfo->use_shadow = use_shadow;


	/* === Public State === */
	p->status = PAYMENT_PENDING;

	/* I shouldn't be calling a payment_update on a payment that already
	 * succeed */
	assert(p->preimage == NULL);

	p->error_code = LIGHTNINGD;
	p->error_msg = tal_free(p->error_msg);;
	p->total_sent = AMOUNT_MSAT(0);
	p->total_delivering = AMOUNT_MSAT(0);
	// p->paynotes are unchanged, they accumulate messages
	p->groupid++;


	/* === Hidden State === */
	p->exec_state = INVALID_STATE;
	p->next_partid = 1;

	/* I shouldn't be calling a payment_update on a payment that has pending
	 * cmds. */
	assert(p->cmd_array);
	assert(tal_count(p->cmd_array) == 0);

	p->local_gossmods = tal_free(p->local_gossmods);

	assert(p->disabledmap);
	disabledmap_reset(p->disabledmap);

	p->have_results = false;
	p->retry = false;
	p->waitresult_timer = tal_free(p->waitresult_timer);

	/* It is weird to have routes here stuck. */
	if (p->routes_computed)
		plugin_log(pay_plugin->plugin, LOG_UNUSUAL,
			   "We have %zu unsent routes in this payment.",
			   tal_count(p->routes_computed));
	p->routes_computed = tal_free(p->routes_computed);
	return true;
}

struct amount_msat payment_sent(const struct payment *p)
{
	assert(p);
	return p->total_sent;
}
struct amount_msat payment_delivered(const struct payment *p)
{
	assert(p);
	return p->total_delivering;
}
struct amount_msat payment_amount(const struct payment *p)
{
	assert(p);
	return p->payment_info.amount;
}

struct amount_msat payment_fees(const struct payment *p)
{
	assert(p);
	struct amount_msat fees;
	struct amount_msat sent = payment_sent(p),
			   delivered = payment_delivered(p);

	if (!amount_msat_sub(&fees, sent, delivered))
		plugin_err(
		    pay_plugin->plugin,
		    "Strange, sent amount (%s) is less than delivered (%s), "
		    "aborting.",
		    fmt_amount_msat(tmpctx, sent),
		    fmt_amount_msat(tmpctx, delivered));
	return fees;
}

u64 payment_parts(const struct payment *payment)
{
	assert(payment);
	return payment->next_partid - 1;
}

/* attach a command to this payment */
bool payment_register_command(struct payment *p, struct command *cmd)
{
	assert(p);
	assert(cmd);
	assert(p->cmd_array);
	tal_arr_expand(&p->cmd_array, cmd);
	return true;
}

/* are there pending commands on this payment? */
bool payment_commands_empty(const struct payment *p)
{
	assert(p);
	assert(p->cmd_array);
	return tal_count(p->cmd_array) == 0;
}

struct command *payment_command(struct payment *p)
{
	assert(p);
	assert(p->cmd_array);
	if (tal_count(p->cmd_array) == 0)
		return NULL;
	return p->cmd_array[0];
}

struct command_result *payment_success(struct payment *payment,
				       const struct preimage *preimage TAKES)
{
	assert(payment);
	assert(preimage);
	payment->status = PAYMENT_SUCCESS;
	payment->preimage = tal_free(payment->preimage);
	if(taken(preimage))
		payment->preimage = tal_steal(payment, preimage);
	else
		payment->preimage = tal_dup(payment, struct preimage, preimage);
	return payment_finish(payment);
}

struct command_result *payment_fail(struct payment *payment,
				    enum jsonrpc_errcode code, const char *fmt,
				    ...)
{
	payment->status = PAYMENT_FAIL;
	payment->error_code = code;
	payment->error_msg = tal_free(payment->error_msg);

	va_list args;
	va_start(args, fmt);
	payment->error_msg = tal_vfmt(payment, fmt, args);
	va_end(args);

	payment_note(payment, LOG_DBG, "Payment failed: %s",
		     payment->error_msg);

	return payment_finish(payment);
}

void payment_note(struct payment *p, enum log_level lvl, const char *fmt, ...)
{
	va_list ap;
	const char *str;

	va_start(ap, fmt);
	str = tal_vfmt(p->paynotes, fmt, ap);
	va_end(ap);

	tal_arr_expand(&p->paynotes, str);
	/* Log at debug, unless it's weird... */
	plugin_log(pay_plugin->plugin, lvl < LOG_UNUSUAL ? LOG_DBG : lvl, "%s",
		   str);

	for (size_t i = 0; i < tal_count(p->cmd_array); i++) {
		struct command *cmd = p->cmd_array[i];
		plugin_notify_message(cmd, lvl, "%s", str);
	}
}

static struct command_result *my_command_finish(struct payment *p,
						struct command *cmd)
{
	struct json_stream *result;
	if (p->status == PAYMENT_SUCCESS) {
		result = jsonrpc_stream_success(cmd);
		json_add_payment(result, p);
		return command_finished(cmd, result);
	}
	assert(p->status == PAYMENT_FAIL);
	assert(p->error_msg);
	return command_fail(cmd, p->error_code, "%s", p->error_msg);
}

static struct command_result *payment_finish(struct payment *p)
{
	assert(p->status == PAYMENT_FAIL || p->status == PAYMENT_SUCCESS);
	assert(!payment_commands_empty(p));
	struct command *cmd = p->cmd_array[0];

	// notify all commands that the payment completed
	for (size_t i = 1; i < tal_count(p->cmd_array); ++i) {
		my_command_finish(p, p->cmd_array[i]);
	}

	// set the payment into a valid final state
	payment_cleanup(p);

	return my_command_finish(p, cmd);
}

void payment_disable_chan(struct payment *p, struct short_channel_id scid,
			  enum log_level lvl, const char *fmt, ...)
{
	assert(p);
	assert(p->disabledmap);
	va_list ap;
	const char *str;

	va_start(ap, fmt);
	str = tal_vfmt(tmpctx, fmt, ap);
	va_end(ap);
	payment_note(p, lvl, "disabling %s: %s",
		     fmt_short_channel_id(tmpctx, scid),
		     str);
	disabledmap_add_channel(p->disabledmap, scid);
}

void payment_warn_chan(struct payment *p, struct short_channel_id scid,
		       enum log_level lvl, const char *fmt, ...)
{
	assert(p);
	assert(p->disabledmap);
	va_list ap;
	const char *str;

	va_start(ap, fmt);
	str = tal_vfmt(tmpctx, fmt, ap);
	va_end(ap);

	if (disabledmap_channel_is_warned(p->disabledmap, scid)) {
		payment_disable_chan(p, scid, lvl, "%s, channel warned twice",
				     str);
		return;
	}

	payment_note(
	    p, lvl, "flagged for warning %s: %s, next time it will be disabled",
	    fmt_short_channel_id(tmpctx, scid), str);
	disabledmap_warn_channel(p->disabledmap, scid);
}

void payment_disable_node(struct payment *p, struct node_id node,
			  enum log_level lvl, const char *fmt, ...)
{
	assert(p);
	assert(p->disabledmap);
	va_list ap;
	const char *str;

	va_start(ap, fmt);
	str = tal_vfmt(tmpctx, fmt, ap);
	va_end(ap);
	payment_note(p, lvl, "disabling node %s: %s",
		     fmt_node_id(tmpctx, &node),
		     str);
	disabledmap_add_node(p->disabledmap, node);
}