Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2016-2025 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-coins.c
18 : * @brief audits coins in an exchange database.
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : #include "auditordb_lib.h"
23 : #include "report-lib.h"
24 : #include "taler/taler_dbevents.h"
25 : #include "exchangedb_lib.h"
26 : #include "auditor-database/delete_denomination_balance.h"
27 : #include "auditor-database/event_listen.h"
28 : #include "auditor-database/get_auditor_progress.h"
29 : #include "auditor-database/get_balance.h"
30 : #include "auditor-database/get_denomination_balance.h"
31 : #include "auditor-database/get_exists_historic_denom_revenue.h"
32 : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
33 : #include "auditor-database/insert_auditor_progress.h"
34 : #include "auditor-database/insert_bad_sig_losses.h"
35 : #include "auditor-database/insert_balance.h"
36 : #include "auditor-database/insert_denomination_balance.h"
37 : #include "auditor-database/insert_denominations_without_sigs.h"
38 : #include "auditor-database/insert_emergency.h"
39 : #include "auditor-database/insert_emergency_by_count.h"
40 : #include "auditor-database/insert_historic_denom_revenue.h"
41 : #include "auditor-database/insert_row_inconsistency.h"
42 : #include "auditor-database/update_denomination_balance.h"
43 : #include "exchange-database/get_count_known_coins.h"
44 : #include "exchange-database/get_coin_transactions.h"
45 : #include "exchange-database/get_denomination_revocation.h"
46 : #include "exchange-database/get_known_coin.h"
47 : struct CoinContext;
48 : struct SweepContext;
49 : #define TALER_AUDITORDB_DENOMINATION_PENDING_RESULT_CLOSURE struct SweepContext
50 : #include "auditor-database/iterate_denomination_pending.h"
51 : #define TALER_EXCHANGEDB_DENOMINATION_RESULT_CLOSURE enum GNUNET_DB_QueryStatus
52 : #define TALER_EXCHANGEDB_DEPOSIT_RESULT_CLOSURE struct CoinContext
53 : #define TALER_EXCHANGEDB_PURSE_DECISION_RESULT_CLOSURE struct CoinContext
54 : #define TALER_EXCHANGEDB_PURSE_DEPOSIT_RESULT_CLOSURE struct CoinContext
55 : #define TALER_EXCHANGEDB_PURSE_REFUND_COIN_RESULT_CLOSURE struct CoinContext
56 : #define TALER_EXCHANGEDB_RECOUP_RESULT_CLOSURE struct CoinContext
57 : #define TALER_EXCHANGEDB_RECOUP_REFRESH_RESULT_CLOSURE struct CoinContext
58 : #define TALER_EXCHANGEDB_REFRESHES_RESULT_CLOSURE struct CoinContext
59 : #define TALER_EXCHANGEDB_REFUND_RESULT_CLOSURE struct CoinContext
60 : #define TALER_EXCHANGEDB_WITHDRAW_RESULT_CLOSURE struct CoinContext
61 : #include "exchange-database/iterate_denomination_info.h"
62 : #include "exchange-database/get_auditor_denom_sig.h"
63 : #include "exchange-database/iterate_coin_deposits_above_serial_id.h"
64 : #include "exchange-database/iterate_purse_decisions_above_serial_id.h"
65 : #include "exchange-database/iterate_purse_deposits_above_serial_id.h"
66 : #include "exchange-database/iterate_purse_deposits_by_purse.h"
67 : #include "exchange-database/iterate_recoups_above_serial_id.h"
68 : #include "exchange-database/iterate_recoup_refreshes_above_serial_id.h"
69 : #include "exchange-database/iterate_refreshes_above_serial_id.h"
70 : #include "exchange-database/iterate_refunds_above_serial_id.h"
71 : #include "exchange-database/iterate_withdrawals_above_serial_id.h"
72 :
73 :
74 : /**
75 : * How many coin histories do we keep in RAM at any given point in time?
76 : * Expect a few kB per coin history to be used. Used bound memory consumption
77 : * of the auditor. Larger values reduce database accesses.
78 : */
79 : #define MAX_COIN_HISTORIES (16 * 1024 * 1024)
80 :
81 : /**
82 : * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
83 : */
84 : #define DEPOSIT_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
85 :
86 : /**
87 : * How many pending denominations do we fetch per query when looking for
88 : * those that no transaction of this pass touched?
89 : */
90 : #define SWEEP_BATCH_SIZE 1024
91 :
92 : /**
93 : * Return value from main().
94 : */
95 : static int global_ret;
96 :
97 : /**
98 : * Run in test mode. Exit when idle instead of
99 : * going to sleep and waiting for more work.
100 : */
101 : static int test_mode;
102 :
103 : /**
104 : * Checkpointing our progress for coins.
105 : */
106 : static TALER_ARL_DEF_PP (coins_withdraw_serial_id);
107 : static TALER_ARL_DEF_PP (coins_deposit_serial_id);
108 : static TALER_ARL_DEF_PP (coins_melt_serial_id);
109 : static TALER_ARL_DEF_PP (coins_refund_serial_id);
110 : static TALER_ARL_DEF_PP (coins_recoup_serial_id);
111 : static TALER_ARL_DEF_PP (coins_recoup_refresh_serial_id);
112 : static TALER_ARL_DEF_PP (coins_purse_deposits_serial_id);
113 : static TALER_ARL_DEF_PP (coins_purse_refunds_serial_id);
114 :
115 :
116 : /**
117 : * Global coin balance sheet (for coins).
118 : */
119 : static TALER_ARL_DEF_AB (coin_balance_risk);
120 : static TALER_ARL_DEF_AB (total_escrowed);
121 : static TALER_ARL_DEF_AB (coin_irregular_loss);
122 : static TALER_ARL_DEF_AB (coin_melt_fee_revenue);
123 : static TALER_ARL_DEF_AB (coin_deposit_fee_revenue);
124 : static TALER_ARL_DEF_AB (coin_deposit_fee_loss);
125 : static TALER_ARL_DEF_AB (coin_refund_fee_revenue);
126 : static TALER_ARL_DEF_AB (total_recoup_loss);
127 :
128 : /**
129 : * Profits the exchange made by bad amount calculations.
130 : */
131 : static TALER_ARL_DEF_AB (coins_total_arithmetic_delta_plus);
132 :
133 : /**
134 : * Losses the exchange made by bad amount calculations.
135 : */
136 : static TALER_ARL_DEF_AB (coins_total_arithmetic_delta_minus);
137 :
138 : /**
139 : * Total amount reported in all calls to #report_emergency_by_count().
140 : */
141 : static TALER_ARL_DEF_AB (coins_reported_emergency_risk_by_count);
142 :
143 : /**
144 : * Total amount reported in all calls to #report_emergency_by_amount().
145 : */
146 : static TALER_ARL_DEF_AB (coins_reported_emergency_risk_by_amount);
147 :
148 : /**
149 : * Total amount in losses reported in all calls to #report_emergency_by_amount().
150 : */
151 : static TALER_ARL_DEF_AB (coins_emergencies_loss);
152 :
153 : /**
154 : * Total amount in losses reported in all calls to #report_emergency_by_count().
155 : */
156 : static TALER_ARL_DEF_AB (coins_emergencies_loss_by_count);
157 :
158 :
159 : /**
160 : * Coin and associated transaction history.
161 : */
162 : struct CoinHistory
163 : {
164 : /**
165 : * Public key of the coin.
166 : */
167 : struct TALER_CoinSpendPublicKeyP coin_pub;
168 :
169 : /**
170 : * The transaction list for the @a coin_pub.
171 : */
172 : struct TALER_EXCHANGEDB_TransactionList *tl;
173 : };
174 :
175 : /**
176 : * Array of transaction histories for coins. The index is based on the coin's
177 : * public key. Entries are replaced whenever we have a collision.
178 : */
179 : static struct CoinHistory coin_histories[MAX_COIN_HISTORIES];
180 :
181 : /**
182 : * Indices of the entries of #coin_histories that are in use. Used to
183 : * flush the cache at the end of a pass without touching the entire
184 : * (huge) #coin_histories array. An index is recorded only when its
185 : * entry goes from empty to used, so there are never more than
186 : * #MAX_COIN_HISTORIES of them. Static rather than grown with
187 : * GNUNET_array_append(): that copies the whole array on every call
188 : * (quadratic in the number of coins of a pass) and aborts once the
189 : * array exceeds GNUNET_MAX_MALLOC_CHECKED (about 10.5M entries).
190 : */
191 : static unsigned int used_histories[MAX_COIN_HISTORIES];
192 :
193 : /**
194 : * Number of entries of #used_histories that are in use.
195 : */
196 : static unsigned int num_used_histories;
197 :
198 : /**
199 : * Should we run checks that only work for exchange-internal audits?
200 : */
201 : static int internal_checks;
202 :
203 : static struct GNUNET_DB_EventHandler *eh;
204 :
205 : /**
206 : * The auditors's configuration.
207 : */
208 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
209 :
210 :
211 : /**
212 : * Return the index we should use for @a coin_pub in #coin_histories.
213 : *
214 : * @param coin_pub a coin's public key
215 : * @return index for caching this coin's history in #coin_histories
216 : */
217 : static unsigned int
218 74 : coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
219 : {
220 : uint32_t i;
221 :
222 74 : GNUNET_memcpy (&i,
223 : coin_pub,
224 : sizeof (i));
225 74 : return i % MAX_COIN_HISTORIES;
226 : }
227 :
228 :
229 : /**
230 : * Add a coin history to our in-memory cache.
231 : *
232 : * @param coin_pub public key of the coin to cache
233 : * @param tl history to store
234 : */
235 : static void
236 34 : cache_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
237 : struct TALER_EXCHANGEDB_TransactionList *tl)
238 : {
239 34 : unsigned int i = coin_history_index (coin_pub);
240 :
241 34 : if (NULL == tl)
242 0 : return; /* nothing to cache; get_cached_history() would miss anyway */
243 34 : if (NULL != coin_histories[i].tl)
244 : {
245 0 : TALER_EXCHANGEDB_free_coin_transaction_list (coin_histories[i].tl);
246 : }
247 : else
248 : {
249 34 : GNUNET_assert (num_used_histories < MAX_COIN_HISTORIES);
250 34 : used_histories[num_used_histories++] = i;
251 : }
252 34 : coin_histories[i].coin_pub = *coin_pub;
253 34 : coin_histories[i].tl = tl;
254 : }
255 :
256 :
257 : /**
258 : * Drop all cached coin histories.
259 : *
260 : * Must be called at the end of each pass: within a pass the history we
261 : * fetched is complete (we read it from a consistent snapshot), but the
262 : * exchange keeps adding transactions to coins we already saw. Keeping
263 : * the entry across passes would make us skip the over-spending check
264 : * for every coin the process has already looked at.
265 : */
266 : static void
267 10 : flush_coin_histories (void)
268 : {
269 44 : for (unsigned int j = 0; j < num_used_histories; j++)
270 : {
271 34 : struct CoinHistory *ch = &coin_histories[used_histories[j]];
272 :
273 34 : TALER_EXCHANGEDB_free_coin_transaction_list (ch->tl);
274 34 : memset (ch,
275 : 0,
276 : sizeof (*ch));
277 : }
278 10 : num_used_histories = 0;
279 10 : }
280 :
281 :
282 : /**
283 : * Obtain a coin's history from our in-memory cache.
284 : *
285 : * @param coin_pub public key of the coin to cache
286 : * @return NULL if @a coin_pub is not in the cache
287 : */
288 : static struct TALER_EXCHANGEDB_TransactionList *
289 40 : get_cached_history (const struct TALER_CoinSpendPublicKeyP *coin_pub)
290 : {
291 40 : unsigned int i = coin_history_index (coin_pub);
292 :
293 40 : if (0 ==
294 40 : GNUNET_memcmp (coin_pub,
295 : &coin_histories[i].coin_pub))
296 : {
297 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298 : "Found verification of %s in cache\n",
299 : TALER_B2S (coin_pub));
300 6 : return coin_histories[i].tl;
301 : }
302 34 : return NULL;
303 : }
304 :
305 :
306 : /* ***************************** Report logic **************************** */
307 :
308 : /**
309 : * Called in case we detect an emergency situation where the exchange
310 : * is paying out a larger amount on a denomination than we issued in
311 : * that denomination. This means that the exchange's private keys
312 : * might have gotten compromised, and that we need to trigger an
313 : * emergency request to all wallets to deposit pending coins for the
314 : * denomination (and as an exchange suffer a huge financial loss).
315 : *
316 : * @param issue denomination key where the loss was detected
317 : * @param risk maximum risk that might have just become real (coins created by this @a issue)
318 : * @param loss actual losses already (actualized before denomination was revoked)
319 : * @return transaction status
320 : */
321 : static enum GNUNET_DB_QueryStatus
322 0 : report_emergency_by_amount (
323 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
324 : const struct TALER_Amount *risk,
325 : const struct TALER_Amount *loss)
326 : {
327 : enum GNUNET_DB_QueryStatus qs;
328 0 : struct TALER_AUDITORDB_Emergency emergency = {
329 : .denom_loss = *loss,
330 : .denompub_h = *&issue->denom_hash,
331 : .denom_risk = *risk,
332 : .deposit_start = *&issue->start.abs_time,
333 : .deposit_end = *&issue->expire_deposit.abs_time,
334 : .value = *&issue->value
335 : };
336 :
337 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
338 : "Reporting emergency on denomination `%s' over loss of %s\n",
339 : GNUNET_h2s (&issue->denom_hash.hash),
340 : TALER_amount2s (loss));
341 :
342 0 : qs = TALER_AUDITORDB_insert_emergency (
343 : TALER_ARL_adb,
344 : &emergency);
345 0 : if (qs < 0)
346 : {
347 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
348 0 : return qs;
349 : }
350 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
351 : coins_reported_emergency_risk_by_amount),
352 : &TALER_ARL_USE_AB (
353 : coins_reported_emergency_risk_by_amount),
354 : risk);
355 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coins_emergencies_loss),
356 : &TALER_ARL_USE_AB (coins_emergencies_loss),
357 : loss);
358 0 : return qs;
359 : }
360 :
361 :
362 : /**
363 : * Called in case we detect an emergency situation where the exchange
364 : * is paying out a larger NUMBER of coins of a denomination than we
365 : * issued in that denomination. This means that the exchange's
366 : * private keys might have gotten compromised, and that we need to
367 : * trigger an emergency request to all wallets to deposit pending
368 : * coins for the denomination (and as an exchange suffer a huge
369 : * financial loss).
370 : *
371 : * @param issue denomination key where the loss was detected
372 : * @param num_issued number of coins that were issued
373 : * @param num_known number of coins that have been deposited
374 : * @param risk amount that is at risk
375 : * @return transaction status
376 : */
377 : static enum GNUNET_DB_QueryStatus
378 0 : report_emergency_by_count (
379 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
380 : uint64_t num_issued,
381 : uint64_t num_known,
382 : const struct TALER_Amount *risk)
383 : {
384 : enum GNUNET_DB_QueryStatus qs;
385 0 : struct TALER_AUDITORDB_EmergenciesByCount emergenciesByCount = {
386 : .denompub_h = issue->denom_hash,
387 : .num_issued = num_issued,
388 : .num_known = num_known,
389 : .start = issue->start.abs_time,
390 : .deposit_end = issue->expire_deposit.abs_time,
391 : .value = issue->value
392 : };
393 :
394 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
395 : "Reporting emergency on denomination `%s' with issued %lu vs known %lu over risk of %s\n",
396 : GNUNET_h2s (&issue->denom_hash.hash),
397 : num_issued,
398 : num_known,
399 : TALER_amount2s (risk));
400 :
401 0 : qs = TALER_AUDITORDB_insert_emergency_by_count (
402 : TALER_ARL_adb,
403 : &emergenciesByCount);
404 :
405 0 : if (qs < 0)
406 : {
407 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
408 0 : return qs;
409 : }
410 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
411 : coins_reported_emergency_risk_by_count),
412 : &TALER_ARL_USE_AB (
413 : coins_reported_emergency_risk_by_count),
414 : risk);
415 0 : for (uint64_t i = num_issued; i < num_known; i++)
416 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coins_emergencies_loss_by_count),
417 : &TALER_ARL_USE_AB (coins_emergencies_loss_by_count),
418 : &issue->value);
419 0 : return qs;
420 : }
421 :
422 :
423 : /**
424 : * Report a (serious) inconsistency in the exchange's database with
425 : * respect to calculations involving amounts.
426 : *
427 : * @param operation what operation had the inconsistency
428 : * @param rowid affected row, 0 if row is missing
429 : * @param exchange amount calculated by exchange
430 : * @param auditor amount calculated by auditor
431 : * @param profitable 1 if @a exchange being larger than @a auditor is
432 : * profitable for the exchange for this operation
433 : * (and thus @a exchange being smaller than @ auditor
434 : * representing a loss for the exchange);
435 : * -1 if @a exchange being smaller than @a auditor is
436 : * profitable for the exchange; and 0 if it is unclear
437 : * @return transaction status
438 : */
439 : static enum GNUNET_DB_QueryStatus
440 0 : report_amount_arithmetic_inconsistency (
441 : const char *operation,
442 : uint64_t rowid,
443 : const struct TALER_Amount *exchange,
444 : const struct TALER_Amount *auditor,
445 : int profitable)
446 : {
447 : struct TALER_Amount delta;
448 : struct TALER_Amount *target;
449 :
450 0 : if (0 < TALER_amount_cmp (exchange,
451 : auditor))
452 : {
453 : /* exchange > auditor */
454 0 : TALER_ARL_amount_subtract (&delta,
455 : exchange,
456 : auditor);
457 : }
458 : else
459 : {
460 : /* exchange <= auditor */
461 0 : profitable = -profitable;
462 0 : TALER_ARL_amount_subtract (&delta,
463 : auditor,
464 : exchange);
465 : }
466 :
467 : {
468 0 : struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
469 0 : .profitable = profitable,
470 : .problem_row_id = rowid,
471 : .operation = (char *) operation,
472 : .exchange_amount = *exchange,
473 : .auditor_amount = *auditor
474 : };
475 : enum GNUNET_DB_QueryStatus qs;
476 :
477 0 : qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
478 : TALER_ARL_adb,
479 : &aai);
480 0 : if (qs < 0)
481 : {
482 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
483 0 : return qs;
484 : }
485 : }
486 0 : if (0 != profitable)
487 : {
488 0 : target = (1 == profitable)
489 : ? &TALER_ARL_USE_AB (coins_total_arithmetic_delta_plus)
490 0 : : &TALER_ARL_USE_AB (coins_total_arithmetic_delta_minus);
491 0 : TALER_ARL_amount_add (target,
492 : target,
493 : &delta);
494 : }
495 0 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
496 : }
497 :
498 :
499 : /**
500 : * Report a (serious) inconsistency in the exchange's database.
501 : *
502 : * @param table affected table
503 : * @param rowid affected row, 0 if row is missing
504 : * @param diagnostic message explaining the problem
505 : * @return transaction status
506 : */
507 : static enum GNUNET_DB_QueryStatus
508 0 : report_row_inconsistency (const char *table,
509 : uint64_t rowid,
510 : const char *diagnostic)
511 : {
512 :
513 : enum GNUNET_DB_QueryStatus qs;
514 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
515 : .row_table = (char *) table,
516 : .problem_row_id = rowid,
517 : .diagnostic = (char *) diagnostic
518 : };
519 :
520 0 : qs = TALER_AUDITORDB_insert_row_inconsistency (
521 : TALER_ARL_adb,
522 : &ri);
523 0 : if (qs < 0)
524 : {
525 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
526 0 : return qs;
527 : }
528 0 : return qs;
529 : }
530 :
531 :
532 : /* ************* Analyze history of a coin ******************** */
533 :
534 :
535 : /**
536 : * Obtain @a coin_pub's history, verify it, report inconsistencies
537 : * and store the result in our cache.
538 : *
539 : * @param coin_pub public key of the coin to check the history of
540 : * @param rowid a row identifying the transaction
541 : * @param operation operation matching @a rowid
542 : * @param value value of the respective coin's denomination
543 : * @return database status code, negative on failures
544 : */
545 : static enum GNUNET_DB_QueryStatus
546 34 : check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
547 : uint64_t rowid,
548 : const char *operation,
549 : const struct TALER_Amount *value)
550 : {
551 : struct TALER_EXCHANGEDB_TransactionList *tl;
552 34 : enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
553 : struct TALER_Amount total;
554 : struct TALER_Amount spent;
555 : struct TALER_Amount refunded;
556 : struct TALER_Amount deposit_fee;
557 : bool have_refund;
558 : uint64_t etag_out;
559 :
560 : /* FIXME-Optimization: could use 'etag' mechanism to only fetch transactions
561 : we did not yet process, instead of going over them
562 : again and again. */
563 : {
564 : struct TALER_Amount balance;
565 : struct TALER_DenominationHashP h_denom_pub;
566 :
567 34 : qs = TALER_EXCHANGEDB_get_coin_transactions (TALER_ARL_edb,
568 : false,
569 : coin_pub,
570 : 0,
571 : 0,
572 : &etag_out,
573 : &balance,
574 : &h_denom_pub,
575 : &tl);
576 : }
577 34 : if (0 > qs)
578 0 : return qs;
579 34 : GNUNET_assert (GNUNET_OK ==
580 : TALER_amount_set_zero (value->currency,
581 : &refunded));
582 34 : GNUNET_assert (GNUNET_OK ==
583 : TALER_amount_set_zero (value->currency,
584 : &spent));
585 34 : GNUNET_assert (GNUNET_OK ==
586 : TALER_amount_set_zero (value->currency,
587 : &deposit_fee));
588 34 : have_refund = false;
589 34 : for (struct TALER_EXCHANGEDB_TransactionList *pos = tl;
590 76 : NULL != pos;
591 42 : pos = pos->next)
592 : {
593 42 : switch (pos->type)
594 : {
595 34 : case TALER_EXCHANGEDB_TT_DEPOSIT:
596 : /* spent += pos->amount_with_fee */
597 34 : TALER_ARL_amount_add (&spent,
598 : &spent,
599 : &pos->details.deposit->amount_with_fee);
600 34 : deposit_fee = pos->details.deposit->deposit_fee;
601 34 : break;
602 2 : case TALER_EXCHANGEDB_TT_MELT:
603 : /* spent += pos->amount_with_fee */
604 2 : TALER_ARL_amount_add (&spent,
605 : &spent,
606 : &pos->details.melt->amount_with_fee);
607 2 : break;
608 2 : case TALER_EXCHANGEDB_TT_REFUND:
609 : /* refunded += pos->refund_amount - pos->refund_fee */
610 2 : TALER_ARL_amount_add (&refunded,
611 : &refunded,
612 : &pos->details.refund->refund_amount);
613 2 : TALER_ARL_amount_add (&spent,
614 : &spent,
615 : &pos->details.refund->refund_fee);
616 2 : have_refund = true;
617 2 : break;
618 0 : case TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER:
619 : /* refunded += pos->value */
620 0 : TALER_ARL_amount_add (&refunded,
621 : &refunded,
622 : &pos->details.old_coin_recoup->value);
623 0 : break;
624 4 : case TALER_EXCHANGEDB_TT_RECOUP_WITHDRAW:
625 : /* spent += pos->value */
626 4 : TALER_ARL_amount_add (&spent,
627 : &spent,
628 : &pos->details.recoup->value);
629 4 : break;
630 0 : case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
631 : /* spent += pos->value */
632 0 : TALER_ARL_amount_add (&spent,
633 : &spent,
634 : &pos->details.recoup_refresh->value);
635 0 : break;
636 0 : case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
637 : /* spent += pos->value */
638 0 : TALER_ARL_amount_add (&spent,
639 : &spent,
640 : &pos->details.purse_deposit->amount);
641 0 : break;
642 0 : case TALER_EXCHANGEDB_TT_PURSE_REFUND:
643 0 : TALER_ARL_amount_add (&refunded,
644 : &refunded,
645 : &pos->details.purse_refund->refund_amount);
646 0 : TALER_ARL_amount_add (&spent,
647 : &spent,
648 : &pos->details.purse_refund->refund_fee);
649 0 : have_refund = true;
650 0 : break;
651 0 : case TALER_EXCHANGEDB_TT_RESERVE_OPEN:
652 0 : TALER_ARL_amount_add (&spent,
653 : &spent,
654 : &pos->details.reserve_open->coin_contribution);
655 0 : break;
656 : } /* switch (pos->type) */
657 : } /* for (...) */
658 34 : if (have_refund)
659 : {
660 : /* If we gave any refund, also discount ONE deposit fee */
661 2 : TALER_ARL_amount_add (&refunded,
662 : &refunded,
663 : &deposit_fee);
664 : }
665 : /* total coin value = original value plus refunds */
666 34 : TALER_ARL_amount_add (&total,
667 : &refunded,
668 : value);
669 34 : if (1 ==
670 34 : TALER_amount_cmp (&spent,
671 : &total))
672 : {
673 : /* spent > total: bad */
674 : struct TALER_Amount loss;
675 :
676 0 : TALER_ARL_amount_subtract (&loss,
677 : &spent,
678 : &total);
679 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
680 : "Loss detected for coin %s - %s\n",
681 : TALER_B2S (coin_pub),
682 : TALER_amount2s (&loss));
683 0 : qs = report_amount_arithmetic_inconsistency (operation,
684 : rowid,
685 : &spent,
686 : &total,
687 : -1);
688 0 : if (qs < 0)
689 : {
690 0 : TALER_EXCHANGEDB_free_coin_transaction_list (tl);
691 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
692 0 : return qs;
693 : }
694 : }
695 34 : cache_history (coin_pub,
696 : tl);
697 34 : return qs;
698 : }
699 :
700 :
701 : /* ************************* Analyze coins ******************** */
702 : /* This logic checks that the exchange did the right thing for each
703 : coin, checking deposits, refunds, refresh* and known_coins
704 : tables */
705 :
706 :
707 : /**
708 : * Summary data we keep per denomination.
709 : */
710 : struct DenominationSummary
711 : {
712 : /**
713 : * Information about the circulation.
714 : */
715 : struct TALER_AUDITORDB_DenominationCirculationData dcd;
716 :
717 : /**
718 : * Denomination key information for this denomination.
719 : */
720 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
721 :
722 : /**
723 : * True if this record already existed in the DB.
724 : * Used to decide between insert/update in
725 : * #sync_denomination().
726 : */
727 : bool in_db;
728 :
729 : /**
730 : * Should we report an emergency for this denomination, causing it to be
731 : * revoked (because more coins were deposited than issued)?
732 : */
733 : bool report_emergency;
734 :
735 : /**
736 : * True if this denomination was revoked.
737 : */
738 : bool was_revoked;
739 :
740 : /**
741 : * True if the remaining balance of this denomination was already booked
742 : * as revenue in an earlier pass, which left a historic revenue row
743 : * behind. Then @e dcd only holds what happened to its coins in this
744 : * pass.
745 : */
746 : bool finalized;
747 : };
748 :
749 :
750 : /**
751 : * Closure for callbacks during #analyze_coins().
752 : */
753 : struct CoinContext
754 : {
755 :
756 : /**
757 : * Map for tracking information about denominations.
758 : */
759 : struct GNUNET_CONTAINER_MultiHashMap *denom_summaries;
760 :
761 : /**
762 : * Transaction status code.
763 : */
764 : enum GNUNET_DB_QueryStatus qs;
765 :
766 : };
767 :
768 :
769 : /**
770 : * Initialize information about denomination from the database.
771 : *
772 : * @param denom_hash hash of the public key of the denomination
773 : * @param[out] ds summary to initialize
774 : * @return transaction status code
775 : */
776 : static enum GNUNET_DB_QueryStatus
777 8 : init_denomination (const struct TALER_DenominationHashP *denom_hash,
778 : struct DenominationSummary *ds)
779 : {
780 : enum GNUNET_DB_QueryStatus qs;
781 : struct TALER_MasterSignatureP msig;
782 : uint64_t rowid;
783 :
784 8 : qs = TALER_AUDITORDB_get_denomination_balance (TALER_ARL_adb,
785 : denom_hash,
786 : &ds->dcd);
787 8 : if (0 > qs)
788 : {
789 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
790 0 : return qs;
791 : }
792 8 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
793 : {
794 2 : ds->in_db = true;
795 : }
796 : else
797 : {
798 6 : GNUNET_assert (GNUNET_OK ==
799 : TALER_amount_set_zero (TALER_ARL_currency,
800 : &ds->dcd.denom_balance));
801 6 : GNUNET_assert (GNUNET_OK ==
802 : TALER_amount_set_zero (TALER_ARL_currency,
803 : &ds->dcd.denom_loss));
804 6 : GNUNET_assert (GNUNET_OK ==
805 : TALER_amount_set_zero (TALER_ARL_currency,
806 : &ds->dcd.denom_risk));
807 6 : GNUNET_assert (GNUNET_OK ==
808 : TALER_amount_set_zero (TALER_ARL_currency,
809 : &ds->dcd.recoup_loss));
810 : /* No pending balance: either we never saw this denomination before, or
811 : #sync_denomination() already booked its remaining balance as
812 : revenue, which left a historic revenue row behind. */
813 6 : qs = TALER_AUDITORDB_get_exists_historic_denom_revenue (TALER_ARL_adb,
814 : denom_hash);
815 6 : if (0 > qs)
816 : {
817 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
818 0 : return qs;
819 : }
820 6 : ds->finalized = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
821 6 : if (ds->finalized)
822 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
823 : "Denomination `%s' was already booked as finished\n",
824 : GNUNET_h2s (&denom_hash->hash));
825 : }
826 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
827 : "Starting balance for denomination `%s' is %s (%llu)\n",
828 : GNUNET_h2s (&denom_hash->hash),
829 : TALER_amount2s (&ds->dcd.denom_balance),
830 : (unsigned long long) ds->dcd.num_issued);
831 8 : qs = TALER_EXCHANGEDB_get_denomination_revocation (TALER_ARL_edb,
832 : denom_hash,
833 : &msig,
834 : &rowid);
835 8 : if (0 > qs)
836 : {
837 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
838 0 : return qs;
839 : }
840 8 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
841 : {
842 : /* check revocation signature */
843 4 : if (GNUNET_OK !=
844 4 : TALER_exchange_offline_denomination_revoke_verify (
845 : denom_hash,
846 : &TALER_ARL_master_pub,
847 : &msig))
848 : {
849 0 : qs = report_row_inconsistency ("denomination revocations",
850 : rowid,
851 : "revocation signature invalid");
852 0 : if (qs < 0)
853 : {
854 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
855 0 : return qs;
856 : }
857 : }
858 : else
859 : {
860 4 : ds->was_revoked = true;
861 : }
862 : }
863 8 : return ds->in_db
864 : ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
865 8 : : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
866 : }
867 :
868 :
869 : /**
870 : * Obtain the denomination summary for the given @a dh
871 : *
872 : * @param cc our execution context
873 : * @param issue denomination key information for @a dh
874 : * @return NULL on error
875 : */
876 : static struct DenominationSummary *
877 86 : get_denomination_summary (
878 : struct CoinContext *cc,
879 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
880 : {
881 : struct DenominationSummary *ds;
882 86 : const struct TALER_DenominationHashP *dh = &issue->denom_hash;
883 :
884 86 : ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
885 : &dh->hash);
886 86 : if (NULL != ds)
887 78 : return ds;
888 8 : ds = GNUNET_new (struct DenominationSummary);
889 8 : ds->issue = issue;
890 8 : if (0 > (cc->qs = init_denomination (dh,
891 : ds)))
892 : {
893 0 : GNUNET_break (0);
894 0 : GNUNET_free (ds);
895 0 : return NULL;
896 : }
897 8 : GNUNET_assert (GNUNET_OK ==
898 : GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
899 : &dh->hash,
900 : ds,
901 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
902 : );
903 8 : return ds;
904 : }
905 :
906 :
907 : /**
908 : * Check whether the deposit period of a denomination is over, so that no
909 : * coin of it can be spent any more and the risk of a recoup on it is gone.
910 : * Allows for a grace period of #DEPOSIT_GRACE_PERIOD, as clocks are not
911 : * perfectly synchronized.
912 : *
913 : * @param issue denomination to check
914 : * @return true if the deposit period of @a issue (plus grace) is over
915 : */
916 : static bool
917 10 : is_past_deposit_grace (
918 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
919 : {
920 10 : return GNUNET_TIME_absolute_cmp (
921 : GNUNET_TIME_absolute_get (),
922 : >,
923 : GNUNET_TIME_absolute_add (issue->expire_deposit.abs_time,
924 : DEPOSIT_GRACE_PERIOD));
925 : }
926 :
927 :
928 : /**
929 : * Check whether the legal period of a denomination is over. From then on
930 : * the exchange may forget the denomination, so nothing can move value on
931 : * it any anymore and its remaining balance can be booked as revenue for
932 : * good.
933 : *
934 : * @param issue denomination to check
935 : * @return true if the legal period of @a issue (plus grace) is over
936 : */
937 : static bool
938 8 : is_past_legal_grace (
939 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
940 : {
941 8 : return GNUNET_TIME_absolute_cmp (
942 : GNUNET_TIME_absolute_get (),
943 : >,
944 : GNUNET_TIME_absolute_add (issue->expire_legal.abs_time,
945 : DEPOSIT_GRACE_PERIOD));
946 : }
947 :
948 :
949 : /**
950 : * Take the risk of a denomination out of ``coin_balance_risk`` and zero it in @a
951 : * ds. Called once the deposit period of the denomination is over as from
952 : * then on there is nothing left to lose.
953 : *
954 : * @param[in,out] ds summary of the denomination
955 : */
956 : static void
957 0 : release_denomination_risk (struct DenominationSummary *ds)
958 : {
959 0 : if (TALER_amount_is_zero (&ds->dcd.denom_risk))
960 0 : return;
961 0 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (coin_balance_risk),
962 : &TALER_ARL_USE_AB (coin_balance_risk),
963 : &ds->dcd.denom_risk);
964 : /* If the above fails, our risk assessment is inconsistent! This is
965 : really, really bad (auditor-internal invariant would be violated).
966 : Hence we can "safely" assert. If this assertion fails, well, good
967 : luck: there is a bug in the auditor _or_ the auditor's database is
968 : corrupt. */
969 0 : GNUNET_assert (GNUNET_OK ==
970 : TALER_amount_set_zero (TALER_ARL_currency,
971 : &ds->dcd.denom_risk));
972 : }
973 :
974 :
975 : /**
976 : * Book the remaining balance of a denomination as revenue: the coins can
977 : * no longer be spent, so the exchange keeps their value and no longer
978 : * holds it in escrow. Appends a row to the historic revenue table, which
979 : * also marks the denomination as finished for the passes that follow.
980 : *
981 : * @param[in,out] ds summary of the denomination
982 : * @param denom_h hash of the denomination public key
983 : * @return transaction status
984 : */
985 : static enum GNUNET_DB_QueryStatus
986 0 : book_denomination_revenue (struct DenominationSummary *ds,
987 : const struct TALER_DenominationHashP *denom_h)
988 : {
989 : enum GNUNET_DB_QueryStatus qs;
990 :
991 0 : release_denomination_risk (ds);
992 0 : if (! TALER_amount_is_zero (&ds->dcd.denom_balance))
993 : {
994 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
995 : "Denomination `%s' is finished, booking %s in expiration profits\n",
996 : GNUNET_h2s (&denom_h->hash),
997 : TALER_amount2s (&ds->dcd.denom_balance));
998 0 : if (-1 == TALER_amount_cmp (&TALER_ARL_USE_AB (total_escrowed),
999 0 : &ds->dcd.denom_balance))
1000 : {
1001 : /* We never held that much in escrow? Can happen if the exchange
1002 : accepted coins it never issued (an emergency). */
1003 0 : qs = report_amount_arithmetic_inconsistency (
1004 : "subtracting expired coins from escrow balance",
1005 : 0,
1006 : &TALER_ARL_USE_AB (total_escrowed),
1007 0 : &ds->dcd.denom_balance,
1008 : 0);
1009 0 : if (0 > qs)
1010 : {
1011 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1012 0 : return qs;
1013 : }
1014 : }
1015 : else
1016 : {
1017 0 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_escrowed),
1018 : &TALER_ARL_USE_AB (total_escrowed),
1019 : &ds->dcd.denom_balance);
1020 : }
1021 : }
1022 : /* Written unconditionally: the row is what tells the next pass that this
1023 : denomination was already finished. */
1024 0 : return TALER_AUDITORDB_insert_historic_denom_revenue (
1025 : TALER_ARL_adb,
1026 : denom_h,
1027 0 : ds->issue->expire_deposit,
1028 0 : &ds->dcd.denom_balance,
1029 0 : &ds->dcd.recoup_loss);
1030 : }
1031 :
1032 :
1033 : /**
1034 : * Settle the state of a denomination at the end of a pass: release the
1035 : * risk once its deposit period is over, book its remaining balance as
1036 : * revenue once its legal period is over, and otherwise write the pending
1037 : * balance back to the database.
1038 : *
1039 : * @param cc our execution context
1040 : * @param denom_h hash of the denomination public key
1041 : * @param[in,out] ds summary of the denomination
1042 : * @return #GNUNET_OK on success,
1043 : * #GNUNET_SYSERR on failure (@e qs of @a cc is set)
1044 : */
1045 : static enum GNUNET_GenericReturnValue
1046 8 : settle_denomination (struct CoinContext *cc,
1047 : const struct TALER_DenominationHashP *denom_h,
1048 : struct DenominationSummary *ds)
1049 : {
1050 8 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue = ds->issue;
1051 : enum GNUNET_DB_QueryStatus qs;
1052 :
1053 8 : if (ds->finalized)
1054 : {
1055 : /* The denomination was booked as finished in an earlier pass, but the
1056 : exchange moved value on it again. The bound on the purse expiration
1057 : is supposed to prevent that, so report it -- and append the credit
1058 : as another final row instead of rewriting the row that was already
1059 : published. */
1060 0 : qs = report_row_inconsistency (
1061 : "historic denomination revenue",
1062 : 0,
1063 : "denomination was credited after it was booked as finished");
1064 0 : if (0 > qs)
1065 : {
1066 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1067 0 : cc->qs = qs;
1068 0 : return GNUNET_SYSERR;
1069 : }
1070 0 : qs = book_denomination_revenue (ds,
1071 : denom_h);
1072 0 : if (0 > qs)
1073 : {
1074 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1075 0 : cc->qs = qs;
1076 0 : return GNUNET_SYSERR;
1077 : }
1078 0 : return GNUNET_OK;
1079 : }
1080 8 : if (is_past_deposit_grace (issue))
1081 : {
1082 : /* The coins of this denomination can no longer be deposited, so the
1083 : risk of a recoup on it is gone (stage one). What was not spent only
1084 : becomes revenue once the legal period is over as well: until then
1085 : the exchange still has to answer for those coins, and until then it
1086 : can still move value on the denomination, which the pending row has
1087 : to be around to record. */
1088 0 : release_denomination_risk (ds);
1089 : }
1090 8 : if (is_past_legal_grace (issue))
1091 : {
1092 : /* Nothing can happen to this denomination any more: book what is left
1093 : as revenue (stage two) and drop the pending row. The historic row
1094 : is final, and finalization happens exactly once. */
1095 0 : if (ds->in_db)
1096 : {
1097 0 : qs = TALER_AUDITORDB_delete_denomination_balance (TALER_ARL_adb,
1098 : denom_h);
1099 0 : if (0 >= qs)
1100 : {
1101 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1102 0 : cc->qs = qs;
1103 0 : return GNUNET_SYSERR;
1104 : }
1105 : }
1106 0 : qs = book_denomination_revenue (ds,
1107 : denom_h);
1108 0 : if (0 > qs)
1109 : {
1110 : /* Failed to store profits? Bad database */
1111 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1112 0 : cc->qs = qs;
1113 0 : return GNUNET_SYSERR;
1114 : }
1115 0 : return GNUNET_OK;
1116 : }
1117 : /* Not finished yet, just store the current denomination summary to the
1118 : auditor database for the next iteration */
1119 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1120 : "Final balance for denomination `%s' is %s (%llu)\n",
1121 : GNUNET_h2s (&denom_h->hash),
1122 : TALER_amount2s (&ds->dcd.denom_balance),
1123 : (unsigned long long) ds->dcd.num_issued);
1124 8 : if (ds->in_db)
1125 2 : qs = TALER_AUDITORDB_update_denomination_balance (TALER_ARL_adb,
1126 : denom_h,
1127 2 : &ds->dcd);
1128 : else
1129 6 : qs = TALER_AUDITORDB_insert_denomination_balance (TALER_ARL_adb,
1130 : denom_h,
1131 6 : &ds->dcd);
1132 8 : if (qs < 0)
1133 : {
1134 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1135 0 : cc->qs = qs;
1136 0 : return GNUNET_SYSERR;
1137 : }
1138 8 : return GNUNET_OK;
1139 : }
1140 :
1141 :
1142 : /**
1143 : * Write information about the current knowledge about a denomination key
1144 : * back to the database and update our global reporting data about the
1145 : * denomination.
1146 : *
1147 : * @param cls the `struct CoinContext`
1148 : * @param denom_hash the hash of the denomination key
1149 : * @param value a `struct DenominationSummary`
1150 : * @return #GNUNET_OK (continue to iterate)
1151 : * #GNUNET_SYSERR (stop to iterate)
1152 : */
1153 : static enum GNUNET_GenericReturnValue
1154 8 : sync_denomination (void *cls,
1155 : const struct GNUNET_HashCode *denom_hash,
1156 : void *value)
1157 : {
1158 8 : struct CoinContext *cc = cls;
1159 8 : struct TALER_DenominationHashP denom_h = {
1160 : .hash = *denom_hash
1161 : };
1162 8 : struct DenominationSummary *ds = value;
1163 8 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue = ds->issue;
1164 : enum GNUNET_DB_QueryStatus qs;
1165 :
1166 : /* Check for emergencies first: this must happen regardless of whether the
1167 : denomination has expired in the meantime, as a denomination that was
1168 : over-issued or over-spent still signals a key compromise (and rows are
1169 : routinely first analyzed after the deposit expiration). */
1170 : {
1171 8 : long long cnt = 0;
1172 :
1173 : /* For a denomination that was already booked as finished, ds->dcd only
1174 : covers this pass and its num_issued is zero, so comparing it with
1175 : the number of known coins would report a false emergency. The count
1176 : was checked when the denomination was finished, and no coin can be
1177 : issued in it any more. */
1178 8 : if (! ds->finalized)
1179 8 : cnt = TALER_EXCHANGEDB_get_count_known_coins (TALER_ARL_edb,
1180 : &denom_h);
1181 8 : if (0 > cnt)
1182 : {
1183 : /* Failed to obtain count? Bad database */
1184 0 : qs = (enum GNUNET_DB_QueryStatus) cnt;
1185 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1186 0 : cc->qs = qs;
1187 0 : return GNUNET_SYSERR;
1188 : }
1189 8 : if (ds->dcd.num_issued < (uint64_t) cnt)
1190 : {
1191 : /* more coins deposited than issued! very bad */
1192 0 : qs = report_emergency_by_count (issue,
1193 : ds->dcd.num_issued,
1194 : cnt,
1195 0 : &ds->dcd.denom_risk);
1196 0 : if (qs < 0)
1197 : {
1198 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1199 0 : cc->qs = qs;
1200 0 : return GNUNET_SYSERR;
1201 : }
1202 : }
1203 8 : if (ds->report_emergency)
1204 : {
1205 : /* Value of coins deposited exceed value of coins
1206 : issued! Also very bad! */
1207 0 : qs = report_emergency_by_amount (issue,
1208 0 : &ds->dcd.denom_risk,
1209 0 : &ds->dcd.denom_loss);
1210 0 : if (qs < 0)
1211 : {
1212 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1213 0 : cc->qs = qs;
1214 0 : return GNUNET_SYSERR;
1215 : }
1216 : }
1217 : }
1218 8 : return settle_denomination (cc,
1219 : &denom_h,
1220 : ds);
1221 : }
1222 :
1223 :
1224 : /**
1225 : * Closure for #sweep_denomination_cb().
1226 : */
1227 : struct SweepContext
1228 : {
1229 : /**
1230 : * Our execution context.
1231 : */
1232 : struct CoinContext *cc;
1233 :
1234 : /**
1235 : * Row ID of the last pending denomination we looked at.
1236 : */
1237 : uint64_t last_row;
1238 :
1239 : /**
1240 : * Number of pending denominations we looked at.
1241 : */
1242 : unsigned int count;
1243 :
1244 : /**
1245 : * Transaction status code.
1246 : */
1247 : enum GNUNET_DB_QueryStatus qs;
1248 : };
1249 :
1250 :
1251 : /**
1252 : * Function called on each denomination that still has a pending balance.
1253 : * Settles those that no transaction of this pass touched; the ones that
1254 : * were touched are settled by #sync_denomination() and are skipped here.
1255 : *
1256 : * @param sc context for the sweep
1257 : * @param serial_id row of the pending balance
1258 : * @param dp the pending balance
1259 : * @return #GNUNET_OK to continue to iterate
1260 : */
1261 : static enum GNUNET_GenericReturnValue
1262 8 : sweep_denomination_cb (struct SweepContext *sc,
1263 : uint64_t serial_id,
1264 : const struct TALER_AUDITORDB_DenominationPending *dp)
1265 : {
1266 8 : struct CoinContext *cc = sc->cc;
1267 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
1268 : struct DenominationSummary *ds;
1269 : enum GNUNET_DB_QueryStatus qs;
1270 : enum GNUNET_GenericReturnValue ret;
1271 :
1272 8 : sc->last_row = serial_id;
1273 8 : sc->count++;
1274 8 : if (NULL !=
1275 8 : GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
1276 : &dp->denom_pub_hash.hash))
1277 8 : return GNUNET_OK; /* settled by #sync_denomination() */
1278 0 : qs = TALER_ARL_get_denomination_info_by_hash (&dp->denom_pub_hash,
1279 : &issue);
1280 0 : if (0 > qs)
1281 : {
1282 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1283 0 : sc->qs = qs;
1284 0 : return GNUNET_SYSERR;
1285 : }
1286 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1287 : {
1288 : /* The exchange no longer knows the denomination, so we cannot tell
1289 : when it ends and can never settle this balance. exchange_do_main_gc
1290 : only drops a denomination once its legal period is over, so this
1291 : means that the exchange forgot it before we were done with it. */
1292 0 : qs = report_row_inconsistency (
1293 : "denomination pending",
1294 : serial_id,
1295 : "denomination with a pending balance is unknown to the exchange");
1296 0 : if (0 > qs)
1297 : {
1298 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1299 0 : sc->qs = qs;
1300 0 : return GNUNET_SYSERR;
1301 : }
1302 0 : return GNUNET_OK;
1303 : }
1304 0 : if (! is_past_deposit_grace (issue))
1305 0 : return GNUNET_OK; /* nothing to settle yet */
1306 : /* No coin of this denomination was created or spent in this pass, so
1307 : neither the number of known coins nor the balance can have changed
1308 : since the pass that last touched it: the emergency checks of
1309 : #sync_denomination() would only repeat themselves here. */
1310 0 : ds = GNUNET_new (struct DenominationSummary);
1311 0 : ds->issue = issue;
1312 0 : ds->in_db = true;
1313 0 : ds->dcd.denom_balance = dp->denom_balance;
1314 0 : ds->dcd.denom_loss = dp->denom_loss;
1315 0 : ds->dcd.denom_risk = dp->denom_risk;
1316 0 : ds->dcd.recoup_loss = dp->recoup_loss;
1317 0 : ds->dcd.num_issued = dp->num_issued;
1318 0 : ret = settle_denomination (cc,
1319 : &dp->denom_pub_hash,
1320 : ds);
1321 0 : GNUNET_free (ds);
1322 0 : if (GNUNET_OK != ret)
1323 0 : sc->qs = cc->qs;
1324 0 : return ret;
1325 : }
1326 :
1327 :
1328 : /**
1329 : * Settle the denominations that still have a pending balance but that no
1330 : * transaction of this pass touched. Without this, a denomination whose
1331 : * coins see no activity after its deposit period would keep its risk and
1332 : * its outstanding balance on the auditor's books forever.
1333 : *
1334 : * @param cc our execution context
1335 : * @return transaction status
1336 : */
1337 : static enum GNUNET_DB_QueryStatus
1338 10 : sweep_denominations (struct CoinContext *cc)
1339 : {
1340 10 : struct SweepContext sc = {
1341 : .cc = cc
1342 : };
1343 :
1344 : while (true)
1345 0 : {
1346 : enum GNUNET_DB_QueryStatus qs;
1347 :
1348 10 : sc.count = 0;
1349 10 : qs = TALER_AUDITORDB_iterate_denomination_pending (
1350 : TALER_ARL_adb,
1351 : SWEEP_BATCH_SIZE,
1352 : sc.last_row,
1353 : &sweep_denomination_cb,
1354 : &sc);
1355 10 : if (0 > qs)
1356 : {
1357 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1358 0 : return qs;
1359 : }
1360 10 : if (0 > sc.qs)
1361 0 : return sc.qs;
1362 10 : if (SWEEP_BATCH_SIZE > sc.count)
1363 10 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
1364 : }
1365 : }
1366 :
1367 :
1368 : /**
1369 : * Remove and free the memory of @a value from the
1370 : * denomination summaries.
1371 : *
1372 : * @param cls the `struct CoinContext`
1373 : * @param denom_hash the hash of the denomination key
1374 : * @param value a `struct DenominationSummary`
1375 : * @return #GNUNET_OK (continue to iterate)
1376 : */
1377 : static enum GNUNET_GenericReturnValue
1378 8 : cleanup_denomination (void *cls,
1379 : const struct GNUNET_HashCode *denom_hash,
1380 : void *value)
1381 : {
1382 8 : struct CoinContext *cc = cls;
1383 8 : struct DenominationSummary *ds = value;
1384 :
1385 8 : GNUNET_assert (GNUNET_YES ==
1386 : GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
1387 : denom_hash,
1388 : ds));
1389 8 : GNUNET_free (ds);
1390 8 : return GNUNET_OK;
1391 : }
1392 :
1393 :
1394 : /**
1395 : * Function called with details about all withdraw operations.
1396 : * Updates the denomination balance and the overall balance as
1397 : * we now have additional coins that have been issued.
1398 : *
1399 : * Note that the signature was already checked in
1400 : * taler-helper-auditor-reserves.c::#handle_withdrawals(), so we do not check
1401 : * it again here.
1402 : *
1403 : * @param cc our `struct CoinContext`
1404 : * @param rowid unique serial ID for the refresh session in our DB
1405 : * @param num_denom_serials number of elements in @e denom_serials array
1406 : * @param denom_serials array with length @e num_denom_serials of serial ID's of denominations in our DB
1407 : * @param selected_h hash over the gamma-selected planchets
1408 : * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request
1409 : * @param blinding_seed the blinding seed for CS denominations that was provided during withdraw; might be NULL
1410 : * @param age_proof_required true if the withdraw request required an age proof.
1411 : * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins.
1412 : * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase.
1413 : * @param reserve_pub public key of the reserve
1414 : * @param reserve_sig signature over the withdraw operation
1415 : * @param execution_date when did the wallet withdraw the coin
1416 : * @param amount_with_fee amount that was withdrawn
1417 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1418 : */
1419 : static enum GNUNET_GenericReturnValue
1420 36 : withdraw_cb (
1421 : struct CoinContext *cc,
1422 : uint64_t rowid,
1423 : size_t num_denom_serials,
1424 : const uint64_t *denom_serials,
1425 : const struct TALER_HashBlindedPlanchetsP *selected_h,
1426 : const struct TALER_HashBlindedPlanchetsP *h_planchets,
1427 : const struct TALER_BlindingMasterSeedP *blinding_seed,
1428 : bool age_proof_required,
1429 : uint8_t max_age,
1430 : uint8_t noreveal_index,
1431 : const struct TALER_ReservePublicKeyP *reserve_pub,
1432 : const struct TALER_ReserveSignatureP *reserve_sig,
1433 : struct GNUNET_TIME_Timestamp execution_date,
1434 : const struct TALER_Amount *amount_with_fee)
1435 : {
1436 :
1437 : /* Note: some optimization potential here: lots of fields we
1438 : could avoid fetching from the database with a custom function. */
1439 : (void) h_planchets;
1440 : (void) blinding_seed;
1441 : (void) reserve_pub;
1442 : (void) reserve_sig;
1443 : (void) execution_date;
1444 : (void) amount_with_fee;
1445 :
1446 36 : GNUNET_assert (rowid >=
1447 : TALER_ARL_USE_PP (coins_withdraw_serial_id)); /* should be monotonically increasing */
1448 36 : TALER_ARL_USE_PP (coins_withdraw_serial_id) = rowid + 1;
1449 :
1450 72 : for (size_t i=0; i < num_denom_serials; i++)
1451 : {
1452 : struct DenominationSummary *ds;
1453 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
1454 : enum GNUNET_DB_QueryStatus qs;
1455 :
1456 36 : qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
1457 : &issue);
1458 36 : if (0 > qs)
1459 : {
1460 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1461 0 : cc->qs = qs;
1462 0 : return GNUNET_SYSERR;
1463 : }
1464 36 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1465 : {
1466 0 : qs = report_row_inconsistency ("withdraw",
1467 : rowid,
1468 : "denomination key not found");
1469 0 : if (0 > qs)
1470 : {
1471 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1472 0 : cc->qs = qs;
1473 0 : return GNUNET_SYSERR;
1474 : }
1475 0 : return GNUNET_OK;
1476 : }
1477 36 : ds = get_denomination_summary (cc,
1478 : issue);
1479 36 : if (NULL == ds)
1480 : {
1481 : /* cc->qs is set by #get_denomination_summary() */
1482 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc->qs);
1483 0 : return GNUNET_SYSERR;
1484 : }
1485 36 : ds->dcd.num_issued++;
1486 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1487 : "Issued coin in denomination `%s' of total value %s\n",
1488 : GNUNET_h2s (&issue->denom_hash.hash),
1489 : TALER_amount2s (&issue->value));
1490 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1491 : "New balance of denomination `%s' after withdraw is %s\n",
1492 : GNUNET_h2s (&issue->denom_hash.hash),
1493 : TALER_amount2s (&ds->dcd.denom_balance));
1494 36 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
1495 : &TALER_ARL_USE_AB (total_escrowed),
1496 : &issue->value);
1497 36 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
1498 : &TALER_ARL_USE_AB (coin_balance_risk),
1499 : &issue->value);
1500 36 : TALER_ARL_amount_add (&ds->dcd.denom_balance,
1501 : &ds->dcd.denom_balance,
1502 : &issue->value);
1503 36 : TALER_ARL_amount_add (&ds->dcd.denom_risk,
1504 : &ds->dcd.denom_risk,
1505 : &issue->value);
1506 : }
1507 36 : return GNUNET_OK;
1508 : }
1509 :
1510 :
1511 : /**
1512 : * Check that the @a coin_pub is a known coin with a proper
1513 : * signature for denominatinon @a denom_pub. If not, report
1514 : * a loss of @a loss_potential.
1515 : *
1516 : * @param operation which operation is this about
1517 : * @param issue denomination key information about the coin
1518 : * @param rowid which row is this operation in
1519 : * @param coin_pub public key of a coin
1520 : * @param denom_pub expected denomination of the coin
1521 : * @param loss_potential how big could the loss be if the coin is
1522 : * not properly signed
1523 : * @return database transaction status; on success
1524 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, or
1525 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the coin is not known to
1526 : * the exchange at all (which was reported to the auditor database)
1527 : */
1528 : static enum GNUNET_DB_QueryStatus
1529 40 : check_known_coin (
1530 : const char *operation,
1531 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
1532 : uint64_t rowid,
1533 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1534 : const struct TALER_DenominationPublicKey *denom_pub,
1535 : const struct TALER_Amount *loss_potential)
1536 : {
1537 : struct TALER_CoinPublicInfo ci;
1538 : enum GNUNET_DB_QueryStatus qs;
1539 :
1540 40 : if (NULL == get_cached_history (coin_pub))
1541 : {
1542 34 : qs = check_coin_history (coin_pub,
1543 : rowid,
1544 : operation,
1545 : &issue->value);
1546 34 : if (0 > qs)
1547 : {
1548 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1549 0 : return qs;
1550 : }
1551 34 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
1552 : }
1553 :
1554 40 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1555 : "Checking denomination signature on %s\n",
1556 : TALER_B2S (coin_pub));
1557 40 : qs = TALER_EXCHANGEDB_get_known_coin (TALER_ARL_edb,
1558 : coin_pub,
1559 : &ci);
1560 40 : if (0 > qs)
1561 : {
1562 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1563 0 : return qs;
1564 : }
1565 40 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1566 : {
1567 : /* The exchange has no denomination signature on file for this
1568 : coin at all; that is the strongest evidence of a forged coin
1569 : and must be persisted, not merely logged. */
1570 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1571 : .problem_row_id = rowid,
1572 : .operation = (char *) operation,
1573 : .loss = *loss_potential,
1574 : .operation_specific_pub = coin_pub->eddsa_pub
1575 : };
1576 : enum GNUNET_DB_QueryStatus rqs;
1577 :
1578 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1579 : "Coin %s used in %s in row %llu is not known to the exchange\n",
1580 : TALER_B2S (coin_pub),
1581 : operation,
1582 : (unsigned long long) rowid);
1583 0 : rqs = report_row_inconsistency ("known_coins",
1584 : rowid,
1585 : "coin not known to exchange");
1586 0 : if (0 > rqs)
1587 : {
1588 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rqs);
1589 0 : return rqs;
1590 : }
1591 0 : rqs = TALER_AUDITORDB_insert_bad_sig_losses (
1592 : TALER_ARL_adb,
1593 : &bsl);
1594 0 : if (0 > rqs)
1595 : {
1596 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rqs);
1597 0 : return rqs;
1598 : }
1599 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
1600 : &TALER_ARL_USE_AB (coin_irregular_loss),
1601 : loss_potential);
1602 0 : return qs;
1603 : }
1604 40 : if (GNUNET_YES !=
1605 40 : TALER_test_coin_valid (&ci,
1606 : denom_pub))
1607 : {
1608 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1609 : .problem_row_id = rowid,
1610 : .operation = (char *) operation,
1611 : .loss = *loss_potential,
1612 : .operation_specific_pub = coin_pub->eddsa_pub
1613 : };
1614 :
1615 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1616 : "Failed to verify coin denomination signature in row %llu\n",
1617 : (unsigned long long) rowid);
1618 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
1619 : TALER_ARL_adb,
1620 : &bsl);
1621 0 : if (qs < 0)
1622 : {
1623 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1624 0 : return qs;
1625 : }
1626 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
1627 : &TALER_ARL_USE_AB (coin_irregular_loss),
1628 : loss_potential);
1629 : }
1630 40 : TALER_denom_sig_free (&ci.denom_sig);
1631 40 : return qs;
1632 : }
1633 :
1634 :
1635 : /**
1636 : * Update the denom balance in @a dso reducing it by
1637 : * @a amount_with_fee. If this is not possible, report
1638 : * an emergency. Also updates the balance.
1639 : *
1640 : * @param dso denomination summary to update
1641 : * @param rowid responsible row (for logging)
1642 : * @param amount_with_fee amount to subtract
1643 : * @return transaction status
1644 : */
1645 : static enum GNUNET_DB_QueryStatus
1646 36 : reduce_denom_balance (struct DenominationSummary *dso,
1647 : uint64_t rowid,
1648 : const struct TALER_Amount *amount_with_fee)
1649 : {
1650 : struct TALER_Amount tmp;
1651 : enum GNUNET_DB_QueryStatus qs;
1652 :
1653 36 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
1654 36 : TALER_ARL_amount_subtract_neg (&tmp,
1655 : &dso->dcd.denom_balance,
1656 : amount_with_fee))
1657 : {
1658 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1659 : "Emergency: failed to reduce balance of denomination `%s' by %s\n",
1660 : GNUNET_h2s (&dso->issue->denom_hash.hash),
1661 : TALER_amount2s (amount_with_fee));
1662 0 : TALER_ARL_amount_add (&dso->dcd.denom_loss,
1663 : &dso->dcd.denom_loss,
1664 : amount_with_fee);
1665 0 : dso->report_emergency = true;
1666 : }
1667 : else
1668 : {
1669 36 : dso->dcd.denom_balance = tmp;
1670 : }
1671 36 : if (-1 == TALER_amount_cmp (&TALER_ARL_USE_AB (total_escrowed),
1672 : amount_with_fee))
1673 : {
1674 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1675 : "Failed to total escrow by %s\n",
1676 : TALER_amount2s (amount_with_fee));
1677 : /* This can theoretically happen if for example the exchange
1678 : never issued any coins (i.e. escrow balance is zero), but
1679 : accepted a forged coin (i.e. emergency situation after
1680 : private key compromise). In that case, we cannot even
1681 : subtract the profit we make from the fee from the escrow
1682 : balance. Tested as part of test-auditor.sh, case #18 */
1683 0 : qs = report_amount_arithmetic_inconsistency (
1684 : "subtracting amount from escrow balance",
1685 : rowid,
1686 : &TALER_ARL_USE_AB (total_escrowed),
1687 : amount_with_fee,
1688 : 0);
1689 0 : if (0 > qs)
1690 : {
1691 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1692 0 : return qs;
1693 : }
1694 : }
1695 : else
1696 : {
1697 36 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_escrowed),
1698 : &TALER_ARL_USE_AB (total_escrowed),
1699 : amount_with_fee);
1700 : }
1701 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1702 : "New balance of denomination `%s' is %s\n",
1703 : GNUNET_h2s (&dso->issue->denom_hash.hash),
1704 : TALER_amount2s (&dso->dcd.denom_balance));
1705 36 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
1706 : }
1707 :
1708 :
1709 : /**
1710 : * Function called with details about coins that were melted, with the
1711 : * goal of auditing the refresh's execution. Verifies the signature
1712 : * and updates our information about coins outstanding (the old coin's
1713 : * denomination has less, the fresh coins increased outstanding
1714 : * balances).
1715 : *
1716 : * @param cc closure
1717 : * @param rowid unique serial ID for the refresh session in our DB
1718 : * @param old_denom_pub denomination public key of @a coin_pub
1719 : * @param coin_pub public key of the coin
1720 : * @param coin_sig signature from the coin
1721 : * @param h_age_commitment hash of the age commitment for the coin
1722 : * @param amount_with_fee amount that was deposited including fee
1723 : * @param num_nds length of the @a new_denom_serials array
1724 : * @param new_denom_serials array of denomination serials of fresh coins
1725 : * @param rc what the refresh commitment
1726 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1727 : */
1728 : static enum GNUNET_GenericReturnValue
1729 2 : refresh_session_cb (struct CoinContext *cc,
1730 : uint64_t rowid,
1731 : const struct TALER_DenominationPublicKey *old_denom_pub,
1732 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1733 : const struct TALER_CoinSpendSignatureP *coin_sig,
1734 : const struct TALER_AgeCommitmentHashP *h_age_commitment,
1735 : const struct TALER_Amount *amount_with_fee,
1736 : size_t num_nds,
1737 : uint64_t new_denom_serials[static num_nds],
1738 : const struct TALER_RefreshCommitmentP *rc)
1739 2 : {
1740 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
1741 : struct DenominationSummary *dso;
1742 : enum GNUNET_DB_QueryStatus qs;
1743 : struct TALER_DenominationHashP h_denom_pub;
1744 :
1745 2 : GNUNET_assert (rowid >=
1746 : TALER_ARL_USE_PP (coins_melt_serial_id)); /* should be monotonically increasing */
1747 2 : TALER_ARL_USE_PP (coins_melt_serial_id) = rowid + 1;
1748 2 : qs = TALER_ARL_get_denomination_info (old_denom_pub,
1749 : &issue,
1750 : &h_denom_pub);
1751 2 : if (0 > qs)
1752 : {
1753 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1754 0 : cc->qs = qs;
1755 0 : return GNUNET_SYSERR;
1756 : }
1757 2 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1758 : {
1759 0 : qs = report_row_inconsistency ("melt",
1760 : rowid,
1761 : "denomination key not found");
1762 0 : if (0 > qs)
1763 : {
1764 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1765 0 : cc->qs = qs;
1766 0 : return GNUNET_SYSERR;
1767 : }
1768 0 : return GNUNET_OK;
1769 : }
1770 2 : qs = check_known_coin ("melt",
1771 : issue,
1772 : rowid,
1773 : coin_pub,
1774 : old_denom_pub,
1775 : amount_with_fee);
1776 2 : if (0 > qs)
1777 : {
1778 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1779 0 : cc->qs = qs;
1780 0 : return GNUNET_SYSERR;
1781 : }
1782 :
1783 : /* verify melt signature */
1784 2 : if (GNUNET_OK !=
1785 2 : TALER_wallet_melt_verify (amount_with_fee,
1786 2 : &issue->fees.refresh,
1787 : rc,
1788 : &h_denom_pub,
1789 : h_age_commitment,
1790 : coin_pub,
1791 : coin_sig))
1792 : {
1793 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1794 : .problem_row_id = rowid,
1795 : .operation = (char *) "melt",
1796 : .loss = *amount_with_fee,
1797 : .operation_specific_pub = coin_pub->eddsa_pub
1798 : };
1799 :
1800 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1801 : "Failed to verify coin melt signature in row %llu\n",
1802 : (unsigned long long) rowid);
1803 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
1804 : TALER_ARL_adb,
1805 : &bsl);
1806 0 : if (qs < 0)
1807 : {
1808 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1809 0 : cc->qs = qs;
1810 0 : return GNUNET_SYSERR;
1811 : }
1812 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
1813 : &TALER_ARL_USE_AB (coin_irregular_loss),
1814 : amount_with_fee);
1815 : }
1816 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1817 : "Melting coin %s in denomination `%s' of value %s\n",
1818 : TALER_B2S (coin_pub),
1819 : GNUNET_h2s (&issue->denom_hash.hash),
1820 : TALER_amount2s (amount_with_fee));
1821 :
1822 2 : {
1823 : struct TALER_Amount refresh_cost;
1824 : struct TALER_Amount amount_without_fee;
1825 2 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *nis[num_nds];
1826 :
1827 : /* Check that the resulting amounts are consistent with the value being
1828 : refreshed by calculating the total refresh cost */
1829 2 : GNUNET_assert (GNUNET_OK ==
1830 : TALER_amount_set_zero (amount_with_fee->currency,
1831 : &refresh_cost));
1832 10 : for (size_t i = 0; i < num_nds; i++)
1833 : {
1834 8 : qs = TALER_ARL_get_denomination_info_by_serial (new_denom_serials[i],
1835 : &nis[i]);
1836 8 : if (0 > qs)
1837 : {
1838 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1839 0 : cc->qs = qs;
1840 0 : return GNUNET_SYSERR;
1841 : }
1842 8 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1843 : {
1844 : /* nis[i] was not set; we cannot audit this refresh at all */
1845 0 : qs = report_row_inconsistency ("refresh_reveal",
1846 : rowid,
1847 : "denomination key for fresh coin not found");
1848 0 : if (0 > qs)
1849 : {
1850 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1851 0 : cc->qs = qs;
1852 0 : return GNUNET_SYSERR;
1853 : }
1854 0 : return GNUNET_OK;
1855 : }
1856 : /* update cost of refresh */
1857 8 : TALER_ARL_amount_add (&refresh_cost,
1858 : &refresh_cost,
1859 : &nis[i]->fees.withdraw);
1860 8 : TALER_ARL_amount_add (&refresh_cost,
1861 : &refresh_cost,
1862 : &nis[i]->value);
1863 : }
1864 :
1865 : /* compute contribution of old coin */
1866 2 : if (TALER_ARL_SR_POSITIVE !=
1867 2 : TALER_ARL_amount_subtract_neg (&amount_without_fee,
1868 : amount_with_fee,
1869 : &issue->fees.refresh))
1870 : {
1871 : /* Melt fee higher than contribution of melted coin; this makes
1872 : no sense (exchange should never have accepted the operation) */
1873 0 : qs = report_amount_arithmetic_inconsistency ("melt contribution vs. fee",
1874 : rowid,
1875 : amount_with_fee,
1876 0 : &issue->fees.refresh,
1877 : -1);
1878 0 : if (0 > qs)
1879 : {
1880 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1881 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1882 0 : return GNUNET_SYSERR;
1883 : }
1884 : /* To continue, best assumption is the melted coin contributed
1885 : nothing (=> all withdrawal amounts will be counted as losses) */
1886 0 : GNUNET_assert (GNUNET_OK ==
1887 : TALER_amount_set_zero (TALER_ARL_currency,
1888 : &amount_without_fee));
1889 : }
1890 :
1891 : /* check old coin covers complete expenses (of refresh operation) */
1892 2 : if (1 == TALER_amount_cmp (&refresh_cost,
1893 : &amount_without_fee))
1894 : {
1895 : /* refresh_cost > amount_without_fee, which is bad (exchange lost) */
1896 0 : GNUNET_break_op (0);
1897 0 : qs = report_amount_arithmetic_inconsistency ("melt (cost)",
1898 : rowid,
1899 : &amount_without_fee, /* 'exchange' */
1900 : &refresh_cost, /* 'auditor' */
1901 : 1);
1902 0 : if (0 > qs)
1903 : {
1904 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1905 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1906 0 : return GNUNET_SYSERR;
1907 : }
1908 : }
1909 :
1910 : /* update outstanding denomination amounts for fresh coins withdrawn */
1911 10 : for (size_t i = 0; i < num_nds; i++)
1912 : {
1913 8 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *ni
1914 : = nis[i];
1915 : struct DenominationSummary *dsi;
1916 :
1917 8 : dsi = get_denomination_summary (cc,
1918 : ni);
1919 8 : if (NULL == dsi)
1920 : {
1921 0 : qs = report_row_inconsistency ("refresh_reveal",
1922 : rowid,
1923 : "denomination key for fresh coin unknown to auditor");
1924 0 : if (0 > qs)
1925 : {
1926 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1927 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1928 0 : return GNUNET_SYSERR;
1929 : }
1930 : }
1931 : else
1932 : {
1933 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1934 : "Created fresh coin in denomination `%s' of value %s\n",
1935 : GNUNET_h2s (&ni->denom_hash.hash),
1936 : TALER_amount2s (&ni->value));
1937 8 : dsi->dcd.num_issued++;
1938 8 : TALER_ARL_amount_add (&dsi->dcd.denom_balance,
1939 : &dsi->dcd.denom_balance,
1940 : &ni->value);
1941 8 : TALER_ARL_amount_add (&dsi->dcd.denom_risk,
1942 : &dsi->dcd.denom_risk,
1943 : &ni->value);
1944 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1945 : "New balance of denomination `%s' after refresh_reveal is %s\n",
1946 : GNUNET_h2s (&ni->denom_hash.hash),
1947 : TALER_amount2s (&dsi->dcd.denom_balance));
1948 8 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
1949 : &TALER_ARL_USE_AB (total_escrowed),
1950 : &ni->value);
1951 8 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
1952 : &TALER_ARL_USE_AB (coin_balance_risk),
1953 : &ni->value);
1954 : }
1955 : }
1956 : }
1957 :
1958 : /* update old coin's denomination balance */
1959 2 : dso = get_denomination_summary (cc,
1960 : issue);
1961 2 : if (NULL == dso)
1962 : {
1963 0 : qs = report_row_inconsistency ("refresh_reveal",
1964 : rowid,
1965 : "denomination key for dirty coin unknown to auditor");
1966 0 : if (0 > qs)
1967 : {
1968 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1969 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1970 0 : return GNUNET_SYSERR;
1971 : }
1972 : }
1973 : else
1974 : {
1975 2 : qs = reduce_denom_balance (dso,
1976 : rowid,
1977 : amount_with_fee);
1978 2 : if (0 > qs)
1979 : {
1980 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1981 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1982 0 : return GNUNET_SYSERR;
1983 : }
1984 : }
1985 :
1986 : /* update global melt fees */
1987 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_melt_fee_revenue),
1988 : &TALER_ARL_USE_AB (coin_melt_fee_revenue),
1989 : &issue->fees.refresh);
1990 2 : return GNUNET_OK;
1991 : }
1992 :
1993 :
1994 : /**
1995 : * Function called with details about deposits that have been made,
1996 : * with the goal of auditing the deposit's execution.
1997 : *
1998 : * @param cc closure
1999 : * @param rowid unique serial ID for the deposit in our DB
2000 : * @param exchange_timestamp when did the exchange get the deposit
2001 : * @param deposit deposit details
2002 : * @param denom_pub denomination public key of @a coin_pub
2003 : * @param done flag set if the deposit was already executed (or not)
2004 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2005 : */
2006 : static enum GNUNET_GenericReturnValue
2007 34 : deposit_cb (struct CoinContext *cc,
2008 : uint64_t rowid,
2009 : struct GNUNET_TIME_Timestamp exchange_timestamp,
2010 : const struct TALER_EXCHANGEDB_Deposit *deposit,
2011 : const struct TALER_DenominationPublicKey *denom_pub,
2012 : bool done)
2013 : {
2014 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2015 : struct DenominationSummary *ds;
2016 : enum GNUNET_DB_QueryStatus qs;
2017 :
2018 : (void) done;
2019 : (void) exchange_timestamp;
2020 34 : GNUNET_assert (rowid >=
2021 : TALER_ARL_USE_PP (coins_deposit_serial_id)); /* should be monotonically increasing */
2022 34 : TALER_ARL_USE_PP (coins_deposit_serial_id) = rowid + 1;
2023 :
2024 34 : qs = TALER_ARL_get_denomination_info (denom_pub,
2025 : &issue,
2026 : NULL);
2027 34 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2028 : {
2029 0 : qs = report_row_inconsistency ("deposits",
2030 : rowid,
2031 : "denomination key not found");
2032 0 : if (0 > qs)
2033 : {
2034 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2035 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2036 0 : return GNUNET_SYSERR;
2037 : }
2038 0 : return GNUNET_OK;
2039 : }
2040 34 : if (GNUNET_TIME_timestamp_cmp (deposit->refund_deadline,
2041 : >,
2042 : deposit->wire_deadline))
2043 : {
2044 0 : qs = report_row_inconsistency ("deposits",
2045 : rowid,
2046 : "refund deadline past wire deadline");
2047 0 : if (0 > qs)
2048 : {
2049 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2050 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2051 0 : return GNUNET_SYSERR;
2052 : }
2053 : }
2054 :
2055 34 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
2056 : {
2057 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2058 0 : cc->qs = qs;
2059 0 : return GNUNET_SYSERR;
2060 : }
2061 34 : qs = check_known_coin ("deposit",
2062 : issue,
2063 : rowid,
2064 : &deposit->coin.coin_pub,
2065 : denom_pub,
2066 : &deposit->amount_with_fee);
2067 34 : if (0 > qs)
2068 : {
2069 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2070 0 : cc->qs = qs;
2071 0 : return GNUNET_SYSERR;
2072 : }
2073 :
2074 : /* Verify deposit signature */
2075 : {
2076 : struct TALER_MerchantWireHashP h_wire;
2077 : struct TALER_DenominationHashP h_denom_pub;
2078 :
2079 34 : TALER_denom_pub_hash (denom_pub,
2080 : &h_denom_pub);
2081 34 : TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
2082 : &deposit->wire_salt,
2083 : &h_wire);
2084 : /* NOTE: This is one of the operations we might eventually
2085 : want to do in parallel in the background to improve
2086 : auditor performance! */
2087 34 : if (GNUNET_OK !=
2088 34 : TALER_wallet_deposit_verify (&deposit->amount_with_fee,
2089 34 : &issue->fees.deposit,
2090 : &h_wire,
2091 : &deposit->h_contract_terms,
2092 34 : deposit->no_wallet_data_hash
2093 : ? NULL
2094 : : &deposit->wallet_data_hash,
2095 : &deposit->coin.h_age_commitment,
2096 : &deposit->h_policy,
2097 : &h_denom_pub,
2098 : deposit->timestamp,
2099 : &deposit->merchant_pub,
2100 : deposit->refund_deadline,
2101 : &deposit->coin.coin_pub,
2102 : &deposit->csig))
2103 : {
2104 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2105 : .problem_row_id = rowid,
2106 : .operation = (char *) "deposit",
2107 : .loss = deposit->amount_with_fee,
2108 : .operation_specific_pub = deposit->coin.coin_pub.eddsa_pub
2109 : };
2110 :
2111 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2112 : "Failed to verify coin deposit signature in row %llu\n",
2113 : (unsigned long long) rowid);
2114 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2115 : TALER_ARL_adb,
2116 : &bsl);
2117 0 : if (0 > qs)
2118 : {
2119 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2120 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2121 0 : return GNUNET_SYSERR;
2122 : }
2123 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2124 : &TALER_ARL_USE_AB (coin_irregular_loss),
2125 : &deposit->amount_with_fee);
2126 0 : return GNUNET_OK;
2127 : }
2128 : }
2129 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2130 : "Deposited coin %s in denomination `%s' of value %s\n",
2131 : TALER_B2S (&deposit->coin.coin_pub),
2132 : GNUNET_h2s (&issue->denom_hash.hash),
2133 : TALER_amount2s (&deposit->amount_with_fee));
2134 :
2135 : /* update old coin's denomination balance */
2136 34 : ds = get_denomination_summary (cc,
2137 : issue);
2138 34 : if (NULL == ds)
2139 : {
2140 0 : qs = report_row_inconsistency ("deposit",
2141 : rowid,
2142 : "denomination key for deposited coin unknown to auditor");
2143 0 : if (0 > qs)
2144 : {
2145 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2146 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2147 0 : return GNUNET_SYSERR;
2148 : }
2149 : }
2150 : else
2151 : {
2152 34 : qs = reduce_denom_balance (ds,
2153 : rowid,
2154 : &deposit->amount_with_fee);
2155 34 : if (0 > qs)
2156 : {
2157 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2158 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2159 0 : return GNUNET_SYSERR;
2160 : }
2161 : }
2162 :
2163 : /* update global deposit fees */
2164 34 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue),
2165 : &TALER_ARL_USE_AB (coin_deposit_fee_revenue),
2166 : &issue->fees.deposit);
2167 34 : return GNUNET_OK;
2168 : }
2169 :
2170 :
2171 : /**
2172 : * Function called with details about coins that were refunding,
2173 : * with the goal of auditing the refund's execution. Adds the
2174 : * refunded amount back to the outstanding balance of the respective
2175 : * denomination.
2176 : *
2177 : * @param cc closure
2178 : * @param rowid unique serial ID for the refund in our DB
2179 : * @param denom_pub denomination public key of @a coin_pub
2180 : * @param coin_pub public key of the coin
2181 : * @param merchant_pub public key of the merchant
2182 : * @param merchant_sig signature of the merchant
2183 : * @param h_contract_terms hash of the proposal data known to merchant and customer
2184 : * @param rtransaction_id refund transaction ID chosen by the merchant
2185 : * @param full_refund true if the refunds total up to the entire deposited value
2186 : * @param amount_with_fee amount that was deposited including fee
2187 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2188 : */
2189 : static enum GNUNET_GenericReturnValue
2190 2 : refund_cb (struct CoinContext *cc,
2191 : uint64_t rowid,
2192 : const struct TALER_DenominationPublicKey *denom_pub,
2193 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2194 : const struct TALER_MerchantPublicKeyP *merchant_pub,
2195 : const struct TALER_MerchantSignatureP *merchant_sig,
2196 : const struct TALER_PrivateContractHashP *h_contract_terms,
2197 : uint64_t rtransaction_id,
2198 : bool full_refund,
2199 : const struct TALER_Amount *amount_with_fee)
2200 : {
2201 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2202 : struct DenominationSummary *ds;
2203 : struct TALER_Amount amount_without_fee;
2204 : enum GNUNET_DB_QueryStatus qs;
2205 :
2206 2 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_refund_serial_id)); /* should be monotonically increasing */
2207 2 : TALER_ARL_USE_PP (coins_refund_serial_id) = rowid + 1;
2208 :
2209 2 : qs = TALER_ARL_get_denomination_info (denom_pub,
2210 : &issue,
2211 : NULL);
2212 2 : if (0 > qs)
2213 : {
2214 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2215 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2216 0 : return GNUNET_SYSERR;
2217 : }
2218 2 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2219 : {
2220 0 : qs = report_row_inconsistency ("refunds",
2221 : rowid,
2222 : "denomination key not found");
2223 0 : if (0 > qs)
2224 : {
2225 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2226 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2227 0 : return GNUNET_SYSERR;
2228 : }
2229 0 : return GNUNET_OK;
2230 : }
2231 :
2232 : /* verify refund signature */
2233 2 : if (GNUNET_OK !=
2234 2 : TALER_merchant_refund_verify (coin_pub,
2235 : h_contract_terms,
2236 : rtransaction_id,
2237 : amount_with_fee,
2238 : merchant_pub,
2239 : merchant_sig))
2240 : {
2241 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2242 : .problem_row_id = rowid,
2243 : .operation = (char *) "refund",
2244 : .loss = *amount_with_fee,
2245 : .operation_specific_pub = coin_pub->eddsa_pub
2246 : };
2247 :
2248 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2249 : "Failed to verify merchant refund signature in row %llu\n",
2250 : (unsigned long long) rowid);
2251 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2252 : TALER_ARL_adb,
2253 : &bsl);
2254 0 : if (0 > qs)
2255 : {
2256 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2257 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2258 0 : return GNUNET_SYSERR;
2259 : }
2260 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2261 : &TALER_ARL_USE_AB (coin_irregular_loss),
2262 : amount_with_fee);
2263 0 : return GNUNET_OK;
2264 : }
2265 :
2266 2 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
2267 2 : TALER_ARL_amount_subtract_neg (&amount_without_fee,
2268 : amount_with_fee,
2269 : &issue->fees.refund))
2270 : {
2271 0 : qs = report_amount_arithmetic_inconsistency ("refund (fee)",
2272 : rowid,
2273 : amount_with_fee,
2274 0 : &issue->fees.refund,
2275 : -1);
2276 0 : if (0 > qs)
2277 : {
2278 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2279 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2280 0 : return GNUNET_SYSERR;
2281 : }
2282 0 : return GNUNET_OK;
2283 : }
2284 :
2285 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2286 : "Refunding coin %s in denomination `%s' value %s\n",
2287 : TALER_B2S (coin_pub),
2288 : GNUNET_h2s (&issue->denom_hash.hash),
2289 : TALER_amount2s (amount_with_fee));
2290 :
2291 : /* update coin's denomination balance */
2292 2 : ds = get_denomination_summary (cc,
2293 : issue);
2294 2 : if (NULL == ds)
2295 : {
2296 0 : qs = report_row_inconsistency ("refund",
2297 : rowid,
2298 : "denomination key for refunded coin unknown to auditor");
2299 0 : if (0 > qs)
2300 : {
2301 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2302 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2303 0 : return GNUNET_SYSERR;
2304 : }
2305 : }
2306 : else
2307 : {
2308 2 : TALER_ARL_amount_add (&ds->dcd.denom_balance,
2309 : &ds->dcd.denom_balance,
2310 : &amount_without_fee);
2311 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
2312 : &TALER_ARL_USE_AB (total_escrowed),
2313 : &amount_without_fee);
2314 : /* A coin of a denomination whose deposit period is over cannot be
2315 : recouped (#TEH_recoup_check_keys() refuses it), so crediting it back
2316 : does not put anything at risk again; see
2317 : #release_denomination_risk(). */
2318 2 : if (! is_past_deposit_grace (issue))
2319 : {
2320 2 : TALER_ARL_amount_add (&ds->dcd.denom_risk,
2321 : &ds->dcd.denom_risk,
2322 : &amount_without_fee);
2323 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
2324 : &TALER_ARL_USE_AB (coin_balance_risk),
2325 : &amount_without_fee);
2326 : }
2327 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2328 : "New balance of denomination `%s' after refund is %s\n",
2329 : GNUNET_h2s (&issue->denom_hash.hash),
2330 : TALER_amount2s (&ds->dcd.denom_balance));
2331 : }
2332 : /* update total refund fee balance */
2333 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_refund_fee_revenue),
2334 : &TALER_ARL_USE_AB (coin_refund_fee_revenue),
2335 : &issue->fees.refund);
2336 2 : if (full_refund)
2337 : {
2338 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss),
2339 : &TALER_ARL_USE_AB (coin_deposit_fee_loss),
2340 : &issue->fees.deposit);
2341 : }
2342 2 : return GNUNET_OK;
2343 : }
2344 :
2345 :
2346 : /**
2347 : * Function called with details about purse refunds that have been made, with
2348 : * the goal of auditing the purse refund's execution.
2349 : *
2350 : * @param cc closure
2351 : * @param rowid row of the purse-refund
2352 : * @param amount_with_fee amount of the deposit into the purse
2353 : * @param coin_pub coin that is to be refunded the @a given amount_with_fee
2354 : * @param denom_pub denomination of @a coin_pub
2355 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2356 : */
2357 : static enum GNUNET_GenericReturnValue
2358 0 : purse_refund_coin_cb (
2359 : struct CoinContext *cc,
2360 : uint64_t rowid,
2361 : const struct TALER_Amount *amount_with_fee,
2362 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2363 : const struct TALER_DenominationPublicKey *denom_pub)
2364 : {
2365 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2366 : struct DenominationSummary *ds;
2367 : enum GNUNET_DB_QueryStatus qs;
2368 :
2369 0 : qs = TALER_ARL_get_denomination_info (denom_pub,
2370 : &issue,
2371 : NULL);
2372 0 : if (0 > qs)
2373 : {
2374 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2375 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2376 0 : return GNUNET_SYSERR;
2377 : }
2378 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2379 : {
2380 0 : qs = report_row_inconsistency ("purse-refunds",
2381 : rowid,
2382 : "denomination key not found");
2383 0 : if (0 > qs)
2384 : {
2385 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2386 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2387 0 : return GNUNET_SYSERR;
2388 : }
2389 0 : return GNUNET_OK;
2390 : }
2391 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2392 : "Aborted purse-deposit of coin %s in denomination `%s' value %s\n",
2393 : TALER_B2S (coin_pub),
2394 : GNUNET_h2s (&issue->denom_hash.hash),
2395 : TALER_amount2s (amount_with_fee));
2396 :
2397 : /* update coin's denomination balance */
2398 0 : ds = get_denomination_summary (cc,
2399 : issue);
2400 0 : if (NULL == ds)
2401 : {
2402 0 : qs = report_row_inconsistency ("purse-refund",
2403 : rowid,
2404 : "denomination key for purse-refunded coin unknown to auditor");
2405 0 : if (0 > qs)
2406 : {
2407 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2408 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2409 0 : return GNUNET_SYSERR;
2410 : }
2411 : }
2412 : else
2413 : {
2414 0 : TALER_ARL_amount_add (&ds->dcd.denom_balance,
2415 : &ds->dcd.denom_balance,
2416 : amount_with_fee);
2417 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
2418 : &TALER_ARL_USE_AB (total_escrowed),
2419 : amount_with_fee);
2420 : /* A coin of a denomination whose deposit period is over cannot be
2421 : recouped (#TEH_recoup_check_keys() refuses it), so crediting it back
2422 : does not put anything at risk again; see
2423 : #release_denomination_risk(). */
2424 0 : if (! is_past_deposit_grace (issue))
2425 : {
2426 0 : TALER_ARL_amount_add (&ds->dcd.denom_risk,
2427 : &ds->dcd.denom_risk,
2428 : amount_with_fee);
2429 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
2430 : &TALER_ARL_USE_AB (coin_balance_risk),
2431 : amount_with_fee);
2432 : }
2433 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2434 : "New balance of denomination `%s' after purse-refund is %s\n",
2435 : GNUNET_h2s (&issue->denom_hash.hash),
2436 : TALER_amount2s (&ds->dcd.denom_balance));
2437 : }
2438 : /* update total deposit fee balance */
2439 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss),
2440 : &TALER_ARL_USE_AB (coin_deposit_fee_loss),
2441 : &issue->fees.deposit);
2442 :
2443 0 : return GNUNET_OK;
2444 : }
2445 :
2446 :
2447 : /**
2448 : * Function called with details about a purse that was refunded. Adds the
2449 : * refunded amounts back to the outstanding balance of the respective
2450 : * denominations.
2451 : *
2452 : * @param cc closure
2453 : * @param rowid unique serial ID for the refund in our DB
2454 : * @param purse_pub public key of the purse
2455 : * @param reserve_pub public key of the targeted reserve (ignored)
2456 : * @param val targeted amount to be in the reserve (ignored)
2457 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2458 : */
2459 : static enum GNUNET_GenericReturnValue
2460 0 : purse_refund_cb (struct CoinContext *cc,
2461 : uint64_t rowid,
2462 : const struct TALER_PurseContractPublicKeyP *purse_pub,
2463 : const struct TALER_ReservePublicKeyP *reserve_pub,
2464 : const struct TALER_Amount *val)
2465 : {
2466 : enum GNUNET_DB_QueryStatus qs;
2467 :
2468 : (void) val; /* irrelevant on refund */
2469 : (void) reserve_pub; /* irrelevant, may even be NULL */
2470 0 : GNUNET_assert (rowid >=
2471 : TALER_ARL_USE_PP (coins_purse_refunds_serial_id)); /* should be monotonically increasing */
2472 0 : TALER_ARL_USE_PP (coins_purse_refunds_serial_id) = rowid + 1;
2473 0 : qs = TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (TALER_ARL_edb,
2474 : purse_pub,
2475 : &
2476 : purse_refund_coin_cb,
2477 : cc);
2478 0 : if (qs < 0)
2479 : {
2480 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2481 0 : return GNUNET_SYSERR;
2482 : }
2483 0 : return GNUNET_OK;
2484 : }
2485 :
2486 :
2487 : /**
2488 : * Check that the recoup operation was properly initiated by a coin
2489 : * and update the denomination's losses accordingly.
2490 : *
2491 : * @param cc the context with details about the coin
2492 : * @param operation name of the operation matching @a rowid
2493 : * @param rowid row identifier used to uniquely identify the recoup operation
2494 : * @param amount how much should be added back to the reserve
2495 : * @param coin public information about the coin
2496 : * @param denom_pub public key of the denomionation of @a coin
2497 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
2498 : * @param coin_blind blinding factor used to blind the coin
2499 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2500 : */
2501 : static enum GNUNET_GenericReturnValue
2502 4 : check_recoup (struct CoinContext *cc,
2503 : const char *operation,
2504 : uint64_t rowid,
2505 : const struct TALER_Amount *amount,
2506 : const struct TALER_CoinPublicInfo *coin,
2507 : const struct TALER_DenominationPublicKey *denom_pub,
2508 : const struct TALER_CoinSpendSignatureP *coin_sig,
2509 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
2510 : {
2511 : struct DenominationSummary *ds;
2512 : enum GNUNET_DB_QueryStatus qs;
2513 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2514 :
2515 4 : if (GNUNET_OK !=
2516 4 : TALER_wallet_recoup_verify (&coin->denom_pub_hash,
2517 : coin_blind,
2518 : &coin->coin_pub,
2519 : coin_sig))
2520 : {
2521 0 : qs = report_row_inconsistency (operation,
2522 : rowid,
2523 : "recoup signature invalid");
2524 0 : if (0 > qs)
2525 : {
2526 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2527 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2528 0 : return GNUNET_SYSERR;
2529 : }
2530 : }
2531 4 : if (GNUNET_OK !=
2532 4 : TALER_test_coin_valid (coin,
2533 : denom_pub))
2534 : {
2535 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2536 : .problem_row_id = rowid,
2537 : .operation = (char *) operation,
2538 : .loss = *amount,
2539 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2540 : };
2541 :
2542 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2543 : "Failed to verify coin signature in row %llu\n",
2544 : (unsigned long long) rowid);
2545 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2546 : TALER_ARL_adb,
2547 : &bsl);
2548 :
2549 0 : if (0 > qs)
2550 : {
2551 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2552 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2553 0 : return GNUNET_SYSERR;
2554 : }
2555 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2556 : &TALER_ARL_USE_AB (coin_irregular_loss),
2557 : amount);
2558 : }
2559 4 : qs = TALER_ARL_get_denomination_info_by_hash (&coin->denom_pub_hash,
2560 : &issue);
2561 4 : if (0 > qs)
2562 : {
2563 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2564 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2565 0 : return GNUNET_SYSERR;
2566 : }
2567 4 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2568 : {
2569 0 : qs = report_row_inconsistency (operation,
2570 : rowid,
2571 : "denomination key not found");
2572 0 : if (0 > qs)
2573 : {
2574 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2575 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2576 0 : return GNUNET_SYSERR;
2577 : }
2578 0 : return GNUNET_OK;
2579 : }
2580 4 : qs = check_known_coin (operation,
2581 : issue,
2582 : rowid,
2583 : &coin->coin_pub,
2584 : denom_pub,
2585 : amount);
2586 4 : if (0 > qs)
2587 : {
2588 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2589 0 : cc->qs = qs;
2590 0 : return GNUNET_SYSERR;
2591 : }
2592 4 : ds = get_denomination_summary (cc,
2593 : issue);
2594 4 : if (NULL == ds)
2595 : {
2596 0 : qs = report_row_inconsistency ("recoup",
2597 : rowid,
2598 : "denomination key for recouped coin unknown to auditor");
2599 0 : if (0 > qs)
2600 : {
2601 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2602 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2603 0 : return GNUNET_SYSERR;
2604 : }
2605 : }
2606 : else
2607 : {
2608 4 : if (! ds->was_revoked)
2609 : {
2610 0 : struct TALER_AUDITORDB_BadSigLosses bsldnr = {
2611 : .problem_row_id = rowid,
2612 : .operation = (char *) operation,
2613 : .loss = *amount,
2614 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2615 : };
2616 :
2617 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2618 : "Recoup allowed on non-revoked denomination in row %llu\n",
2619 : (unsigned long long) rowid);
2620 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2621 : TALER_ARL_adb,
2622 : &bsldnr);
2623 :
2624 0 : if (qs < 0)
2625 : {
2626 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2627 0 : cc->qs = qs;
2628 0 : return GNUNET_SYSERR;
2629 : }
2630 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2631 : &TALER_ARL_USE_AB (coin_irregular_loss),
2632 : amount);
2633 : }
2634 4 : TALER_ARL_amount_add (&ds->dcd.recoup_loss,
2635 : &ds->dcd.recoup_loss,
2636 : amount);
2637 4 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_recoup_loss),
2638 : &TALER_ARL_USE_AB (total_recoup_loss),
2639 : amount);
2640 : }
2641 4 : return GNUNET_OK;
2642 : }
2643 :
2644 :
2645 : /**
2646 : * Function called about recoups the exchange has to perform.
2647 : *
2648 : * @param cc a `struct CoinContext *`
2649 : * @param rowid row identifier used to uniquely identify the recoup operation
2650 : * @param timestamp when did we receive the recoup request
2651 : * @param amount how much should be added back to the reserve
2652 : * @param reserve_pub public key of the reserve
2653 : * @param coin public information about the coin
2654 : * @param denom_pub denomination public key of @a coin
2655 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
2656 : * @param coin_blind blinding factor used to blind the coin
2657 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2658 : */
2659 : static enum GNUNET_GenericReturnValue
2660 4 : recoup_cb (struct CoinContext *cc,
2661 : uint64_t rowid,
2662 : struct GNUNET_TIME_Timestamp timestamp,
2663 : const struct TALER_Amount *amount,
2664 : const struct TALER_ReservePublicKeyP *reserve_pub,
2665 : const struct TALER_CoinPublicInfo *coin,
2666 : const struct TALER_DenominationPublicKey *denom_pub,
2667 : const struct TALER_CoinSpendSignatureP *coin_sig,
2668 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
2669 : {
2670 : enum GNUNET_DB_QueryStatus qs;
2671 :
2672 4 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_serial_id)); /* should be monotonically increasing */
2673 4 : TALER_ARL_USE_PP (coins_recoup_serial_id) = rowid + 1;
2674 : (void) timestamp;
2675 : (void) reserve_pub;
2676 4 : if (GNUNET_OK !=
2677 4 : TALER_wallet_recoup_verify (&coin->denom_pub_hash,
2678 : coin_blind,
2679 : &coin->coin_pub,
2680 : coin_sig))
2681 : {
2682 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2683 : .problem_row_id = rowid,
2684 : .operation = (char *) "recoup",
2685 : .loss = *amount,
2686 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2687 : };
2688 :
2689 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2690 : "Failed to verify recoup signature in row %llu\n",
2691 : (unsigned long long) rowid);
2692 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2693 : TALER_ARL_adb,
2694 : &bsl);
2695 0 : if (qs < 0)
2696 : {
2697 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2698 0 : cc->qs = qs;
2699 0 : return GNUNET_SYSERR;
2700 : }
2701 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2702 : &TALER_ARL_USE_AB (coin_irregular_loss),
2703 : amount);
2704 0 : return GNUNET_OK;
2705 : }
2706 4 : return check_recoup (cc,
2707 : "recoup",
2708 : rowid,
2709 : amount,
2710 : coin,
2711 : denom_pub,
2712 : coin_sig,
2713 : coin_blind);
2714 : }
2715 :
2716 :
2717 : /**
2718 : * Function called about recoups on refreshed coins the exchange had to
2719 : * perform. Updates the denomination balance(s). Does not change the
2720 : * coin balances, as those are already updated when we check the coin
2721 : * history.
2722 : *
2723 : * @param cc a `struct CoinContext *`
2724 : * @param rowid row identifier used to uniquely identify the recoup operation
2725 : * @param timestamp when did we receive the recoup request
2726 : * @param amount how much should be added back to the old coin
2727 : * @param old_coin_pub original coin that was refreshed to create @a coin
2728 : * @param old_denom_pub_hash hash of the public key of @a old_coin_pub
2729 : * @param coin public information about the fresh coin
2730 : * @param denom_pub denomination public key of @a coin
2731 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
2732 : * @param coin_blind blinding factor used to blind the coin
2733 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2734 : */
2735 : static enum GNUNET_GenericReturnValue
2736 0 : recoup_refresh_cb (struct CoinContext *cc,
2737 : uint64_t rowid,
2738 : struct GNUNET_TIME_Timestamp timestamp,
2739 : const struct TALER_Amount *amount,
2740 : const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
2741 : const struct TALER_DenominationHashP *old_denom_pub_hash,
2742 : const struct TALER_CoinPublicInfo *coin,
2743 : const struct TALER_DenominationPublicKey *denom_pub,
2744 : const struct TALER_CoinSpendSignatureP *coin_sig,
2745 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
2746 : {
2747 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2748 : enum GNUNET_DB_QueryStatus qs;
2749 :
2750 : (void) timestamp;
2751 : (void) old_coin_pub;
2752 0 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_refresh_serial_id)); /* should be monotonically increasing */
2753 0 : TALER_ARL_USE_PP (coins_recoup_refresh_serial_id) = rowid + 1;
2754 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2755 : "Recoup-refresh amount is %s\n",
2756 : TALER_amount2s (amount));
2757 :
2758 : /* Update old coin's denomination balance summary */
2759 0 : qs = TALER_ARL_get_denomination_info_by_hash (old_denom_pub_hash,
2760 : &issue);
2761 0 : if (qs < 0)
2762 : {
2763 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2764 0 : cc->qs = qs;
2765 0 : return GNUNET_SYSERR;
2766 : }
2767 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2768 : {
2769 0 : qs = report_row_inconsistency ("refresh-recoup",
2770 : rowid,
2771 : "denomination key of old coin not found");
2772 0 : if (qs < 0)
2773 : {
2774 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2775 0 : cc->qs = qs;
2776 0 : return GNUNET_SYSERR;
2777 : }
2778 : /* nothing to update without the denomination */
2779 0 : return GNUNET_OK;
2780 : }
2781 :
2782 : {
2783 : struct DenominationSummary *dso;
2784 :
2785 0 : dso = get_denomination_summary (cc,
2786 : issue);
2787 0 : if (NULL == dso)
2788 : {
2789 0 : qs = report_row_inconsistency ("refresh_reveal",
2790 : rowid,
2791 : "denomination key for old coin unknown to auditor");
2792 0 : if (qs < 0)
2793 : {
2794 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2795 0 : cc->qs = qs;
2796 0 : return GNUNET_SYSERR;
2797 : }
2798 : }
2799 : else
2800 : {
2801 0 : TALER_ARL_amount_add (&dso->dcd.denom_balance,
2802 : &dso->dcd.denom_balance,
2803 : amount);
2804 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2805 : "New balance of denomination `%s' after refresh-recoup is %s\n",
2806 : GNUNET_h2s (&issue->denom_hash.hash),
2807 : TALER_amount2s (&dso->dcd.denom_balance));
2808 : }
2809 : }
2810 :
2811 0 : if (GNUNET_OK !=
2812 0 : TALER_wallet_recoup_refresh_verify (&coin->denom_pub_hash,
2813 : coin_blind,
2814 : &coin->coin_pub,
2815 : coin_sig))
2816 : {
2817 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2818 : .problem_row_id = rowid,
2819 : .operation = (char *) "recoup-refresh",
2820 : .loss = *amount,
2821 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2822 : };
2823 :
2824 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2825 : "Failed to verify recoup-refresh signature in row %llu\n",
2826 : (unsigned long long) rowid);
2827 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2828 : TALER_ARL_adb,
2829 : &bsl);
2830 0 : if (qs < 0)
2831 : {
2832 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2833 0 : cc->qs = qs;
2834 0 : return GNUNET_SYSERR;
2835 : }
2836 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2837 : &TALER_ARL_USE_AB (coin_irregular_loss),
2838 : amount);
2839 0 : return GNUNET_OK;
2840 : }
2841 0 : return check_recoup (cc,
2842 : "recoup-refresh",
2843 : rowid,
2844 : amount,
2845 : coin,
2846 : denom_pub,
2847 : coin_sig,
2848 : coin_blind);
2849 : }
2850 :
2851 :
2852 : /**
2853 : * Function called with the results of iterate_denomination_info(),
2854 : * or directly (!). Used to check that we correctly signed the
2855 : * denomination and to warn if there are denominations not approved
2856 : * by this auditor.
2857 : *
2858 : * @param iqs closure, pointer to `enum GNUNET_DB_QueryStatus`
2859 : * @param denom_serial row ID of the denominations table of the exchange DB
2860 : * @param denom_pub public key, sometimes NULL (!)
2861 : * @param issue issuing information with value, fees and other info about the denomination.
2862 : */
2863 : static void
2864 300 : check_denomination (
2865 : enum GNUNET_DB_QueryStatus *iqs,
2866 : uint64_t denom_serial,
2867 : const struct TALER_DenominationPublicKey *denom_pub,
2868 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
2869 : {
2870 : enum GNUNET_DB_QueryStatus qs;
2871 : struct TALER_AuditorSignatureP auditor_sig;
2872 :
2873 : (void) denom_pub;
2874 300 : qs = TALER_EXCHANGEDB_get_auditor_denom_sig (TALER_ARL_edb,
2875 : &issue->denom_hash,
2876 : &TALER_ARL_auditor_pub,
2877 : &auditor_sig);
2878 300 : if (0 > qs)
2879 : {
2880 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2881 0 : *iqs = qs;
2882 0 : return;
2883 : }
2884 300 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2885 : {
2886 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2887 : "Encountered denomination `%s' (%s) valid from %s (%llu-%llu) that this auditor is not auditing!\n",
2888 : GNUNET_h2s (&issue->denom_hash.hash),
2889 : TALER_amount2s (&issue->value),
2890 : GNUNET_TIME_timestamp2s (issue->start),
2891 : (unsigned long long) issue->start.abs_time.abs_value_us,
2892 : (unsigned long long) issue->expire_legal.abs_time.abs_value_us);
2893 0 : return; /* skip! */
2894 : }
2895 300 : if (GNUNET_OK !=
2896 300 : TALER_auditor_denom_validity_verify (
2897 : TALER_ARL_auditor_url,
2898 : &issue->denom_hash,
2899 : &TALER_ARL_master_pub,
2900 : issue->start,
2901 : issue->expire_withdraw,
2902 : issue->expire_deposit,
2903 : issue->expire_legal,
2904 : &issue->value,
2905 : &issue->fees,
2906 : &TALER_ARL_auditor_pub,
2907 : &auditor_sig))
2908 : {
2909 0 : struct TALER_AUDITORDB_DenominationsWithoutSigs dws = {
2910 : .denompub_h = issue->denom_hash,
2911 : .start_time = issue->start.abs_time,
2912 : .end_time = issue->expire_legal.abs_time,
2913 : .value = issue->value
2914 : };
2915 :
2916 0 : qs = TALER_AUDITORDB_insert_denominations_without_sigs (
2917 : TALER_ARL_adb,
2918 : &dws);
2919 :
2920 0 : if (qs < 0)
2921 : {
2922 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2923 0 : *iqs = qs;
2924 0 : return;
2925 : }
2926 : }
2927 300 : *iqs = qs;
2928 : }
2929 :
2930 :
2931 : /**
2932 : * Function called with details about purse deposits that have been made, with
2933 : * the goal of auditing the deposit's execution.
2934 : *
2935 : * @param cc closure
2936 : * @param rowid unique serial ID for the deposit in our DB
2937 : * @param deposit deposit details
2938 : * @param reserve_pub which reserve is the purse merged into, NULL if unknown
2939 : * @param flags purse flags
2940 : * @param auditor_balance purse balance (according to the
2941 : * auditor during auditing)
2942 : * @param purse_total target amount the purse should reach
2943 : * @param denom_pub denomination public key of @a coin_pub
2944 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2945 : */
2946 : static enum GNUNET_GenericReturnValue
2947 0 : purse_deposit_cb (
2948 : struct CoinContext *cc,
2949 : uint64_t rowid,
2950 : const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
2951 : const struct TALER_ReservePublicKeyP *reserve_pub,
2952 : enum TALER_WalletAccountMergeFlags flags,
2953 : const struct TALER_Amount *auditor_balance,
2954 : const struct TALER_Amount *purse_total,
2955 : const struct TALER_DenominationPublicKey *denom_pub)
2956 : {
2957 : enum GNUNET_DB_QueryStatus qs;
2958 : struct TALER_DenominationHashP dh;
2959 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2960 : struct DenominationSummary *ds;
2961 :
2962 : (void) flags;
2963 : (void) auditor_balance;
2964 : (void) purse_total;
2965 : (void) reserve_pub;
2966 0 : GNUNET_assert (rowid >=
2967 : TALER_ARL_USE_PP (coins_purse_deposits_serial_id));
2968 0 : TALER_ARL_USE_PP (coins_purse_deposits_serial_id) = rowid + 1;
2969 0 : qs = TALER_ARL_get_denomination_info (denom_pub,
2970 : &issue,
2971 : &dh);
2972 0 : if (0 > qs)
2973 : {
2974 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2975 0 : cc->qs = qs;
2976 0 : return GNUNET_SYSERR;
2977 : }
2978 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2979 : {
2980 0 : qs = report_row_inconsistency ("purse-deposits",
2981 : rowid,
2982 : "denomination key not found");
2983 0 : if (0 > qs)
2984 : {
2985 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2986 0 : cc->qs = qs;
2987 0 : return GNUNET_SYSERR;
2988 : }
2989 0 : return GNUNET_OK;
2990 : }
2991 0 : qs = check_known_coin ("purse-deposit",
2992 : issue,
2993 : rowid,
2994 : &deposit->coin_pub,
2995 : denom_pub,
2996 : &deposit->amount);
2997 0 : if (0 > qs)
2998 : {
2999 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3000 0 : cc->qs = qs;
3001 0 : return GNUNET_SYSERR;
3002 : }
3003 :
3004 0 : if (GNUNET_OK !=
3005 0 : TALER_wallet_purse_deposit_verify (
3006 0 : NULL != deposit->exchange_base_url
3007 : ? deposit->exchange_base_url
3008 : : TALER_ARL_exchange_url,
3009 : &deposit->purse_pub,
3010 : &deposit->amount,
3011 : &dh,
3012 : &deposit->h_age_commitment,
3013 : &deposit->coin_pub,
3014 : &deposit->coin_sig))
3015 : {
3016 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
3017 : .problem_row_id = rowid,
3018 : .operation = (char *) "purse-deposit",
3019 : .loss = deposit->amount,
3020 : .operation_specific_pub = deposit->coin_pub.eddsa_pub
3021 : };
3022 :
3023 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3024 : "Failed to verify purse deposit signature in row %llu\n",
3025 : (unsigned long long) rowid);
3026 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
3027 : TALER_ARL_adb,
3028 : &bsl);
3029 0 : if (0 > qs)
3030 : {
3031 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3032 0 : cc->qs = qs;
3033 0 : return GNUNET_SYSERR;
3034 : }
3035 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
3036 : &TALER_ARL_USE_AB (coin_irregular_loss),
3037 : &deposit->amount);
3038 0 : return GNUNET_OK;
3039 : }
3040 :
3041 : /* update coin's denomination balance */
3042 0 : ds = get_denomination_summary (cc,
3043 : issue);
3044 0 : if (NULL == ds)
3045 : {
3046 0 : qs = report_row_inconsistency ("purse-deposit",
3047 : rowid,
3048 : "denomination key for purse-deposited coin unknown to auditor");
3049 0 : if (0 > qs)
3050 : {
3051 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3052 0 : cc->qs = qs;
3053 0 : return GNUNET_SYSERR;
3054 : }
3055 : }
3056 : else
3057 : {
3058 0 : qs = reduce_denom_balance (ds,
3059 : rowid,
3060 : &deposit->amount);
3061 0 : if (0 > qs)
3062 : {
3063 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3064 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
3065 0 : return GNUNET_SYSERR;
3066 : }
3067 : }
3068 :
3069 : /* update global deposit fees */
3070 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue),
3071 : &TALER_ARL_USE_AB (coin_deposit_fee_revenue),
3072 : &issue->fees.deposit);
3073 0 : return GNUNET_OK;
3074 : }
3075 :
3076 :
3077 : /**
3078 : * Analyze the exchange's processing of coins.
3079 : *
3080 : * @param cls closure
3081 : * @return transaction status code
3082 : */
3083 : static enum GNUNET_DB_QueryStatus
3084 10 : analyze_coins (void *cls)
3085 : {
3086 : struct CoinContext cc;
3087 : enum GNUNET_DB_QueryStatus qs;
3088 : enum GNUNET_DB_QueryStatus iqs;
3089 :
3090 : (void) cls;
3091 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3092 : "Checking denominations...\n");
3093 10 : iqs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
3094 10 : qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
3095 : &check_denomination,
3096 : &iqs);
3097 10 : if (0 > qs)
3098 : {
3099 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3100 0 : return qs;
3101 : }
3102 10 : if (0 > iqs)
3103 : {
3104 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == iqs);
3105 0 : return iqs;
3106 : }
3107 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3108 : "Analyzing coins\n");
3109 10 : qs = TALER_AUDITORDB_get_auditor_progress (
3110 : TALER_ARL_adb,
3111 : TALER_ARL_GET_PP (coins_withdraw_serial_id),
3112 : TALER_ARL_GET_PP (coins_deposit_serial_id),
3113 : TALER_ARL_GET_PP (coins_melt_serial_id),
3114 : TALER_ARL_GET_PP (coins_refund_serial_id),
3115 : TALER_ARL_GET_PP (coins_recoup_serial_id),
3116 : TALER_ARL_GET_PP (coins_recoup_refresh_serial_id),
3117 : TALER_ARL_GET_PP (coins_purse_deposits_serial_id),
3118 : TALER_ARL_GET_PP (coins_purse_refunds_serial_id),
3119 : NULL);
3120 10 : if (0 > qs)
3121 : {
3122 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3123 0 : return qs;
3124 : }
3125 10 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
3126 : {
3127 0 : GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
3128 : "First analysis using this auditor, starting from scratch\n");
3129 : }
3130 : else
3131 : {
3132 10 : GNUNET_log (
3133 : GNUNET_ERROR_TYPE_INFO,
3134 : "Resuming coin audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
3135 : (unsigned long long) TALER_ARL_USE_PP (
3136 : coins_withdraw_serial_id),
3137 : (unsigned long long) TALER_ARL_USE_PP (
3138 : coins_deposit_serial_id),
3139 : (unsigned long long) TALER_ARL_USE_PP (
3140 : coins_melt_serial_id),
3141 : (unsigned long long) TALER_ARL_USE_PP (
3142 : coins_refund_serial_id),
3143 : (unsigned long long) TALER_ARL_USE_PP (
3144 : coins_recoup_serial_id),
3145 : (unsigned long long) TALER_ARL_USE_PP (
3146 : coins_recoup_refresh_serial_id),
3147 : (unsigned long long) TALER_ARL_USE_PP (
3148 : coins_purse_deposits_serial_id),
3149 : (unsigned long long) TALER_ARL_USE_PP (
3150 : coins_purse_refunds_serial_id));
3151 : }
3152 :
3153 : /* setup 'cc' */
3154 10 : cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3155 10 : cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
3156 : GNUNET_NO);
3157 10 : qs = TALER_AUDITORDB_get_balance (
3158 : TALER_ARL_adb,
3159 : TALER_ARL_GET_AB (coin_balance_risk),
3160 : TALER_ARL_GET_AB (total_escrowed),
3161 : TALER_ARL_GET_AB (coin_irregular_loss),
3162 : TALER_ARL_GET_AB (coin_melt_fee_revenue),
3163 : TALER_ARL_GET_AB (coin_deposit_fee_revenue),
3164 : TALER_ARL_GET_AB (coin_deposit_fee_loss),
3165 : TALER_ARL_GET_AB (coin_refund_fee_revenue),
3166 : TALER_ARL_GET_AB (total_recoup_loss),
3167 : TALER_ARL_GET_AB (coins_total_arithmetic_delta_plus),
3168 : TALER_ARL_GET_AB (coins_total_arithmetic_delta_minus),
3169 : TALER_ARL_GET_AB (coins_reported_emergency_risk_by_count),
3170 : TALER_ARL_GET_AB (coins_reported_emergency_risk_by_amount),
3171 : TALER_ARL_GET_AB (coins_emergencies_loss),
3172 : TALER_ARL_GET_AB (coins_emergencies_loss_by_count),
3173 : NULL);
3174 10 : if (0 > qs)
3175 : {
3176 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3177 0 : goto cleanup;
3178 : }
3179 : /* process withdrawals */
3180 10 : if (0 >
3181 10 : (qs = TALER_EXCHANGEDB_iterate_withdrawals_above_serial_id (
3182 : TALER_ARL_edb,
3183 : TALER_ARL_USE_PP (coins_withdraw_serial_id),
3184 : &withdraw_cb,
3185 : &cc)))
3186 : {
3187 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3188 0 : goto cleanup;
3189 : }
3190 10 : if (0 > cc.qs)
3191 : {
3192 0 : qs = cc.qs;
3193 0 : goto cleanup;
3194 : }
3195 : /* Process all operations that credit a denomination balance (refunds,
3196 : recoup-refresh, purse refunds) before those that debit it (melts,
3197 : deposits, purse deposits): within a pass the tables are processed
3198 : one after the other and not in time order, so a coin that spends
3199 : a credit it received in the same pass would otherwise make
3200 : reduce_denom_balance() see a balance that is too low and report a
3201 : false emergency. */
3202 : /* process refunds */
3203 10 : if (0 >
3204 10 : (qs = TALER_EXCHANGEDB_iterate_refunds_above_serial_id (
3205 : TALER_ARL_edb,
3206 : TALER_ARL_USE_PP (coins_refund_serial_id),
3207 : &refund_cb,
3208 : &cc)))
3209 : {
3210 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3211 0 : goto cleanup;
3212 : }
3213 10 : if (0 > cc.qs)
3214 : {
3215 0 : qs = cc.qs;
3216 0 : goto cleanup;
3217 : }
3218 : /* process recoups of refreshed coins (credit the old coin) */
3219 10 : if (0 >
3220 10 : (qs = TALER_EXCHANGEDB_iterate_recoup_refreshes_above_serial_id (
3221 : TALER_ARL_edb,
3222 : TALER_ARL_USE_PP (coins_recoup_refresh_serial_id),
3223 : &recoup_refresh_cb,
3224 : &cc)))
3225 : {
3226 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3227 0 : goto cleanup;
3228 : }
3229 10 : if (0 > cc.qs)
3230 : {
3231 0 : qs = cc.qs;
3232 0 : goto cleanup;
3233 : }
3234 : /* process purse_refunds */
3235 10 : if (0 >
3236 10 : (qs = TALER_EXCHANGEDB_iterate_purse_decisions_above_serial_id (
3237 : TALER_ARL_edb,
3238 : TALER_ARL_USE_PP (coins_purse_refunds_serial_id),
3239 : true, /* only go for refunds! */
3240 : &purse_refund_cb,
3241 : &cc)))
3242 : {
3243 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3244 0 : goto cleanup;
3245 : }
3246 10 : if (0 > cc.qs)
3247 : {
3248 0 : qs = cc.qs;
3249 0 : goto cleanup;
3250 : }
3251 : /* process refreshes */
3252 10 : if (0 >
3253 10 : (qs = TALER_EXCHANGEDB_iterate_refreshes_above_serial_id (
3254 : TALER_ARL_edb,
3255 : TALER_ARL_USE_PP (coins_melt_serial_id),
3256 : &refresh_session_cb,
3257 : &cc)))
3258 : {
3259 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3260 0 : goto cleanup;
3261 : }
3262 10 : if (0 > cc.qs)
3263 : {
3264 0 : qs = cc.qs;
3265 0 : goto cleanup;
3266 : }
3267 : /* process deposits */
3268 10 : if (0 >
3269 10 : (qs = TALER_EXCHANGEDB_iterate_coin_deposits_above_serial_id (
3270 : TALER_ARL_edb,
3271 : TALER_ARL_USE_PP (coins_deposit_serial_id),
3272 : &deposit_cb,
3273 : &cc)))
3274 : {
3275 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3276 0 : goto cleanup;
3277 : }
3278 10 : if (0 > cc.qs)
3279 : {
3280 0 : qs = cc.qs;
3281 0 : goto cleanup;
3282 : }
3283 : /* process purse_deposits */
3284 10 : if (0 >
3285 10 : (qs = TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
3286 : TALER_ARL_edb,
3287 : TALER_ARL_USE_PP (coins_purse_deposits_serial_id),
3288 : &purse_deposit_cb,
3289 : &cc)))
3290 : {
3291 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3292 0 : goto cleanup;
3293 : }
3294 10 : if (0 > cc.qs)
3295 : {
3296 0 : qs = cc.qs;
3297 0 : goto cleanup;
3298 : }
3299 10 : if (0 >
3300 10 : (qs = TALER_EXCHANGEDB_iterate_recoups_above_serial_id (
3301 : TALER_ARL_edb,
3302 : TALER_ARL_USE_PP (coins_recoup_serial_id),
3303 : &recoup_cb,
3304 : &cc)))
3305 : {
3306 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3307 0 : goto cleanup;
3308 : }
3309 10 : if (0 > cc.qs)
3310 : {
3311 0 : qs = cc.qs;
3312 0 : goto cleanup;
3313 : }
3314 : /* sync 'cc' back to disk */
3315 10 : cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3316 10 : GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
3317 : &sync_denomination,
3318 : &cc);
3319 :
3320 10 : if (0 > cc.qs)
3321 : {
3322 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs);
3323 0 : qs = cc.qs;
3324 0 : goto cleanup;
3325 : }
3326 :
3327 : /* settle the denominations that nothing touched in this pass */
3328 10 : qs = sweep_denominations (&cc);
3329 10 : if (0 > qs)
3330 : {
3331 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3332 0 : goto cleanup;
3333 : }
3334 :
3335 10 : qs = TALER_AUDITORDB_insert_balance (
3336 : TALER_ARL_adb,
3337 : TALER_ARL_SET_AB (coin_balance_risk),
3338 : TALER_ARL_SET_AB (total_escrowed),
3339 : TALER_ARL_SET_AB (coin_irregular_loss),
3340 : TALER_ARL_SET_AB (coin_melt_fee_revenue),
3341 : TALER_ARL_SET_AB (coin_deposit_fee_revenue),
3342 : TALER_ARL_SET_AB (coin_deposit_fee_loss),
3343 : TALER_ARL_SET_AB (coin_refund_fee_revenue),
3344 : TALER_ARL_SET_AB (total_recoup_loss),
3345 : TALER_ARL_SET_AB (coins_total_arithmetic_delta_plus),
3346 : TALER_ARL_SET_AB (coins_total_arithmetic_delta_minus),
3347 : TALER_ARL_SET_AB (coins_reported_emergency_risk_by_count),
3348 : TALER_ARL_SET_AB (coins_reported_emergency_risk_by_amount),
3349 : TALER_ARL_SET_AB (coins_emergencies_loss),
3350 : TALER_ARL_SET_AB (coins_emergencies_loss_by_count),
3351 : NULL);
3352 10 : if (0 > qs)
3353 : {
3354 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3355 : "Failed to update auditor DB, not recording progress\n");
3356 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3357 0 : goto cleanup;
3358 : }
3359 :
3360 10 : qs = TALER_AUDITORDB_insert_auditor_progress (
3361 : TALER_ARL_adb,
3362 : TALER_ARL_SET_PP (coins_withdraw_serial_id),
3363 : TALER_ARL_SET_PP (coins_deposit_serial_id),
3364 : TALER_ARL_SET_PP (coins_melt_serial_id),
3365 : TALER_ARL_SET_PP (coins_refund_serial_id),
3366 : TALER_ARL_SET_PP (coins_recoup_serial_id),
3367 : TALER_ARL_SET_PP (coins_recoup_refresh_serial_id),
3368 : TALER_ARL_SET_PP (coins_purse_deposits_serial_id),
3369 : TALER_ARL_SET_PP (coins_purse_refunds_serial_id),
3370 : NULL);
3371 10 : if (0 > qs)
3372 : {
3373 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3374 : "Failed to update auditor DB, not recording progress\n");
3375 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3376 0 : goto cleanup;
3377 : }
3378 :
3379 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3380 : "Concluded coin audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
3381 : (unsigned long long) TALER_ARL_USE_PP (coins_withdraw_serial_id),
3382 : (unsigned long long) TALER_ARL_USE_PP (coins_deposit_serial_id),
3383 : (unsigned long long) TALER_ARL_USE_PP (coins_melt_serial_id),
3384 : (unsigned long long) TALER_ARL_USE_PP (coins_refund_serial_id),
3385 : (unsigned long long) TALER_ARL_USE_PP (coins_recoup_serial_id),
3386 : (unsigned long long) TALER_ARL_USE_PP (
3387 : coins_recoup_refresh_serial_id),
3388 : (unsigned long long) TALER_ARL_USE_PP (
3389 : coins_purse_deposits_serial_id),
3390 : (unsigned long long) TALER_ARL_USE_PP (
3391 : coins_purse_refunds_serial_id));
3392 10 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3393 10 : cleanup:
3394 10 : flush_coin_histories ();
3395 10 : GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
3396 : &cleanup_denomination,
3397 : &cc);
3398 10 : GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
3399 10 : return qs;
3400 : }
3401 :
3402 :
3403 : /**
3404 : * Function called on events received from Postgres.
3405 : *
3406 : * @param cls closure, NULL
3407 : * @param extra additional event data provided
3408 : * @param extra_size number of bytes in @a extra
3409 : */
3410 : static void
3411 0 : db_notify (void *cls,
3412 : const void *extra,
3413 : size_t extra_size)
3414 : {
3415 : (void) cls;
3416 : (void) extra;
3417 : (void) extra_size;
3418 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3419 : "Received notification to wake coins helper\n");
3420 0 : if (GNUNET_OK !=
3421 0 : TALER_ARL_setup_sessions_and_run (&analyze_coins,
3422 : NULL))
3423 : {
3424 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3425 : "Audit failed\n");
3426 0 : GNUNET_SCHEDULER_shutdown ();
3427 0 : global_ret = EXIT_FAILURE;
3428 0 : return;
3429 : }
3430 : }
3431 :
3432 :
3433 : /**
3434 : * Function called on shutdown.
3435 : */
3436 : static void
3437 10 : do_shutdown (void *cls)
3438 : {
3439 : (void) cls;
3440 10 : if (NULL != eh)
3441 : {
3442 4 : TALER_AUDITORDB_event_listen_cancel (eh);
3443 4 : eh = NULL;
3444 : }
3445 10 : TALER_ARL_done ();
3446 10 : }
3447 :
3448 :
3449 : /**
3450 : * Main function that will be run.
3451 : *
3452 : * @param cls closure
3453 : * @param args remaining command-line arguments
3454 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3455 : * @param c configuration
3456 : */
3457 : static void
3458 10 : run (void *cls,
3459 : char *const *args,
3460 : const char *cfgfile,
3461 : const struct GNUNET_CONFIGURATION_Handle *c)
3462 : {
3463 : (void) cls;
3464 : (void) args;
3465 : (void) cfgfile;
3466 10 : cfg = c;
3467 10 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
3468 : NULL);
3469 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3470 : "Launching coins auditor\n");
3471 10 : if (EXIT_SUCCESS !=
3472 10 : (global_ret = TALER_ARL_init (c)))
3473 : {
3474 0 : return;
3475 : }
3476 10 : if (test_mode != 1)
3477 : {
3478 4 : struct GNUNET_DB_EventHeaderP es = {
3479 4 : .size = htons (sizeof (es)),
3480 4 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_COINS)
3481 : };
3482 :
3483 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3484 : "Running helper indefinitely\n");
3485 4 : eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
3486 : &es,
3487 4 : GNUNET_TIME_UNIT_FOREVER_REL,
3488 : &db_notify,
3489 : NULL);
3490 : }
3491 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3492 : "Starting audit\n");
3493 10 : if (GNUNET_OK !=
3494 10 : TALER_ARL_setup_sessions_and_run (&analyze_coins,
3495 : NULL))
3496 : {
3497 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3498 : "Audit failed\n");
3499 0 : GNUNET_SCHEDULER_shutdown ();
3500 0 : global_ret = EXIT_FAILURE;
3501 0 : return;
3502 : }
3503 : }
3504 :
3505 :
3506 : /**
3507 : * The main function to audit operations on coins.
3508 : *
3509 : * @param argc number of arguments from the command line
3510 : * @param argv command line arguments
3511 : * @return 0 ok, 1 on error
3512 : */
3513 : int
3514 10 : main (int argc,
3515 : char *const *argv)
3516 : {
3517 10 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
3518 10 : GNUNET_GETOPT_option_flag ('i',
3519 : "internal",
3520 : "perform checks only applicable for exchange-internal audits",
3521 : &internal_checks),
3522 10 : GNUNET_GETOPT_option_flag ('t',
3523 : "test",
3524 : "run in test mode and exit when idle",
3525 : &test_mode),
3526 10 : GNUNET_GETOPT_option_timetravel ('T',
3527 : "timetravel"),
3528 : GNUNET_GETOPT_OPTION_END
3529 : };
3530 : enum GNUNET_GenericReturnValue ret;
3531 :
3532 10 : ret = GNUNET_PROGRAM_run (
3533 : TALER_AUDITOR_project_data (),
3534 : argc,
3535 : argv,
3536 : "taler-helper-auditor-coins",
3537 : gettext_noop ("Audit Taler coin processing"),
3538 : options,
3539 : &run,
3540 : NULL);
3541 10 : if (GNUNET_SYSERR == ret)
3542 0 : return EXIT_NOTCONFIGURED;
3543 10 : if (GNUNET_NO == ret)
3544 0 : return EXIT_SUCCESS;
3545 10 : return global_ret;
3546 : }
3547 :
3548 :
3549 : /* end of taler-helper-auditor-coins.c */
|