1 #include <ccan/mem/mem.h>
2 #include <ccan/tal/str/str.h>
3 #include <common/bolt12_merkle.h>
4 #include <common/type_to_string.h>
5 #include <plugins/offers.h>
6 #include <plugins/offers_inv_hook.h>
7 #include <secp256k1_schnorrsig.h>
8 
9 /* We need to keep the reply path around so we can reply if error */
10 struct inv {
11 	struct tlv_invoice *inv;
12 
13 	const char *buf;
14 	/* May be NULL */
15 	const jsmntok_t *replytok;
16 	struct tlv_onionmsg_payload_reply_path *reply_path;
17 
18 	/* The offer, once we've looked it up. */
19 	struct tlv_offer *offer;
20 };
21 
22 static struct command_result *WARN_UNUSED_RESULT
fail_inv_level(struct command * cmd,const struct inv * inv,enum log_level l,const char * fmt,va_list ap)23 fail_inv_level(struct command *cmd,
24 	       const struct inv *inv,
25 	       enum log_level l,
26 	       const char *fmt, va_list ap)
27 {
28 	char *full_fmt, *msg;
29 	struct tlv_invoice_error *err;
30 	u8 *errdata;
31 
32 	full_fmt = tal_fmt(tmpctx, "Failed invoice %s",
33 			   invoice_encode(tmpctx, inv->inv));
34 	if (inv->inv->offer_id)
35 		tal_append_fmt(&full_fmt, " for offer %s",
36 			       type_to_string(tmpctx, struct sha256,
37 					      inv->inv->offer_id));
38 	tal_append_fmt(&full_fmt, ": %s", fmt);
39 
40 	msg = tal_vfmt(tmpctx, full_fmt, ap);
41 	plugin_log(cmd->plugin, l, "%s", msg);
42 
43 	/* Only reply if they gave us a path */
44 	if (!inv->replytok && !inv->reply_path)
45 		return command_hook_success(cmd);
46 
47 	/* Don't send back internal error details. */
48 	if (l == LOG_BROKEN)
49 		msg = "Internal error";
50 
51 	err = tlv_invoice_error_new(cmd);
52 	/* Remove NUL terminator */
53 	err->error = tal_dup_arr(err, char, msg, strlen(msg), 0);
54 	/* FIXME: Add suggested_value / erroneous_field! */
55 
56 	errdata = tal_arr(cmd, u8, 0);
57 	towire_invoice_error(&errdata, err);
58 	return send_onion_reply(cmd, inv->reply_path, inv->buf, inv->replytok,
59 				"invoice_error", errdata);
60 }
61 
62 static struct command_result *WARN_UNUSED_RESULT
fail_inv(struct command * cmd,const struct inv * inv,const char * fmt,...)63 fail_inv(struct command *cmd,
64 	 const struct inv *inv,
65 	 const char *fmt, ...)
66 {
67 	va_list ap;
68 	struct command_result *ret;
69 
70 	va_start(ap, fmt);
71 	ret = fail_inv_level(cmd, inv, LOG_DBG, fmt, ap);
72 	va_end(ap);
73 
74 	return ret;
75 }
76 
77 static struct command_result *WARN_UNUSED_RESULT
fail_internalerr(struct command * cmd,const struct inv * inv,const char * fmt,...)78 fail_internalerr(struct command *cmd,
79 		 const struct inv *inv,
80 		 const char *fmt, ...)
81 {
82 	va_list ap;
83 	struct command_result *ret;
84 
85 	va_start(ap, fmt);
86 	ret = fail_inv_level(cmd, inv, LOG_BROKEN, fmt, ap);
87 	va_end(ap);
88 
89 	return ret;
90 }
91 
92 #define inv_must_have(cmd_, i_, fld_)				\
93 	test_field(cmd_, i_, i_->inv->fld_ != NULL, #fld_, "missing")
94 #define inv_must_not_have(cmd_, i_, fld_)				\
95 	test_field(cmd_, i_, i_->inv->fld_ == NULL, #fld_, "unexpected")
96 #define inv_must_equal_offer(cmd_, i_, fld_)				\
97 	test_field_eq(cmd_, i_, i_->inv->fld_, i_->offer->fld_, #fld_)
98 
99 static struct command_result *
test_field(struct command * cmd,const struct inv * inv,bool test,const char * fieldname,const char * what)100 test_field(struct command *cmd,
101 	   const struct inv *inv,
102 	   bool test, const char *fieldname, const char *what)
103 {
104 	if (!test)
105 		return fail_inv(cmd, inv, "%s %s", what, fieldname);
106 	return NULL;
107 }
108 
109 static struct command_result *
test_field_eq(struct command * cmd,const struct inv * inv,const tal_t * invfield,const tal_t * offerfield,const char * fieldname)110 test_field_eq(struct command *cmd,
111 	      const struct inv *inv,
112 	      const tal_t *invfield,
113 	      const tal_t *offerfield,
114 	      const char *fieldname)
115 {
116 	if (invfield && !offerfield)
117 		return fail_inv(cmd, inv, "Unexpected %s", fieldname);
118 	if (!invfield && offerfield)
119 		return fail_inv(cmd, inv, "Expected %s", fieldname);
120 	if (!memeq(invfield, tal_bytelen(invfield),
121 		   offerfield, tal_bytelen(offerfield)))
122 		return fail_inv(cmd, inv, "Different %s", fieldname);
123 	return NULL;
124 }
125 
pay_done(struct command * cmd,const char * buf,const jsmntok_t * result,struct inv * inv)126 static struct command_result *pay_done(struct command *cmd,
127 				       const char *buf,
128 				       const jsmntok_t *result,
129 				       struct inv *inv)
130 {
131 	struct amount_msat msat = amount_msat(*inv->inv->amount);
132 
133 	plugin_log(cmd->plugin, LOG_INFORM,
134 		   "Payed out %s for offer %s%s: %.*s",
135 		   type_to_string(tmpctx, struct amount_msat, &msat),
136 		   type_to_string(tmpctx, struct sha256, inv->inv->offer_id),
137 		   inv->offer->refund_for ? " (refund)": "",
138 		   json_tok_full_len(result),
139 		   json_tok_full(buf, result));
140 	return command_hook_success(cmd);
141 }
142 
pay_error(struct command * cmd,const char * buf,const jsmntok_t * error,struct inv * inv)143 static struct command_result *pay_error(struct command *cmd,
144 					const char *buf,
145 					const jsmntok_t *error,
146 					struct inv *inv)
147 {
148 	const jsmntok_t *msgtok = json_get_member(buf, error, "message");
149 
150 	return fail_inv(cmd, inv, "pay attempt failed: %.*s",
151 			json_tok_full_len(msgtok),
152 			json_tok_full(buf, msgtok));
153 }
154 
listoffers_done(struct command * cmd,const char * buf,const jsmntok_t * result,struct inv * inv)155 static struct command_result *listoffers_done(struct command *cmd,
156 					      const char *buf,
157 					      const jsmntok_t *result,
158 					      struct inv *inv)
159 {
160 	const jsmntok_t *arr = json_get_member(buf, result, "offers");
161 	const jsmntok_t *offertok, *activetok, *b12tok;
162 	bool active;
163 	struct amount_msat amt;
164 	char *fail;
165 	struct out_req *req;
166 	struct command_result *err;
167 
168 	/* BOLT-offers #12:
169 	 * - otherwise if `offer_id` is set:
170 	 *   - MUST reject the invoice if the `offer_id` does not refer an
171 	 *     unexpired offer with `send_invoice`
172 	 */
173 	if (arr->size == 0)
174 		return fail_inv(cmd, inv, "Unknown offer");
175 
176 	plugin_log(cmd->plugin, LOG_INFORM,
177 		   "Attempting payment of offer %.*s",
178 		   json_tok_full_len(result),
179 		   json_tok_full(buf, result));
180 
181 	offertok = arr + 1;
182 	activetok = json_get_member(buf, offertok, "active");
183 	if (!activetok) {
184 		return fail_internalerr(cmd, inv,
185 					"Missing active: %.*s",
186 					json_tok_full_len(offertok),
187 					json_tok_full(buf, offertok));
188 	}
189 	json_to_bool(buf, activetok, &active);
190 	if (!active)
191 		return fail_inv(cmd, inv, "Offer no longer available");
192 
193 	b12tok = json_get_member(buf, offertok, "bolt12");
194 	if (!b12tok) {
195 		return fail_internalerr(cmd, inv,
196 					"Missing bolt12: %.*s",
197 					json_tok_full_len(offertok),
198 					json_tok_full(buf, offertok));
199 	}
200 	inv->offer = offer_decode(inv,
201 				 buf + b12tok->start,
202 				 b12tok->end - b12tok->start,
203 				 plugin_feature_set(cmd->plugin),
204 				 chainparams, &fail);
205 	if (!inv->offer) {
206 		return fail_internalerr(cmd, inv,
207 					"Invalid offer: %s (%.*s)",
208 					fail,
209 					json_tok_full_len(offertok),
210 					json_tok_full(buf, offertok));
211 	}
212 
213 	if (inv->offer->absolute_expiry
214 	    && time_now().ts.tv_sec >= *inv->offer->absolute_expiry) {
215 		/* FIXME: do deloffer to disable it */
216 		return fail_inv(cmd, inv, "Offer expired");
217 	}
218 
219 	if (!inv->offer->send_invoice) {
220 		return fail_inv(cmd, inv, "Offer did not expect invoice");
221 	}
222 
223 	/* BOLT-offers #12:
224 	 * - MUST reject the invoice unless the following fields are equal
225 	 *   or unset exactly as they are in the `offer`:
226 	 *   - `refund_for`
227 	 *   - `description`
228 	 */
229 	err = inv_must_equal_offer(cmd, inv, refund_for);
230 	if (err)
231 		return err;
232 	err = inv_must_equal_offer(cmd, inv, description);
233 	if (err)
234 		return err;
235 
236 	/* BOLT-offers #12:
237 	 * - if the offer had a `quantity_min` or `quantity_max` field:
238 	 *   - MUST fail the request if there is no `quantity` field.
239 	 *   - MUST fail the request if there is `quantity` is not within
240 	 *     that (inclusive) range.
241 	 * - otherwise:
242 	 *   - MUST fail the request if there is a `quantity` field.
243 	 */
244 	if (inv->offer->quantity_min || inv->offer->quantity_max) {
245 		err = inv_must_have(cmd, inv, quantity);
246 		if (err)
247 			return err;
248 
249 		if (inv->offer->quantity_min &&
250 		    *inv->inv->quantity < *inv->offer->quantity_min) {
251 			return fail_inv(cmd, inv,
252 					"quantity %"PRIu64 " < %"PRIu64,
253 					*inv->inv->quantity,
254 					*inv->offer->quantity_min);
255 		}
256 
257 		if (inv->offer->quantity_max &&
258 		    *inv->inv->quantity > *inv->offer->quantity_max) {
259 			return fail_inv(cmd, inv,
260 					"quantity %"PRIu64" > %"PRIu64,
261 					*inv->inv->quantity,
262 					*inv->offer->quantity_max);
263 		}
264 	} else {
265 		err = inv_must_not_have(cmd, inv, quantity);
266 		if (err)
267 			return err;
268 	}
269 
270 	/* BOLT-offers #12:
271 	 * - MUST reject the invoice if `msat` is not present.
272 	 */
273 	err = inv_must_have(cmd, inv, amount);
274 	if (err)
275 		return err;
276 
277 	/* FIXME: Handle alternate currency conversion here! */
278 	if (inv->offer->currency)
279 		return fail_inv(cmd, inv, "FIXME: support currency");
280 
281 	amt = amount_msat(*inv->inv->amount);
282 	/* If you send an offer without an amount, you want to give away
283 	 * unlimited money.  Err, ok? */
284 	if (inv->offer->amount) {
285 		struct amount_msat expected = amount_msat(*inv->offer->amount);
286 
287 		/* We could allow invoices for less, I suppose. */
288 		if (!amount_msat_eq(expected, amt))
289 			return fail_inv(cmd, inv, "Expected invoice for %s",
290 					fmt_amount_msat(tmpctx, expected));
291 	}
292 
293 	plugin_log(cmd->plugin, LOG_INFORM,
294 		   "Attempting payment of %s for offer %s%s",
295 		   type_to_string(tmpctx, struct amount_msat, &amt),
296 		   type_to_string(tmpctx, struct sha256, inv->inv->offer_id),
297 		   inv->offer->refund_for ? " (refund)": "");
298 
299 	req = jsonrpc_request_start(cmd->plugin, cmd, "pay",
300 				    pay_done, pay_error, inv);
301 	json_add_string(req->js, "bolt11", invoice_encode(tmpctx, inv->inv));
302 	json_add_sha256(req->js, "localofferid", inv->inv->offer_id);
303 	return send_outreq(cmd->plugin, req);
304 }
305 
listoffers_error(struct command * cmd,const char * buf,const jsmntok_t * err,struct inv * inv)306 static struct command_result *listoffers_error(struct command *cmd,
307 					       const char *buf,
308 					       const jsmntok_t *err,
309 					       struct inv *inv)
310 {
311 	return fail_internalerr(cmd, inv,
312 				"listoffers gave JSON error: %.*s",
313 				json_tok_full_len(err),
314 				json_tok_full(buf, err));
315 }
316 
handle_invoice(struct command * cmd,const char * buf,const jsmntok_t * invtok,const jsmntok_t * replytok,struct tlv_onionmsg_payload_reply_path * reply_path)317 struct command_result *handle_invoice(struct command *cmd,
318 				      const char *buf,
319 				      const jsmntok_t *invtok,
320 				      const jsmntok_t *replytok,
321 				      struct tlv_onionmsg_payload_reply_path *reply_path)
322 {
323 	const u8 *invbin = json_tok_bin_from_hex(cmd, buf, invtok);
324 	size_t len = tal_count(invbin);
325 	struct inv *inv = tal(cmd, struct inv);
326 	struct out_req *req;
327 	struct command_result *err;
328 	int bad_feature;
329 	struct sha256 m, shash;
330 
331 	if (reply_path) {
332 		inv->buf = NULL;
333 		inv->replytok = NULL;
334 		inv->reply_path = reply_path;
335 	} else {
336 		/* Make a copy of entire buffer, for later. */
337 		inv->buf = tal_dup_arr(inv, char, buf, replytok->end, 0);
338 		inv->replytok = tal_dup_arr(inv, jsmntok_t, replytok,
339 					    json_next(replytok) - replytok, 0);
340 		inv->reply_path = NULL;
341 	}
342 
343 	inv->inv = tlv_invoice_new(cmd);
344 	if (!fromwire_invoice(&invbin, &len, inv->inv)) {
345 		return fail_inv(cmd, inv,
346 				"Invalid invoice %s",
347 				tal_hex(tmpctx, invbin));
348 	}
349 
350 	/* BOLT-offers #12:
351 	 *
352 	 * The reader of an invoice_request:
353 	 *...
354 	 *   - MUST fail the request if `features` contains unknown even bits.
355 	 */
356 	bad_feature = features_unsupported(plugin_feature_set(cmd->plugin),
357 					   inv->inv->features,
358 					   BOLT11_FEATURE);
359 	if (bad_feature != -1) {
360 		return fail_inv(cmd, inv,
361 				"Unsupported inv feature %i",
362 				bad_feature);
363 	}
364 
365 	/* BOLT-offers #12:
366 	 *
367 	 * The reader of an invoice_request:
368 	 *...
369 	 *   - MUST fail the request if `chains` does not include (or imply) a
370 	 *     supported chain.
371 	 */
372 	if (!bolt12_chain_matches(inv->inv->chain, chainparams, inv->inv->chains)) {
373 		return fail_inv(cmd, inv,
374 				"Wrong chains %s",
375 				tal_hex(tmpctx, inv->inv->chain));
376 	}
377 
378 	/* BOLT-offers #12:
379 	 * - MUST reject the invoice if `signature` is not a valid signature
380 	 *   using `node_id` as described in
381 	 *   [Signature Calculation](#signature-calculation).
382 	 */
383 	err = inv_must_have(cmd, inv, node_id);
384 	if (err)
385 		return err;
386 
387 	err = inv_must_have(cmd, inv, signature);
388 	if (err)
389 		return err;
390 
391 	merkle_tlv(inv->inv->fields, &m);
392 	sighash_from_merkle("invoice", "signature", &m, &shash);
393 	if (secp256k1_schnorrsig_verify(secp256k1_ctx,
394 					inv->inv->signature->u8,
395 					shash.u.u8,
396 					&inv->inv->node_id->pubkey) != 1) {
397 		return fail_inv(cmd, inv, "Bad signature");
398 	}
399 
400 	/* We don't pay random invoices off the internet, sorry. */
401 	err = inv_must_have(cmd, inv, offer_id);
402 	if (err)
403 		return err;
404 
405 	/* Now find the offer. */
406 	req = jsonrpc_request_start(cmd->plugin, cmd, "listoffers",
407 				    listoffers_done, listoffers_error, inv);
408 	json_add_sha256(req->js, "offer_id", inv->inv->offer_id);
409 	return send_outreq(cmd->plugin, req);
410 }
411 
412