fakebank_tbr_get_history.c (9541B)
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 "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_common_lookup.h" 32 #include "fakebank_common_lp.h" 33 #include "fakebank_common_parser.h" 34 #include "fakebank_tbr_get_history.h" 35 36 37 /** 38 * Function called to clean up a history context. 39 * 40 * @param cls a `struct HistoryContext *` 41 */ 42 static void 43 history_cleanup (void *cls) 44 { 45 struct HistoryContext *hc = cls; 46 47 json_decref (hc->history); 48 GNUNET_free (hc); 49 } 50 51 52 MHD_RESULT 53 TALER_FAKEBANK_tbr_get_history ( 54 struct TALER_FAKEBANK_Handle *h, 55 struct MHD_Connection *connection, 56 const char *account, 57 void **con_cls) 58 { 59 struct ConnectionContext *cc = *con_cls; 60 struct HistoryContext *hc; 61 const struct Transaction *pos; 62 enum GNUNET_GenericReturnValue ret; 63 bool in_shutdown; 64 const char *acc_payto_uri; 65 66 if (NULL == cc) 67 { 68 cc = GNUNET_new (struct ConnectionContext); 69 cc->ctx_cleaner = &history_cleanup; 70 *con_cls = cc; 71 hc = GNUNET_new (struct HistoryContext); 72 cc->ctx = hc; 73 hc->history = json_array (); 74 if (NULL == hc->history) 75 { 76 GNUNET_break (0); 77 return MHD_NO; 78 } 79 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 80 "Handling /accounts/%s/taler-revenue/history request\n", 81 account); 82 if (GNUNET_OK != 83 (ret = TALER_FAKEBANK_common_parse_history_args (h, 84 connection, 85 &hc->ha))) 86 { 87 GNUNET_break_op (0); 88 return (GNUNET_SYSERR == ret) ? MHD_NO : MHD_YES; 89 } 90 hc->timeout = GNUNET_TIME_relative_to_absolute (hc->ha.lp_timeout); 91 GNUNET_assert (0 == 92 pthread_mutex_lock (&h->big_lock)); 93 if (UINT64_MAX == hc->ha.start_idx) 94 hc->ha.start_idx = h->serial_counter; 95 hc->acc = TALER_FAKEBANK_lookup_account_ (h, 96 account, 97 NULL); 98 if (NULL == hc->acc) 99 { 100 GNUNET_assert (0 == 101 pthread_mutex_unlock (&h->big_lock)); 102 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 103 "Account %s is unknown\n", 104 account); 105 return TALER_MHD_reply_with_error (connection, 106 MHD_HTTP_NOT_FOUND, 107 TALER_EC_BANK_UNKNOWN_ACCOUNT, 108 account); 109 } 110 } 111 else 112 { 113 hc = cc->ctx; 114 GNUNET_assert (0 == 115 pthread_mutex_lock (&h->big_lock)); 116 } 117 118 if (! hc->ha.have_start) 119 { 120 pos = (0 > hc->ha.delta) 121 ? hc->acc->in_tail 122 : hc->acc->in_head; 123 } 124 else 125 { 126 struct Transaction *t = h->transactions[hc->ha.start_idx % h->ram_limit]; 127 bool overflow; 128 uint64_t dir; 129 bool skip = true; 130 131 overflow = ( (NULL != t) && (t->row_id != hc->ha.start_idx) ); 132 dir = (0 > hc->ha.delta) ? (h->ram_limit - 1) : 1; 133 /* If account does not match, linear scan for 134 first matching account. */ 135 while ( (! overflow) && 136 (NULL != t) && 137 (t->credit_account != hc->acc) ) 138 { 139 skip = false; 140 t = h->transactions[(t->row_id + dir) % h->ram_limit]; 141 if ( (NULL != t) && 142 (t->row_id == hc->ha.start_idx) ) 143 overflow = true; /* full circle, give up! */ 144 } 145 if ( (NULL == t) || 146 overflow) 147 { 148 in_shutdown = h->in_shutdown; 149 /* FIXME: these conditions are unclear to me. */ 150 if (GNUNET_TIME_relative_is_zero (hc->ha.lp_timeout) && 151 (0 < hc->ha.delta)) 152 { 153 acc_payto_uri = hc->acc->payto_uri; 154 GNUNET_assert (0 == 155 pthread_mutex_unlock (&h->big_lock)); 156 if (overflow) 157 { 158 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 159 "Transactions lost due to RAM limits\n"); 160 return TALER_MHD_reply_with_ec ( 161 connection, 162 TALER_EC_BANK_ANCIENT_TRANSACTION_GONE, 163 NULL); 164 } 165 goto finish; 166 } 167 if (in_shutdown) 168 { 169 acc_payto_uri = hc->acc->payto_uri; 170 GNUNET_assert (0 == 171 pthread_mutex_unlock (&h->big_lock)); 172 goto finish; 173 } 174 TALER_FAKEBANK_start_lp_ (h, 175 connection, 176 hc->acc, 177 GNUNET_TIME_absolute_get_remaining ( 178 hc->timeout), 179 LP_CREDIT, 180 NULL); 181 GNUNET_assert (0 == 182 pthread_mutex_unlock (&h->big_lock)); 183 return MHD_YES; 184 } 185 if (skip) 186 { 187 /* range from application is exclusive, skip the 188 matching entry */ 189 if (0 > hc->ha.delta) 190 pos = t->prev_in; 191 else 192 pos = t->next_in; 193 } 194 else 195 { 196 pos = t; 197 } 198 } 199 if (NULL != pos) 200 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 201 "Returning %lld credit transactions starting (inclusive) from %llu\n", 202 (long long) hc->ha.delta, 203 (unsigned long long) pos->row_id); 204 while ( (0 != hc->ha.delta) && 205 (NULL != pos) ) 206 { 207 json_t *trans; 208 char *subject; 209 210 if (T_DEBIT != pos->type) 211 { 212 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 213 "Unexpected CREDIT transaction #%llu for account `%s'\n", 214 (unsigned long long) pos->row_id, 215 account); 216 if (0 > hc->ha.delta) 217 pos = pos->prev_in; 218 if (0 < hc->ha.delta) 219 pos = pos->next_in; 220 continue; 221 } 222 223 { 224 char *wtids; 225 226 wtids = GNUNET_STRINGS_data_to_string_alloc ( 227 &pos->subject.debit.wtid, 228 sizeof (pos->subject.debit.wtid)); 229 GNUNET_asprintf (&subject, 230 "%s %s", 231 wtids, 232 pos->subject.debit.exchange_base_url); 233 GNUNET_free (wtids); 234 } 235 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 236 "Found transaction over %s with subject %s\n", 237 TALER_amount2s (&pos->amount), 238 subject); 239 trans = GNUNET_JSON_PACK ( 240 GNUNET_JSON_pack_string ("type", 241 "RESERVE"), 242 GNUNET_JSON_pack_uint64 ("row_id", 243 pos->row_id), 244 GNUNET_JSON_pack_timestamp ("date", 245 pos->date), 246 TALER_JSON_pack_amount ("amount", 247 &pos->amount), 248 GNUNET_JSON_pack_string ("debit_account", 249 pos->debit_account->payto_uri), 250 GNUNET_JSON_pack_string ("subject", 251 subject)); 252 GNUNET_free (subject); 253 GNUNET_assert (NULL != trans); 254 GNUNET_assert (0 == 255 json_array_append_new (hc->history, 256 trans)); 257 if (hc->ha.delta > 0) 258 hc->ha.delta--; 259 else 260 hc->ha.delta++; 261 if (0 > hc->ha.delta) 262 pos = pos->prev_in; 263 if (0 < hc->ha.delta) 264 pos = pos->next_in; 265 } 266 if ( (0 == json_array_size (hc->history)) && 267 (! h->in_shutdown) && 268 (GNUNET_TIME_absolute_is_future (hc->timeout)) && 269 (0 < hc->ha.delta)) 270 { 271 TALER_FAKEBANK_start_lp_ (h, 272 connection, 273 hc->acc, 274 GNUNET_TIME_absolute_get_remaining (hc->timeout), 275 LP_CREDIT, 276 NULL); 277 GNUNET_assert (0 == 278 pthread_mutex_unlock (&h->big_lock)); 279 return MHD_YES; 280 } 281 in_shutdown = h->in_shutdown; 282 acc_payto_uri = hc->acc->payto_uri; 283 GNUNET_assert (0 == 284 pthread_mutex_unlock (&h->big_lock)); 285 finish: 286 if (0 == json_array_size (hc->history)) 287 { 288 GNUNET_break (in_shutdown || 289 (! GNUNET_TIME_absolute_is_future (hc->timeout))); 290 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 291 "Zero transactions found\n"); 292 return TALER_MHD_reply_static (connection, 293 MHD_HTTP_NO_CONTENT, 294 NULL, 295 NULL, 296 0); 297 } 298 { 299 json_t *jh = hc->history; 300 301 hc->history = NULL; 302 return TALER_MHD_REPLY_JSON_PACK ( 303 connection, 304 MHD_HTTP_OK, 305 GNUNET_JSON_pack_string ( 306 "credit_account", 307 acc_payto_uri), 308 GNUNET_JSON_pack_array_steal ( 309 "incoming_transactions", 310 jh)); 311 } 312 }