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: 6 78 7.7 %
Date: 2022-06-30 06:15:34 Functions: 2 9 22.2 %
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           0 : free_reserve (struct Reserve *r)
     123             : {
     124           0 :   GNUNET_CONTAINER_DLL_remove (reserves_head,
     125             :                                reserves_tail,
     126             :                                r);
     127           0 :   if (NULL != r->fo)
     128             :   {
     129           0 :     TMH_EXCHANGES_find_exchange_cancel (r->fo);
     130           0 :     r->fo = NULL;
     131             :   }
     132           0 :   if (NULL != r->gh)
     133             :   {
     134           0 :     TALER_EXCHANGE_reserves_get_cancel (r->gh);
     135           0 :     r->gh = NULL;
     136             :   }
     137           0 :   if (NULL != r->tt)
     138             :   {
     139           0 :     GNUNET_SCHEDULER_cancel (r->tt);
     140           0 :     r->tt = NULL;
     141             :   }
     142           0 :   GNUNET_free (r->exchange_url);
     143           0 :   GNUNET_free (r->instance_id);
     144           0 :   GNUNET_free (r);
     145           0 : }
     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 rs HTTP response data
     173             :  */
     174             : static void
     175           0 : reserve_cb (void *cls,
     176             :             const struct TALER_EXCHANGE_ReserveSummary *rs)
     177             : {
     178           0 :   struct Reserve *r = cls;
     179             :   enum GNUNET_DB_QueryStatus qs;
     180             : 
     181           0 :   r->gh = NULL;
     182           0 :   if (MHD_HTTP_OK != rs->hr.http_status)
     183             :   {
     184           0 :     try_later (r);
     185           0 :     return;
     186             :   }
     187           0 :   if (GNUNET_OK !=
     188           0 :       TALER_amount_cmp_currency (&r->expected_amount,
     189             :                                  &rs->details.ok.balance))
     190             :   {
     191           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     192             :                 "Reserve currency disagreement: exchange `%s' has %s, expected %s\n",
     193             :                 r->exchange_url,
     194             :                 rs->details.ok.balance.currency,
     195             :                 r->expected_amount.currency);
     196           0 :     free_reserve (r);
     197           0 :     return;
     198             :   }
     199           0 :   if (0 !=
     200           0 :       TALER_amount_cmp (&r->expected_amount,
     201             :                         &rs->details.ok.balance))
     202             :   {
     203           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     204             :                 "Reserve initial balance disagreement: exchange `%s' received `%s'\n",
     205             :                 r->exchange_url,
     206             :                 TALER_amount2s (&rs->details.ok.balance));
     207             :   }
     208           0 :   qs = TMH_db->activate_reserve (TMH_db->cls,
     209           0 :                                  r->instance_id,
     210           0 :                                  &r->reserve_pub,
     211             :                                  &rs->details.ok.balance);
     212           0 :   if (qs <= 0)
     213             :   {
     214           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     215             :                 "Failed to commit reserve activation to database (%d)\n",
     216             :                 (int) qs);
     217             :   }
     218             :   else
     219             :   {
     220           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     221             :                 "Reserve activated with initial balance %s\n",
     222             :                 TALER_amount2s (&rs->details.ok.balance));
     223             :   }
     224           0 :   free_reserve (r);
     225             : }
     226             : 
     227             : 
     228             : /**
     229             :  * Function called with the result of a #TMH_EXCHANGES_find_exchange()
     230             :  * operation.
     231             :  *
     232             :  * @param cls closure
     233             :  * @param hr HTTP response details
     234             :  * @param eh handle to the exchange context
     235             :  * @param payto_uri payto://-URI of the exchange
     236             :  * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
     237             :  * @param exchange_trusted true if this exchange is trusted by config
     238             :  */
     239             : static void
     240           0 : find_cb (void *cls,
     241             :          const struct TALER_EXCHANGE_HttpResponse *hr,
     242             :          struct TALER_EXCHANGE_Handle *eh,
     243             :          const char *payto_uri,
     244             :          const struct TALER_Amount *wire_fee,
     245             :          bool exchange_trusted)
     246             : {
     247           0 :   struct Reserve *r = cls;
     248             : 
     249           0 :   r->fo = NULL;
     250           0 :   if (NULL == eh)
     251             :   {
     252           0 :     try_later (r);
     253           0 :     return;
     254             :   }
     255           0 :   r->gh = TALER_EXCHANGE_reserves_get (eh,
     256           0 :                                        &r->reserve_pub,
     257             :                                        LONGPOLL_DELAY,
     258             :                                        &reserve_cb,
     259             :                                        r);
     260           0 :   if (NULL == r->gh)
     261             :   {
     262           0 :     try_later (r);
     263           0 :     return;
     264             :   }
     265             : }
     266             : 
     267             : 
     268             : /**
     269             :  * Function called to probe a reserve now.
     270             :  *
     271             :  * @param cls a `struct Reserve` to query
     272             :  */
     273             : static void
     274           0 : try_now (void *cls)
     275             : {
     276           0 :   struct Reserve *r = cls;
     277             : 
     278           0 :   r->tt = NULL;
     279           0 :   r->fo = TMH_EXCHANGES_find_exchange (r->exchange_url,
     280             :                                        NULL,
     281             :                                        GNUNET_NO,
     282             :                                        &find_cb,
     283             :                                        r);
     284           0 :   if (NULL == r->fo)
     285             :   {
     286           0 :     try_later (r);
     287           0 :     return;
     288             :   }
     289             : }
     290             : 
     291             : 
     292             : /**
     293             :  * Function called with information about a reserve that we need
     294             :  * to check the status from at the exchange to see if/when it has
     295             :  * been filled (and with what amount).
     296             :  *
     297             :  * @param cls closure, NULL
     298             :  * @param instance_id for which instance is this reserve
     299             :  * @param exchange_url base URL of the exchange at which the reserve lives
     300             :  * @param reserve_pub public key of the reserve
     301             :  * @param expected_amount how much do we expect to see in the reserve
     302             :  */
     303             : static void
     304           0 : add_reserve (void *cls,
     305             :              const char *instance_id,
     306             :              const char *exchange_url,
     307             :              const struct TALER_ReservePublicKeyP *reserve_pub,
     308             :              const struct TALER_Amount *expected_amount)
     309             : {
     310             :   struct Reserve *r;
     311             : 
     312             :   (void) cls;
     313           0 :   r = GNUNET_new (struct Reserve);
     314           0 :   r->exchange_url = GNUNET_strdup (exchange_url);
     315           0 :   r->instance_id = GNUNET_strdup (instance_id);
     316           0 :   r->reserve_pub = *reserve_pub;
     317           0 :   r->expected_amount = *expected_amount;
     318           0 :   GNUNET_CONTAINER_DLL_insert (reserves_head,
     319             :                                reserves_tail,
     320             :                                r);
     321           0 :   try_now (r);
     322           0 : }
     323             : 
     324             : 
     325             : void
     326           3 : TMH_RESERVES_init (void)
     327             : {
     328           3 :   TMH_db->lookup_pending_reserves (TMH_db->cls,
     329             :                                    &add_reserve,
     330             :                                    NULL);
     331           3 : }
     332             : 
     333             : 
     334             : void
     335           0 : TMH_RESERVES_check (const char *instance_id,
     336             :                     const char *exchange_url,
     337             :                     const struct TALER_ReservePublicKeyP *reserve_pub,
     338             :                     const struct TALER_Amount *expected_amount)
     339             : {
     340           0 :   add_reserve (NULL,
     341             :                instance_id,
     342             :                exchange_url,
     343             :                reserve_pub,
     344             :                expected_amount);
     345           0 : }
     346             : 
     347             : 
     348             : void
     349           3 : TMH_RESERVES_done (void)
     350             : {
     351           3 :   while (NULL != reserves_head)
     352           0 :     free_reserve (reserves_head);
     353           3 : }
     354             : 
     355             : 
     356             : /* end of taler-merchant-httpd_reserves.c */

Generated by: LCOV version 1.14