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 */