exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

fakebank_tbr_get_history.c (9541B)


      1 /*
      2   This file is part of TALER
      3   (C) 2016-2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or
      6   modify it under the terms of the GNU General Public License
      7   as published by the Free Software Foundation; either version 3,
      8   or (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful,
     11   but WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file bank-lib/fakebank_tbr_get_history.c
     21  * @brief library that fakes being a Taler bank for testcases
     22  * @author Christian Grothoff <christian@grothoff.org>
     23  */
     24 #include "taler/platform.h"
     25 #include <pthread.h>
     26 #include "taler/taler_fakebank_lib.h"
     27 #include "taler/taler_bank_service.h"
     28 #include "taler/taler_mhd_lib.h"
     29 #include <gnunet/gnunet_mhd_compat.h>
     30 #include "fakebank.h"
     31 #include "fakebank_common_lookup.h"
     32 #include "fakebank_common_lp.h"
     33 #include "fakebank_common_parser.h"
     34 #include "fakebank_tbr_get_history.h"
     35 
     36 
     37 /**
     38  * Function called to clean up a history context.
     39  *
     40  * @param cls a `struct HistoryContext *`
     41  */
     42 static void
     43 history_cleanup (void *cls)
     44 {
     45   struct HistoryContext *hc = cls;
     46 
     47   json_decref (hc->history);
     48   GNUNET_free (hc);
     49 }
     50 
     51 
     52 MHD_RESULT
     53 TALER_FAKEBANK_tbr_get_history (
     54   struct TALER_FAKEBANK_Handle *h,
     55   struct MHD_Connection *connection,
     56   const char *account,
     57   void **con_cls)
     58 {
     59   struct ConnectionContext *cc = *con_cls;
     60   struct HistoryContext *hc;
     61   const struct Transaction *pos;
     62   enum GNUNET_GenericReturnValue ret;
     63   bool in_shutdown;
     64   const char *acc_payto_uri;
     65 
     66   if (NULL == cc)
     67   {
     68     cc = GNUNET_new (struct ConnectionContext);
     69     cc->ctx_cleaner = &history_cleanup;
     70     *con_cls = cc;
     71     hc = GNUNET_new (struct HistoryContext);
     72     cc->ctx = hc;
     73     hc->history = json_array ();
     74     if (NULL == hc->history)
     75     {
     76       GNUNET_break (0);
     77       return MHD_NO;
     78     }
     79     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     80                 "Handling /accounts/%s/taler-revenue/history request\n",
     81                 account);
     82     if (GNUNET_OK !=
     83         (ret = TALER_FAKEBANK_common_parse_history_args (h,
     84                                                          connection,
     85                                                          &hc->ha)))
     86     {
     87       GNUNET_break_op (0);
     88       return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
     89     }
     90     hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
     91     GNUNET_assert (0 ==
     92                    pthread_mutex_lock (&h->big_lock));
     93     if (UINT64_MAX == hc->ha.start_idx)
     94       hc->ha.start_idx = h->serial_counter;
     95     hc->acc = TALER_FAKEBANK_lookup_account_ (h,
     96                                               account,
     97                                               NULL);
     98     if (NULL == hc->acc)
     99     {
    100       GNUNET_assert (0 ==
    101                      pthread_mutex_unlock (&h->big_lock));
    102       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    103                   "Account %s is unknown\n",
    104                   account);
    105       return TALER_MHD_reply_with_error (connection,
    106                                          MHD_HTTP_NOT_FOUND,
    107                                          TALER_EC_BANK_UNKNOWN_ACCOUNT,
    108                                          account);
    109     }
    110   }
    111   else
    112   {
    113     hc = cc->ctx;
    114     GNUNET_assert (0 ==
    115                    pthread_mutex_lock (&h->big_lock));
    116   }
    117 
    118   if (! hc->ha.have_start)
    119   {
    120     pos = (0 > hc->ha.delta)
    121           ? hc->acc->in_tail
    122           : hc->acc->in_head;
    123   }
    124   else
    125   {
    126     struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
    127     bool overflow;
    128     uint64_t dir;
    129     bool skip = true;
    130 
    131     overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
    132     dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
    133     /* If account does not match, linear scan for
    134        first matching account. */
    135     while ( (! overflow) &&
    136             (NULL != t) &&
    137             (t->credit_account != hc->acc) )
    138     {
    139       skip = false;
    140       t = h->transactions[(t->row_id + dir) % h->ram_limit];
    141       if ( (NULL != t) &&
    142            (t->row_id == hc->ha.start_idx) )
    143         overflow = true; /* full circle, give up! */
    144     }
    145     if ( (NULL == t) ||
    146          overflow)
    147     {
    148       in_shutdown = h->in_shutdown;
    149       /* FIXME: these conditions are unclear to me. */
    150       if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
    151           (0 < hc->ha.delta))
    152       {
    153         acc_payto_uri = hc->acc->payto_uri;
    154         GNUNET_assert (0 ==
    155                        pthread_mutex_unlock (&h->big_lock));
    156         if (overflow)
    157         {
    158           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    159                       "Transactions lost due to RAM limits\n");
    160           return TALER_MHD_reply_with_ec (
    161             connection,
    162             TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
    163             NULL);
    164         }
    165         goto finish;
    166       }
    167       if (in_shutdown)
    168       {
    169         acc_payto_uri = hc->acc->payto_uri;
    170         GNUNET_assert (0 ==
    171                        pthread_mutex_unlock (&h->big_lock));
    172         goto finish;
    173       }
    174       TALER_FAKEBANK_start_lp_ (h,
    175                                 connection,
    176                                 hc->acc,
    177                                 GNUNET_TIME_absolute_get_remaining (
    178                                   hc->timeout),
    179                                 LP_CREDIT,
    180                                 NULL);
    181       GNUNET_assert (0 ==
    182                      pthread_mutex_unlock (&h->big_lock));
    183       return MHD_YES;
    184     }
    185     if (skip)
    186     {
    187       /* range from application is exclusive, skip the
    188   matching entry */
    189       if (0 > hc->ha.delta)
    190         pos = t->prev_in;
    191       else
    192         pos = t->next_in;
    193     }
    194     else
    195     {
    196       pos = t;
    197     }
    198   }
    199   if (NULL != pos)
    200     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    201                 "Returning %lld credit transactions starting (inclusive) from %llu\n",
    202                 (long long) hc->ha.delta,
    203                 (unsigned long long) pos->row_id);
    204   while ( (0 != hc->ha.delta) &&
    205           (NULL != pos) )
    206   {
    207     json_t *trans;
    208     char *subject;
    209 
    210     if (T_DEBIT != pos->type)
    211     {
    212       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    213                   "Unexpected CREDIT transaction #%llu for account `%s'\n",
    214                   (unsigned long long) pos->row_id,
    215                   account);
    216       if (0 > hc->ha.delta)
    217         pos = pos->prev_in;
    218       if (0 < hc->ha.delta)
    219         pos = pos->next_in;
    220       continue;
    221     }
    222 
    223     {
    224       char *wtids;
    225 
    226       wtids = GNUNET_STRINGS_data_to_string_alloc (
    227         &pos->subject.debit.wtid,
    228         sizeof (pos->subject.debit.wtid));
    229       GNUNET_asprintf (&subject,
    230                        "%s %s",
    231                        wtids,
    232                        pos->subject.debit.exchange_base_url);
    233       GNUNET_free (wtids);
    234     }
    235     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    236                 "Found transaction over %s with subject %s\n",
    237                 TALER_amount2s (&pos->amount),
    238                 subject);
    239     trans = GNUNET_JSON_PACK (
    240       GNUNET_JSON_pack_string ("type",
    241                                "RESERVE"),
    242       GNUNET_JSON_pack_uint64 ("row_id",
    243                                pos->row_id),
    244       GNUNET_JSON_pack_timestamp ("date",
    245                                   pos->date),
    246       TALER_JSON_pack_amount ("amount",
    247                               &pos->amount),
    248       GNUNET_JSON_pack_string ("debit_account",
    249                                pos->debit_account->payto_uri),
    250       GNUNET_JSON_pack_string ("subject",
    251                                subject));
    252     GNUNET_free (subject);
    253     GNUNET_assert (NULL != trans);
    254     GNUNET_assert (0 ==
    255                    json_array_append_new (hc->history,
    256                                           trans));
    257     if (hc->ha.delta > 0)
    258       hc->ha.delta--;
    259     else
    260       hc->ha.delta++;
    261     if (0 > hc->ha.delta)
    262       pos = pos->prev_in;
    263     if (0 < hc->ha.delta)
    264       pos = pos->next_in;
    265   }
    266   if ( (0 == json_array_size (hc->history)) &&
    267        (! h->in_shutdown) &&
    268        (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
    269        (0 < hc->ha.delta))
    270   {
    271     TALER_FAKEBANK_start_lp_ (h,
    272                               connection,
    273                               hc->acc,
    274                               GNUNET_TIME_absolute_get_remaining (hc->timeout),
    275                               LP_CREDIT,
    276                               NULL);
    277     GNUNET_assert (0 ==
    278                    pthread_mutex_unlock (&h->big_lock));
    279     return MHD_YES;
    280   }
    281   in_shutdown = h->in_shutdown;
    282   acc_payto_uri = hc->acc->payto_uri;
    283   GNUNET_assert (0 ==
    284                  pthread_mutex_unlock (&h->big_lock));
    285 finish:
    286   if (0 == json_array_size (hc->history))
    287   {
    288     GNUNET_break (in_shutdown ||
    289                   (! GNUNET_TIME_absolute_is_future (hc->timeout)));
    290     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    291                 "Zero transactions found\n");
    292     return TALER_MHD_reply_static (connection,
    293                                    MHD_HTTP_NO_CONTENT,
    294                                    NULL,
    295                                    NULL,
    296                                    0);
    297   }
    298   {
    299     json_t *jh = hc->history;
    300 
    301     hc->history = NULL;
    302     return TALER_MHD_REPLY_JSON_PACK (
    303       connection,
    304       MHD_HTTP_OK,
    305       GNUNET_JSON_pack_string (
    306         "credit_account",
    307         acc_payto_uri),
    308       GNUNET_JSON_pack_array_steal (
    309         "incoming_transactions",
    310         jh));
    311   }
    312 }