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