Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-merchant-httpd_exchanges.c
18 : * @brief logic this HTTPD keeps for each exchange we interact with
19 : * @author Marcello Stanisci
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <taler/taler_json_lib.h>
24 : #include <taler/taler_dbevents.h>
25 : #include "taler-merchant-httpd_exchanges.h"
26 : #include "taler-merchant-httpd.h"
27 : #include <regex.h>
28 :
29 : /**
30 : * How often do we retry DB transactions with soft errors?
31 : */
32 : #define MAX_RETRIES 3
33 :
34 : /**
35 : * Threshold after which exponential backoff should not increase.
36 : */
37 : #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \
38 : GNUNET_TIME_UNIT_SECONDS, 60)
39 :
40 : /**
41 : * This is how long /keys long-polls for, so we should
42 : * allow this time between requests if there is no
43 : * answer. See exchange_api_handle.c.
44 : */
45 : #define LONG_POLL_THRESHOLD GNUNET_TIME_relative_multiply ( \
46 : GNUNET_TIME_UNIT_SECONDS, 120)
47 :
48 :
49 : /**
50 : * Information we keep for a pending #MMH_EXCHANGES_keys4exchange() operation.
51 : */
52 : struct TMH_EXCHANGES_KeysOperation
53 : {
54 :
55 : /**
56 : * Kept in a DLL.
57 : */
58 : struct TMH_EXCHANGES_KeysOperation *next;
59 :
60 : /**
61 : * Kept in a DLL.
62 : */
63 : struct TMH_EXCHANGES_KeysOperation *prev;
64 :
65 : /**
66 : * Function to call with the result.
67 : */
68 : TMH_EXCHANGES_Find2Continuation fc;
69 :
70 : /**
71 : * Closure for @e fc.
72 : */
73 : void *fc_cls;
74 :
75 : /**
76 : * Exchange we wait for the /keys for.
77 : */
78 : struct TMH_Exchange *my_exchange;
79 :
80 : /**
81 : * Task scheduled to asynchronously return the result to
82 : * the find continuation.
83 : */
84 : struct GNUNET_SCHEDULER_Task *at;
85 :
86 : };
87 :
88 :
89 : /**
90 : * Information about wire transfer fees of an exchange, by wire method.
91 : */
92 : struct FeesByWireMethod
93 : {
94 :
95 : /**
96 : * Kept in a DLL.
97 : */
98 : struct FeesByWireMethod *next;
99 :
100 : /**
101 : * Kept in a DLL.
102 : */
103 : struct FeesByWireMethod *prev;
104 :
105 : /**
106 : * Wire method these fees are for.
107 : */
108 : char *wire_method;
109 :
110 : /**
111 : * Applicable fees, NULL if unknown/error.
112 : */
113 : struct TALER_EXCHANGE_WireAggregateFees *af;
114 :
115 : };
116 :
117 :
118 : /**
119 : * Internal representation for an exchange
120 : */
121 : struct TMH_Exchange
122 : {
123 :
124 : /**
125 : * Kept in a DLL.
126 : */
127 : struct TMH_Exchange *next;
128 :
129 : /**
130 : * Kept in a DLL.
131 : */
132 : struct TMH_Exchange *prev;
133 :
134 : /**
135 : * Head of FOs pending for this exchange.
136 : */
137 : struct TMH_EXCHANGES_KeysOperation *keys_head;
138 :
139 : /**
140 : * Tail of FOs pending for this exchange.
141 : */
142 : struct TMH_EXCHANGES_KeysOperation *keys_tail;
143 :
144 : /**
145 : * (base) URL of the exchange.
146 : */
147 : char *url;
148 :
149 : /**
150 : * Currency offered by the exchange according to OUR configuration.
151 : */
152 : char *currency;
153 :
154 : /**
155 : * The keys of this exchange.
156 : */
157 : struct TALER_EXCHANGE_Keys *keys;
158 :
159 : /**
160 : * Head of wire fees from /wire request.
161 : */
162 : struct FeesByWireMethod *wire_fees_head;
163 :
164 : /**
165 : * Tail of wire fees from /wire request.
166 : */
167 : struct FeesByWireMethod *wire_fees_tail;
168 :
169 : /**
170 : * Task to retry downloading /keys again.
171 : */
172 : struct GNUNET_SCHEDULER_Task *retry_task;
173 :
174 : /**
175 : * When are we willing to force downloading again?
176 : */
177 : struct GNUNET_TIME_Absolute first_retry;
178 :
179 : /**
180 : * Current exponential back-off for @e retry_task.
181 : */
182 : struct GNUNET_TIME_Relative retry_delay;
183 :
184 : /**
185 : * Master public key of the exchange.
186 : */
187 : struct TALER_MasterPublicKeyP master_pub;
188 :
189 : /**
190 : * true if this exchange is from our configuration and
191 : * explicitly trusted, false if we need to check each
192 : * key to be sure it is trusted.
193 : */
194 : bool trusted;
195 :
196 : };
197 :
198 :
199 : /**
200 : * Head of exchanges we know about.
201 : */
202 : static struct TMH_Exchange *exchange_head;
203 :
204 : /**
205 : * Tail of exchanges we know about.
206 : */
207 : static struct TMH_Exchange *exchange_tail;
208 :
209 : /**
210 : * Our event handler listening for /keys downloads
211 : * being put into the database.
212 : */
213 : static struct GNUNET_DB_EventHandler *keys_eh;
214 :
215 : /**
216 : * How many exchanges do we trust (for our configured
217 : * currency) as per our configuration? Used for a
218 : * sanity-check on startup.
219 : */
220 : static int trusted_exchange_count;
221 :
222 :
223 : const struct TALER_MasterPublicKeyP *
224 78 : TMH_EXCHANGES_get_master_pub (
225 : const struct TMH_Exchange *exchange)
226 : {
227 78 : GNUNET_break ( (exchange->trusted) ||
228 : (NULL != exchange->keys) );
229 78 : return &exchange->master_pub;
230 : }
231 :
232 :
233 : const char *
234 78 : TMH_EXCHANGES_get_currency (
235 : const struct TMH_Exchange *exchange)
236 : {
237 78 : return exchange->currency;
238 : }
239 :
240 :
241 : /**
242 : * Lookup exchange by @a exchange_url. Create one
243 : * if it does not exist.
244 : *
245 : * @param exchange_url base URL to match against
246 : * @return fresh entry if exchange was not yet known
247 : */
248 : static struct TMH_Exchange *
249 152 : lookup_exchange (const char *exchange_url)
250 : {
251 152 : for (struct TMH_Exchange *exchange = exchange_head;
252 152 : NULL != exchange;
253 0 : exchange = exchange->next)
254 137 : if (0 == strcmp (exchange->url,
255 : exchange_url))
256 137 : return exchange;
257 15 : return NULL;
258 : }
259 :
260 :
261 : bool
262 9 : TMH_EXCHANGES_check_trusted (
263 : const char *exchange_url)
264 : {
265 9 : struct TMH_Exchange *exchange = lookup_exchange (exchange_url);
266 :
267 9 : if (NULL == exchange)
268 0 : return false;
269 9 : return exchange->trusted;
270 : }
271 :
272 :
273 : /**
274 : * Check if we have any remaining pending requests for the
275 : * given @a exchange, and if we have the required data, call
276 : * the callback.
277 : *
278 : * @param exchange the exchange to check for pending find operations
279 : */
280 : static void
281 117 : process_find_operations (struct TMH_Exchange *exchange)
282 : {
283 : struct GNUNET_TIME_Timestamp now;
284 :
285 117 : now = GNUNET_TIME_timestamp_get ();
286 117 : for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
287 232 : NULL != fbw;
288 115 : fbw = fbw->next)
289 : {
290 115 : while ( (NULL != fbw->af) &&
291 115 : (GNUNET_TIME_timestamp_cmp (fbw->af->end_date,
292 : <,
293 : now)) )
294 : {
295 0 : struct TALER_EXCHANGE_WireAggregateFees *af = fbw->af;
296 :
297 0 : fbw->af = af->next;
298 0 : GNUNET_free (af);
299 : }
300 115 : if (NULL == fbw->af)
301 : {
302 : /* Disagreement on the current time */
303 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
304 : "Exchange has no wire fees configured for `%s' wire method\n",
305 : fbw->wire_method);
306 : }
307 115 : else if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date,
308 : >,
309 : now))
310 : {
311 : /* Disagreement on the current time */
312 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
313 : "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n",
314 : GNUNET_TIME_relative2s (
315 : GNUNET_TIME_absolute_get_remaining (
316 : fbw->af->start_date.abs_time),
317 : true));
318 : }
319 : } /* for all wire methods */
320 :
321 : {
322 : struct TMH_EXCHANGES_KeysOperation *kon;
323 :
324 117 : kon = NULL;
325 117 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
326 : "Processing find operations for `%s'\n",
327 : exchange->url);
328 117 : for (struct TMH_EXCHANGES_KeysOperation *ko = exchange->keys_head;
329 228 : NULL != ko;
330 111 : ko = kon)
331 : {
332 111 : kon = ko->next;
333 111 : ko->fc (ko->fc_cls,
334 : exchange->keys,
335 : exchange);
336 111 : TMH_EXCHANGES_keys4exchange_cancel (ko);
337 : }
338 : }
339 117 : }
340 :
341 :
342 : /**
343 : * Function called with information about the wire fees for each wire method.
344 : * Stores the wire fees within our internal data structures for later use.
345 : *
346 : * @param exchange connection to the exchange
347 : * @param master_pub public key of the exchange
348 : * @param num_methods number of wire methods supported
349 : * @param fbm wire fees by method
350 : * @return #GNUNET_OK on success
351 : */
352 : static enum GNUNET_GenericReturnValue
353 11 : process_wire_fees (
354 : struct TMH_Exchange *exchange,
355 : const struct TALER_MasterPublicKeyP *master_pub,
356 : unsigned int num_methods,
357 : const struct TALER_EXCHANGE_WireFeesByMethod fbm[static num_methods])
358 11 : {
359 22 : for (unsigned int i = 0; i<num_methods; i++)
360 : {
361 11 : const char *wire_method = fbm[i].method;
362 11 : const struct TALER_EXCHANGE_WireAggregateFees *fees = fbm[i].fees_head;
363 : struct FeesByWireMethod *f;
364 : struct TALER_EXCHANGE_WireAggregateFees *endp;
365 :
366 11 : for (f = exchange->wire_fees_head; NULL != f; f = f->next)
367 0 : if (0 == strcasecmp (wire_method,
368 0 : f->wire_method))
369 0 : break;
370 11 : if (NULL == f)
371 : {
372 11 : f = GNUNET_new (struct FeesByWireMethod);
373 11 : f->wire_method = GNUNET_strdup (wire_method);
374 11 : GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
375 : exchange->wire_fees_tail,
376 : f);
377 : }
378 11 : endp = f->af;
379 11 : while ( (NULL != endp) &&
380 0 : (NULL != endp->next) )
381 0 : endp = endp->next;
382 11 : while ( (NULL != endp) &&
383 11 : (NULL != fees) &&
384 0 : (GNUNET_TIME_timestamp_cmp (fees->start_date,
385 : <,
386 : endp->end_date)) )
387 0 : fees = fees->next;
388 11 : if ( (NULL != endp) &&
389 0 : (NULL != fees) &&
390 0 : (GNUNET_TIME_timestamp_cmp (fees->start_date,
391 : !=,
392 : endp->end_date)) )
393 : {
394 : /* Hole or overlap in the fee structure, not allowed! */
395 0 : GNUNET_break_op (0);
396 0 : return GNUNET_SYSERR;
397 : }
398 22 : while (NULL != fees)
399 : {
400 : struct TALER_EXCHANGE_WireAggregateFees *af;
401 :
402 11 : af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
403 11 : *af = *fees;
404 11 : af->next = NULL;
405 11 : if (NULL == endp)
406 11 : f->af = af;
407 : else
408 0 : endp->next = af;
409 11 : endp = af;
410 11 : fees = fees->next;
411 : } /* all fees for this method */
412 : } /* for all methods (i) */
413 11 : return GNUNET_OK;
414 : }
415 :
416 :
417 : /**
418 : * Retry getting keys from the given exchange in the closure.
419 : *
420 : * @param cls the `struct TMH_Exchange *`
421 : */
422 : static void
423 17 : retry_exchange (void *cls)
424 : {
425 17 : struct TMH_Exchange *exchange = cls;
426 17 : struct GNUNET_DB_EventHeaderP es = {
427 17 : .size = ntohs (sizeof (es)),
428 17 : .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS)
429 : };
430 :
431 17 : exchange->retry_task = NULL;
432 : exchange->retry_delay
433 17 : = GNUNET_TIME_randomized_backoff (exchange->retry_delay,
434 : RETRY_BACKOFF_THRESHOLD);
435 : exchange->first_retry
436 17 : = GNUNET_TIME_relative_to_absolute (
437 : exchange->retry_delay);
438 :
439 17 : TMH_db->event_notify (TMH_db->cls,
440 : &es,
441 17 : exchange->url,
442 17 : strlen (exchange->url) + 1);
443 17 : }
444 :
445 :
446 : /**
447 : * Task to asynchronously return keys operation result to caller.
448 : *
449 : * @param cls a `struct TMH_EXCHANGES_KeysOperation`
450 : */
451 : static void
452 106 : return_keys (void *cls)
453 : {
454 106 : struct TMH_EXCHANGES_KeysOperation *fo = cls;
455 106 : struct TMH_Exchange *exchange = fo->my_exchange;
456 :
457 106 : fo->at = NULL;
458 106 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
459 : "Returning key data for %s instantly\n",
460 : exchange->url);
461 106 : process_find_operations (exchange);
462 106 : }
463 :
464 :
465 : struct TMH_EXCHANGES_KeysOperation *
466 111 : TMH_EXCHANGES_keys4exchange (
467 : const char *chosen_exchange,
468 : bool force_download,
469 : TMH_EXCHANGES_Find2Continuation fc,
470 : void *fc_cls)
471 : {
472 : struct TMH_Exchange *exchange;
473 : struct TMH_EXCHANGES_KeysOperation *fo;
474 :
475 111 : if (NULL == TMH_curl_ctx)
476 : {
477 0 : GNUNET_break (0);
478 0 : return NULL;
479 : }
480 111 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
481 : "Trying to find chosen exchange `%s'\n",
482 : chosen_exchange);
483 111 : exchange = lookup_exchange (chosen_exchange);
484 111 : if (NULL == exchange)
485 : {
486 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
487 : "Exchange `%s' not configured\n",
488 : chosen_exchange);
489 0 : return NULL;
490 : }
491 111 : if (! exchange->trusted)
492 : {
493 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
494 : "Exchange `%s' not trusted\n",
495 : chosen_exchange);
496 0 : return NULL;
497 : }
498 111 : fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation);
499 111 : fo->fc = fc;
500 111 : fo->fc_cls = fc_cls;
501 111 : fo->my_exchange = exchange;
502 111 : GNUNET_CONTAINER_DLL_insert (exchange->keys_head,
503 : exchange->keys_tail,
504 : fo);
505 111 : if ( (NULL == exchange->keys) &&
506 4 : (! force_download) )
507 : {
508 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
509 : "Waiting for `%skeys' already, failing query instantly\n",
510 : exchange->url);
511 2 : GNUNET_assert (NULL == fo->at);
512 2 : fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
513 : fo);
514 2 : return fo;
515 : }
516 109 : if ( (NULL != exchange->keys) &&
517 214 : (! force_download) &&
518 107 : (GNUNET_TIME_absolute_is_future (
519 107 : exchange->keys->key_data_expiration.abs_time)) )
520 : {
521 : /* We have a valid reply, immediately return result */
522 107 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
523 : "The exchange `%s' is ready\n",
524 : exchange->url);
525 107 : GNUNET_assert (NULL == fo->at);
526 107 : fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
527 : fo);
528 107 : return fo;
529 : }
530 4 : if ( (force_download) &&
531 2 : (GNUNET_TIME_absolute_is_future (exchange->first_retry)) &&
532 0 : (NULL != exchange->keys) )
533 : {
534 : /* Return results immediately. */
535 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
536 : "Earliest retry is in the future, returning keys now\n");
537 0 : fo->at = GNUNET_SCHEDULER_add_now (&return_keys,
538 : fo);
539 : /* *no* return here, we MAY schedule a 'retry_task' in the
540 : next block if there isn't one yet */
541 : }
542 2 : if (NULL == exchange->retry_task)
543 : exchange->retry_task
544 2 : = GNUNET_SCHEDULER_add_at (exchange->first_retry,
545 : &retry_exchange,
546 : exchange);
547 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
548 : "Next %skeys request scheduled for %s\n",
549 : exchange->url,
550 : GNUNET_TIME_absolute2s (
551 : exchange->first_retry));
552 : /* No activity to launch, we are already doing so. */
553 2 : return fo;
554 : }
555 :
556 :
557 : void
558 111 : TMH_EXCHANGES_keys4exchange_cancel (
559 : struct TMH_EXCHANGES_KeysOperation *fo)
560 : {
561 111 : struct TMH_Exchange *exchange = fo->my_exchange;
562 :
563 111 : if (NULL != fo->at)
564 : {
565 3 : GNUNET_SCHEDULER_cancel (fo->at);
566 3 : fo->at = NULL;
567 : }
568 111 : GNUNET_CONTAINER_DLL_remove (exchange->keys_head,
569 : exchange->keys_tail,
570 : fo);
571 111 : GNUNET_free (fo);
572 111 : }
573 :
574 :
575 : /**
576 : * Obtain applicable fees for @a exchange and @a wire_method.
577 : *
578 : * @param exchange the exchange to query
579 : * @param now current time
580 : * @param wire_method the wire method we want the fees for
581 : * @return NULL if we do not have fees for this method yet
582 : */
583 : static const struct FeesByWireMethod *
584 35 : get_wire_fees (const struct TMH_Exchange *exchange,
585 : struct GNUNET_TIME_Timestamp now,
586 : const char *wire_method)
587 : {
588 35 : for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
589 35 : NULL != fbw;
590 0 : fbw = fbw->next)
591 : {
592 35 : if (0 == strcasecmp (fbw->wire_method,
593 : wire_method) )
594 : {
595 : struct TALER_EXCHANGE_WireAggregateFees *af;
596 :
597 : /* Advance through list up to current time */
598 35 : while ( (NULL != (af = fbw->af)) &&
599 35 : (GNUNET_TIME_timestamp_cmp (now,
600 : >=,
601 : af->end_date)) )
602 : {
603 0 : fbw->af = af->next;
604 0 : GNUNET_free (af);
605 : }
606 35 : return fbw;
607 : }
608 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
609 : "Exchange supports `%s' as a wire method (but we do not use that one)\n",
610 : fbw->wire_method);
611 : }
612 0 : return NULL;
613 : }
614 :
615 :
616 : /**
617 : * Free @a exchange.
618 : *
619 : * @param[in] exchange entry to free
620 : */
621 : static void
622 15 : free_exchange_entry (struct TMH_Exchange *exchange)
623 : {
624 : struct FeesByWireMethod *f;
625 :
626 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
627 : "Releasing %s exchange %s\n",
628 : exchange->trusted ? "trusted" : "untrusted",
629 : exchange->url);
630 15 : GNUNET_CONTAINER_DLL_remove (exchange_head,
631 : exchange_tail,
632 : exchange);
633 26 : while (NULL != (f = exchange->wire_fees_head))
634 : {
635 : struct TALER_EXCHANGE_WireAggregateFees *af;
636 :
637 11 : GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
638 : exchange->wire_fees_tail,
639 : f);
640 22 : while (NULL != (af = f->af))
641 : {
642 11 : f->af = af->next;
643 11 : GNUNET_free (af);
644 : }
645 11 : GNUNET_free (f->wire_method);
646 11 : GNUNET_free (f);
647 : }
648 15 : TALER_EXCHANGE_keys_decref (exchange->keys);
649 15 : exchange->keys = NULL;
650 15 : if (NULL != exchange->retry_task)
651 : {
652 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
653 0 : exchange->retry_task = NULL;
654 : }
655 15 : GNUNET_assert (NULL == exchange->keys_head);
656 15 : GNUNET_assert (NULL == exchange->keys_tail);
657 15 : GNUNET_free (exchange->currency);
658 15 : GNUNET_free (exchange->url);
659 15 : GNUNET_free (exchange);
660 15 : }
661 :
662 :
663 : enum GNUNET_GenericReturnValue
664 35 : TMH_EXCHANGES_lookup_wire_fee (
665 : const struct TMH_Exchange *exchange,
666 : const char *wire_method,
667 : struct TALER_Amount *wire_fee)
668 : {
669 : const struct FeesByWireMethod *fbm;
670 : const struct TALER_EXCHANGE_WireAggregateFees *af;
671 :
672 35 : fbm = get_wire_fees (exchange,
673 : GNUNET_TIME_timestamp_get (),
674 : wire_method);
675 35 : if (NULL == fbm)
676 0 : return GNUNET_NO;
677 35 : af = fbm->af;
678 35 : *wire_fee = af->fees.wire;
679 35 : return GNUNET_OK;
680 : }
681 :
682 :
683 : enum TMH_ExchangeStatus
684 99 : TMH_exchange_check_debit (
685 : const char *instance_id,
686 : const struct TMH_Exchange *exchange,
687 : const struct TMH_WireMethod *wm,
688 : struct TALER_Amount *max_amount)
689 : {
690 99 : const struct TALER_EXCHANGE_Keys *keys = exchange->keys;
691 99 : bool have_kyc = false;
692 99 : bool no_access_token = true;
693 99 : enum TMH_ExchangeStatus retry_ok = 0;
694 :
695 99 : if (GNUNET_TIME_absolute_is_past (exchange->first_retry))
696 0 : retry_ok = TMH_ES_RETRY_OK;
697 :
698 99 : if (NULL == keys)
699 0 : return TMH_ES_NO_KEYS | retry_ok;
700 99 : if (0 != strcasecmp (keys->currency,
701 99 : max_amount->currency))
702 : {
703 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
704 : "Currency mismatch: exchange %s uses %s, we need %s\n",
705 : exchange->url,
706 : keys->currency,
707 : max_amount->currency);
708 0 : return TMH_ES_NO_CURR | retry_ok;
709 : }
710 : {
711 : struct TALER_NormalizedPayto np;
712 : bool account_ok;
713 :
714 99 : np = TALER_payto_normalize (wm->payto_uri);
715 99 : account_ok = TALER_EXCHANGE_keys_test_account_allowed (keys,
716 : false,
717 : np);
718 99 : GNUNET_free (np.normalized_payto);
719 99 : if (! account_ok)
720 0 : return TMH_ES_NO_ACC | retry_ok;
721 : }
722 99 : if (! keys->kyc_enabled)
723 95 : return TMH_ES_OK | retry_ok;
724 :
725 : {
726 4 : json_t *jlimits = NULL;
727 : enum GNUNET_DB_QueryStatus qs;
728 :
729 4 : qs = TMH_db->get_kyc_limits (TMH_db->cls,
730 : wm->payto_uri,
731 : instance_id,
732 4 : exchange->url,
733 : &have_kyc,
734 : &no_access_token,
735 : &jlimits);
736 4 : GNUNET_break (qs >= 0);
737 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
738 : "get_kyc_limits for %s at %s returned %s/%s\n",
739 : wm->payto_uri.full_payto,
740 : exchange->url,
741 : have_kyc ? "KYC OK" : "KYC missing",
742 : NULL == jlimits ? "default limits" : "custom limits");
743 4 : if ( (qs > 0) &&
744 4 : (NULL != jlimits) )
745 : {
746 : json_t *jlimit;
747 : size_t idx;
748 : struct TALER_Amount kyc_limit;
749 4 : bool unlimited = true;
750 :
751 8 : json_array_foreach (jlimits, idx, jlimit)
752 : {
753 : enum TALER_KYCLOGIC_KycTriggerEvent ot;
754 : struct TALER_Amount threshold;
755 4 : bool soft_limit = false;
756 : struct GNUNET_JSON_Specification spec[] = {
757 4 : TALER_JSON_spec_kycte ("operation_type",
758 : &ot),
759 4 : TALER_JSON_spec_amount_any ("threshold",
760 : &threshold),
761 4 : GNUNET_JSON_spec_mark_optional (
762 : GNUNET_JSON_spec_bool ("soft_limit",
763 : &soft_limit),
764 : NULL),
765 4 : GNUNET_JSON_spec_end ()
766 : };
767 :
768 4 : if (GNUNET_OK !=
769 4 : GNUNET_JSON_parse (jlimit,
770 : spec,
771 : NULL, NULL))
772 : {
773 0 : GNUNET_break (0);
774 4 : continue;
775 : }
776 4 : if (soft_limit)
777 4 : continue;
778 0 : if ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT != ot) &&
779 0 : (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION != ot) )
780 0 : continue;
781 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
782 : "KYC rule %u with limit %s applies\n",
783 : (unsigned int) idx,
784 : TALER_amount2s (&threshold));
785 0 : if (unlimited)
786 : {
787 0 : unlimited = false;
788 0 : kyc_limit = threshold;
789 : }
790 : else
791 : {
792 0 : TALER_amount_min (&kyc_limit,
793 : &kyc_limit,
794 : &threshold);
795 : }
796 : }
797 4 : json_decref (jlimits);
798 : /* We had custom rules, do not evaluate default rules */
799 4 : if (! unlimited)
800 0 : TALER_amount_min (max_amount,
801 : max_amount,
802 : &kyc_limit);
803 4 : return TMH_ES_OK | retry_ok;
804 : } /* END of if qs > 0, NULL != jlimits */
805 : }
806 :
807 : /* Check zero limits *only* if we did no KYC process at all yet.
808 : Because if we did, there is at least a chance that those have
809 : been lifted. */
810 0 : if ( (no_access_token) ||
811 0 : ( (! have_kyc) &&
812 0 : (TALER_EXCHANGE_keys_evaluate_zero_limits (
813 : keys,
814 0 : TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
815 0 : TALER_EXCHANGE_keys_evaluate_zero_limits (
816 : keys,
817 : TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION)) ) )
818 : {
819 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
820 : "KYC requirements of %s not satisfied\n",
821 : exchange->url);
822 0 : GNUNET_assert (GNUNET_OK ==
823 : TALER_amount_set_zero (
824 : max_amount->currency,
825 : max_amount));
826 0 : return TMH_ES_OK | retry_ok;
827 : }
828 : /* In any case, abide by hard limits (unless we have custom rules). */
829 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
830 : "Evaluating default hard limits of %s\n",
831 : exchange->url);
832 0 : TALER_EXCHANGE_keys_evaluate_hard_limits (
833 : keys,
834 : TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
835 : max_amount);
836 0 : TALER_EXCHANGE_keys_evaluate_hard_limits (
837 : keys,
838 : TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION,
839 : max_amount);
840 0 : if (TALER_EXCHANGE_keys_evaluate_zero_limits (
841 : keys,
842 0 : TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
843 0 : TALER_EXCHANGE_keys_evaluate_zero_limits (
844 : keys,
845 : TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION))
846 : {
847 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
848 : "Operation is zero-limited by default\n");
849 0 : GNUNET_assert (GNUNET_OK ==
850 : TALER_amount_set_zero (max_amount->currency,
851 : max_amount));
852 : }
853 0 : return TMH_ES_OK | retry_ok;
854 : }
855 :
856 :
857 : void
858 79 : TMH_exchange_get_trusted (TMH_ExchangeCallback cb,
859 : void *cb_cls)
860 : {
861 79 : for (const struct TMH_Exchange *exchange = exchange_head;
862 158 : NULL != exchange;
863 79 : exchange = exchange->next)
864 : {
865 79 : if (! exchange->trusted)
866 : {
867 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
868 : "Exchange %s not trusted, skipping!\n",
869 : exchange->url);
870 0 : continue;
871 : }
872 79 : cb (cb_cls,
873 79 : exchange->url,
874 : exchange);
875 : }
876 79 : }
877 :
878 :
879 : bool
880 144 : TMH_test_exchange_configured_for_currency (
881 : const char *currency)
882 : {
883 144 : for (const struct TMH_Exchange *exchange = exchange_head;
884 202 : NULL != exchange;
885 58 : exchange = exchange->next)
886 : {
887 144 : if (! exchange->trusted)
888 0 : continue;
889 144 : if (NULL == exchange->currency)
890 0 : continue;
891 144 : if (0 == strcmp (currency,
892 144 : exchange->currency))
893 86 : return true;
894 : }
895 58 : return false;
896 : }
897 :
898 :
899 : /**
900 : * (Re)load of keys from DB.
901 : *
902 : * @param exchange exchange to reload keys of
903 : */
904 : static void
905 26 : reload_exchange_keys (struct TMH_Exchange *exchange)
906 : {
907 : enum GNUNET_DB_QueryStatus qs;
908 : struct TALER_EXCHANGE_Keys *keys;
909 : struct GNUNET_TIME_Absolute first_retry;
910 :
911 26 : qs = TMH_db->select_exchange_keys (TMH_db->cls,
912 26 : exchange->url,
913 : &first_retry,
914 : &keys);
915 26 : if (qs < 0)
916 : {
917 0 : GNUNET_break (0);
918 15 : return;
919 : }
920 26 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
921 : {
922 15 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
923 : "No keys yet for `%s'\n",
924 : exchange->url);
925 15 : return;
926 : }
927 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
928 : "Loading latest keys of `%s' from database\n",
929 : exchange->url);
930 11 : exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
931 11 : exchange->first_retry = first_retry;
932 11 : if (NULL == exchange->currency)
933 0 : exchange->currency = GNUNET_strdup (keys->currency);
934 11 : if (0 != strcmp (keys->currency,
935 11 : exchange->currency))
936 : {
937 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
938 : "/keys cached in our database are for currency `%s', but we expected `%s'\n",
939 : keys->currency,
940 : exchange->currency);
941 0 : return;
942 : }
943 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
944 : "Loaded /keys from database with %u accounts\n",
945 : keys->accounts_len);
946 11 : if (GNUNET_OK !=
947 11 : process_wire_fees (exchange,
948 11 : &keys->master_pub,
949 11 : keys->fees_len,
950 11 : keys->fees))
951 : {
952 : /* invalid wire fee specification given */
953 0 : GNUNET_break_op (0);
954 : /* but: we can continue anyway, things may just not
955 : work, but probably better than to not keep going. */
956 0 : return;
957 : }
958 :
959 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
960 : "Reloaded /keys of %s from database\n",
961 : exchange->url);
962 11 : TALER_EXCHANGE_keys_decref (exchange->keys);
963 11 : exchange->keys = keys;
964 11 : if ( (exchange->trusted) &&
965 11 : (0 != GNUNET_memcmp (&exchange->master_pub,
966 : &keys->master_pub)) )
967 : {
968 : /* master pub differs => do not trust the exchange (without auditor) */
969 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
970 : "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
971 : exchange->url);
972 0 : exchange->trusted = false;
973 : }
974 11 : if (! exchange->trusted)
975 : {
976 0 : exchange->master_pub = keys->master_pub;
977 0 : for (struct TMH_Exchange *e = exchange_head;
978 0 : NULL != e;
979 0 : e = e->next)
980 : {
981 0 : if (e == exchange)
982 0 : continue;
983 0 : if (! e->trusted)
984 0 : continue;
985 0 : if (0 ==
986 0 : GNUNET_memcmp (&e->master_pub,
987 : &exchange->master_pub))
988 0 : exchange->trusted = true; /* same exchange, different URL => trust applies */
989 : }
990 : }
991 :
992 11 : process_find_operations (exchange);
993 : }
994 :
995 :
996 : /**
997 : * Function called on each configuration section. Finds sections
998 : * about exchanges, parses the entries.
999 : *
1000 : * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
1001 : * @param section name of the section
1002 : */
1003 : static void
1004 603 : accept_exchanges (void *cls,
1005 : const char *section)
1006 : {
1007 603 : const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1008 : char *url;
1009 : char *mks;
1010 : struct TMH_Exchange *exchange;
1011 : char *currency;
1012 :
1013 603 : if (GNUNET_SYSERR == trusted_exchange_count)
1014 588 : return;
1015 603 : if (0 != strncasecmp (section,
1016 : "merchant-exchange-",
1017 : strlen ("merchant-exchange-")))
1018 558 : return;
1019 45 : if (GNUNET_YES ==
1020 45 : GNUNET_CONFIGURATION_get_value_yesno (cfg,
1021 : section,
1022 : "DISABLED"))
1023 30 : return;
1024 15 : if (GNUNET_OK !=
1025 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
1026 : section,
1027 : "EXCHANGE_BASE_URL",
1028 : &url))
1029 : {
1030 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1031 : section,
1032 : "EXCHANGE_BASE_URL");
1033 0 : return;
1034 : }
1035 15 : exchange = lookup_exchange (url);
1036 15 : if (NULL != exchange)
1037 : {
1038 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1039 : section,
1040 : "EXCHANGE_BASE_URL",
1041 : "same base URL specified again");
1042 0 : GNUNET_free (url);
1043 0 : return;
1044 : }
1045 15 : if (GNUNET_OK !=
1046 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
1047 : section,
1048 : "CURRENCY",
1049 : ¤cy))
1050 : {
1051 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1052 : section,
1053 : "CURRENCY");
1054 0 : GNUNET_free (url);
1055 0 : return;
1056 : }
1057 15 : exchange = GNUNET_new (struct TMH_Exchange);
1058 15 : exchange->url = url;
1059 15 : exchange->currency = currency;
1060 15 : GNUNET_CONTAINER_DLL_insert (exchange_head,
1061 : exchange_tail,
1062 : exchange);
1063 15 : if (GNUNET_OK ==
1064 15 : GNUNET_CONFIGURATION_get_value_string (cfg,
1065 : section,
1066 : "MASTER_KEY",
1067 : &mks))
1068 : {
1069 15 : if (GNUNET_OK ==
1070 15 : GNUNET_CRYPTO_eddsa_public_key_from_string (
1071 : mks,
1072 : strlen (mks),
1073 : &exchange->master_pub.eddsa_pub))
1074 : {
1075 15 : exchange->trusted = true;
1076 15 : trusted_exchange_count++;
1077 : }
1078 : else
1079 : {
1080 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1081 : section,
1082 : "MASTER_KEY",
1083 : "malformed EdDSA key");
1084 : }
1085 15 : GNUNET_free (mks);
1086 : }
1087 : else
1088 : {
1089 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1090 : "MASTER_KEY missing in section '%s', not trusting exchange\n",
1091 : section);
1092 : }
1093 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1094 : "Setup exchange %s as %s\n",
1095 : exchange->url,
1096 : exchange->trusted ? "trusted" : "untrusted");
1097 15 : reload_exchange_keys (exchange);
1098 15 : if (NULL != exchange->retry_task)
1099 : {
1100 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1101 : "Exchange at `%s' configured in multiple configuration sections (see `%s')!\n",
1102 : exchange->url,
1103 : section);
1104 0 : trusted_exchange_count = GNUNET_SYSERR;
1105 0 : return;
1106 : }
1107 : exchange->retry_task
1108 15 : = GNUNET_SCHEDULER_add_now (&retry_exchange,
1109 : exchange);
1110 : }
1111 :
1112 :
1113 : /**
1114 : * Trigger (re)loading of keys from DB.
1115 : *
1116 : * @param cls NULL
1117 : * @param extra base URL of the exchange that changed
1118 : * @param extra_len number of bytes in @a extra
1119 : */
1120 : static void
1121 11 : update_exchange_keys (void *cls,
1122 : const void *extra,
1123 : size_t extra_len)
1124 : {
1125 11 : const char *url = extra;
1126 : struct TMH_Exchange *exchange;
1127 :
1128 11 : if ( (NULL == extra) ||
1129 : (0 == extra_len) )
1130 : {
1131 0 : GNUNET_break (0);
1132 0 : return;
1133 : }
1134 11 : if ('\0' != url[extra_len - 1])
1135 : {
1136 0 : GNUNET_break (0);
1137 0 : return;
1138 : }
1139 11 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1140 : "Received keys change notification: reload `%s'\n",
1141 : url);
1142 11 : exchange = lookup_exchange (url);
1143 11 : GNUNET_break (NULL != exchange);
1144 11 : if (NULL != exchange)
1145 11 : reload_exchange_keys (exchange);
1146 : }
1147 :
1148 :
1149 : bool
1150 35 : TMH_EXCHANGES_is_below_limit (
1151 : const struct TALER_EXCHANGE_Keys *keys,
1152 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
1153 : const struct TALER_Amount *amount)
1154 : {
1155 35 : if (NULL == keys)
1156 : {
1157 : /* should only be called after we have keys! */
1158 0 : GNUNET_break (0);
1159 0 : return false;
1160 : }
1161 35 : for (unsigned int i = 0; i<keys->hard_limits_length; i++)
1162 : {
1163 0 : const struct TALER_EXCHANGE_AccountLimit *al
1164 0 : = &keys->hard_limits[i];
1165 :
1166 0 : if (operation_type != al->operation_type)
1167 0 : continue;
1168 0 : if (-1 ==
1169 0 : TALER_amount_cmp (&al->threshold,
1170 : amount))
1171 : /* -1: threshold < amount */
1172 0 : return false;
1173 : }
1174 35 : return true;
1175 : }
1176 :
1177 :
1178 : void
1179 6 : TMH_EXCHANGES_get_limit (
1180 : const char *exchange_url,
1181 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type,
1182 : struct TALER_Amount *amount)
1183 : {
1184 : struct TMH_Exchange *exchange;
1185 : const struct TALER_EXCHANGE_Keys *keys;
1186 :
1187 6 : exchange = lookup_exchange (exchange_url);
1188 6 : if ( (NULL == exchange) ||
1189 6 : (NULL == (keys = exchange->keys)) )
1190 : {
1191 0 : GNUNET_assert (GNUNET_OK ==
1192 : TALER_amount_set_zero (
1193 : amount->currency,
1194 : amount));
1195 0 : return;
1196 : }
1197 6 : for (unsigned int i = 0; i<keys->hard_limits_length; i++)
1198 : {
1199 0 : const struct TALER_EXCHANGE_AccountLimit *al
1200 0 : = &keys->hard_limits[i];
1201 :
1202 0 : if (operation_type != al->operation_type)
1203 0 : continue;
1204 0 : TALER_amount_min (amount,
1205 : amount,
1206 : &al->threshold);
1207 : }
1208 : }
1209 :
1210 :
1211 : enum GNUNET_GenericReturnValue
1212 15 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
1213 : {
1214 : /* get exchanges from the merchant configuration and try to connect to them */
1215 : {
1216 15 : struct GNUNET_DB_EventHeaderP es = {
1217 15 : .size = ntohs (sizeof (es)),
1218 15 : .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
1219 : };
1220 :
1221 15 : GNUNET_assert (NULL == keys_eh);
1222 30 : keys_eh = TMH_db->event_listen (TMH_db->cls,
1223 : &es,
1224 15 : GNUNET_TIME_UNIT_FOREVER_REL,
1225 : &update_exchange_keys,
1226 : NULL);
1227 : }
1228 15 : GNUNET_CONFIGURATION_iterate_sections (cfg,
1229 : &accept_exchanges,
1230 : (void *) cfg);
1231 : /* build JSON with list of trusted exchanges (will be included in contracts) */
1232 15 : return trusted_exchange_count;
1233 : }
1234 :
1235 :
1236 : void
1237 15 : TMH_EXCHANGES_done ()
1238 : {
1239 15 : if (NULL != keys_eh)
1240 : {
1241 15 : TMH_db->event_listen_cancel (keys_eh);
1242 15 : keys_eh = NULL;
1243 : }
1244 30 : while (NULL != exchange_head)
1245 15 : free_exchange_entry (exchange_head);
1246 15 : }
1247 :
1248 :
1249 : /* end of taler-merchant-httpd_exchanges.c */
|