fakebank_tbr_get_history.c (9513B)
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_tbr_get_history.c 21 * @brief library that fakes being a Taler bank for testcases 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_common_lookup.h" 31 #include "fakebank_common_lp.h" 32 #include "fakebank_common_parser.h" 33 #include "fakebank_tbr_get_history.h" 34 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_tbr_get_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 const 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 hc->history = json_array (); 73 if (NULL == hc->history) 74 { 75 GNUNET_break (0); 76 return MHD_NO; 77 } 78 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 79 "Handling /accounts/%s/taler-revenue/history request\n", 80 account); 81 if (GNUNET_OK != 82 (ret = TALER_FAKEBANK_common_parse_history_args (h, 83 connection, 84 &hc->ha))) 85 { 86 GNUNET_break_op (0); 87 return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; 88 } 89 hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout); 90 GNUNET_assert (0 == 91 pthread_mutex_lock (&h->big_lock)); 92 if (UINT64_MAX == hc->ha.start_idx) 93 hc->ha.start_idx = h->serial_counter; 94 hc->acc = TALER_FAKEBANK_lookup_account_ (h, 95 account, 96 NULL); 97 if (NULL == hc->acc) 98 { 99 GNUNET_assert (0 == 100 pthread_mutex_unlock (&h->big_lock)); 101 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 102 "Account %s is unknown\n", 103 account); 104 return TALER_MHD_reply_with_error (connection, 105 MHD_HTTP_NOT_FOUND, 106 TALER_EC_BANK_UNKNOWN_ACCOUNT, 107 account); 108 } 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->in_tail 121 : hc->acc->in_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 overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) ); 131 dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1; 132 /* If account does not match, linear scan for 133 first matching account. */ 134 while ( (! overflow) && 135 (NULL != t) && 136 (t->credit_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 in_shutdown = h->in_shutdown; 148 /* FIXME: these conditions are unclear to me. */ 149 if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) && 150 (0 < hc->ha.delta)) 151 { 152 acc_payto_uri = hc->acc->payto_uri; 153 GNUNET_assert (0 == 154 pthread_mutex_unlock (&h->big_lock)); 155 if (overflow) 156 { 157 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 158 "Transactions lost due to RAM limits\n"); 159 return TALER_MHD_reply_with_ec ( 160 connection, 161 TALER_EC_BANK_ANCIENT_TRANSACTION_GONE, 162 NULL); 163 } 164 goto finish; 165 } 166 if (in_shutdown) 167 { 168 acc_payto_uri = hc->acc->payto_uri; 169 GNUNET_assert (0 == 170 pthread_mutex_unlock (&h->big_lock)); 171 goto finish; 172 } 173 TALER_FAKEBANK_start_lp_ (h, 174 connection, 175 hc->acc, 176 GNUNET_TIME_absolute_get_remaining ( 177 hc->timeout), 178 LP_CREDIT, 179 NULL); 180 GNUNET_assert (0 == 181 pthread_mutex_unlock (&h->big_lock)); 182 return MHD_YES; 183 } 184 if (skip) 185 { 186 /* range from application is exclusive, skip the 187 matching entry */ 188 if (0 > hc->ha.delta) 189 pos = t->prev_in; 190 else 191 pos = t->next_in; 192 } 193 else 194 { 195 pos = t; 196 } 197 } 198 if (NULL != pos) 199 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 200 "Returning %lld credit transactions starting (inclusive) from %llu\n", 201 (long long) hc->ha.delta, 202 (unsigned long long) pos->row_id); 203 while ( (0 != hc->ha.delta) && 204 (NULL != pos) ) 205 { 206 json_t *trans; 207 char *subject; 208 209 if (T_DEBIT != pos->type) 210 { 211 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 212 "Unexpected CREDIT transaction #%llu for account `%s'\n", 213 (unsigned long long) pos->row_id, 214 account); 215 if (0 > hc->ha.delta) 216 pos = pos->prev_in; 217 if (0 < hc->ha.delta) 218 pos = pos->next_in; 219 continue; 220 } 221 222 { 223 char *wtids; 224 225 wtids = GNUNET_STRINGS_data_to_string_alloc ( 226 &pos->subject.debit.wtid, 227 sizeof (pos->subject.debit.wtid)); 228 GNUNET_asprintf (&subject, 229 "%s %s", 230 wtids, 231 pos->subject.debit.exchange_base_url); 232 GNUNET_free (wtids); 233 } 234 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 235 "Found transaction over %s with subject %s\n", 236 TALER_amount2s (&pos->amount), 237 subject); 238 trans = GNUNET_JSON_PACK ( 239 GNUNET_JSON_pack_string ("type", 240 "RESERVE"), 241 GNUNET_JSON_pack_uint64 ("row_id", 242 pos->row_id), 243 GNUNET_JSON_pack_timestamp ("date", 244 pos->date), 245 TALER_JSON_pack_amount ("amount", 246 &pos->amount), 247 GNUNET_JSON_pack_string ("debit_account", 248 pos->debit_account->payto_uri), 249 GNUNET_JSON_pack_string ("subject", 250 subject)); 251 GNUNET_free (subject); 252 GNUNET_assert (NULL != trans); 253 GNUNET_assert (0 == 254 json_array_append_new (hc->history, 255 trans)); 256 if (hc->ha.delta > 0) 257 hc->ha.delta--; 258 else 259 hc->ha.delta++; 260 if (0 > hc->ha.delta) 261 pos = pos->prev_in; 262 if (0 < hc->ha.delta) 263 pos = pos->next_in; 264 } 265 if ( (0 == json_array_size (hc->history)) && 266 (! h->in_shutdown) && 267 (GNUNET_TIME_absolute_is_future (hc->timeout)) && 268 (0 < hc->ha.delta)) 269 { 270 TALER_FAKEBANK_start_lp_ (h, 271 connection, 272 hc->acc, 273 GNUNET_TIME_absolute_get_remaining (hc->timeout), 274 LP_CREDIT, 275 NULL); 276 GNUNET_assert (0 == 277 pthread_mutex_unlock (&h->big_lock)); 278 return MHD_YES; 279 } 280 in_shutdown = h->in_shutdown; 281 acc_payto_uri = hc->acc->payto_uri; 282 GNUNET_assert (0 == 283 pthread_mutex_unlock (&h->big_lock)); 284 finish: 285 if (0 == json_array_size (hc->history)) 286 { 287 GNUNET_break (in_shutdown || 288 (! GNUNET_TIME_absolute_is_future (hc->timeout))); 289 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 290 "Zero transactions found\n"); 291 return TALER_MHD_reply_static (connection, 292 MHD_HTTP_NO_CONTENT, 293 NULL, 294 NULL, 295 0); 296 } 297 { 298 json_t *jh = hc->history; 299 300 hc->history = NULL; 301 return TALER_MHD_REPLY_JSON_PACK ( 302 connection, 303 MHD_HTTP_OK, 304 GNUNET_JSON_pack_string ( 305 "credit_account", 306 acc_payto_uri), 307 GNUNET_JSON_pack_array_steal ( 308 "incoming_transactions", 309 jh)); 310 } 311 }