anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

anastasis-db_increment_lifetime.c (8841B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2020-2022 Anastasis SARL
      4 
      5   Anastasis is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Anastasis 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 Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   Anastasis; see the file COPYING.GPL.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file stasis/anastasis-db_increment_lifetime.c
     18  * @brief Anastasis database: increment lifetime
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "anastasis-db_pg.h"
     23 #include "anastasis/anastasis-database/increment_lifetime.h"
     24 #include "anastasis/anastasis-database/transaction.h"
     25 #include "anastasis/anastasis-database/preflight.h"
     26 #include <taler/taler_pq_lib.h>
     27 
     28 
     29 /**
     30  * Increment account lifetime based on payment having been received.
     31  * Does nothing if the payment is not new.
     32  *
     33  * @param account_pub which account received a payment
     34  * @param payment_identifier proof of payment, must be unique and match pending payment
     35  * @param lifetime for how long is the account now paid (increment)
     36  * @param[out] paid_until set to the end of the lifetime after the operation
     37  * @return transaction status
     38  */
     39 enum GNUNET_DB_QueryStatus
     40 ANASTASIS_DB_increment_lifetime (
     41   const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub,
     42   const struct ANASTASIS_PaymentSecretP *payment_identifier,
     43   struct GNUNET_TIME_Relative lifetime,
     44   struct GNUNET_TIME_Timestamp *paid_until)
     45 {
     46   enum GNUNET_DB_QueryStatus qs;
     47 
     48   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     49               "Incrementing lifetime of account %s based on payment by %s\n",
     50               TALER_B2S (account_pub),
     51               GNUNET_TIME_relative2s (lifetime,
     52                                       true));
     53   PREPARE ("user_insert",
     54            "INSERT INTO anastasis_user "
     55            "(user_id"
     56            ",expiration_date"
     57            ") VALUES "
     58            "($1, $2);");
     59   PREPARE ("user_select",
     60            "SELECT"
     61            " expiration_date "
     62            "FROM anastasis_user"
     63            " WHERE user_id=$1"
     64            " FOR UPDATE;");
     65   PREPARE ("user_update",
     66            "UPDATE anastasis_user"
     67            " SET "
     68            " expiration_date=$1"
     69            " WHERE user_id=$2;");
     70   PREPARE ("recdoc_payment_done",
     71            "UPDATE anastasis_recdoc_payment "
     72            "SET"
     73            " paid=TRUE "
     74            "WHERE"
     75            "  payment_identifier=$1"
     76            " AND"
     77            "  user_id=$2"
     78            " AND"
     79            "  paid=FALSE;");
     80   for (unsigned int retries = 0; retries<MAX_RETRIES; retries++)
     81   {
     82     if (GNUNET_OK !=
     83         ANASTASIS_DB_start (
     84           "increment lifetime"))
     85     {
     86       GNUNET_break (0);
     87       return GNUNET_DB_STATUS_HARD_ERROR;
     88     }
     89 
     90     {
     91       struct GNUNET_PQ_QueryParam params[] = {
     92         GNUNET_PQ_query_param_auto_from_type (payment_identifier),
     93         GNUNET_PQ_query_param_auto_from_type (account_pub),
     94         GNUNET_PQ_query_param_end
     95       };
     96       qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
     97                                                "recdoc_payment_done",
     98                                                params);
     99       switch (qs)
    100       {
    101       case GNUNET_DB_STATUS_HARD_ERROR:
    102         ANASTASIS_DB_rollback ();
    103         *paid_until = GNUNET_TIME_UNIT_ZERO_TS;
    104         return qs;
    105       case GNUNET_DB_STATUS_SOFT_ERROR:
    106         goto retry;
    107       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    108         /* Payment not new or payment request unknown. */
    109         /* continued below */
    110         break;
    111       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    112         /* Payment just now marked as 'paid' */
    113         /* continued below */
    114         break;
    115       }
    116     }
    117 
    118     {
    119       enum GNUNET_DB_QueryStatus qs2;
    120       struct GNUNET_PQ_QueryParam params[] = {
    121         GNUNET_PQ_query_param_auto_from_type (account_pub),
    122         GNUNET_PQ_query_param_end
    123       };
    124       struct GNUNET_TIME_Timestamp expiration;
    125       struct GNUNET_PQ_ResultSpec rs[] = {
    126         GNUNET_PQ_result_spec_timestamp ("expiration_date",
    127                                          &expiration),
    128         GNUNET_PQ_result_spec_end
    129       };
    130 
    131       qs2 = GNUNET_PQ_eval_prepared_singleton_select (pg->conn,
    132                                                       "user_select",
    133                                                       params,
    134                                                       rs);
    135       switch (qs2)
    136       {
    137       case GNUNET_DB_STATUS_HARD_ERROR:
    138         ANASTASIS_DB_rollback ();
    139         return qs2;
    140       case GNUNET_DB_STATUS_SOFT_ERROR:
    141         goto retry;
    142       case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    143         if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    144         {
    145           /* inconsistent, cannot have recdoc payment but no user!? */
    146           GNUNET_break (0);
    147           ANASTASIS_DB_rollback ();
    148           return GNUNET_DB_STATUS_HARD_ERROR;
    149         }
    150         else
    151         {
    152           /* user does not exist, create new one */
    153           struct GNUNET_PQ_QueryParam iparams[] = {
    154             GNUNET_PQ_query_param_auto_from_type (account_pub),
    155             GNUNET_PQ_query_param_timestamp (&expiration),
    156             GNUNET_PQ_query_param_end
    157           };
    158 
    159           expiration = GNUNET_TIME_relative_to_timestamp (lifetime);
    160           GNUNET_break (! GNUNET_TIME_absolute_is_never (expiration.abs_time));
    161           *paid_until = expiration;
    162           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    163                       "Creating new account %s with initial lifetime of %s\n",
    164                       TALER_B2S (account_pub),
    165                       GNUNET_TIME_relative2s (lifetime,
    166                                               true));
    167           qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    168                                                    "user_insert",
    169                                                    iparams);
    170         }
    171         break;
    172       case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    173         if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    174         {
    175           /* existing rec doc payment (payment replay), return
    176              existing expiration */
    177           *paid_until = expiration;
    178           ANASTASIS_DB_rollback ();
    179           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    180                       "Payment existed, lifetime of account %s unchanged at %s\n",
    181                       TALER_B2S (account_pub),
    182                       GNUNET_TIME_timestamp2s (*paid_until));
    183           return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    184         }
    185         else
    186         {
    187           /* user exists, payment is new, update expiration_date */
    188           struct GNUNET_PQ_QueryParam iparams[] = {
    189             GNUNET_PQ_query_param_timestamp (&expiration),
    190             GNUNET_PQ_query_param_auto_from_type (account_pub),
    191             GNUNET_PQ_query_param_end
    192           };
    193 
    194           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    195                       "Incrementing lifetime of account %s by %s\n",
    196                       TALER_B2S (account_pub),
    197                       GNUNET_TIME_relative2s (lifetime,
    198                                               true));
    199           expiration
    200             = GNUNET_TIME_absolute_to_timestamp (
    201                 GNUNET_TIME_absolute_add (expiration.abs_time,
    202                                           lifetime));
    203           GNUNET_break (! GNUNET_TIME_absolute_is_never (
    204                           expiration.abs_time));
    205           *paid_until = expiration;
    206           qs = GNUNET_PQ_eval_prepared_non_select (pg->conn,
    207                                                    "user_update",
    208                                                    iparams);
    209         }
    210         break;
    211       }
    212     }
    213 
    214     switch (qs)
    215     {
    216     case GNUNET_DB_STATUS_HARD_ERROR:
    217       ANASTASIS_DB_rollback ();
    218       return qs;
    219     case GNUNET_DB_STATUS_SOFT_ERROR:
    220       goto retry;
    221     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    222       GNUNET_break (0);
    223       ANASTASIS_DB_rollback ();
    224       return GNUNET_DB_STATUS_HARD_ERROR;
    225     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    226       break;
    227     }
    228     qs = ANASTASIS_DB_commit ();
    229     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    230       goto retry;
    231     if (qs < 0)
    232       return GNUNET_DB_STATUS_HARD_ERROR;
    233     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    234                 "Incremented lifetime of account %s to %s\n",
    235                 TALER_B2S (account_pub),
    236                 GNUNET_TIME_timestamp2s (*paid_until));
    237     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    238 retry:
    239     ANASTASIS_DB_rollback ();
    240   }
    241   return GNUNET_DB_STATUS_SOFT_ERROR;
    242 }
    243 
    244 
    245 /* end of anastasis-db_increment_lifetime.c */