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