LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_db.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 46 73 63.0 %
Date: 2017-09-17 17:24:28 Functions: 2 2 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2017 GNUnet e.V.
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU 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-exchange-httpd_db.c
      18             :  * @brief Generic database operations for the exchange.
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <pthread.h>
      23             : #include <jansson.h>
      24             : #include <gnunet/gnunet_json_lib.h>
      25             : #include "taler_json_lib.h"
      26             : #include "taler-exchange-httpd_responses.h"
      27             : #include "taler-exchange-httpd_keystate.h"
      28             : 
      29             : /**
      30             :  * How often should we retry a transaction before giving up
      31             :  * (for transactions resulting in serialization/dead locks only).
      32             :  */
      33             : #define MAX_TRANSACTION_COMMIT_RETRIES 3
      34             : 
      35             : 
      36             : /**
      37             :  * Run a database transaction for @a connection.
      38             :  * Starts a transaction and calls @a cb.  Upon success,
      39             :  * attempts to commit the transaction.  Upon soft failures,
      40             :  * retries @a cb a few times.  Upon hard or persistent soft
      41             :  * errors, generates an error message for @a connection.
      42             :  * 
      43             :  * @param connection MHD connection to run @a cb for
      44             :  * @param[out] set to MHD response code, if transaction failed
      45             :  * @param cb callback implementing transaction logic
      46             :  * @param cb_cls closure for @a cb, must be read-only!
      47             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
      48             :  */
      49             : int
      50          72 : TEH_DB_run_transaction (struct MHD_Connection *connection,
      51             :                         int *mhd_ret,
      52             :                         TEH_DB_TransactionCallback cb,
      53             :                         void *cb_cls)
      54             : {
      55             :   struct TALER_EXCHANGEDB_Session *session;
      56             : 
      57          72 :   if (NULL != mhd_ret)
      58          41 :     *mhd_ret = -1; /* invalid value */
      59          72 :   if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
      60             :   {
      61           0 :     GNUNET_break (0);
      62           0 :     if (NULL != mhd_ret)
      63           0 :       *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
      64             :                                                        TALER_EC_DB_SETUP_FAILED);
      65           0 :     return GNUNET_SYSERR;
      66             :   }
      67          72 :   for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES; retries++)
      68             :   {
      69             :     enum GNUNET_DB_QueryStatus qs;
      70             : 
      71          72 :     if (GNUNET_OK !=                                            
      72          72 :         TEH_plugin->start (TEH_plugin->cls,                     
      73             :                            session))                            
      74             :     {                                      
      75           0 :       GNUNET_break (0);
      76           0 :       if (NULL != mhd_ret)
      77           0 :         *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, 
      78             :                                                          TALER_EC_DB_START_FAILED);
      79           0 :       return GNUNET_SYSERR;
      80             :     }
      81          72 :     qs = cb (cb_cls,
      82             :              connection,
      83             :              session,
      84             :              mhd_ret);
      85          72 :     if (0 > qs)
      86           9 :       TEH_plugin->rollback (TEH_plugin->cls,
      87             :                             session);      
      88          72 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
      89           9 :       return GNUNET_SYSERR;
      90          63 :     if (0 <= qs)
      91          63 :       qs = TEH_plugin->commit (TEH_plugin->cls,
      92             :                                session);                              
      93          63 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
      94             :     {
      95           0 :       if (NULL != mhd_ret)
      96           0 :         *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
      97             :                                                     TALER_EC_DB_COMMIT_FAILED_HARD);
      98           0 :       return GNUNET_SYSERR;
      99             :     }
     100             :     /* make sure callback did not violate invariants! */
     101          63 :     GNUNET_assert ( (NULL == mhd_ret) ||
     102             :                     (-1 == *mhd_ret) );
     103          63 :     if (0 <= qs)
     104          63 :       return GNUNET_OK;
     105             :   }
     106           0 :   TALER_LOG_WARNING ("Transaction commit failed %u times\n",
     107             :                      MAX_TRANSACTION_COMMIT_RETRIES);
     108           0 :   if (NULL != mhd_ret)
     109           0 :     *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
     110             :                                                 TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
     111           0 :   return GNUNET_SYSERR;
     112             : }
     113             : 
     114             : 
     115             : /**
     116             :  * Calculate the total value of all transactions performed.
     117             :  * Stores @a off plus the cost of all transactions in @a tl
     118             :  * in @a ret.
     119             :  *
     120             :  * @param tl transaction list to process
     121             :  * @param off offset to use as the starting value
     122             :  * @param[out] ret where the resulting total is to be stored
     123             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
     124             :  */
     125             : // FIXME: maybe move to another module, i.e. exchangedb???
     126             : int
     127          15 : TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
     128             :                                           const struct TALER_Amount *off,
     129             :                                           struct TALER_Amount *ret)
     130             : {
     131          15 :   struct TALER_Amount spent = *off;
     132             :   struct TALER_EXCHANGEDB_TransactionList *pos;
     133             :   struct TALER_Amount refunded;
     134             : 
     135          15 :   TALER_amount_get_zero (spent.currency,
     136             :                          &refunded);
     137          26 :   for (pos = tl; NULL != pos; pos = pos->next)
     138             :   {
     139          11 :     switch (pos->type)
     140             :     {
     141             :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     142             :       /* spent += pos->amount_with_fee */
     143           8 :       if (GNUNET_OK !=
     144           8 :           TALER_amount_add (&spent,
     145             :                             &spent,
     146           8 :                             &pos->details.deposit->amount_with_fee))
     147             :       {
     148           0 :         GNUNET_break (0);
     149           0 :         return GNUNET_SYSERR;
     150             :       }
     151           8 :       break;
     152             :     case TALER_EXCHANGEDB_TT_REFRESH_MELT:
     153             :       /* spent += pos->amount_with_fee */
     154           1 :       if (GNUNET_OK !=
     155           1 :           TALER_amount_add (&spent,
     156             :                             &spent,
     157           1 :                             &pos->details.melt->amount_with_fee))
     158             :       {
     159           0 :         GNUNET_break (0);
     160           0 :         return GNUNET_SYSERR;
     161             :       }
     162           1 :       break;
     163             :     case TALER_EXCHANGEDB_TT_REFUND:
     164             :       /* refunded += pos->refund_amount - pos->refund_fee */
     165           1 :       if (GNUNET_OK !=
     166           1 :           TALER_amount_add (&refunded,
     167             :                             &refunded,
     168           1 :                             &pos->details.refund->refund_amount))
     169             :       {
     170           0 :         GNUNET_break (0);
     171           0 :         return GNUNET_SYSERR;
     172             :       }
     173           1 :       if (GNUNET_OK !=
     174           1 :           TALER_amount_subtract (&refunded,
     175             :                                  &refunded,
     176           1 :                                  &pos->details.refund->refund_fee))
     177             :       {
     178           0 :         GNUNET_break (0);
     179           0 :         return GNUNET_SYSERR;
     180             :       }
     181           1 :       break;
     182             :     case TALER_EXCHANGEDB_TT_PAYBACK:
     183             :       /* spent += pos->value */
     184           1 :       if (GNUNET_OK !=
     185           1 :           TALER_amount_add (&spent,
     186             :                             &spent,
     187           1 :                             &pos->details.payback->value))
     188             :       {
     189           0 :         GNUNET_break (0);
     190           0 :         return GNUNET_SYSERR;
     191             :       }
     192           1 :       break;
     193             :     }
     194             :   }
     195             :   /* spent = spent - refunded */
     196          15 :   if (GNUNET_SYSERR ==
     197          15 :       TALER_amount_subtract (&spent,
     198             :                              &spent,
     199             :                              &refunded))
     200             :   {
     201           0 :     GNUNET_break (0);
     202           0 :     return GNUNET_SYSERR;
     203             :   }
     204             : 
     205          15 :   *ret = spent;
     206          15 :   return GNUNET_OK;
     207             : }
     208             : 
     209             : 
     210             : /* end of taler-exchange-httpd_db.c */

Generated by: LCOV version 1.13