exchange

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

fakebank_twg_history.c (18249B)


      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_twg_history.c
     21  * @brief routines to return account histories for the Taler Wire Gateway API
     22  * @author Christian Grothoff <christian@grothoff.org>
     23  */
     24 #include <pthread.h>
     25 #include "taler/taler_fakebank_lib.h"
     26 #include "taler/taler_bank_service.h"
     27 #include "taler/taler_mhd_lib.h"
     28 #include <gnunet/gnunet_mhd_compat.h>
     29 #include "fakebank.h"
     30 #include "fakebank_twg_history.h"
     31 #include "fakebank_common_lookup.h"
     32 #include "fakebank_common_lp.h"
     33 #include "fakebank_common_parser.h"
     34 
     35 /**
     36  * Function called to clean up a history context.
     37  *
     38  * @param cls a `struct HistoryContext *`
     39  */
     40 static void
     41 history_cleanup (void *cls)
     42 {
     43   struct HistoryContext *hc = cls;
     44 
     45   json_decref (hc->history);
     46   GNUNET_free (hc);
     47 }
     48 
     49 
     50 MHD_RESULT
     51 TALER_FAKEBANK_twg_get_debit_history_ (
     52   struct TALER_FAKEBANK_Handle *h,
     53   struct MHD_Connection *connection,
     54   const char *account,
     55   void **con_cls)
     56 {
     57   struct ConnectionContext *cc = *con_cls;
     58   struct HistoryContext *hc;
     59   struct Transaction *pos;
     60   enum GNUNET_GenericReturnValue ret;
     61   bool in_shutdown;
     62   const char *acc_payto_uri;
     63 
     64   if (NULL == cc)
     65   {
     66     cc = GNUNET_new (struct ConnectionContext);
     67     cc->ctx_cleaner = &history_cleanup;
     68     *con_cls = cc;
     69     hc = GNUNET_new (struct HistoryContext);
     70     cc->ctx = hc;
     71 
     72     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     73                 "Handling /history/outgoing connection %p\n",
     74                 connection);
     75     if (GNUNET_OK !=
     76         (ret = TALER_FAKEBANK_common_parse_history_args (h,
     77                                                          connection,
     78                                                          &hc->ha)))
     79     {
     80       GNUNET_break_op (0);
     81       return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
     82     }
     83     GNUNET_assert (0 ==
     84                    pthread_mutex_lock (&h->big_lock));
     85     if (UINT64_MAX == hc->ha.start_idx)
     86       hc->ha.start_idx = h->serial_counter;
     87     hc->acc = TALER_FAKEBANK_lookup_account_ (h,
     88                                               account,
     89                                               NULL);
     90     if (NULL == hc->acc)
     91     {
     92       GNUNET_assert (0 ==
     93                      pthread_mutex_unlock (&h->big_lock));
     94       return TALER_MHD_reply_with_error (connection,
     95                                          MHD_HTTP_NOT_FOUND,
     96                                          TALER_EC_BANK_UNKNOWN_ACCOUNT,
     97                                          account);
     98     }
     99     hc->history = json_array ();
    100     if (NULL == hc->history)
    101     {
    102       GNUNET_break (0);
    103       GNUNET_assert (0 ==
    104                      pthread_mutex_unlock (&h->big_lock));
    105       return MHD_NO;
    106     }
    107     hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
    108   }
    109   else
    110   {
    111     hc = cc->ctx;
    112     GNUNET_assert (0 ==
    113                    pthread_mutex_lock (&h->big_lock));
    114   }
    115 
    116   if (! hc->ha.have_start)
    117   {
    118     pos = (0 > hc->ha.delta)
    119       ? hc->acc->out_tail
    120       : hc->acc->out_head;
    121   }
    122   else
    123   {
    124     struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
    125     bool overflow;
    126     uint64_t dir;
    127     bool skip = true;
    128 
    129     dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
    130     overflow = (t->row_id != hc->ha.start_idx);
    131     /* If account does not match, linear scan for
    132        first matching account. */
    133     while ( (! overflow) &&
    134             (NULL != t) &&
    135             (t->debit_account != hc->acc) )
    136     {
    137       skip = false;
    138       t = h->transactions[(t->row_id + dir) % h->ram_limit];
    139       if ( (NULL != t) &&
    140            (t->row_id == hc->ha.start_idx) )
    141         overflow = true; /* full circle, give up! */
    142     }
    143     if ( (NULL == t) ||
    144          overflow)
    145     {
    146       /* FIXME: these conditions are unclear to me. */
    147       if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
    148            (0 < hc->ha.delta))
    149       {
    150         acc_payto_uri = hc->acc->payto_uri;
    151         in_shutdown = h->in_shutdown;
    152         GNUNET_assert (0 ==
    153                        pthread_mutex_unlock (&h->big_lock));
    154         if (overflow)
    155         {
    156           return TALER_MHD_reply_with_ec (
    157             connection,
    158             TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
    159             NULL);
    160         }
    161         goto finish;
    162       }
    163       if (h->in_shutdown)
    164       {
    165         acc_payto_uri = hc->acc->payto_uri;
    166         in_shutdown = h->in_shutdown;
    167         GNUNET_assert (0 ==
    168                        pthread_mutex_unlock (&h->big_lock));
    169         goto finish;
    170       }
    171       TALER_FAKEBANK_start_lp_ (h,
    172                                 connection,
    173                                 hc->acc,
    174                                 GNUNET_TIME_absolute_get_remaining (
    175                                   hc->timeout),
    176                                 LP_DEBIT,
    177                                 NULL);
    178       GNUNET_assert (0 ==
    179                      pthread_mutex_unlock (&h->big_lock));
    180       return MHD_YES;
    181     }
    182     if (t->debit_account != hc->acc)
    183     {
    184       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    185                   "Invalid start specified, transaction %llu not with account %s!\n",
    186                   (unsigned long long) hc->ha.start_idx,
    187                   account);
    188       GNUNET_assert (0 ==
    189                      pthread_mutex_unlock (&h->big_lock));
    190       return MHD_NO;
    191     }
    192     if (skip)
    193     {
    194       /* range is exclusive, skip the matching entry */
    195       if (0 > hc->ha.delta)
    196         pos = t->prev_out;
    197       else
    198         pos = t->next_out;
    199     }
    200     else
    201     {
    202       pos = t;
    203     }
    204   }
    205   if (NULL != pos)
    206     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    207                 "Returning %lld debit transactions starting (inclusive) from %llu\n",
    208                 (long long) hc->ha.delta,
    209                 (unsigned long long) pos->row_id);
    210   while ( (0 != hc->ha.delta) &&
    211           (NULL != pos) )
    212   {
    213     json_t *trans;
    214     char *credit_payto;
    215 
    216     if (T_DEBIT != pos->type)
    217     {
    218       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    219                   "Unexpected CREDIT transaction #%llu for account `%s'\n",
    220                   (unsigned long long) pos->row_id,
    221                   account);
    222       if (0 > hc->ha.delta)
    223         pos = pos->prev_in;
    224       if (0 < hc->ha.delta)
    225         pos = pos->next_in;
    226       continue;
    227     }
    228     GNUNET_asprintf (&credit_payto,
    229                      "payto://x-taler-bank/localhost/%s?receiver-name=%s",
    230                      pos->credit_account->account_name,
    231                      pos->credit_account->receiver_name);
    232 
    233     trans = GNUNET_JSON_PACK (
    234       GNUNET_JSON_pack_uint64 ("row_id",
    235                                pos->row_id),
    236       GNUNET_JSON_pack_timestamp ("date",
    237                                   pos->date),
    238       TALER_JSON_pack_amount ("amount",
    239                               &pos->amount),
    240       GNUNET_JSON_pack_string ("credit_account",
    241                                credit_payto),
    242       GNUNET_JSON_pack_string ("exchange_base_url",
    243                                pos->subject.debit.exchange_base_url),
    244       GNUNET_JSON_pack_data_auto ("wtid",
    245                                   &pos->subject.debit.wtid));
    246     GNUNET_assert (NULL != trans);
    247     GNUNET_free (credit_payto);
    248     GNUNET_assert (0 ==
    249                    json_array_append_new (hc->history,
    250                                           trans));
    251     if (hc->ha.delta > 0)
    252       hc->ha.delta--;
    253     else
    254       hc->ha.delta++;
    255     if (0 > hc->ha.delta)
    256       pos = pos->prev_out;
    257     if (0 < hc->ha.delta)
    258       pos = pos->next_out;
    259   }
    260   if ( (0 == json_array_size (hc->history)) &&
    261        (! h->in_shutdown) &&
    262        (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
    263        (0 < hc->ha.delta))
    264   {
    265     TALER_FAKEBANK_start_lp_ (h,
    266                               connection,
    267                               hc->acc,
    268                               GNUNET_TIME_absolute_get_remaining (hc->timeout),
    269                               LP_DEBIT,
    270                               NULL);
    271     GNUNET_assert (0 ==
    272                    pthread_mutex_unlock (&h->big_lock));
    273     return MHD_YES;
    274   }
    275   in_shutdown = h->in_shutdown;
    276   acc_payto_uri = hc->acc->payto_uri;
    277   GNUNET_assert (0 ==
    278                  pthread_mutex_unlock (&h->big_lock));
    279 finish:
    280   if (0 == json_array_size (hc->history))
    281   {
    282     GNUNET_break (in_shutdown ||
    283                   (! GNUNET_TIME_absolute_is_future (hc->timeout)));
    284     return TALER_MHD_reply_static (connection,
    285                                    MHD_HTTP_NO_CONTENT,
    286                                    NULL,
    287                                    NULL,
    288                                    0);
    289   }
    290   {
    291     json_t *jh = hc->history;
    292 
    293     hc->history = NULL;
    294     return TALER_MHD_REPLY_JSON_PACK (
    295       connection,
    296       MHD_HTTP_OK,
    297       GNUNET_JSON_pack_string (
    298         "debit_account",
    299         acc_payto_uri),
    300       GNUNET_JSON_pack_array_steal (
    301         "outgoing_transactions",
    302         jh));
    303   }
    304 }
    305 
    306 
    307 MHD_RESULT
    308 TALER_FAKEBANK_twg_get_credit_history_ (
    309   struct TALER_FAKEBANK_Handle *h,
    310   struct MHD_Connection *connection,
    311   const char *account,
    312   void **con_cls)
    313 {
    314   struct ConnectionContext *cc = *con_cls;
    315   struct HistoryContext *hc;
    316   const struct Transaction *pos;
    317   enum GNUNET_GenericReturnValue ret;
    318   bool in_shutdown;
    319   const char *acc_payto_uri;
    320 
    321   if (NULL == cc)
    322   {
    323     cc = GNUNET_new (struct ConnectionContext);
    324     cc->ctx_cleaner = &history_cleanup;
    325     *con_cls = cc;
    326     hc = GNUNET_new (struct HistoryContext);
    327     cc->ctx = hc;
    328     hc->history = json_array ();
    329     if (NULL == hc->history)
    330     {
    331       GNUNET_break (0);
    332       return MHD_NO;
    333     }
    334     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    335                 "Handling /history/incoming connection %p\n",
    336                 connection);
    337     if (GNUNET_OK !=
    338         (ret = TALER_FAKEBANK_common_parse_history_args (h,
    339                                                          connection,
    340                                                          &hc->ha)))
    341     {
    342       GNUNET_break_op (0);
    343       return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
    344     }
    345     hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
    346     GNUNET_assert (0 ==
    347                    pthread_mutex_lock (&h->big_lock));
    348     if (UINT64_MAX == hc->ha.start_idx)
    349       hc->ha.start_idx = h->serial_counter;
    350     hc->acc = TALER_FAKEBANK_lookup_account_ (h,
    351                                               account,
    352                                               NULL);
    353     if (NULL == hc->acc)
    354     {
    355       GNUNET_assert (0 ==
    356                      pthread_mutex_unlock (&h->big_lock));
    357       return TALER_MHD_reply_with_error (connection,
    358                                          MHD_HTTP_NOT_FOUND,
    359                                          TALER_EC_BANK_UNKNOWN_ACCOUNT,
    360                                          account);
    361     }
    362   }
    363   else
    364   {
    365     hc = cc->ctx;
    366     GNUNET_assert (0 ==
    367                    pthread_mutex_lock (&h->big_lock));
    368   }
    369 
    370   if (! hc->ha.have_start)
    371   {
    372     pos = (0 > hc->ha.delta)
    373           ? hc->acc->in_tail
    374           : hc->acc->in_head;
    375   }
    376   else
    377   {
    378     struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
    379     bool overflow;
    380     uint64_t dir;
    381     bool skip = true;
    382 
    383     overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
    384     dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
    385     /* If account does not match, linear scan for
    386        first matching account. */
    387     while ( (! overflow) &&
    388             (NULL != t) &&
    389             (t->credit_account != hc->acc) )
    390     {
    391       skip = false;
    392       t = h->transactions[(t->row_id + dir) % h->ram_limit];
    393       if ( (NULL != t) &&
    394            (t->row_id == hc->ha.start_idx) )
    395         overflow = true; /* full circle, give up! */
    396     }
    397     if ( (NULL == t) ||
    398          overflow)
    399     {
    400       in_shutdown = h->in_shutdown;
    401       /* FIXME: these conditions are unclear to me. */
    402       if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
    403           (0 < hc->ha.delta))
    404       {
    405         acc_payto_uri = hc->acc->payto_uri;
    406         GNUNET_assert (0 ==
    407                        pthread_mutex_unlock (&h->big_lock));
    408         if (overflow)
    409           return TALER_MHD_reply_with_ec (
    410             connection,
    411             TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
    412             NULL);
    413         goto finish;
    414       }
    415       if (in_shutdown)
    416       {
    417         acc_payto_uri = hc->acc->payto_uri;
    418         GNUNET_assert (0 ==
    419                        pthread_mutex_unlock (&h->big_lock));
    420         goto finish;
    421       }
    422       TALER_FAKEBANK_start_lp_ (h,
    423                                 connection,
    424                                 hc->acc,
    425                                 GNUNET_TIME_absolute_get_remaining (
    426                                   hc->timeout),
    427                                 LP_CREDIT,
    428                                 NULL);
    429       GNUNET_assert (0 ==
    430                      pthread_mutex_unlock (&h->big_lock));
    431       return MHD_YES;
    432     }
    433     if (skip)
    434     {
    435       /* range from application is exclusive, skip the
    436   matching entry */
    437       if (0 > hc->ha.delta)
    438         pos = t->prev_in;
    439       else
    440         pos = t->next_in;
    441     }
    442     else
    443     {
    444       pos = t;
    445     }
    446   }
    447   if (NULL != pos)
    448     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    449                 "Returning %lld credit transactions starting (inclusive) from %llu\n",
    450                 (long long) hc->ha.delta,
    451                 (unsigned long long) pos->row_id);
    452   while ( (0 != hc->ha.delta) &&
    453           (NULL != pos) )
    454   {
    455     json_t *trans;
    456 
    457     if ( (T_CREDIT != pos->type) &&
    458          (T_AUTH != pos->type) )
    459     {
    460       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    461                   "Unexpected DEBIT transaction #%llu for account `%s'\n",
    462                   (unsigned long long) pos->row_id,
    463                   account);
    464       if (0 > hc->ha.delta)
    465         pos = pos->prev_in;
    466       if (0 < hc->ha.delta)
    467         pos = pos->next_in;
    468       continue;
    469     }
    470     switch (pos->type)
    471     {
    472     case T_DEBIT:
    473       GNUNET_assert (0);
    474       break;
    475     case T_WAD:
    476       trans = GNUNET_JSON_PACK (
    477         GNUNET_JSON_pack_string ("type",
    478                                  "WAD"),
    479         GNUNET_JSON_pack_uint64 ("row_id",
    480                                  pos->row_id),
    481         GNUNET_JSON_pack_timestamp ("date",
    482                                     pos->date),
    483         TALER_JSON_pack_amount ("amount",
    484                                 &pos->amount),
    485         GNUNET_JSON_pack_string ("debit_account",
    486                                  pos->debit_account->payto_uri),
    487         GNUNET_JSON_pack_string ("origin_exchange_url",
    488                                  pos->subject.wad.origin_base_url),
    489         GNUNET_JSON_pack_data_auto ("wad_id",
    490                                     &pos->subject.wad.wad_id));
    491       break;
    492     case T_CREDIT:
    493       trans = GNUNET_JSON_PACK (
    494         GNUNET_JSON_pack_string ("type",
    495                                  "RESERVE"),
    496         GNUNET_JSON_pack_uint64 ("row_id",
    497                                  pos->row_id),
    498         GNUNET_JSON_pack_timestamp ("date",
    499                                     pos->date),
    500         TALER_JSON_pack_amount ("amount",
    501                                 &pos->amount),
    502         GNUNET_JSON_pack_string ("debit_account",
    503                                  pos->debit_account->payto_uri),
    504         GNUNET_JSON_pack_data_auto ("reserve_pub",
    505                                     &pos->subject.credit.reserve_pub));
    506       break;
    507     case T_AUTH:
    508       trans = GNUNET_JSON_PACK (
    509         GNUNET_JSON_pack_string ("type",
    510                                  "KYCAUTH"),
    511         GNUNET_JSON_pack_uint64 ("row_id",
    512                                  pos->row_id),
    513         GNUNET_JSON_pack_timestamp ("date",
    514                                     pos->date),
    515         TALER_JSON_pack_amount ("amount",
    516                                 &pos->amount),
    517         GNUNET_JSON_pack_string ("debit_account",
    518                                  pos->debit_account->payto_uri),
    519         GNUNET_JSON_pack_data_auto ("account_pub",
    520                                     &pos->subject.auth.account_pub));
    521       break;
    522     }
    523     GNUNET_assert (NULL != trans);
    524     GNUNET_assert (0 ==
    525                    json_array_append_new (hc->history,
    526                                           trans));
    527     if (hc->ha.delta > 0)
    528       hc->ha.delta--;
    529     else
    530       hc->ha.delta++;
    531     if (0 > hc->ha.delta)
    532       pos = pos->prev_in;
    533     if (0 < hc->ha.delta)
    534       pos = pos->next_in;
    535   }
    536   if ( (0 == json_array_size (hc->history)) &&
    537        (! h->in_shutdown) &&
    538        (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
    539        (0 < hc->ha.delta))
    540   {
    541     TALER_FAKEBANK_start_lp_ (h,
    542                               connection,
    543                               hc->acc,
    544                               GNUNET_TIME_absolute_get_remaining (hc->timeout),
    545                               LP_CREDIT,
    546                               NULL);
    547     GNUNET_assert (0 ==
    548                    pthread_mutex_unlock (&h->big_lock));
    549     return MHD_YES;
    550   }
    551   in_shutdown = h->in_shutdown;
    552   acc_payto_uri = hc->acc->payto_uri;
    553   GNUNET_assert (0 ==
    554                  pthread_mutex_unlock (&h->big_lock));
    555 finish:
    556   if (0 == json_array_size (hc->history))
    557   {
    558     GNUNET_break (in_shutdown ||
    559                   (! GNUNET_TIME_absolute_is_future (hc->timeout)));
    560     return TALER_MHD_reply_static (connection,
    561                                    MHD_HTTP_NO_CONTENT,
    562                                    NULL,
    563                                    NULL,
    564                                    0);
    565   }
    566   {
    567     json_t *jh = hc->history;
    568 
    569     hc->history = NULL;
    570     return TALER_MHD_REPLY_JSON_PACK (
    571       connection,
    572       MHD_HTTP_OK,
    573       GNUNET_JSON_pack_string (
    574         "credit_account",
    575         acc_payto_uri),
    576       GNUNET_JSON_pack_array_steal (
    577         "incoming_transactions",
    578         jh));
    579   }
    580 }