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 (18303B)


      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 enum 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 = (NULL != t) && (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_out;
    224       if (0 < hc->ha.delta)
    225         pos = pos->next_out;
    226       continue;
    227     }
    228     GNUNET_asprintf (&credit_payto,
    229                      "payto://x-taler-bank/%s/%s?receiver-name=%s",
    230                      h->hostname,
    231                      pos->credit_account->account_name,
    232                      pos->credit_account->receiver_name);
    233 
    234     trans = GNUNET_JSON_PACK (
    235       GNUNET_JSON_pack_uint64 ("row_id",
    236                                pos->row_id),
    237       GNUNET_JSON_pack_timestamp ("date",
    238                                   pos->date),
    239       TALER_JSON_pack_amount ("amount",
    240                               &pos->amount),
    241       GNUNET_JSON_pack_string ("credit_account",
    242                                credit_payto),
    243       GNUNET_JSON_pack_string ("exchange_base_url",
    244                                pos->subject.debit.exchange_base_url),
    245       GNUNET_JSON_pack_data_auto ("wtid",
    246                                   &pos->subject.debit.wtid));
    247     GNUNET_assert (NULL != trans);
    248     GNUNET_free (credit_payto);
    249     GNUNET_assert (0 ==
    250                    json_array_append_new (hc->history,
    251                                           trans));
    252     if (hc->ha.delta > 0)
    253       hc->ha.delta--;
    254     else
    255       hc->ha.delta++;
    256     if (0 > hc->ha.delta)
    257       pos = pos->prev_out;
    258     if (0 < hc->ha.delta)
    259       pos = pos->next_out;
    260   }
    261   if ( (0 == json_array_size (hc->history)) &&
    262        (! h->in_shutdown) &&
    263        (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
    264        (0 < hc->ha.delta))
    265   {
    266     TALER_FAKEBANK_start_lp_ (h,
    267                               connection,
    268                               hc->acc,
    269                               GNUNET_TIME_absolute_get_remaining (hc->timeout),
    270                               LP_DEBIT,
    271                               NULL);
    272     GNUNET_assert (0 ==
    273                    pthread_mutex_unlock (&h->big_lock));
    274     return MHD_YES;
    275   }
    276   in_shutdown = h->in_shutdown;
    277   acc_payto_uri = hc->acc->payto_uri;
    278   GNUNET_assert (0 ==
    279                  pthread_mutex_unlock (&h->big_lock));
    280 finish:
    281   if (0 == json_array_size (hc->history))
    282   {
    283     GNUNET_break (in_shutdown ||
    284                   (! GNUNET_TIME_absolute_is_future (hc->timeout)));
    285     return TALER_MHD_reply_static (connection,
    286                                    MHD_HTTP_NO_CONTENT,
    287                                    NULL,
    288                                    NULL,
    289                                    0);
    290   }
    291   {
    292     json_t *jh = hc->history;
    293 
    294     hc->history = NULL;
    295     return TALER_MHD_REPLY_JSON_PACK (
    296       connection,
    297       MHD_HTTP_OK,
    298       GNUNET_JSON_pack_string (
    299         "debit_account",
    300         acc_payto_uri),
    301       GNUNET_JSON_pack_array_steal (
    302         "outgoing_transactions",
    303         jh));
    304   }
    305 }
    306 
    307 
    308 enum MHD_Result
    309 TALER_FAKEBANK_twg_get_credit_history_ (
    310   struct TALER_FAKEBANK_Handle *h,
    311   struct MHD_Connection *connection,
    312   const char *account,
    313   void **con_cls)
    314 {
    315   struct ConnectionContext *cc = *con_cls;
    316   struct HistoryContext *hc;
    317   const struct Transaction *pos;
    318   enum GNUNET_GenericReturnValue ret;
    319   bool in_shutdown;
    320   const char *acc_payto_uri;
    321 
    322   if (NULL == cc)
    323   {
    324     cc = GNUNET_new (struct ConnectionContext);
    325     cc->ctx_cleaner = &history_cleanup;
    326     *con_cls = cc;
    327     hc = GNUNET_new (struct HistoryContext);
    328     cc->ctx = hc;
    329     hc->history = json_array ();
    330     if (NULL == hc->history)
    331     {
    332       GNUNET_break (0);
    333       return MHD_NO;
    334     }
    335     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    336                 "Handling /history/incoming connection %p\n",
    337                 connection);
    338     if (GNUNET_OK !=
    339         (ret = TALER_FAKEBANK_common_parse_history_args (h,
    340                                                          connection,
    341                                                          &hc->ha)))
    342     {
    343       GNUNET_break_op (0);
    344       return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
    345     }
    346     hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
    347     GNUNET_assert (0 ==
    348                    pthread_mutex_lock (&h->big_lock));
    349     if (UINT64_MAX == hc->ha.start_idx)
    350       hc->ha.start_idx = h->serial_counter;
    351     hc->acc = TALER_FAKEBANK_lookup_account_ (h,
    352                                               account,
    353                                               NULL);
    354     if (NULL == hc->acc)
    355     {
    356       GNUNET_assert (0 ==
    357                      pthread_mutex_unlock (&h->big_lock));
    358       return TALER_MHD_reply_with_error (connection,
    359                                          MHD_HTTP_NOT_FOUND,
    360                                          TALER_EC_BANK_UNKNOWN_ACCOUNT,
    361                                          account);
    362     }
    363   }
    364   else
    365   {
    366     hc = cc->ctx;
    367     GNUNET_assert (0 ==
    368                    pthread_mutex_lock (&h->big_lock));
    369   }
    370 
    371   if (! hc->ha.have_start)
    372   {
    373     pos = (0 > hc->ha.delta)
    374           ? hc->acc->in_tail
    375           : hc->acc->in_head;
    376   }
    377   else
    378   {
    379     struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
    380     bool overflow;
    381     uint64_t dir;
    382     bool skip = true;
    383 
    384     overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) );
    385     dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
    386     /* If account does not match, linear scan for
    387        first matching account. */
    388     while ( (! overflow) &&
    389             (NULL != t) &&
    390             (t->credit_account != hc->acc) )
    391     {
    392       skip = false;
    393       t = h->transactions[(t->row_id + dir) % h->ram_limit];
    394       if ( (NULL != t) &&
    395            (t->row_id == hc->ha.start_idx) )
    396         overflow = true; /* full circle, give up! */
    397     }
    398     if ( (NULL == t) ||
    399          overflow)
    400     {
    401       in_shutdown = h->in_shutdown;
    402       /* FIXME: these conditions are unclear to me. */
    403       if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) &&
    404           (0 < hc->ha.delta))
    405       {
    406         acc_payto_uri = hc->acc->payto_uri;
    407         GNUNET_assert (0 ==
    408                        pthread_mutex_unlock (&h->big_lock));
    409         if (overflow)
    410           return TALER_MHD_reply_with_ec (
    411             connection,
    412             TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
    413             NULL);
    414         goto finish;
    415       }
    416       if (in_shutdown)
    417       {
    418         acc_payto_uri = hc->acc->payto_uri;
    419         GNUNET_assert (0 ==
    420                        pthread_mutex_unlock (&h->big_lock));
    421         goto finish;
    422       }
    423       TALER_FAKEBANK_start_lp_ (h,
    424                                 connection,
    425                                 hc->acc,
    426                                 GNUNET_TIME_absolute_get_remaining (
    427                                   hc->timeout),
    428                                 LP_CREDIT,
    429                                 NULL);
    430       GNUNET_assert (0 ==
    431                      pthread_mutex_unlock (&h->big_lock));
    432       return MHD_YES;
    433     }
    434     if (skip)
    435     {
    436       /* range from application is exclusive, skip the
    437   matching entry */
    438       if (0 > hc->ha.delta)
    439         pos = t->prev_in;
    440       else
    441         pos = t->next_in;
    442     }
    443     else
    444     {
    445       pos = t;
    446     }
    447   }
    448   if (NULL != pos)
    449     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    450                 "Returning %lld credit transactions starting (inclusive) from %llu\n",
    451                 (long long) hc->ha.delta,
    452                 (unsigned long long) pos->row_id);
    453   while ( (0 != hc->ha.delta) &&
    454           (NULL != pos) )
    455   {
    456     json_t *trans;
    457 
    458     if ( (T_CREDIT != pos->type) &&
    459          (T_AUTH != pos->type) )
    460     {
    461       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    462                   "Unexpected DEBIT transaction #%llu for account `%s'\n",
    463                   (unsigned long long) pos->row_id,
    464                   account);
    465       if (0 > hc->ha.delta)
    466         pos = pos->prev_in;
    467       if (0 < hc->ha.delta)
    468         pos = pos->next_in;
    469       continue;
    470     }
    471     switch (pos->type)
    472     {
    473     case T_DEBIT:
    474       GNUNET_assert (0);
    475       break;
    476     case T_WAD:
    477       trans = GNUNET_JSON_PACK (
    478         GNUNET_JSON_pack_string ("type",
    479                                  "WAD"),
    480         GNUNET_JSON_pack_uint64 ("row_id",
    481                                  pos->row_id),
    482         GNUNET_JSON_pack_timestamp ("date",
    483                                     pos->date),
    484         TALER_JSON_pack_amount ("amount",
    485                                 &pos->amount),
    486         GNUNET_JSON_pack_string ("debit_account",
    487                                  pos->debit_account->payto_uri),
    488         GNUNET_JSON_pack_string ("origin_exchange_url",
    489                                  pos->subject.wad.origin_base_url),
    490         GNUNET_JSON_pack_data_auto ("wad_id",
    491                                     &pos->subject.wad.wad_id));
    492       break;
    493     case T_CREDIT:
    494       trans = GNUNET_JSON_PACK (
    495         GNUNET_JSON_pack_string ("type",
    496                                  "RESERVE"),
    497         GNUNET_JSON_pack_uint64 ("row_id",
    498                                  pos->row_id),
    499         GNUNET_JSON_pack_timestamp ("date",
    500                                     pos->date),
    501         TALER_JSON_pack_amount ("amount",
    502                                 &pos->amount),
    503         GNUNET_JSON_pack_string ("debit_account",
    504                                  pos->debit_account->payto_uri),
    505         GNUNET_JSON_pack_data_auto ("reserve_pub",
    506                                     &pos->subject.credit.reserve_pub));
    507       break;
    508     case T_AUTH:
    509       trans = GNUNET_JSON_PACK (
    510         GNUNET_JSON_pack_string ("type",
    511                                  "KYCAUTH"),
    512         GNUNET_JSON_pack_uint64 ("row_id",
    513                                  pos->row_id),
    514         GNUNET_JSON_pack_timestamp ("date",
    515                                     pos->date),
    516         TALER_JSON_pack_amount ("amount",
    517                                 &pos->amount),
    518         GNUNET_JSON_pack_string ("debit_account",
    519                                  pos->debit_account->payto_uri),
    520         GNUNET_JSON_pack_data_auto ("account_pub",
    521                                     &pos->subject.auth.account_pub));
    522       break;
    523     }
    524     GNUNET_assert (NULL != trans);
    525     GNUNET_assert (0 ==
    526                    json_array_append_new (hc->history,
    527                                           trans));
    528     if (hc->ha.delta > 0)
    529       hc->ha.delta--;
    530     else
    531       hc->ha.delta++;
    532     if (0 > hc->ha.delta)
    533       pos = pos->prev_in;
    534     if (0 < hc->ha.delta)
    535       pos = pos->next_in;
    536   }
    537   if ( (0 == json_array_size (hc->history)) &&
    538        (! h->in_shutdown) &&
    539        (GNUNET_TIME_absolute_is_future (hc->timeout)) &&
    540        (0 < hc->ha.delta))
    541   {
    542     TALER_FAKEBANK_start_lp_ (h,
    543                               connection,
    544                               hc->acc,
    545                               GNUNET_TIME_absolute_get_remaining (hc->timeout),
    546                               LP_CREDIT,
    547                               NULL);
    548     GNUNET_assert (0 ==
    549                    pthread_mutex_unlock (&h->big_lock));
    550     return MHD_YES;
    551   }
    552   in_shutdown = h->in_shutdown;
    553   acc_payto_uri = hc->acc->payto_uri;
    554   GNUNET_assert (0 ==
    555                  pthread_mutex_unlock (&h->big_lock));
    556 finish:
    557   if (0 == json_array_size (hc->history))
    558   {
    559     GNUNET_break (in_shutdown ||
    560                   (! GNUNET_TIME_absolute_is_future (hc->timeout)));
    561     return TALER_MHD_reply_static (connection,
    562                                    MHD_HTTP_NO_CONTENT,
    563                                    NULL,
    564                                    NULL,
    565                                    0);
    566   }
    567   {
    568     json_t *jh = hc->history;
    569 
    570     hc->history = NULL;
    571     return TALER_MHD_REPLY_JSON_PACK (
    572       connection,
    573       MHD_HTTP_OK,
    574       GNUNET_JSON_pack_string (
    575         "credit_account",
    576         acc_payto_uri),
    577       GNUNET_JSON_pack_array_steal (
    578         "incoming_transactions",
    579         jh));
    580   }
    581 }