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