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-11-25 11:31:41 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 :   GNUNET_assert (GNUNET_OK ==
     136             :                  TALER_amount_get_zero (spent.currency,
     137             :                                         &refunded));
     138          26 :   for (pos = tl; NULL != pos; pos = pos->next)
     139             :   {
     140          11 :     switch (pos->type)
     141             :     {
     142             :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     143             :       /* spent += pos->amount_with_fee */
     144           8 :       if (GNUNET_OK !=
     145           8 :           TALER_amount_add (&spent,
     146             :                             &spent,
     147           8 :                             &pos->details.deposit->amount_with_fee))
     148             :       {
     149           0 :         GNUNET_break (0);
     150           0 :         return GNUNET_SYSERR;
     151             :       }
     152           8 :       break;
     153             :     case TALER_EXCHANGEDB_TT_REFRESH_MELT:
     154             :       /* spent += pos->amount_with_fee */
     155           1 :       if (GNUNET_OK !=
     156           1 :           TALER_amount_add (&spent,
     157             :                             &spent,
     158           1 :                             &pos->details.melt->amount_with_fee))
     159             :       {
     160           0 :         GNUNET_break (0);
     161           0 :         return GNUNET_SYSERR;
     162             :       }
     163           1 :       break;
     164             :     case TALER_EXCHANGEDB_TT_REFUND:
     165             :       /* refunded += pos->refund_amount - pos->refund_fee */
     166           1 :       if (GNUNET_OK !=
     167           1 :           TALER_amount_add (&refunded,
     168             :                             &refunded,
     169           1 :                             &pos->details.refund->refund_amount))
     170             :       {
     171           0 :         GNUNET_break (0);
     172           0 :         return GNUNET_SYSERR;
     173             :       }
     174           1 :       if (GNUNET_OK !=
     175           1 :           TALER_amount_subtract (&refunded,
     176             :                                  &refunded,
     177           1 :                                  &pos->details.refund->refund_fee))
     178             :       {
     179           0 :         GNUNET_break (0);
     180           0 :         return GNUNET_SYSERR;
     181             :       }
     182           1 :       break;
     183             :     case TALER_EXCHANGEDB_TT_PAYBACK:
     184             :       /* spent += pos->value */
     185           1 :       if (GNUNET_OK !=
     186           1 :           TALER_amount_add (&spent,
     187             :                             &spent,
     188           1 :                             &pos->details.payback->value))
     189             :       {
     190           0 :         GNUNET_break (0);
     191           0 :         return GNUNET_SYSERR;
     192             :       }
     193           1 :       break;
     194             :     }
     195             :   }
     196             :   /* spent = spent - refunded */
     197          15 :   if (GNUNET_SYSERR ==
     198          15 :       TALER_amount_subtract (&spent,
     199             :                              &spent,
     200             :                              &refunded))
     201             :   {
     202           0 :     GNUNET_break (0);
     203           0 :     return GNUNET_SYSERR;
     204             :   }
     205             : 
     206          15 :   *ret = spent;
     207          15 :   return GNUNET_OK;
     208             : }
     209             : 
     210             : 
     211             : /* end of taler-exchange-httpd_db.c */

Generated by: LCOV version 1.13