Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2016-2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER 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 Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file auditor/taler-helper-auditor-reserves.c
18 : * @brief audits the reserves of an exchange database
19 : * @author Christian Grothoff
20 : */
21 : #include "taler/platform.h"
22 : #include "taler/taler_auditordb_plugin.h"
23 : #include "report-lib.h"
24 : #include "taler/taler_dbevents.h"
25 : #include "taler/taler_exchangedb_lib.h"
26 :
27 :
28 : /**
29 : * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
30 : */
31 : #define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
32 :
33 : /**
34 : * Return value from main().
35 : */
36 : static int global_ret;
37 :
38 : /**
39 : * State of the last database transaction.
40 : */
41 : static enum GNUNET_DB_QueryStatus global_qs;
42 :
43 : /**
44 : * Run in test mode. Exit when idle instead of
45 : * going to sleep and waiting for more work.
46 : */
47 : static int test_mode;
48 :
49 : /**
50 : * After how long should idle reserves be closed?
51 : */
52 : static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
53 :
54 : /**
55 : * Checkpointing our progress for reserves.
56 : */
57 : static TALER_ARL_DEF_PP (reserves_reserve_in_serial_id);
58 : static TALER_ARL_DEF_PP (reserves_withdraw_serial_id);
59 : static TALER_ARL_DEF_PP (reserves_reserve_recoup_serial_id);
60 : static TALER_ARL_DEF_PP (reserves_reserve_open_serial_id);
61 : static TALER_ARL_DEF_PP (reserves_reserve_close_serial_id);
62 : static TALER_ARL_DEF_PP (reserves_purse_decisions_serial_id);
63 : static TALER_ARL_DEF_PP (reserves_account_merges_serial_id);
64 : static TALER_ARL_DEF_PP (reserves_history_requests_serial_id);
65 :
66 : /**
67 : * Tracked global reserve balances.
68 : */
69 : static TALER_ARL_DEF_AB (reserves_reserve_total_balance);
70 : static TALER_ARL_DEF_AB (reserves_reserve_loss);
71 : static TALER_ARL_DEF_AB (reserves_withdraw_fee_revenue);
72 : static TALER_ARL_DEF_AB (reserves_close_fee_revenue);
73 : static TALER_ARL_DEF_AB (reserves_purse_fee_revenue);
74 : static TALER_ARL_DEF_AB (reserves_open_fee_revenue);
75 : static TALER_ARL_DEF_AB (reserves_history_fee_revenue);
76 :
77 : /**
78 : * Total amount lost by operations for which signatures were invalid.
79 : */
80 : static TALER_ARL_DEF_AB (reserves_total_bad_sig_loss);
81 :
82 : /**
83 : * Total amount affected by reserves not having been closed on time.
84 : */
85 : static TALER_ARL_DEF_AB (total_balance_reserve_not_closed);
86 :
87 : /**
88 : * Total delta between expected and stored reserve balance summaries,
89 : * for positive deltas. Used only when internal checks are
90 : * enabled.
91 : */
92 : static TALER_ARL_DEF_AB (total_balance_summary_delta_plus);
93 :
94 : /**
95 : * Total delta between expected and stored reserve balance summaries,
96 : * for negative deltas. Used only when internal checks are
97 : * enabled.
98 : */
99 : static TALER_ARL_DEF_AB (total_balance_summary_delta_minus);
100 :
101 : /**
102 : * Profits the exchange made by bad amount calculations.
103 : */
104 : static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_plus);
105 :
106 : /**
107 : * Losses the exchange made by bad amount calculations.
108 : */
109 : static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_minus);
110 :
111 : /**
112 : * Should we run checks that only work for exchange-internal audits?
113 : */
114 : static int internal_checks;
115 :
116 : static struct GNUNET_DB_EventHandler *eh;
117 :
118 : /**
119 : * The auditors's configuration.
120 : */
121 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
122 :
123 : /* ***************************** Report logic **************************** */
124 :
125 :
126 : /**
127 : * Report a (serious) inconsistency in the exchange's database with
128 : * respect to calculations involving amounts.
129 : *
130 : * @param operation what operation had the inconsistency
131 : * @param rowid affected row, 0 if row is missing
132 : * @param exchange amount calculated by exchange
133 : * @param auditor amount calculated by auditor
134 : * @param profitable 1 if @a exchange being larger than @a auditor is
135 : * profitable for the exchange for this operation,
136 : * -1 if @a exchange being smaller than @a auditor is
137 : * profitable for the exchange, and 0 if it is unclear
138 : */
139 : static void
140 1 : report_amount_arithmetic_inconsistency (
141 : const char *operation,
142 : uint64_t rowid,
143 : const struct TALER_Amount *exchange,
144 : const struct TALER_Amount *auditor,
145 : int profitable)
146 : {
147 : struct TALER_Amount delta;
148 : struct TALER_Amount *target;
149 : enum GNUNET_DB_QueryStatus qs;
150 :
151 1 : if (0 < TALER_amount_cmp (exchange,
152 : auditor))
153 : {
154 : /* exchange > auditor */
155 0 : TALER_ARL_amount_subtract (&delta,
156 : exchange,
157 : auditor);
158 : }
159 : else
160 : {
161 : /* auditor < exchange */
162 1 : profitable = -profitable;
163 1 : TALER_ARL_amount_subtract (&delta,
164 : auditor,
165 : exchange);
166 : }
167 :
168 : {
169 1 : struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
170 : .problem_row_id = rowid,
171 : .profitable = profitable,
172 : .operation = (char *) operation,
173 : .exchange_amount = *exchange,
174 : .auditor_amount = *auditor,
175 : };
176 :
177 1 : qs = TALER_ARL_adb->insert_amount_arithmetic_inconsistency (
178 1 : TALER_ARL_adb->cls,
179 : &aai);
180 :
181 1 : if (qs < 0)
182 : {
183 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
184 0 : global_qs = qs;
185 0 : return;
186 : }
187 : }
188 :
189 1 : if (0 != profitable)
190 : {
191 0 : target = (1 == profitable)
192 : ? &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_plus)
193 0 : : &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_minus);
194 0 : TALER_ARL_amount_add (target,
195 : target,
196 : &delta);
197 : }
198 : }
199 :
200 :
201 : /**
202 : * Report a (serious) inconsistency in the exchange's database.
203 : *
204 : * @param table affected table
205 : * @param rowid affected row, 0 if row is missing
206 : * @param diagnostic message explaining the problem
207 : */
208 : static void
209 1 : report_row_inconsistency (const char *table,
210 : uint64_t rowid,
211 : const char *diagnostic)
212 : {
213 : enum GNUNET_DB_QueryStatus qs;
214 1 : struct TALER_AUDITORDB_RowInconsistency ri = {
215 : .diagnostic = (char *) diagnostic,
216 : .row_table = (char *) table,
217 : .row_id = rowid
218 : };
219 :
220 1 : qs = TALER_ARL_adb->insert_row_inconsistency (
221 1 : TALER_ARL_adb->cls,
222 : &ri);
223 :
224 1 : if (qs < 0)
225 : {
226 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
227 0 : global_qs = qs;
228 0 : return;
229 : }
230 : }
231 :
232 :
233 : /* ***************************** Analyze reserves ************************ */
234 : /* This logic checks the reserves_in, withdraw and reserves-tables */
235 :
236 : /**
237 : * Summary data we keep per reserve.
238 : */
239 : struct ReserveSummary
240 : {
241 : /**
242 : * Public key of the reserve.
243 : * Always set when the struct is first initialized.
244 : */
245 : struct TALER_ReservePublicKeyP reserve_pub;
246 :
247 : /**
248 : * Sum of all incoming transfers during this transaction.
249 : * Updated only in #handle_reserve_in().
250 : */
251 : struct TALER_Amount total_in;
252 :
253 : /**
254 : * Sum of all outgoing transfers during this transaction (includes fees).
255 : * Updated only in #handle_withdrawals().
256 : */
257 : struct TALER_Amount total_out;
258 :
259 : /**
260 : * Sum of balance and fees encountered during this transaction.
261 : */
262 : struct TALER_AUDITORDB_ReserveFeeBalance curr_balance;
263 :
264 : /**
265 : * Previous balances of the reserve as remembered by the auditor.
266 : * (updated based on @e total_in and @e total_out at the end).
267 : */
268 : struct TALER_AUDITORDB_ReserveFeeBalance prev_balance;
269 :
270 : /**
271 : * Previous reserve expiration data, as remembered by the auditor.
272 : * (updated on-the-fly in #handle_reserve_in()).
273 : */
274 : struct GNUNET_TIME_Timestamp a_expiration_date;
275 :
276 : /**
277 : * Which account did originally put money into the reserve?
278 : */
279 : struct TALER_FullPayto sender_account;
280 :
281 : /**
282 : * Did we have a previous reserve info? Used to decide between
283 : * UPDATE and INSERT later. Initialized in
284 : * #load_auditor_reserve_summary() together with the a-* values
285 : * (if available).
286 : */
287 : bool had_ri;
288 :
289 : };
290 :
291 :
292 : /**
293 : * Load the auditor's remembered state about the reserve into @a rs.
294 : * The "total_in" and "total_out" amounts of @a rs must already be
295 : * initialized (so we can determine the currency).
296 : *
297 : * @param[in,out] rs reserve summary to (fully) initialize
298 : * @return transaction status code
299 : */
300 : static enum GNUNET_DB_QueryStatus
301 68 : load_auditor_reserve_summary (struct ReserveSummary *rs)
302 : {
303 : enum GNUNET_DB_QueryStatus qs;
304 : uint64_t rowid;
305 :
306 68 : qs = TALER_ARL_adb->get_reserve_info (TALER_ARL_adb->cls,
307 68 : &rs->reserve_pub,
308 : &rowid,
309 : &rs->prev_balance,
310 : &rs->a_expiration_date,
311 : &rs->sender_account);
312 68 : if (0 > qs)
313 : {
314 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
315 0 : return qs;
316 : }
317 68 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
318 : {
319 68 : rs->had_ri = false;
320 68 : GNUNET_assert (GNUNET_OK ==
321 : TALER_amount_set_zero (rs->total_in.currency,
322 : &rs->prev_balance.reserve_balance));
323 68 : GNUNET_assert (GNUNET_OK ==
324 : TALER_amount_set_zero (rs->total_in.currency,
325 : &rs->prev_balance.reserve_loss));
326 68 : GNUNET_assert (GNUNET_OK ==
327 : TALER_amount_set_zero (rs->total_in.currency,
328 : &rs->prev_balance.withdraw_fee_balance
329 : ));
330 68 : GNUNET_assert (GNUNET_OK ==
331 : TALER_amount_set_zero (rs->total_in.currency,
332 : &rs->prev_balance.close_fee_balance));
333 68 : GNUNET_assert (GNUNET_OK ==
334 : TALER_amount_set_zero (rs->total_in.currency,
335 : &rs->prev_balance.purse_fee_balance));
336 68 : GNUNET_assert (GNUNET_OK ==
337 : TALER_amount_set_zero (rs->total_in.currency,
338 : &rs->prev_balance.open_fee_balance));
339 68 : GNUNET_assert (GNUNET_OK ==
340 : TALER_amount_set_zero (rs->total_in.currency,
341 : &rs->prev_balance.history_fee_balance)
342 : );
343 68 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
344 : "Creating fresh reserve `%s'\n",
345 : TALER_B2S (&rs->reserve_pub));
346 68 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
347 : }
348 0 : rs->had_ri = true;
349 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350 : "Auditor remembers reserve `%s' has balance %s\n",
351 : TALER_B2S (&rs->reserve_pub),
352 : TALER_amount2s (&rs->prev_balance.reserve_balance));
353 0 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
354 : }
355 :
356 :
357 : /**
358 : * Closure to the various callbacks we make while checking a reserve.
359 : */
360 : struct ReserveContext
361 : {
362 : /**
363 : * Map from hash of reserve's public key to a `struct ReserveSummary`.
364 : */
365 : struct GNUNET_CONTAINER_MultiHashMap *reserves;
366 :
367 : /**
368 : * Map from hash of denomination's public key to a
369 : * static string "revoked" for keys that have been revoked,
370 : * or "master signature invalid" in case the revocation is
371 : * there but bogus.
372 : */
373 : struct GNUNET_CONTAINER_MultiHashMap *revoked;
374 :
375 : /**
376 : * Transaction status code, set to error codes if applicable.
377 : */
378 : enum GNUNET_DB_QueryStatus qs;
379 :
380 : };
381 :
382 :
383 : /**
384 : * Create a new reserve for @a reserve_pub in @a rc.
385 : *
386 : * @param[in,out] rc context to update
387 : * @param reserve_pub key for which to create a reserve
388 : * @return NULL on error
389 : */
390 : static struct ReserveSummary *
391 135 : setup_reserve (struct ReserveContext *rc,
392 : const struct TALER_ReservePublicKeyP *reserve_pub)
393 : {
394 : struct ReserveSummary *rs;
395 : struct GNUNET_HashCode key;
396 : enum GNUNET_DB_QueryStatus qs;
397 :
398 135 : GNUNET_CRYPTO_hash (reserve_pub,
399 : sizeof (*reserve_pub),
400 : &key);
401 135 : rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
402 : &key);
403 135 : if (NULL != rs)
404 67 : return rs;
405 68 : rs = GNUNET_new (struct ReserveSummary);
406 68 : rs->reserve_pub = *reserve_pub;
407 68 : GNUNET_assert (GNUNET_OK ==
408 : TALER_amount_set_zero (TALER_ARL_currency,
409 : &rs->total_in));
410 68 : GNUNET_assert (GNUNET_OK ==
411 : TALER_amount_set_zero (TALER_ARL_currency,
412 : &rs->total_out));
413 68 : GNUNET_assert (GNUNET_OK ==
414 : TALER_amount_set_zero (TALER_ARL_currency,
415 : &rs->curr_balance.reserve_balance));
416 68 : GNUNET_assert (GNUNET_OK ==
417 : TALER_amount_set_zero (TALER_ARL_currency,
418 : &rs->curr_balance.reserve_loss));
419 68 : GNUNET_assert (GNUNET_OK ==
420 : TALER_amount_set_zero (TALER_ARL_currency,
421 : &rs->curr_balance.withdraw_fee_balance))
422 : ;
423 68 : GNUNET_assert (GNUNET_OK ==
424 : TALER_amount_set_zero (TALER_ARL_currency,
425 : &rs->curr_balance.close_fee_balance));
426 68 : GNUNET_assert (GNUNET_OK ==
427 : TALER_amount_set_zero (TALER_ARL_currency,
428 : &rs->curr_balance.purse_fee_balance));
429 68 : GNUNET_assert (GNUNET_OK ==
430 : TALER_amount_set_zero (TALER_ARL_currency,
431 : &rs->curr_balance.open_fee_balance));
432 68 : GNUNET_assert (GNUNET_OK ==
433 : TALER_amount_set_zero (TALER_ARL_currency,
434 : &rs->curr_balance.history_fee_balance));
435 68 : if (0 > (qs = load_auditor_reserve_summary (rs)))
436 : {
437 0 : GNUNET_free (rs);
438 0 : rc->qs = qs;
439 0 : return NULL;
440 : }
441 68 : GNUNET_assert (GNUNET_OK ==
442 : GNUNET_CONTAINER_multihashmap_put (rc->reserves,
443 : &key,
444 : rs,
445 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
446 68 : return rs;
447 : }
448 :
449 :
450 : /**
451 : * Function called with details about incoming wire transfers.
452 : *
453 : * @param cls our `struct ReserveContext`
454 : * @param rowid unique serial ID for the refresh session in our DB
455 : * @param reserve_pub public key of the reserve (also the WTID)
456 : * @param credit amount that was received
457 : * @param sender_account_details information about the sender's bank account
458 : * @param wire_reference unique reference identifying the wire transfer
459 : * @param execution_date when did we receive the funds
460 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
461 : */
462 : static enum GNUNET_GenericReturnValue
463 68 : handle_reserve_in (
464 : void *cls,
465 : uint64_t rowid,
466 : const struct TALER_ReservePublicKeyP *reserve_pub,
467 : const struct TALER_Amount *credit,
468 : const struct TALER_FullPayto sender_account_details,
469 : uint64_t wire_reference,
470 : struct GNUNET_TIME_Timestamp execution_date)
471 : {
472 68 : struct ReserveContext *rc = cls;
473 : struct ReserveSummary *rs;
474 : struct GNUNET_TIME_Timestamp expiry;
475 :
476 : (void) wire_reference;
477 : /* should be monotonically increasing */
478 68 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_in_serial_id));
479 68 : TALER_ARL_USE_PP (reserves_reserve_in_serial_id) = rowid + 1;
480 68 : rs = setup_reserve (rc,
481 : reserve_pub);
482 68 : if (NULL == rs)
483 : {
484 0 : GNUNET_break (0);
485 0 : return GNUNET_SYSERR;
486 : }
487 68 : if (NULL == rs->sender_account.full_payto)
488 : rs->sender_account.full_payto
489 68 : = GNUNET_strdup (sender_account_details.full_payto);
490 68 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
491 : "Additional incoming wire transfer for reserve `%s' of %s\n",
492 : TALER_B2S (reserve_pub),
493 : TALER_amount2s (credit));
494 68 : expiry = GNUNET_TIME_absolute_to_timestamp (
495 : GNUNET_TIME_absolute_add (execution_date.abs_time,
496 : idle_reserve_expiration_time));
497 68 : rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
498 : expiry);
499 68 : TALER_ARL_amount_add (&rs->total_in,
500 : &rs->total_in,
501 : credit);
502 68 : return GNUNET_OK;
503 : }
504 :
505 :
506 : /**
507 : * Function called with details about withdraw operations. Verifies
508 : * the signature and updates the reserve's balance.
509 : *
510 : * @param cls our `struct ReserveContext`
511 : * @param rowid unique serial ID for the refresh session in our DB
512 : * @param num_denom_serials number of elements in @e denom_serials array
513 : * @param denom_serials array with length @e num_denom_serials of serial ID's of denominations in our DB
514 : * @param selected_h hash over the gamma-selected planchets
515 : * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request
516 : * @param blinding_seed the blinding seed for CS denominations that was provided during withdraw; might be NULL
517 : * @param age_proof_required true if the withdraw request required an age proof.
518 : * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins.
519 : * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase.
520 : * @param reserve_pub public key of the reserve
521 : * @param reserve_sig signature over the withdraw operation
522 : * @param execution_date when did the wallet withdraw the coin
523 : * @param amount_with_fee amount that was withdrawn
524 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
525 : */
526 : static enum GNUNET_GenericReturnValue
527 66 : handle_withdrawals (
528 : void *cls,
529 : uint64_t rowid,
530 : size_t num_denom_serials,
531 : const uint64_t *denom_serials,
532 : const struct TALER_HashBlindedPlanchetsP *selected_h,
533 : const struct TALER_HashBlindedPlanchetsP *h_planchets,
534 : const struct TALER_BlindingMasterSeedP *blinding_seed,
535 : bool age_proof_required,
536 : uint8_t max_age,
537 : uint8_t noreveal_index,
538 : const struct TALER_ReservePublicKeyP *reserve_pub,
539 : const struct TALER_ReserveSignatureP *reserve_sig,
540 : struct GNUNET_TIME_Timestamp execution_date,
541 : const struct TALER_Amount *amount_with_fee)
542 : {
543 66 : struct ReserveContext *rc = cls;
544 : struct ReserveSummary *rs;
545 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
546 : struct TALER_Amount auditor_amount;
547 : struct TALER_Amount auditor_fee;
548 : struct TALER_Amount auditor_amount_with_fee;
549 : enum GNUNET_DB_QueryStatus qs;
550 :
551 : /* should be monotonically increasing */
552 66 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
553 : "Analyzing withdrawal row %llu\n",
554 : (unsigned long long) rowid);
555 66 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_withdraw_serial_id));
556 66 : TALER_ARL_USE_PP (reserves_withdraw_serial_id) = rowid + 1;
557 :
558 66 : GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency,
559 : &auditor_amount));
560 66 : GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency,
561 : &auditor_fee));
562 66 : GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency,
563 : &auditor_amount_with_fee));
564 :
565 924 : for (size_t i = 0; i < num_denom_serials; i++)
566 : {
567 : /* lookup denomination pub data (make sure denom_pub is valid, establish fees);
568 : initializes wsrd.h_denomination_pub! */
569 858 : qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
570 : &issue);
571 858 : if (0 > qs)
572 : {
573 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
574 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
575 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
576 : "Hard database error trying to get denomination by serial %llu (%s) from database!\n",
577 : (unsigned long long) denom_serials[i],
578 : GNUNET_h2s (&h_planchets->hash));
579 0 : rc->qs = qs;
580 0 : return GNUNET_SYSERR;
581 : }
582 858 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
583 : {
584 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
585 : "Denomination #%llu not found\n",
586 : (unsigned long long) denom_serials[i]);
587 0 : report_row_inconsistency ("withdraw",
588 : rowid,
589 : "denomination key not found");
590 0 : if (global_qs < 0)
591 0 : return GNUNET_SYSERR;
592 0 : return GNUNET_OK;
593 : }
594 858 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
595 : "Analyzing withdrawn denomination #%llu (%s)\n",
596 : (unsigned long long) denom_serials[i],
597 : TALER_amount2s (&issue->value));
598 :
599 : /* check that execution date is within withdraw range for denom_pub */
600 858 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
601 : "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n",
602 : (unsigned long long) issue->start.abs_time.abs_value_us,
603 : (unsigned long
604 : long) issue->expire_withdraw.abs_time.abs_value_us,
605 : (unsigned long long) execution_date.abs_time.abs_value_us);
606 858 : if (GNUNET_TIME_timestamp_cmp (issue->start,
607 : >,
608 858 : execution_date) ||
609 858 : GNUNET_TIME_timestamp_cmp (issue->expire_withdraw,
610 : <,
611 : execution_date))
612 : {
613 : struct TALER_AUDITORDB_DenominationKeyValidityWithdrawInconsistency
614 1 : dkvwi ={
615 : .problem_row_id = rowid,
616 : .execution_date = execution_date.abs_time,
617 1 : .denompub_h = issue->denom_hash,
618 : .reserve_pub = *reserve_pub
619 : };
620 :
621 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
622 : "Withdraw outside of denomination #%llu validity period detected\n",
623 : (unsigned long long) denom_serials[i]);
624 : qs =
625 1 : TALER_ARL_adb->insert_denomination_key_validity_withdraw_inconsistency (
626 1 : TALER_ARL_adb->cls,
627 : &dkvwi);
628 :
629 1 : if (qs < 0)
630 : {
631 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
632 0 : rc->qs = qs;
633 0 : return GNUNET_SYSERR;
634 : }
635 : }
636 858 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
637 : "Adding withdraw fee of denomination (%s)\n",
638 : TALER_amount2s (&issue->fees.withdraw));
639 858 : TALER_ARL_amount_add (&auditor_amount,
640 : &auditor_amount,
641 : &issue->value);
642 858 : TALER_ARL_amount_add (&auditor_fee,
643 : &auditor_fee,
644 : &issue->fees.withdraw);
645 : {
646 : struct TALER_Amount issue_amount_with_fee;
647 :
648 858 : TALER_ARL_amount_add (&issue_amount_with_fee,
649 : &issue->value,
650 : &issue->fees.withdraw);
651 858 : TALER_ARL_amount_add (&auditor_amount_with_fee,
652 : &auditor_amount_with_fee,
653 : &issue_amount_with_fee);
654 : }
655 : }
656 :
657 : /* check reserve_sig (first: setup remaining members of wsrd) */
658 66 : if (GNUNET_OK !=
659 66 : TALER_wallet_withdraw_verify (
660 : &auditor_amount,
661 : &auditor_fee,
662 : h_planchets,
663 : blinding_seed,
664 : age_proof_required
665 0 : ? &issue->age_mask
666 : : NULL,
667 : age_proof_required
668 : ? max_age
669 : : 0,
670 : reserve_pub,
671 : reserve_sig))
672 : {
673 1 : struct TALER_AUDITORDB_BadSigLosses bsl = {
674 : .problem_row_id = rowid,
675 : .operation = (char *) "withdraw",
676 : .loss = *amount_with_fee,
677 : .operation_specific_pub = reserve_pub->eddsa_pub
678 : };
679 :
680 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
681 : "Withdraw signature invalid (row #%llu)\n",
682 : (unsigned long long) rowid);
683 1 : qs = TALER_ARL_adb->insert_bad_sig_losses (
684 1 : TALER_ARL_adb->cls,
685 : &bsl);
686 1 : if (qs < 0)
687 : {
688 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
689 0 : rc->qs = qs;
690 0 : return GNUNET_SYSERR;
691 : }
692 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
693 : &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
694 : amount_with_fee);
695 1 : return GNUNET_OK; /* exit function here, we cannot add this to the legitimate withdrawals */
696 : }
697 :
698 65 : if (0 !=
699 65 : TALER_amount_cmp (&auditor_amount_with_fee,
700 : amount_with_fee))
701 : {
702 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
703 : "Withdraw fee inconsistent (row #%llu)\n",
704 : (unsigned long long) rowid);
705 1 : report_row_inconsistency ("withdraw",
706 : rowid,
707 : "amount with fee from exchange does not match denomination value plus fee");
708 1 : if (global_qs < 0)
709 0 : return GNUNET_SYSERR;
710 : }
711 65 : rs = setup_reserve (rc,
712 : reserve_pub);
713 65 : if (NULL == rs)
714 : {
715 0 : GNUNET_break (0);
716 0 : return GNUNET_SYSERR;
717 : }
718 65 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719 : "Reserve `%s' reduced by %s from withdraw\n",
720 : TALER_B2S (reserve_pub),
721 : TALER_amount2s (&auditor_amount_with_fee));
722 65 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
723 : "Increasing withdraw profits by fee %s\n",
724 : TALER_amount2s (&issue->fees.withdraw));
725 65 : TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance,
726 : &rs->curr_balance.withdraw_fee_balance,
727 : &issue->fees.withdraw);
728 65 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
729 : &TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
730 : &issue->fees.withdraw);
731 65 : TALER_ARL_amount_add (&rs->total_out,
732 : &rs->total_out,
733 : &auditor_amount_with_fee);
734 65 : return GNUNET_OK;
735 : }
736 :
737 :
738 : /**
739 : * Function called with details about withdraw operations. Verifies
740 : * the signature and updates the reserve's balance.
741 : *
742 : * @param cls our `struct ReserveContext`
743 : * @param rowid unique serial ID for the refresh session in our DB
744 : * @param timestamp when did we receive the recoup request
745 : * @param amount how much should be added back to the reserve
746 : * @param reserve_pub public key of the reserve
747 : * @param coin public information about the coin, denomination signature is
748 : * already verified in #check_recoup()
749 : * @param denom_pub public key of the denomionation of @a coin
750 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
751 : * @param coin_blind blinding factor used to blind the coin
752 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
753 : */
754 : static enum GNUNET_GenericReturnValue
755 0 : handle_recoup_by_reserve (
756 : void *cls,
757 : uint64_t rowid,
758 : struct GNUNET_TIME_Timestamp timestamp,
759 : const struct TALER_Amount *amount,
760 : const struct TALER_ReservePublicKeyP *reserve_pub,
761 : const struct TALER_CoinPublicInfo *coin,
762 : const struct TALER_DenominationPublicKey *denom_pub,
763 : const struct TALER_CoinSpendSignatureP *coin_sig,
764 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
765 : {
766 0 : struct ReserveContext *rc = cls;
767 : struct ReserveSummary *rs;
768 : struct GNUNET_TIME_Timestamp expiry;
769 : struct TALER_MasterSignatureP msig;
770 : uint64_t rev_rowid;
771 : enum GNUNET_DB_QueryStatus qs;
772 : const char *rev;
773 :
774 : (void) denom_pub;
775 : /* should be monotonically increasing */
776 0 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id));
777 0 : TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id) = rowid + 1;
778 : /* We know that denom_pub matches denom_pub_hash because this
779 : is how the SQL statement joined the tables. */
780 0 : if (GNUNET_OK !=
781 0 : TALER_wallet_recoup_verify (&coin->denom_pub_hash,
782 : coin_blind,
783 : &coin->coin_pub,
784 : coin_sig))
785 : {
786 0 : struct TALER_AUDITORDB_BadSigLosses bslr = {
787 : .problem_row_id = rowid,
788 : .operation = (char *) "recoup",
789 : .loss = *amount,
790 : .operation_specific_pub = coin->coin_pub.eddsa_pub
791 : };
792 :
793 0 : qs = TALER_ARL_adb->insert_bad_sig_losses (
794 0 : TALER_ARL_adb->cls,
795 : &bslr);
796 :
797 0 : if (qs < 0)
798 : {
799 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
800 0 : rc->qs = qs;
801 0 : return GNUNET_SYSERR;
802 : }
803 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
804 : &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
805 : amount);
806 : }
807 :
808 : /* check that the coin was eligible for recoup!*/
809 0 : rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
810 : &coin->denom_pub_hash.hash);
811 0 : if (NULL == rev)
812 : {
813 0 : qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls,
814 : &coin->denom_pub_hash,
815 : &msig,
816 : &rev_rowid);
817 0 : if (0 > qs)
818 : {
819 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
820 0 : rc->qs = qs;
821 0 : return GNUNET_SYSERR;
822 : }
823 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
824 : {
825 0 : report_row_inconsistency ("recoup",
826 : rowid,
827 : "denomination key not in revocation set");
828 0 : if (global_qs < 0)
829 0 : return GNUNET_SYSERR;
830 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
831 : &TALER_ARL_USE_AB (reserves_reserve_loss),
832 : amount);
833 : }
834 : else
835 : {
836 0 : if (GNUNET_OK !=
837 0 : TALER_exchange_offline_denomination_revoke_verify (
838 : &coin->denom_pub_hash,
839 : &TALER_ARL_master_pub,
840 : &msig))
841 : {
842 0 : rev = "master signature invalid";
843 : }
844 : else
845 : {
846 0 : rev = "revoked";
847 : }
848 0 : GNUNET_assert (
849 : GNUNET_OK ==
850 : GNUNET_CONTAINER_multihashmap_put (
851 : rc->revoked,
852 : &coin->denom_pub_hash.hash,
853 : (void *) rev,
854 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
855 : }
856 : }
857 : else
858 : {
859 0 : rev_rowid = 0; /* reported elsewhere */
860 : }
861 0 : if ((NULL != rev) &&
862 0 : (0 == strcmp (rev,
863 : "master signature invalid")))
864 : {
865 0 : struct TALER_AUDITORDB_BadSigLosses bslrm = {
866 : .problem_row_id = rev_rowid,
867 : .operation = (char *) "recoup-master",
868 : .loss = *amount,
869 : .operation_specific_pub = TALER_ARL_master_pub.eddsa_pub
870 : };
871 :
872 0 : qs = TALER_ARL_adb->insert_bad_sig_losses (
873 0 : TALER_ARL_adb->cls,
874 : &bslrm);
875 :
876 0 : if (qs < 0)
877 : {
878 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
879 0 : rc->qs = qs;
880 0 : return GNUNET_SYSERR;
881 : }
882 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
883 : &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
884 : amount);
885 : }
886 :
887 0 : rs = setup_reserve (rc,
888 : reserve_pub);
889 0 : if (NULL == rs)
890 : {
891 0 : GNUNET_break (0);
892 0 : return GNUNET_SYSERR;
893 : }
894 0 : TALER_ARL_amount_add (&rs->total_in,
895 : &rs->total_in,
896 : amount);
897 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
898 : "Additional /recoup value to for reserve `%s' of %s\n",
899 : TALER_B2S (reserve_pub),
900 : TALER_amount2s (amount));
901 0 : expiry = GNUNET_TIME_absolute_to_timestamp (
902 : GNUNET_TIME_absolute_add (timestamp.abs_time,
903 : idle_reserve_expiration_time));
904 0 : rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
905 : expiry);
906 0 : return GNUNET_OK;
907 : }
908 :
909 :
910 : /**
911 : * Obtain the closing fee for a transfer at @a time for target
912 : * @a receiver_account.
913 : *
914 : * @param receiver_account payto:// URI of the target account
915 : * @param atime when was the transfer made
916 : * @param[out] fee set to the closing fee
917 : * @return #GNUNET_OK on success
918 : */
919 : static enum GNUNET_GenericReturnValue
920 5 : get_closing_fee (const struct TALER_FullPayto receiver_account,
921 : struct GNUNET_TIME_Timestamp atime,
922 : struct TALER_Amount *fee)
923 : {
924 : struct TALER_MasterSignatureP master_sig;
925 : struct GNUNET_TIME_Timestamp start_date;
926 : struct GNUNET_TIME_Timestamp end_date;
927 : struct TALER_WireFeeSet fees;
928 : char *method;
929 : uint64_t rowid;
930 :
931 5 : method = TALER_payto_get_method (receiver_account.full_payto);
932 5 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
933 : "Method is `%s'\n",
934 : method);
935 5 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
936 5 : TALER_ARL_edb->get_wire_fee (TALER_ARL_edb->cls,
937 : method,
938 : atime,
939 : &rowid,
940 : &start_date,
941 : &end_date,
942 : &fees,
943 : &master_sig))
944 : {
945 : char *diag;
946 :
947 0 : GNUNET_asprintf (&diag,
948 : "closing fee for `%s' unavailable at %s\n",
949 : method,
950 : GNUNET_TIME_timestamp2s (atime));
951 0 : report_row_inconsistency ("closing-fee",
952 : rowid,
953 : diag);
954 0 : GNUNET_free (diag);
955 0 : GNUNET_free (method);
956 0 : return GNUNET_SYSERR;
957 : }
958 5 : *fee = fees.closing;
959 5 : GNUNET_free (method);
960 5 : return GNUNET_OK;
961 : }
962 :
963 :
964 : /**
965 : * Function called about reserve opening operations.
966 : *
967 : * @param cls closure
968 : * @param rowid row identifier used to uniquely identify the reserve closing operation
969 : * @param reserve_payment how much to pay from the
970 : * reserve's own balance for opening the reserve
971 : * @param request_timestamp when was the request created
972 : * @param reserve_expiration desired expiration time for the reserve
973 : * @param purse_limit minimum number of purses the client
974 : * wants to have concurrently open for this reserve
975 : * @param reserve_pub public key of the reserve
976 : * @param reserve_sig signature affirming the operation
977 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
978 : */
979 : static enum GNUNET_GenericReturnValue
980 0 : handle_reserve_open (
981 : void *cls,
982 : uint64_t rowid,
983 : const struct TALER_Amount *reserve_payment,
984 : struct GNUNET_TIME_Timestamp request_timestamp,
985 : struct GNUNET_TIME_Timestamp reserve_expiration,
986 : uint32_t purse_limit,
987 : const struct TALER_ReservePublicKeyP *reserve_pub,
988 : const struct TALER_ReserveSignatureP *reserve_sig)
989 : {
990 0 : struct ReserveContext *rc = cls;
991 : struct ReserveSummary *rs;
992 : enum GNUNET_DB_QueryStatus qs;
993 :
994 : /* should be monotonically increasing */
995 0 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_open_serial_id));
996 0 : TALER_ARL_USE_PP (reserves_reserve_open_serial_id) = rowid + 1;
997 :
998 0 : rs = setup_reserve (rc,
999 : reserve_pub);
1000 0 : if (NULL == rs)
1001 : {
1002 0 : GNUNET_break (0);
1003 0 : return GNUNET_SYSERR;
1004 : }
1005 0 : if (GNUNET_OK !=
1006 0 : TALER_wallet_reserve_open_verify (reserve_payment,
1007 : request_timestamp,
1008 : reserve_expiration,
1009 : purse_limit,
1010 : reserve_pub,
1011 : reserve_sig))
1012 : {
1013 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1014 : .problem_row_id = rowid,
1015 : .operation = (char *) "reserve-open",
1016 : .loss = *reserve_payment,
1017 : .operation_specific_pub = reserve_pub->eddsa_pub
1018 : };
1019 :
1020 0 : qs = TALER_ARL_adb->insert_bad_sig_losses (
1021 0 : TALER_ARL_adb->cls,
1022 : &bsl);
1023 :
1024 0 : if (qs < 0)
1025 : {
1026 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1027 0 : rc->qs = qs;
1028 0 : return GNUNET_SYSERR;
1029 : }
1030 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
1031 : &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
1032 : reserve_payment);
1033 0 : return GNUNET_OK;
1034 : }
1035 0 : TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance,
1036 : &rs->curr_balance.open_fee_balance,
1037 : reserve_payment);
1038 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_open_fee_revenue),
1039 : &TALER_ARL_USE_AB (reserves_open_fee_revenue),
1040 : reserve_payment);
1041 0 : TALER_ARL_amount_add (&rs->total_out,
1042 : &rs->total_out,
1043 : reserve_payment);
1044 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1045 : "Additional open operation for reserve `%s' of %s\n",
1046 : TALER_B2S (reserve_pub),
1047 : TALER_amount2s (reserve_payment));
1048 0 : return GNUNET_OK;
1049 : }
1050 :
1051 :
1052 : /**
1053 : * Function called about reserve closing operations
1054 : * the aggregator triggered.
1055 : *
1056 : * @param cls closure
1057 : * @param rowid row identifier used to uniquely identify the reserve closing operation
1058 : * @param execution_date when did we execute the close operation
1059 : * @param amount_with_fee how much did we debit the reserve
1060 : * @param closing_fee how much did we charge for closing the reserve
1061 : * @param reserve_pub public key of the reserve
1062 : * @param receiver_account where did we send the funds
1063 : * @param transfer_details details about the wire transfer
1064 : * @param close_request_row which close request triggered the operation?
1065 : * 0 if it was a timeout
1066 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1067 : */
1068 : static enum GNUNET_GenericReturnValue
1069 2 : handle_reserve_closed (
1070 : void *cls,
1071 : uint64_t rowid,
1072 : struct GNUNET_TIME_Timestamp execution_date,
1073 : const struct TALER_Amount *amount_with_fee,
1074 : const struct TALER_Amount *closing_fee,
1075 : const struct TALER_ReservePublicKeyP *reserve_pub,
1076 : const struct TALER_FullPayto receiver_account,
1077 : const struct TALER_WireTransferIdentifierRawP *transfer_details,
1078 : uint64_t close_request_row)
1079 : {
1080 2 : struct ReserveContext *rc = cls;
1081 : struct ReserveSummary *rs;
1082 :
1083 : (void) transfer_details;
1084 : /* should be monotonically increasing */
1085 2 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_close_serial_id));
1086 2 : TALER_ARL_USE_PP (reserves_reserve_close_serial_id) = rowid + 1;
1087 :
1088 2 : rs = setup_reserve (rc,
1089 : reserve_pub);
1090 2 : if (NULL == rs)
1091 : {
1092 0 : GNUNET_break (0);
1093 0 : return GNUNET_SYSERR;
1094 : }
1095 : {
1096 : struct TALER_Amount expected_fee;
1097 :
1098 : /* verify closing_fee is correct! */
1099 2 : if (GNUNET_OK !=
1100 2 : get_closing_fee (receiver_account,
1101 : execution_date,
1102 : &expected_fee))
1103 : {
1104 0 : GNUNET_break (0);
1105 : }
1106 2 : else if (0 != TALER_amount_cmp (&expected_fee,
1107 : closing_fee))
1108 : {
1109 0 : report_amount_arithmetic_inconsistency (
1110 : "closing aggregation fee",
1111 : rowid,
1112 : closing_fee,
1113 : &expected_fee,
1114 : 1);
1115 0 : if (global_qs < 0)
1116 0 : return GNUNET_SYSERR;
1117 : }
1118 : }
1119 :
1120 2 : TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance,
1121 : &rs->curr_balance.close_fee_balance,
1122 : closing_fee);
1123 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_close_fee_revenue),
1124 : &TALER_ARL_USE_AB (reserves_close_fee_revenue),
1125 : closing_fee);
1126 2 : TALER_ARL_amount_add (&rs->total_out,
1127 : &rs->total_out,
1128 : amount_with_fee);
1129 2 : if (0 != close_request_row)
1130 : {
1131 : struct TALER_ReserveSignatureP reserve_sig;
1132 : struct GNUNET_TIME_Timestamp request_timestamp;
1133 : struct TALER_Amount close_balance;
1134 : struct TALER_Amount close_fee;
1135 : struct TALER_FullPayto payto_uri;
1136 : enum GNUNET_DB_QueryStatus qs;
1137 :
1138 0 : qs = TALER_ARL_edb->select_reserve_close_request_info (
1139 0 : TALER_ARL_edb->cls,
1140 : reserve_pub,
1141 : close_request_row,
1142 : &reserve_sig,
1143 : &request_timestamp,
1144 : &close_balance,
1145 : &close_fee,
1146 : &payto_uri);
1147 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
1148 : {
1149 0 : report_row_inconsistency ("reserves_close",
1150 : rowid,
1151 : "reserve close request unknown");
1152 0 : if (global_qs < 0)
1153 0 : return GNUNET_SYSERR;
1154 : }
1155 : else
1156 : {
1157 : struct TALER_FullPaytoHashP h_payto;
1158 :
1159 0 : TALER_full_payto_hash (payto_uri,
1160 : &h_payto);
1161 0 : if (GNUNET_OK !=
1162 0 : TALER_wallet_reserve_close_verify (
1163 : request_timestamp,
1164 : &h_payto,
1165 : reserve_pub,
1166 : &reserve_sig))
1167 : {
1168 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1169 : .problem_row_id = close_request_row,
1170 : .operation = (char *) "close-request",
1171 : .loss = *amount_with_fee,
1172 : .operation_specific_pub = reserve_pub->eddsa_pub
1173 : };
1174 :
1175 0 : qs = TALER_ARL_adb->insert_bad_sig_losses (
1176 0 : TALER_ARL_adb->cls,
1177 : &bsl);
1178 :
1179 0 : if (qs < 0)
1180 : {
1181 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1182 0 : rc->qs = qs;
1183 0 : GNUNET_free (payto_uri.full_payto);
1184 0 : return GNUNET_SYSERR;
1185 : }
1186 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
1187 : &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
1188 : amount_with_fee);
1189 : }
1190 : }
1191 0 : if ( (NULL == payto_uri.full_payto) &&
1192 0 : (NULL == rs->sender_account.full_payto) )
1193 : {
1194 0 : GNUNET_break (! rs->had_ri);
1195 0 : report_row_inconsistency ("reserves_close",
1196 : rowid,
1197 : "target account not verified, auditor does not know reserve");
1198 0 : if (global_qs < 0)
1199 0 : return GNUNET_SYSERR;
1200 : }
1201 0 : if (NULL == payto_uri.full_payto)
1202 : {
1203 0 : if ((NULL == rs->sender_account.full_payto) ||
1204 0 : (0 != TALER_full_payto_cmp (rs->sender_account,
1205 : receiver_account)))
1206 : {
1207 0 : report_row_inconsistency ("reserves_close",
1208 : rowid,
1209 : "target account does not match origin account");
1210 0 : if (global_qs < 0)
1211 0 : return GNUNET_SYSERR;
1212 : }
1213 : }
1214 : else
1215 : {
1216 0 : if (0 != TALER_full_payto_cmp (payto_uri,
1217 : receiver_account))
1218 : {
1219 0 : report_row_inconsistency ("reserves_close",
1220 : rowid,
1221 : "target account does not match origin account");
1222 0 : if (global_qs < 0)
1223 : {
1224 0 : GNUNET_free (payto_uri.full_payto);
1225 0 : return GNUNET_SYSERR;
1226 : }
1227 : }
1228 : }
1229 0 : GNUNET_free (payto_uri.full_payto);
1230 : }
1231 : else
1232 : {
1233 2 : if (NULL == rs->sender_account.full_payto)
1234 : {
1235 0 : GNUNET_break (! rs->had_ri);
1236 0 : report_row_inconsistency ("reserves_close",
1237 : rowid,
1238 : "target account not verified, auditor does not know reserve");
1239 0 : if (global_qs < 0)
1240 0 : return GNUNET_SYSERR;
1241 : }
1242 2 : else if (0 != TALER_full_payto_cmp (rs->sender_account,
1243 : receiver_account))
1244 : {
1245 0 : report_row_inconsistency ("reserves_close",
1246 : rowid,
1247 : "target account does not match origin account");
1248 0 : if (global_qs < 0)
1249 0 : return GNUNET_SYSERR;
1250 : }
1251 : }
1252 :
1253 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1254 : "Additional closing operation for reserve `%s' of %s\n",
1255 : TALER_B2S (reserve_pub),
1256 : TALER_amount2s (amount_with_fee));
1257 2 : return GNUNET_OK;
1258 : }
1259 :
1260 :
1261 : /**
1262 : * Function called with details about account merge requests that have been
1263 : * made, with the goal of accounting for the merge fee paid by the reserve (if
1264 : * applicable).
1265 : *
1266 : * @param cls closure
1267 : * @param rowid unique serial ID for the deposit in our DB
1268 : * @param reserve_pub reserve affected by the merge
1269 : * @param purse_pub purse being merged
1270 : * @param h_contract_terms hash over contract of the purse
1271 : * @param purse_expiration when would the purse expire
1272 : * @param amount total amount in the purse
1273 : * @param min_age minimum age of all coins deposited into the purse
1274 : * @param flags how was the purse created
1275 : * @param purse_fee if a purse fee was paid, how high is it
1276 : * @param merge_timestamp when was the merge approved
1277 : * @param reserve_sig signature by reserve approving the merge
1278 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1279 : */
1280 : static enum GNUNET_GenericReturnValue
1281 0 : handle_account_merged (
1282 : void *cls,
1283 : uint64_t rowid,
1284 : const struct TALER_ReservePublicKeyP *reserve_pub,
1285 : const struct TALER_PurseContractPublicKeyP *purse_pub,
1286 : const struct TALER_PrivateContractHashP *h_contract_terms,
1287 : struct GNUNET_TIME_Timestamp purse_expiration,
1288 : const struct TALER_Amount *amount,
1289 : uint32_t min_age,
1290 : enum TALER_WalletAccountMergeFlags flags,
1291 : const struct TALER_Amount *purse_fee,
1292 : struct GNUNET_TIME_Timestamp merge_timestamp,
1293 : const struct TALER_ReserveSignatureP *reserve_sig)
1294 : {
1295 0 : struct ReserveContext *rc = cls;
1296 : struct ReserveSummary *rs;
1297 : enum GNUNET_DB_QueryStatus qs;
1298 :
1299 : /* should be monotonically increasing */
1300 0 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_account_merges_serial_id));
1301 0 : TALER_ARL_USE_PP (reserves_account_merges_serial_id) = rowid + 1;
1302 0 : if (GNUNET_OK !=
1303 0 : TALER_wallet_account_merge_verify (merge_timestamp,
1304 : purse_pub,
1305 : purse_expiration,
1306 : h_contract_terms,
1307 : amount,
1308 : purse_fee,
1309 : min_age,
1310 : flags,
1311 : reserve_pub,
1312 : reserve_sig))
1313 : {
1314 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1315 : .problem_row_id = rowid,
1316 : .operation = (char *) "account-merge",
1317 : .loss = *purse_fee,
1318 : .operation_specific_pub = reserve_pub->eddsa_pub
1319 : };
1320 :
1321 0 : qs = TALER_ARL_adb->insert_bad_sig_losses (
1322 0 : TALER_ARL_adb->cls,
1323 : &bsl);
1324 0 : if (qs < 0)
1325 : {
1326 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1327 0 : rc->qs = qs;
1328 0 : return GNUNET_SYSERR;
1329 : }
1330 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
1331 : &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
1332 : purse_fee);
1333 0 : return GNUNET_OK;
1334 : }
1335 0 : if ((flags & TALER_WAMF_MERGE_MODE_MASK) !=
1336 : TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE)
1337 0 : return GNUNET_OK; /* no impact on reserve balance */
1338 0 : rs = setup_reserve (rc,
1339 : reserve_pub);
1340 0 : if (NULL == rs)
1341 : {
1342 0 : GNUNET_break (0);
1343 0 : return GNUNET_SYSERR;
1344 : }
1345 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_purse_fee_revenue),
1346 : &TALER_ARL_USE_AB (reserves_purse_fee_revenue),
1347 : purse_fee);
1348 0 : TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance,
1349 : &rs->curr_balance.purse_fee_balance,
1350 : purse_fee);
1351 0 : TALER_ARL_amount_add (&rs->total_out,
1352 : &rs->total_out,
1353 : purse_fee);
1354 0 : return GNUNET_OK;
1355 : }
1356 :
1357 :
1358 : /**
1359 : * Function called with details about a purse that was merged into an account.
1360 : * Only updates the reserve balance, the actual verifications are done in the
1361 : * purse helper.
1362 : *
1363 : * @param cls closure
1364 : * @param rowid unique serial ID for the refund in our DB
1365 : * @param purse_pub public key of the purse
1366 : * @param reserve_pub which reserve is the purse credited to
1367 : * @param purse_value what is the target value of the purse
1368 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1369 : */
1370 : static enum GNUNET_GenericReturnValue
1371 0 : purse_decision_cb (void *cls,
1372 : uint64_t rowid,
1373 : const struct TALER_PurseContractPublicKeyP *purse_pub,
1374 : const struct TALER_ReservePublicKeyP *reserve_pub,
1375 : const struct TALER_Amount *purse_value)
1376 : {
1377 0 : struct ReserveContext *rc = cls;
1378 : struct ReserveSummary *rs;
1379 :
1380 0 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (
1381 : reserves_purse_decisions_serial_id)); /* should be monotonically increasing */
1382 0 : TALER_ARL_USE_PP (reserves_purse_decisions_serial_id) = rowid + 1;
1383 0 : rs = setup_reserve (rc,
1384 : reserve_pub);
1385 0 : if (NULL == rs)
1386 : {
1387 0 : GNUNET_break (0);
1388 0 : return GNUNET_SYSERR;
1389 : }
1390 0 : TALER_ARL_amount_add (&rs->total_in,
1391 : &rs->total_in,
1392 : purse_value);
1393 0 : return GNUNET_OK;
1394 : }
1395 :
1396 :
1397 : /**
1398 : * Check that the reserve summary matches what the exchange database
1399 : * thinks about the reserve, and update our own state of the reserve.
1400 : *
1401 : * Remove all reserves that we are happy with from the DB.
1402 : *
1403 : * @param cls our `struct ReserveContext`
1404 : * @param key hash of the reserve public key
1405 : * @param value a `struct ReserveSummary`
1406 : * @return #GNUNET_OK to process more entries
1407 : */
1408 : static enum GNUNET_GenericReturnValue
1409 68 : verify_reserve_balance (void *cls,
1410 : const struct GNUNET_HashCode *key,
1411 : void *value)
1412 : {
1413 68 : struct ReserveContext *rc = cls;
1414 68 : struct ReserveSummary *rs = value;
1415 : struct TALER_Amount mbalance;
1416 : struct TALER_Amount nbalance;
1417 : enum GNUNET_DB_QueryStatus qs;
1418 : enum GNUNET_GenericReturnValue ret;
1419 :
1420 68 : ret = GNUNET_OK;
1421 : /* Check our reserve summary balance calculation shows that
1422 : the reserve balance is acceptable (i.e. non-negative) */
1423 68 : TALER_ARL_amount_add (&mbalance,
1424 : &rs->total_in,
1425 : &rs->prev_balance.reserve_balance);
1426 68 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
1427 68 : TALER_ARL_amount_subtract_neg (&nbalance,
1428 : &mbalance,
1429 : &rs->total_out))
1430 : {
1431 1 : struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiil = {
1432 : .reserve_pub = rs->reserve_pub.eddsa_pub,
1433 : .inconsistency_gain = false
1434 : };
1435 :
1436 1 : TALER_ARL_amount_subtract (&rbiil.inconsistency_amount,
1437 : &rs->total_out,
1438 : &mbalance);
1439 1 : TALER_ARL_amount_add (&rs->curr_balance.reserve_loss,
1440 : &rs->prev_balance.reserve_loss,
1441 : &rbiil.inconsistency_amount);
1442 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
1443 : &TALER_ARL_USE_AB (reserves_reserve_loss),
1444 : &rbiil.inconsistency_amount);
1445 1 : qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency (
1446 1 : TALER_ARL_adb->cls,
1447 : &rbiil);
1448 :
1449 1 : if (qs < 0)
1450 : {
1451 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1452 0 : rc->qs = qs;
1453 0 : return GNUNET_SYSERR;
1454 : }
1455 : /* Continue with a reserve balance of zero */
1456 1 : GNUNET_assert (GNUNET_OK ==
1457 : TALER_amount_set_zero (TALER_ARL_currency,
1458 : &rs->curr_balance.reserve_balance));
1459 1 : nbalance = rs->curr_balance.reserve_balance;
1460 : }
1461 : else
1462 : {
1463 : /* Update remaining reserve balance! */
1464 67 : rs->curr_balance.reserve_balance = nbalance;
1465 : }
1466 :
1467 68 : if (internal_checks)
1468 : {
1469 : /* Now check OUR balance calculation vs. the one the exchange has
1470 : in its database. This can only be done when we are doing an
1471 : internal audit, as otherwise the balance of the 'reserves' table
1472 : is not replicated at the auditor. */
1473 68 : struct TALER_EXCHANGEDB_Reserve reserve = {
1474 : .pub = rs->reserve_pub
1475 : };
1476 :
1477 68 : qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls,
1478 : &reserve);
1479 68 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
1480 : {
1481 : /* If the exchange doesn't have this reserve in the summary, it
1482 : is like the exchange 'lost' that amount from its records,
1483 : making an illegitimate gain over the amount it dropped.
1484 : We don't add the amount to some total simply because it is
1485 : not an actualized gain and could be trivially corrected by
1486 : restoring the summary. */
1487 0 : struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = {
1488 : .reserve_pub = rs->reserve_pub.eddsa_pub,
1489 : .inconsistency_amount = nbalance,
1490 : .inconsistency_gain = true
1491 : };
1492 :
1493 0 : qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency (
1494 0 : TALER_ARL_adb->cls,
1495 : &rbiig);
1496 :
1497 0 : if (qs < 0)
1498 : {
1499 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1500 0 : rc->qs = qs;
1501 0 : return GNUNET_SYSERR;
1502 : }
1503 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1504 : {
1505 0 : GNUNET_break (0);
1506 0 : qs = GNUNET_DB_STATUS_HARD_ERROR;
1507 : }
1508 0 : rc->qs = qs;
1509 : }
1510 : else
1511 : {
1512 : /* Check that exchange's balance matches our expected balance for the reserve */
1513 68 : if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance,
1514 : &reserve.balance))
1515 : {
1516 : struct TALER_Amount delta;
1517 :
1518 5 : if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance,
1519 : &reserve.balance))
1520 : {
1521 : /* balance > reserve.balance */
1522 4 : TALER_ARL_amount_subtract (&delta,
1523 : &rs->curr_balance.reserve_balance,
1524 : &reserve.balance);
1525 4 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
1526 : total_balance_summary_delta_plus),
1527 : &TALER_ARL_USE_AB (
1528 : total_balance_summary_delta_plus),
1529 : &delta);
1530 : }
1531 : else
1532 : {
1533 : /* balance < reserve.balance */
1534 1 : TALER_ARL_amount_subtract (&delta,
1535 : &reserve.balance,
1536 : &rs->curr_balance.reserve_balance);
1537 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
1538 : total_balance_summary_delta_minus),
1539 : &TALER_ARL_USE_AB (
1540 : total_balance_summary_delta_minus),
1541 : &delta);
1542 : }
1543 :
1544 : {
1545 5 : struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig =
1546 : {
1547 : .reserve_pub = rs->reserve_pub.eddsa_pub,
1548 : .inconsistency_amount = nbalance,
1549 : .inconsistency_gain = true
1550 : };
1551 :
1552 5 : qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency
1553 : (
1554 5 : TALER_ARL_adb->cls,
1555 : &rbiig);
1556 : }
1557 5 : if (qs < 0)
1558 : {
1559 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1560 0 : rc->qs = qs;
1561 0 : return GNUNET_SYSERR;
1562 : }
1563 :
1564 : {
1565 5 : struct TALER_AUDITORDB_ReserveBalanceSummaryWrongInconsistency rbswi =
1566 : {
1567 : .exchange_amount = reserve.balance,
1568 : .auditor_amount = rs->curr_balance.reserve_balance,
1569 : .reserve_pub = rs->reserve_pub
1570 : };
1571 :
1572 5 : qs = TALER_ARL_adb->insert_reserve_balance_summary_wrong_inconsistency
1573 : (
1574 5 : TALER_ARL_adb->cls,
1575 : &rbswi);
1576 : }
1577 5 : if (qs < 0)
1578 : {
1579 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1580 0 : rc->qs = qs;
1581 0 : return GNUNET_SYSERR;
1582 : }
1583 : }
1584 : }
1585 : } /* end of 'if (internal_checks)' */
1586 :
1587 : /* Check that reserve is being closed if it is past its expiration date
1588 : (and the closing fee would not exceed the remaining balance) */
1589 68 : if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD,
1590 : <,
1591 : GNUNET_TIME_absolute_get_duration (
1592 : rs->a_expiration_date.abs_time)))
1593 : {
1594 : /* Reserve is expired */
1595 : struct TALER_Amount cfee;
1596 :
1597 6 : if ( (NULL != rs->sender_account.full_payto) &&
1598 : (GNUNET_OK ==
1599 3 : get_closing_fee (rs->sender_account,
1600 : rs->a_expiration_date,
1601 : &cfee)) )
1602 : {
1603 : /* We got the closing fee */
1604 3 : if (1 == TALER_amount_cmp (&nbalance,
1605 : &cfee))
1606 : {
1607 1 : struct TALER_AUDITORDB_ReserveNotClosedInconsistency rnci = {
1608 : .reserve_pub = rs->reserve_pub,
1609 : .expiration_time = rs->a_expiration_date.abs_time,
1610 : .balance = nbalance,
1611 1 : .diagnostic = rs->sender_account.full_payto
1612 : };
1613 :
1614 : /* remaining balance (according to us) exceeds closing fee */
1615 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
1616 : total_balance_reserve_not_closed),
1617 : &TALER_ARL_USE_AB (
1618 : total_balance_reserve_not_closed),
1619 : &rnci.balance);
1620 1 : qs = TALER_ARL_adb->insert_reserve_not_closed_inconsistency (
1621 1 : TALER_ARL_adb->cls,
1622 : &rnci);
1623 1 : if (qs < 0)
1624 : {
1625 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1626 0 : rc->qs = qs;
1627 0 : return GNUNET_SYSERR;
1628 : }
1629 : }
1630 : }
1631 : else
1632 : {
1633 : /* We failed to determine the closing fee, complain! */
1634 0 : struct TALER_AUDITORDB_ReserveNotClosedInconsistency rncid = {
1635 : .reserve_pub = rs->reserve_pub,
1636 : .balance = nbalance,
1637 : .expiration_time = rs->a_expiration_date.abs_time,
1638 : .diagnostic = (char *) "could not determine closing fee"
1639 : };
1640 :
1641 : /* Even if we don't know the closing fee, update the
1642 : total_balance_reserve_not_closed */
1643 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
1644 : total_balance_reserve_not_closed),
1645 : &TALER_ARL_USE_AB (
1646 : total_balance_reserve_not_closed),
1647 : &nbalance);
1648 0 : qs = TALER_ARL_adb->insert_reserve_not_closed_inconsistency (
1649 0 : TALER_ARL_adb->cls,
1650 : &rncid);
1651 0 : if (qs < 0)
1652 : {
1653 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1654 0 : rc->qs = qs;
1655 0 : return GNUNET_SYSERR;
1656 : }
1657 : }
1658 : }
1659 : /* We already computed the 'new' balance in 'curr_balance'
1660 : to include the previous balance, so this one is just
1661 : an assignment, not adding up! */
1662 68 : rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance;
1663 :
1664 : /* Add up new totals to previous totals */
1665 68 : TALER_ARL_amount_add (&rs->prev_balance.reserve_loss,
1666 : &rs->prev_balance.reserve_loss,
1667 : &rs->curr_balance.reserve_loss);
1668 68 : TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance,
1669 : &rs->prev_balance.withdraw_fee_balance,
1670 : &rs->curr_balance.withdraw_fee_balance);
1671 68 : TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance,
1672 : &rs->prev_balance.close_fee_balance,
1673 : &rs->curr_balance.close_fee_balance);
1674 68 : TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance,
1675 : &rs->prev_balance.purse_fee_balance,
1676 : &rs->curr_balance.purse_fee_balance);
1677 68 : TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance,
1678 : &rs->prev_balance.open_fee_balance,
1679 : &rs->curr_balance.open_fee_balance);
1680 68 : TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance,
1681 : &rs->prev_balance.history_fee_balance,
1682 : &rs->curr_balance.history_fee_balance);
1683 : /* Update global balance: add incoming first, then try
1684 : to subtract outgoing... */
1685 68 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_total_balance),
1686 : &TALER_ARL_USE_AB (reserves_reserve_total_balance),
1687 : &rs->total_in);
1688 : {
1689 : struct TALER_Amount r;
1690 :
1691 68 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
1692 68 : TALER_ARL_amount_subtract_neg (&r,
1693 : &TALER_ARL_USE_AB (
1694 : reserves_reserve_total_balance),
1695 : &rs->total_out))
1696 : {
1697 : /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!)
1698 : to be withdrawn more than it was IN TOTAL ever given (exchange balance
1699 : went negative!). Woopsie. Calculate how badly it went and log. */
1700 1 : report_amount_arithmetic_inconsistency ("global escrow balance",
1701 : 0,
1702 : &TALER_ARL_USE_AB (
1703 : reserves_reserve_total_balance), /* what we had */
1704 1 : &rs->total_out, /* what we needed */
1705 : 0 /* specific profit/loss does not apply to the total summary */
1706 : );
1707 1 : if (global_qs < 0)
1708 0 : return GNUNET_SYSERR;
1709 : /* We unexpectedly went negative, so a sane value to continue from
1710 : would be zero. */
1711 1 : GNUNET_assert (GNUNET_OK ==
1712 : TALER_amount_set_zero (TALER_ARL_currency,
1713 : &TALER_ARL_USE_AB (
1714 : reserves_reserve_total_balance)));
1715 : }
1716 : else
1717 : {
1718 67 : TALER_ARL_USE_AB (reserves_reserve_total_balance) = r;
1719 : }
1720 : }
1721 68 : if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance))
1722 : {
1723 : /* balance is zero, drop reserve details (and then do not update/insert) */
1724 3 : if (rs->had_ri)
1725 : {
1726 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1727 : "Final balance of reserve `%s' is zero, dropping it\n",
1728 : TALER_B2S (&rs->reserve_pub));
1729 0 : qs = TALER_ARL_adb->del_reserve_info (TALER_ARL_adb->cls,
1730 0 : &rs->reserve_pub);
1731 0 : if (0 >= qs)
1732 : {
1733 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1734 0 : ret = GNUNET_SYSERR;
1735 0 : rc->qs = qs;
1736 : }
1737 : }
1738 : else
1739 : {
1740 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1741 : "Final balance of reserve `%s' is zero, no need to remember it\n",
1742 : TALER_B2S (&rs->reserve_pub));
1743 : }
1744 : }
1745 : else
1746 : {
1747 : /* balance is non-zero, persist for future audits */
1748 65 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1749 : "Remembering final balance of reserve `%s' as %s\n",
1750 : TALER_B2S (&rs->reserve_pub),
1751 : TALER_amount2s (&rs->prev_balance.reserve_balance));
1752 65 : if (rs->had_ri)
1753 0 : qs = TALER_ARL_adb->update_reserve_info (TALER_ARL_adb->cls,
1754 0 : &rs->reserve_pub,
1755 0 : &rs->prev_balance,
1756 : rs->a_expiration_date);
1757 : else
1758 65 : qs = TALER_ARL_adb->insert_reserve_info (TALER_ARL_adb->cls,
1759 65 : &rs->reserve_pub,
1760 65 : &rs->prev_balance,
1761 : rs->a_expiration_date,
1762 : rs->sender_account);
1763 65 : if (0 >= qs)
1764 : {
1765 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1766 0 : ret = GNUNET_SYSERR;
1767 0 : rc->qs = qs;
1768 : }
1769 : }
1770 : /* now we can discard the cached entry */
1771 68 : GNUNET_assert (GNUNET_YES ==
1772 : GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
1773 : key,
1774 : rs));
1775 68 : GNUNET_free (rs->sender_account.full_payto);
1776 68 : GNUNET_free (rs);
1777 68 : return ret;
1778 : }
1779 :
1780 :
1781 : #define CHECK_DB() do { \
1782 : if (qs < 0) { \
1783 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); \
1784 : goto cleanup; \
1785 : } \
1786 : if (global_qs < 0) { \
1787 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == global_qs); \
1788 : qs = global_qs; \
1789 : goto cleanup; \
1790 : } \
1791 : if (rc.qs < 0) { \
1792 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rc.qs); \
1793 : qs = rc.qs; \
1794 : goto cleanup; \
1795 : } \
1796 : } while (0)
1797 :
1798 :
1799 : /**
1800 : * Analyze reserves for being well-formed.
1801 : *
1802 : * @param cls NULL
1803 : * @return transaction status code
1804 : */
1805 : static enum GNUNET_DB_QueryStatus
1806 74 : analyze_reserves (void *cls)
1807 : {
1808 74 : struct ReserveContext rc = {
1809 : .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
1810 : };
1811 : enum GNUNET_DB_QueryStatus qs;
1812 :
1813 : (void) cls;
1814 74 : global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1815 74 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1816 : "Analyzing reserves\n");
1817 74 : qs = TALER_ARL_adb->get_auditor_progress (
1818 74 : TALER_ARL_adb->cls,
1819 : TALER_ARL_GET_PP (reserves_reserve_in_serial_id),
1820 : TALER_ARL_GET_PP (reserves_withdraw_serial_id),
1821 : TALER_ARL_GET_PP (reserves_reserve_recoup_serial_id),
1822 : TALER_ARL_GET_PP (reserves_reserve_open_serial_id),
1823 : TALER_ARL_GET_PP (reserves_reserve_close_serial_id),
1824 : TALER_ARL_GET_PP (reserves_purse_decisions_serial_id),
1825 : TALER_ARL_GET_PP (reserves_account_merges_serial_id),
1826 : TALER_ARL_GET_PP (reserves_history_requests_serial_id),
1827 : NULL);
1828 74 : if (0 > qs)
1829 : {
1830 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1831 0 : return qs;
1832 : }
1833 74 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1834 : {
1835 0 : GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1836 : "First analysis using this auditor, starting audit from scratch\n");
1837 : }
1838 : else
1839 : {
1840 74 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1841 : "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
1842 : (unsigned long long) TALER_ARL_USE_PP (
1843 : reserves_reserve_in_serial_id),
1844 : (unsigned long long) TALER_ARL_USE_PP (
1845 : reserves_withdraw_serial_id),
1846 : (unsigned long long) TALER_ARL_USE_PP (
1847 : reserves_reserve_recoup_serial_id),
1848 : (unsigned long long) TALER_ARL_USE_PP (
1849 : reserves_reserve_open_serial_id),
1850 : (unsigned long long) TALER_ARL_USE_PP (
1851 : reserves_reserve_close_serial_id),
1852 : (unsigned long long) TALER_ARL_USE_PP (
1853 : reserves_purse_decisions_serial_id),
1854 : (unsigned long long) TALER_ARL_USE_PP (
1855 : reserves_account_merges_serial_id),
1856 : (unsigned long long) TALER_ARL_USE_PP (
1857 : reserves_history_requests_serial_id));
1858 : }
1859 74 : qs = TALER_ARL_adb->get_balance (
1860 74 : TALER_ARL_adb->cls,
1861 : TALER_ARL_GET_AB (reserves_reserve_total_balance),
1862 : TALER_ARL_GET_AB (reserves_reserve_loss),
1863 : TALER_ARL_GET_AB (reserves_withdraw_fee_revenue),
1864 : TALER_ARL_GET_AB (reserves_close_fee_revenue),
1865 : TALER_ARL_GET_AB (reserves_purse_fee_revenue),
1866 : TALER_ARL_GET_AB (reserves_open_fee_revenue),
1867 : TALER_ARL_GET_AB (reserves_history_fee_revenue),
1868 : TALER_ARL_GET_AB (reserves_total_bad_sig_loss),
1869 : TALER_ARL_GET_AB (total_balance_reserve_not_closed),
1870 : TALER_ARL_GET_AB (reserves_total_arithmetic_delta_plus),
1871 : TALER_ARL_GET_AB (reserves_total_arithmetic_delta_minus),
1872 : TALER_ARL_GET_AB (total_balance_summary_delta_plus),
1873 : TALER_ARL_GET_AB (total_balance_summary_delta_minus),
1874 : NULL);
1875 74 : if (qs < 0)
1876 : {
1877 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1878 0 : return qs;
1879 : }
1880 74 : rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
1881 : GNUNET_NO);
1882 74 : rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
1883 : GNUNET_NO);
1884 :
1885 74 : qs = TALER_ARL_edb->select_reserves_in_above_serial_id (
1886 74 : TALER_ARL_edb->cls,
1887 : TALER_ARL_USE_PP (reserves_reserve_in_serial_id),
1888 : &handle_reserve_in,
1889 : &rc);
1890 74 : CHECK_DB ();
1891 74 : qs = TALER_ARL_edb->select_withdrawals_above_serial_id (
1892 74 : TALER_ARL_edb->cls,
1893 : TALER_ARL_USE_PP (reserves_withdraw_serial_id),
1894 : &handle_withdrawals,
1895 : &rc);
1896 74 : CHECK_DB ();
1897 74 : qs = TALER_ARL_edb->select_recoup_above_serial_id (
1898 74 : TALER_ARL_edb->cls,
1899 : TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id),
1900 : &handle_recoup_by_reserve,
1901 : &rc);
1902 74 : if ( (qs < 0) ||
1903 74 : (rc.qs < 0) ||
1904 74 : (global_qs < 0) )
1905 : {
1906 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1907 0 : return qs;
1908 : }
1909 :
1910 74 : qs = TALER_ARL_edb->select_reserve_open_above_serial_id (
1911 74 : TALER_ARL_edb->cls,
1912 : TALER_ARL_USE_PP (reserves_reserve_open_serial_id),
1913 : &handle_reserve_open,
1914 : &rc);
1915 74 : CHECK_DB ();
1916 74 : qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
1917 74 : TALER_ARL_edb->cls,
1918 : TALER_ARL_USE_PP (reserves_reserve_close_serial_id),
1919 : &handle_reserve_closed,
1920 : &rc);
1921 74 : CHECK_DB ();
1922 : /* process purse_decisions (to credit reserve) */
1923 74 : qs = TALER_ARL_edb->select_purse_decisions_above_serial_id (
1924 74 : TALER_ARL_edb->cls,
1925 : TALER_ARL_USE_PP (reserves_purse_decisions_serial_id),
1926 : false, /* only go for merged purses! */
1927 : &purse_decision_cb,
1928 : &rc);
1929 74 : CHECK_DB ();
1930 : /* Charge purse fee! */
1931 :
1932 74 : qs = TALER_ARL_edb->select_account_merges_above_serial_id (
1933 74 : TALER_ARL_edb->cls,
1934 : TALER_ARL_USE_PP (reserves_account_merges_serial_id),
1935 : &handle_account_merged,
1936 : &rc);
1937 74 : CHECK_DB ();
1938 74 : GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
1939 : &verify_reserve_balance,
1940 : &rc);
1941 74 : CHECK_DB ();
1942 74 : GNUNET_break (0 ==
1943 : GNUNET_CONTAINER_multihashmap_size (rc.reserves));
1944 :
1945 74 : qs = TALER_ARL_adb->insert_balance (
1946 74 : TALER_ARL_adb->cls,
1947 : TALER_ARL_SET_AB (reserves_reserve_total_balance),
1948 : TALER_ARL_SET_AB (reserves_reserve_loss),
1949 : TALER_ARL_SET_AB (reserves_withdraw_fee_revenue),
1950 : TALER_ARL_SET_AB (reserves_close_fee_revenue),
1951 : TALER_ARL_SET_AB (reserves_purse_fee_revenue),
1952 : TALER_ARL_SET_AB (reserves_open_fee_revenue),
1953 : TALER_ARL_SET_AB (reserves_history_fee_revenue),
1954 : TALER_ARL_SET_AB (reserves_total_bad_sig_loss),
1955 : TALER_ARL_SET_AB (total_balance_reserve_not_closed),
1956 : TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus),
1957 : TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus),
1958 : TALER_ARL_SET_AB (total_balance_summary_delta_plus),
1959 : TALER_ARL_SET_AB (total_balance_summary_delta_minus),
1960 : NULL);
1961 74 : if (0 > qs)
1962 : {
1963 2 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1964 2 : goto cleanup;
1965 : }
1966 :
1967 72 : qs = TALER_ARL_adb->update_balance (
1968 72 : TALER_ARL_adb->cls,
1969 : TALER_ARL_SET_AB (reserves_reserve_total_balance),
1970 : TALER_ARL_SET_AB (reserves_reserve_loss),
1971 : TALER_ARL_SET_AB (reserves_withdraw_fee_revenue),
1972 : TALER_ARL_SET_AB (reserves_close_fee_revenue),
1973 : TALER_ARL_SET_AB (reserves_purse_fee_revenue),
1974 : TALER_ARL_SET_AB (reserves_open_fee_revenue),
1975 : TALER_ARL_SET_AB (reserves_history_fee_revenue),
1976 : TALER_ARL_SET_AB (reserves_total_bad_sig_loss),
1977 : TALER_ARL_SET_AB (total_balance_reserve_not_closed),
1978 : TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus),
1979 : TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus),
1980 : TALER_ARL_SET_AB (total_balance_summary_delta_plus),
1981 : TALER_ARL_SET_AB (total_balance_summary_delta_minus),
1982 : NULL);
1983 72 : if (0 > qs)
1984 : {
1985 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1986 0 : goto cleanup;
1987 : }
1988 :
1989 72 : qs = TALER_ARL_adb->insert_auditor_progress (
1990 72 : TALER_ARL_adb->cls,
1991 : TALER_ARL_SET_PP (reserves_reserve_in_serial_id),
1992 : TALER_ARL_SET_PP (reserves_withdraw_serial_id),
1993 : TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id),
1994 : TALER_ARL_SET_PP (reserves_reserve_open_serial_id),
1995 : TALER_ARL_SET_PP (reserves_reserve_close_serial_id),
1996 : TALER_ARL_SET_PP (reserves_purse_decisions_serial_id),
1997 : TALER_ARL_SET_PP (reserves_account_merges_serial_id),
1998 : TALER_ARL_SET_PP (reserves_history_requests_serial_id),
1999 : NULL);
2000 72 : if (0 > qs)
2001 : {
2002 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2003 : "Failed to update auditor DB, not recording progress\n");
2004 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2005 0 : goto cleanup;
2006 : }
2007 72 : qs = TALER_ARL_adb->update_auditor_progress (
2008 72 : TALER_ARL_adb->cls,
2009 : TALER_ARL_SET_PP (reserves_reserve_in_serial_id),
2010 : TALER_ARL_SET_PP (reserves_withdraw_serial_id),
2011 : TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id),
2012 : TALER_ARL_SET_PP (reserves_reserve_open_serial_id),
2013 : TALER_ARL_SET_PP (reserves_reserve_close_serial_id),
2014 : TALER_ARL_SET_PP (reserves_purse_decisions_serial_id),
2015 : TALER_ARL_SET_PP (reserves_account_merges_serial_id),
2016 : TALER_ARL_SET_PP (reserves_history_requests_serial_id),
2017 : NULL);
2018 72 : if (0 > qs)
2019 : {
2020 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2021 : "Failed to update auditor DB, not recording progress\n");
2022 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2023 0 : goto cleanup;
2024 : }
2025 :
2026 72 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2027 : "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
2028 : (unsigned long long) TALER_ARL_USE_PP (
2029 : reserves_reserve_in_serial_id),
2030 : (unsigned long long) TALER_ARL_USE_PP (
2031 : reserves_withdraw_serial_id),
2032 : (unsigned long long) TALER_ARL_USE_PP (
2033 : reserves_reserve_recoup_serial_id),
2034 : (unsigned long long) TALER_ARL_USE_PP (
2035 : reserves_reserve_open_serial_id),
2036 : (unsigned long long) TALER_ARL_USE_PP (
2037 : reserves_reserve_close_serial_id),
2038 : (unsigned long long) TALER_ARL_USE_PP (
2039 : reserves_purse_decisions_serial_id),
2040 : (unsigned long long) TALER_ARL_USE_PP (
2041 : reserves_account_merges_serial_id),
2042 : (unsigned long long) TALER_ARL_USE_PP (
2043 : reserves_history_requests_serial_id));
2044 72 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
2045 74 : cleanup:
2046 74 : GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
2047 74 : GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
2048 74 : return qs;
2049 : }
2050 :
2051 :
2052 : #undef CHECK_DB
2053 :
2054 :
2055 : /**
2056 : * Function called on events received from Postgres.
2057 : *
2058 : * @param cls closure, NULL
2059 : * @param extra additional event data provided
2060 : * @param extra_size number of bytes in @a extra
2061 : */
2062 : static void
2063 0 : db_notify (void *cls,
2064 : const void *extra,
2065 : size_t extra_size)
2066 : {
2067 : (void) cls;
2068 : (void) extra;
2069 : (void) extra_size;
2070 :
2071 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2072 : "Received notification to wake reserves helper\n");
2073 0 : if (GNUNET_OK !=
2074 0 : TALER_ARL_setup_sessions_and_run (&analyze_reserves,
2075 : NULL))
2076 : {
2077 0 : GNUNET_SCHEDULER_shutdown ();
2078 0 : global_ret = EXIT_FAILURE;
2079 0 : return;
2080 : }
2081 : }
2082 :
2083 :
2084 : /**
2085 : * Function called on shutdown.
2086 : */
2087 : static void
2088 74 : do_shutdown (void *cls)
2089 : {
2090 : (void) cls;
2091 74 : if (NULL != eh)
2092 : {
2093 6 : TALER_ARL_adb->event_listen_cancel (eh);
2094 6 : eh = NULL;
2095 : }
2096 74 : TALER_ARL_done ();
2097 74 : }
2098 :
2099 :
2100 : /**
2101 : * Main function that will be run.
2102 : *
2103 : * @param cls closure
2104 : * @param args remaining command-line arguments
2105 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2106 : * @param c configuration
2107 : */
2108 : static void
2109 74 : run (void *cls,
2110 : char *const *args,
2111 : const char *cfgfile,
2112 : const struct GNUNET_CONFIGURATION_Handle *c)
2113 : {
2114 : (void) cls;
2115 : (void) args;
2116 : (void) cfgfile;
2117 :
2118 74 : cfg = c;
2119 74 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2120 : NULL);
2121 74 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2122 : "Launching reserves auditor\n");
2123 74 : if (GNUNET_OK !=
2124 74 : TALER_ARL_init (c))
2125 : {
2126 0 : global_ret = EXIT_FAILURE;
2127 0 : return;
2128 : }
2129 74 : if (GNUNET_OK !=
2130 74 : GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg,
2131 : "exchangedb",
2132 : "IDLE_RESERVE_EXPIRATION_TIME",
2133 : &idle_reserve_expiration_time))
2134 : {
2135 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2136 : "exchangedb",
2137 : "IDLE_RESERVE_EXPIRATION_TIME");
2138 0 : GNUNET_SCHEDULER_shutdown ();
2139 0 : global_ret = EXIT_FAILURE;
2140 0 : return;
2141 : }
2142 74 : if (test_mode != 1)
2143 : {
2144 6 : struct GNUNET_DB_EventHeaderP es = {
2145 6 : .size = htons (sizeof (es)),
2146 6 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_RESERVES)
2147 : };
2148 :
2149 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2150 : "Running helper indefinitely\n");
2151 6 : eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls,
2152 : &es,
2153 6 : GNUNET_TIME_UNIT_FOREVER_REL,
2154 : &db_notify,
2155 : NULL);
2156 : }
2157 74 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2158 : "Starting audit\n");
2159 74 : if (GNUNET_OK !=
2160 74 : TALER_ARL_setup_sessions_and_run (&analyze_reserves,
2161 : NULL))
2162 : {
2163 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2164 : "Audit failed\n");
2165 0 : GNUNET_SCHEDULER_shutdown ();
2166 0 : global_ret = EXIT_FAILURE;
2167 0 : return;
2168 : }
2169 : }
2170 :
2171 :
2172 : /**
2173 : * The main function to check the database's handling of reserves.
2174 : *
2175 : * @param argc number of arguments from the command line
2176 : * @param argv command line arguments
2177 : * @return 0 ok, 1 on error
2178 : */
2179 : int
2180 74 : main (int argc,
2181 : char *const *argv)
2182 : {
2183 74 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
2184 74 : GNUNET_GETOPT_option_flag ('i',
2185 : "internal",
2186 : "perform checks only applicable for exchange-internal audits",
2187 : &internal_checks),
2188 74 : GNUNET_GETOPT_option_flag ('t',
2189 : "test",
2190 : "run in test mode and exit when idle",
2191 : &test_mode),
2192 74 : GNUNET_GETOPT_option_timetravel ('T',
2193 : "timetravel"),
2194 : GNUNET_GETOPT_OPTION_END
2195 : };
2196 : enum GNUNET_GenericReturnValue ret;
2197 :
2198 74 : ret = GNUNET_PROGRAM_run (
2199 : TALER_AUDITOR_project_data (),
2200 : argc,
2201 : argv,
2202 : "taler-helper-auditor-reserves",
2203 : gettext_noop ("Audit Taler exchange reserve handling"),
2204 : options,
2205 : &run,
2206 : NULL);
2207 74 : if (GNUNET_SYSERR == ret)
2208 0 : return EXIT_INVALIDARGUMENT;
2209 74 : if (GNUNET_NO == ret)
2210 0 : return EXIT_SUCCESS;
2211 74 : return global_ret;
2212 : }
2213 :
2214 :
2215 : /* end of taler-helper-auditor-reserves.c */
|