LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_reserves.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 54 79 68.4 %
Date: 2021-08-30 06:54:17 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2020, 2021 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_reserves.c
      18             :  * @brief logic for initially tracking a reserve's status
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <taler/taler_json_lib.h>
      23             : #include "taler-merchant-httpd.h"
      24             : #include "taler-merchant-httpd_exchanges.h"
      25             : #include "taler-merchant-httpd_reserves.h"
      26             : 
      27             : /**
      28             :  * How long do we keep the long-poller open?
      29             :  * Not very long here, as if the money has not
      30             :  * yet arrived, there is a fair chance that it'll
      31             :  * take much longer, and in that case we rather
      32             :  * enter into the delay created by try_later().
      33             :  */
      34             : #define LONGPOLL_DELAY GNUNET_TIME_UNIT_MINUTES
      35             : 
      36             : /**
      37             :  * Our representation of a reserve that we are (still) checking the status of.
      38             :  */
      39             : struct Reserve
      40             : {
      41             : 
      42             :   /**
      43             :    * Kept in a DLL.
      44             :    */
      45             :   struct Reserve *next;
      46             : 
      47             :   /**
      48             :    * Kept in a DLL.
      49             :    */
      50             :   struct Reserve *prev;
      51             : 
      52             :   /**
      53             :    * Reserve's public key.
      54             :    */
      55             :   struct TALER_ReservePublicKeyP reserve_pub;
      56             : 
      57             :   /**
      58             :    * Amount the merchant expects to see in the reserve initially.
      59             :    * We log a warning if there is a mismatch.
      60             :    */
      61             :   struct TALER_Amount expected_amount;
      62             : 
      63             :   /**
      64             :    * URL of the exchange hosting this reserve.
      65             :    */
      66             :   char *exchange_url;
      67             : 
      68             :   /**
      69             :    * Instance this reserve belongs with.
      70             :    */
      71             :   char *instance_id;
      72             : 
      73             :   /**
      74             :    * Active find operation for this reserve.
      75             :    */
      76             :   struct TMH_EXCHANGES_FindOperation *fo;
      77             : 
      78             :   /**
      79             :    * Task scheduled waiting for a timeout for this reserve.
      80             :    */
      81             :   struct GNUNET_SCHEDULER_Task *tt;
      82             : 
      83             :   /**
      84             :    * Get operation with the exchange.
      85             :    */
      86             :   struct TALER_EXCHANGE_ReservesGetHandle *gh;
      87             : 
      88             :   /**
      89             :    * How long do we wait before trying this reserve again?
      90             :    */
      91             :   struct GNUNET_TIME_Relative delay;
      92             : 
      93             : };
      94             : 
      95             : 
      96             : /**
      97             :  * Head of DLL of pending reserves.
      98             :  */
      99             : static struct Reserve *reserves_head;
     100             : 
     101             : /**
     102             :  * Tail of DLL of pending reserves.
     103             :  */
     104             : static struct Reserve *reserves_tail;
     105             : 
     106             : 
     107             : /**
     108             :  * Function called to probe a reserve now.
     109             :  *
     110             :  * @param cls a `struct Reserve` to query
     111             :  */
     112             : static void
     113             : try_now (void *cls);
     114             : 
     115             : 
     116             : /**
     117             :  * Free reserve data structure.
     118             :  *
     119             :  * @param r reserve to free
     120             :  */
     121             : static void
     122           2 : free_reserve (struct Reserve *r)
     123             : {
     124           2 :   GNUNET_CONTAINER_DLL_remove (reserves_head,
     125             :                                reserves_tail,
     126             :                                r);
     127           2 :   if (NULL != r->fo)
     128             :   {
     129           0 :     TMH_EXCHANGES_find_exchange_cancel (r->fo);
     130           0 :     r->fo = NULL;
     131             :   }
     132           2 :   if (NULL != r->gh)
     133             :   {
     134           0 :     TALER_EXCHANGE_reserves_get_cancel (r->gh);
     135           0 :     r->gh = NULL;
     136             :   }
     137           2 :   if (NULL != r->tt)
     138             :   {
     139           0 :     GNUNET_SCHEDULER_cancel (r->tt);
     140           0 :     r->tt = NULL;
     141             :   }
     142           2 :   GNUNET_free (r->exchange_url);
     143           2 :   GNUNET_free (r->instance_id);
     144           2 :   GNUNET_free (r);
     145           2 : }
     146             : 
     147             : 
     148             : /**
     149             :  * Schedule a job to probe a reserve later again.
     150             :  *
     151             :  * @param r reserve to try again later
     152             :  */
     153             : static void
     154           0 : try_later (struct Reserve *r)
     155             : {
     156             :   /* minimum delay is the #LONGPOLL_DELAY */
     157           0 :   r->delay = GNUNET_TIME_relative_max (LONGPOLL_DELAY,
     158             :                                        r->delay);
     159             :   /* STD_BACKOFF has a maximum of 15 minutes */
     160           0 :   r->delay = GNUNET_TIME_STD_BACKOFF (r->delay);
     161           0 :   r->tt = GNUNET_SCHEDULER_add_delayed (r->delay,
     162             :                                         &try_now,
     163             :                                         r);
     164           0 : }
     165             : 
     166             : 
     167             : /**
     168             :  * Callbacks of this type are used to serve the result of submitting a
     169             :  * reserve status request to a exchange.
     170             :  *
     171             :  * @param cls closure with a `struct Reserve *`
     172             :  * @param hr HTTP response data
     173             :  * @param balance current balance in the reserve, NULL on error
     174             :  * @param history_length number of entries in the transaction history, 0 on error
     175             :  * @param history detailed transaction history, NULL on error
     176             :  */
     177             : static void
     178           2 : reserve_cb (void *cls,
     179             :             const struct TALER_EXCHANGE_HttpResponse *hr,
     180             :             const struct TALER_Amount *balance,
     181             :             unsigned int history_length,
     182             :             const struct TALER_EXCHANGE_ReserveHistory *history)
     183             : {
     184           2 :   struct Reserve *r = cls;
     185             :   enum GNUNET_DB_QueryStatus qs;
     186             : 
     187           2 :   r->gh = NULL;
     188           2 :   if ( (NULL == hr) ||
     189           2 :        (MHD_HTTP_OK != hr->http_status) )
     190             :   {
     191           0 :     try_later (r);
     192           0 :     return;
     193             :   }
     194           2 :   if (GNUNET_OK !=
     195           2 :       TALER_amount_cmp_currency (&r->expected_amount,
     196             :                                  balance))
     197             :   {
     198           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     199             :                 "Reserve currency disagreement: exchange `%s' has %s, expected %s\n",
     200             :                 r->exchange_url,
     201             :                 balance->currency,
     202             :                 r->expected_amount.currency);
     203           0 :     free_reserve (r);
     204           0 :     return;
     205             :   }
     206           2 :   if (0 !=
     207           2 :       TALER_amount_cmp (&r->expected_amount,
     208             :                         balance))
     209             :   {
     210           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     211             :                 "Reserve initial balance disagreement: exchange `%s' received `%s'\n",
     212             :                 r->exchange_url,
     213             :                 TALER_amount2s (balance));
     214             :   }
     215           2 :   qs = TMH_db->activate_reserve (TMH_db->cls,
     216           2 :                                  r->instance_id,
     217           2 :                                  &r->reserve_pub,
     218             :                                  balance);
     219           2 :   if (qs <= 0)
     220             :   {
     221           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     222             :                 "Failed to commit reserve activation to database (%d)\n",
     223             :                 (int) qs);
     224             :   }
     225             :   else
     226             :   {
     227           2 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     228             :                 "Reserve activated with initial balance %s\n",
     229             :                 TALER_amount2s (balance));
     230             :   }
     231           2 :   free_reserve (r);
     232             : }
     233             : 
     234             : 
     235             : /**
     236             :  * Function called with the result of a #TMH_EXCHANGES_find_exchange()
     237             :  * operation.
     238             :  *
     239             :  * @param cls closure
     240             :  * @param hr HTTP response details
     241             :  * @param eh handle to the exchange context
     242             :  * @param payto_uri payto://-URI of the exchange
     243             :  * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
     244             :  * @param exchange_trusted true if this exchange is trusted by config
     245             :  */
     246             : static void
     247           2 : find_cb (void *cls,
     248             :          const struct TALER_EXCHANGE_HttpResponse *hr,
     249             :          struct TALER_EXCHANGE_Handle *eh,
     250             :          const char *payto_uri,
     251             :          const struct TALER_Amount *wire_fee,
     252             :          bool exchange_trusted)
     253             : {
     254           2 :   struct Reserve *r = cls;
     255             : 
     256           2 :   r->fo = NULL;
     257           2 :   if (NULL == eh)
     258             :   {
     259           0 :     try_later (r);
     260           0 :     return;
     261             :   }
     262           2 :   r->gh = TALER_EXCHANGE_reserves_get (eh,
     263           2 :                                        &r->reserve_pub,
     264             :                                        LONGPOLL_DELAY,
     265             :                                        &reserve_cb,
     266             :                                        r);
     267           2 :   if (NULL == r->gh)
     268             :   {
     269           0 :     try_later (r);
     270           0 :     return;
     271             :   }
     272             : }
     273             : 
     274             : 
     275             : /**
     276             :  * Function called to probe a reserve now.
     277             :  *
     278             :  * @param cls a `struct Reserve` to query
     279             :  */
     280             : static void
     281           2 : try_now (void *cls)
     282             : {
     283           2 :   struct Reserve *r = cls;
     284             : 
     285           2 :   r->tt = NULL;
     286           2 :   r->fo = TMH_EXCHANGES_find_exchange (r->exchange_url,
     287             :                                        NULL,
     288             :                                        GNUNET_NO,
     289             :                                        &find_cb,
     290             :                                        r);
     291           2 :   if (NULL == r->fo)
     292             :   {
     293           0 :     try_later (r);
     294           0 :     return;
     295             :   }
     296             : }
     297             : 
     298             : 
     299             : /**
     300             :  * Function called with information about a reserve that we need
     301             :  * to check the status from at the exchange to see if/when it has
     302             :  * been filled (and with what amount).
     303             :  *
     304             :  * @param cls closure, NULL
     305             :  * @param instance_id for which instance is this reserve
     306             :  * @param exchange_url base URL of the exchange at which the reserve lives
     307             :  * @param reserve_pub public key of the reserve
     308             :  * @param expected_amount how much do we expect to see in the reserve
     309             :  */
     310             : static void
     311           2 : add_reserve (void *cls,
     312             :              const char *instance_id,
     313             :              const char *exchange_url,
     314             :              const struct TALER_ReservePublicKeyP *reserve_pub,
     315             :              const struct TALER_Amount *expected_amount)
     316             : {
     317             :   struct Reserve *r;
     318             : 
     319             :   (void) cls;
     320           2 :   r = GNUNET_new (struct Reserve);
     321           2 :   r->exchange_url = GNUNET_strdup (exchange_url);
     322           2 :   r->instance_id = GNUNET_strdup (instance_id);
     323           2 :   r->reserve_pub = *reserve_pub;
     324           2 :   r->expected_amount = *expected_amount;
     325           2 :   GNUNET_CONTAINER_DLL_insert (reserves_head,
     326             :                                reserves_tail,
     327             :                                r);
     328           2 :   try_now (r);
     329           2 : }
     330             : 
     331             : 
     332             : void
     333          16 : TMH_RESERVES_init (void)
     334             : {
     335          16 :   TMH_db->lookup_pending_reserves (TMH_db->cls,
     336             :                                    &add_reserve,
     337             :                                    NULL);
     338          16 : }
     339             : 
     340             : 
     341             : void
     342           2 : TMH_RESERVES_check (const char *instance_id,
     343             :                     const char *exchange_url,
     344             :                     const struct TALER_ReservePublicKeyP *reserve_pub,
     345             :                     const struct TALER_Amount *expected_amount)
     346             : {
     347           2 :   add_reserve (NULL,
     348             :                instance_id,
     349             :                exchange_url,
     350             :                reserve_pub,
     351             :                expected_amount);
     352           2 : }
     353             : 
     354             : 
     355             : void
     356          16 : TMH_RESERVES_done (void)
     357             : {
     358          16 :   while (NULL != reserves_head)
     359           0 :     free_reserve (reserves_head);
     360          16 : }
     361             : 
     362             : 
     363             : /* end of taler-merchant-httpd_reserves.c */

Generated by: LCOV version 1.14