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


      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 "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_twg_history.h"
     32 #include "fakebank_common_lookup.h"
     33 #include "fakebank_common_lp.h"
     34 #include "fakebank_common_parser.h"
     35 
     36 /**
     37  * Function called to clean up a history context.
     38  *
     39  * @param cls a `struct HistoryContext *`
     40  */
     41 static void
     42 history_cleanup (void *cls)
     43 {
     44   struct HistoryContext *hc = cls;
     45 
     46   json_decref (hc->history);
     47   GNUNET_free (hc);
     48 }
     49 
     50 
     51 MHD_RESULT
     52 TALER_FAKEBANK_twg_get_debit_history_ (
     53   struct TALER_FAKEBANK_Handle *h,
     54   struct MHD_Connection *connection,
     55   const char *account,
     56   void **con_cls)
     57 {
     58   struct ConnectionContext *cc = *con_cls;
     59   struct HistoryContext *hc;
     60   struct Transaction *pos;
     61   enum GNUNET_GenericReturnValue ret;
     62   bool in_shutdown;
     63   const char *acc_payto_uri;
     64 
     65   if (NULL == cc)
     66   {
     67     cc = GNUNET_new (struct ConnectionContext);
     68     cc->ctx_cleaner = &history_cleanup;
     69     *con_cls = cc;
     70     hc = GNUNET_new (struct HistoryContext);
     71     cc->ctx = hc;
     72 
     73     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     74                 "Handling /history/outgoing connection %p\n",
     75                 connection);
     76     if (GNUNET_OK !=
     77         (ret = TALER_FAKEBANK_common_parse_history_args (h,
     78                                                          connection,
     79                                                          &hc->ha)))
     80     {
     81       GNUNET_break_op (0);
     82       return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES;
     83     }
     84     GNUNET_assert (0 ==
     85                    pthread_mutex_lock (&h->big_lock));
     86     if (UINT64_MAX == hc->ha.start_idx)
     87       hc->ha.start_idx = h->serial_counter;
     88     hc->acc = TALER_FAKEBANK_lookup_account_ (h,
     89                                               account,
     90                                               NULL);
     91     if (NULL == hc->acc)
     92     {
     93       GNUNET_assert (0 ==
     94                      pthread_mutex_unlock (&h->big_lock));
     95       return TALER_MHD_reply_with_error (connection,
     96                                          MHD_HTTP_NOT_FOUND,
     97                                          TALER_EC_BANK_UNKNOWN_ACCOUNT,
     98                                          account);
     99     }
    100     hc->history = json_array ();
    101     if (NULL == hc->history)
    102     {
    103       GNUNET_break (0);
    104       GNUNET_assert (0 ==
    105                      pthread_mutex_unlock (&h->big_lock));
    106       return MHD_NO;
    107     }
    108     hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout);
    109   }
    110   else
    111   {
    112     hc = cc->ctx;
    113     GNUNET_assert (0 ==
    114                    pthread_mutex_lock (&h->big_lock));
    115   }
    116 
    117   if (! hc->ha.have_start)
    118   {
    119     pos = (0 > hc->ha.delta)
    120       ? hc->acc->out_tail
    121       : hc->acc->out_head;
    122   }
    123   else
    124   {
    125     struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit];
    126     bool overflow;
    127     uint64_t dir;
    128     bool skip = true;
    129 
    130     dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1;
    131     overflow = (t->row_id != hc->ha.start_idx);
    132     /* If account does not match, linear scan for
    133        first matching account. */
    134     while ( (! overflow) &&
    135             (NULL != t) &&
    136             (t->debit_account != hc->acc) )
    137     {
    138       skip = false;
    139       t = h->transactions[(t->row_id + dir) % h->ram_limit];
    140       if ( (NULL != t) &&
    141            (t->row_id == hc->ha.start_idx) )
    142         overflow = true; /* full circle, give up! */
    143     }
    144     if ( (NULL == t) ||
    145          overflow)
    146     {
    147       /* FIXME: these conditions are unclear to me. */
    148       if ( (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout)) &&
    149            (0 < hc->ha.delta))
    150       {
    151         acc_payto_uri = hc->acc->payto_uri;
    152         in_shutdown = h->in_shutdown;
    153         GNUNET_assert (0 ==
    154                        pthread_mutex_unlock (&h->big_lock));
    155         if (overflow)
    156         {
    157           return TALER_MHD_reply_with_ec (
    158             connection,
    159             TALER_EC_BANK_ANCIENT_TRANSACTION_GONE,
    160             NULL);
    161         }
    162         goto finish;
    163       }
    164       if (h->in_shutdown)
    165       {
    166         acc_payto_uri = hc->acc->payto_uri;
    167         in_shutdown = h->in_shutdown;
    168         GNUNET_assert (0 ==
    169                        pthread_mutex_unlock (&h->big_lock));
    170         goto finish;
    171       }
    172       TALER_FAKEBANK_start_lp_ (h,
    173                                 connection,
    174                                 hc->acc,
    175                                 GNUNET_TIME_absolute_get_remaining (
    176                                   hc->timeout),
    177                                 LP_DEBIT,
    178                                 NULL);
    179       GNUNET_assert (0 ==
    180                      pthread_mutex_unlock (&h->big_lock));
    181       return MHD_YES;
    182     }
    183     if (t->debit_account != hc->acc)
    184     {
    185       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    186                   "Invalid start specified, transaction %llu not with account %s!\n",
    187                   (unsigned long long) hc->ha.start_idx,
    188                   account);
    189       GNUNET_assert (0 ==
    190                      pthread_mutex_unlock (&h->big_lock));
    191       return MHD_NO;
    192     }
    193     if (skip)
    194     {
    195       /* range is exclusive, skip the matching entry */
    196       if (0 > hc->ha.delta)
    197         pos = t->prev_out;
    198       else
    199         pos = t->next_out;
    200     }
    201     else
    202     {
    203       pos = t;
    204     }
    205   }
    206   if (NULL != pos)
    207     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    208                 "Returning %lld debit transactions starting (inclusive) from %llu\n",
    209                 (long long) hc->ha.delta,
    210                 (unsigned long long) pos->row_id);
    211   while ( (0 != hc->ha.delta) &&
    212           (NULL != pos) )
    213   {
    214     json_t *trans;
    215     char *credit_payto;
    216 
    217     if (T_DEBIT != pos->type)
    218     {
    219       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    220                   "Unexpected CREDIT transaction #%llu for account `%s'\n",
    221                   (unsigned long long) pos->row_id,
    222                   account);
    223       if (0 > hc->ha.delta)
    224         pos = pos->prev_in;
    225       if (0 < hc->ha.delta)
    226         pos = pos->next_in;
    227       continue;
    228     }
    229     GNUNET_asprintf (&credit_payto,
    230                      "payto://x-taler-bank/localhost/%s?receiver-name=%s",
    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 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 }