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 : .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 : /* We cannot account for this planchet, but the other planchets
1476 : of the batch must still be processed. */
1477 0 : continue;
1478 : }
1479 36 : ds = get_denomination_summary (cc,
1480 : issue);
1481 36 : if (NULL == ds)
1482 : {
1483 : /* cc->qs is set by #get_denomination_summary() */
1484 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc->qs);
1485 0 : return GNUNET_SYSERR;
1486 : }
1487 36 : ds->dcd.num_issued++;
1488 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1489 : "Issued coin in denomination `%s' of total value %s\n",
1490 : GNUNET_h2s (&issue->denom_hash.hash),
1491 : TALER_amount2s (&issue->value));
1492 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1493 : "New balance of denomination `%s' after withdraw is %s\n",
1494 : GNUNET_h2s (&issue->denom_hash.hash),
1495 : TALER_amount2s (&ds->dcd.denom_balance));
1496 36 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
1497 : &TALER_ARL_USE_AB (total_escrowed),
1498 : &issue->value);
1499 36 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
1500 : &TALER_ARL_USE_AB (coin_balance_risk),
1501 : &issue->value);
1502 36 : TALER_ARL_amount_add (&ds->dcd.denom_balance,
1503 : &ds->dcd.denom_balance,
1504 : &issue->value);
1505 36 : TALER_ARL_amount_add (&ds->dcd.denom_risk,
1506 : &ds->dcd.denom_risk,
1507 : &issue->value);
1508 : }
1509 36 : return GNUNET_OK;
1510 : }
1511 :
1512 :
1513 : /**
1514 : * Check that the @a coin_pub is a known coin with a proper
1515 : * signature for denominatinon @a denom_pub. If not, report
1516 : * a loss of @a loss_potential.
1517 : *
1518 : * @param operation which operation is this about
1519 : * @param issue denomination key information about the coin
1520 : * @param rowid which row is this operation in
1521 : * @param coin_pub public key of a coin
1522 : * @param denom_pub expected denomination of the coin
1523 : * @param loss_potential how big could the loss be if the coin is
1524 : * not properly signed
1525 : * @return database transaction status; on success
1526 : * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, or
1527 : * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the coin is not known to
1528 : * the exchange at all (which was reported to the auditor database)
1529 : */
1530 : static enum GNUNET_DB_QueryStatus
1531 40 : check_known_coin (
1532 : const char *operation,
1533 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
1534 : uint64_t rowid,
1535 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1536 : const struct TALER_DenominationPublicKey *denom_pub,
1537 : const struct TALER_Amount *loss_potential)
1538 : {
1539 : struct TALER_CoinPublicInfo ci;
1540 : enum GNUNET_DB_QueryStatus qs;
1541 :
1542 40 : if (NULL == get_cached_history (coin_pub))
1543 : {
1544 34 : qs = check_coin_history (coin_pub,
1545 : rowid,
1546 : operation,
1547 : &issue->value);
1548 34 : if (0 > qs)
1549 : {
1550 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1551 0 : return qs;
1552 : }
1553 34 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
1554 : }
1555 :
1556 40 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1557 : "Checking denomination signature on %s\n",
1558 : TALER_B2S (coin_pub));
1559 40 : qs = TALER_EXCHANGEDB_get_known_coin (TALER_ARL_edb,
1560 : coin_pub,
1561 : &ci);
1562 40 : if (0 > qs)
1563 : {
1564 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1565 0 : return qs;
1566 : }
1567 40 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1568 : {
1569 : /* The exchange has no denomination signature on file for this
1570 : coin at all; that is the strongest evidence of a forged coin
1571 : and must be persisted, not merely logged. */
1572 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1573 : .problem_row_id = rowid,
1574 : .operation = (char *) operation,
1575 : .loss = *loss_potential,
1576 : .operation_specific_pub = coin_pub->eddsa_pub
1577 : };
1578 : enum GNUNET_DB_QueryStatus rqs;
1579 :
1580 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1581 : "Coin %s used in %s in row %llu is not known to the exchange\n",
1582 : TALER_B2S (coin_pub),
1583 : operation,
1584 : (unsigned long long) rowid);
1585 0 : rqs = report_row_inconsistency ("known_coins",
1586 : rowid,
1587 : "coin not known to exchange");
1588 0 : if (0 > rqs)
1589 : {
1590 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rqs);
1591 0 : return rqs;
1592 : }
1593 0 : rqs = TALER_AUDITORDB_insert_bad_sig_losses (
1594 : TALER_ARL_adb,
1595 : &bsl);
1596 0 : if (0 > rqs)
1597 : {
1598 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rqs);
1599 0 : return rqs;
1600 : }
1601 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
1602 : &TALER_ARL_USE_AB (coin_irregular_loss),
1603 : loss_potential);
1604 0 : return qs;
1605 : }
1606 40 : if (GNUNET_YES !=
1607 40 : TALER_test_coin_valid (&ci,
1608 : denom_pub))
1609 : {
1610 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1611 : .problem_row_id = rowid,
1612 : .operation = (char *) operation,
1613 : .loss = *loss_potential,
1614 : .operation_specific_pub = coin_pub->eddsa_pub
1615 : };
1616 :
1617 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1618 : "Failed to verify coin denomination signature in row %llu\n",
1619 : (unsigned long long) rowid);
1620 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
1621 : TALER_ARL_adb,
1622 : &bsl);
1623 0 : if (qs < 0)
1624 : {
1625 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1626 0 : return qs;
1627 : }
1628 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
1629 : &TALER_ARL_USE_AB (coin_irregular_loss),
1630 : loss_potential);
1631 : }
1632 40 : TALER_denom_sig_free (&ci.denom_sig);
1633 40 : return qs;
1634 : }
1635 :
1636 :
1637 : /**
1638 : * Update the denom balance in @a dso reducing it by
1639 : * @a amount_with_fee. If this is not possible, report
1640 : * an emergency. Also updates the balance.
1641 : *
1642 : * @param dso denomination summary to update
1643 : * @param rowid responsible row (for logging)
1644 : * @param amount_with_fee amount to subtract
1645 : * @return transaction status
1646 : */
1647 : static enum GNUNET_DB_QueryStatus
1648 36 : reduce_denom_balance (struct DenominationSummary *dso,
1649 : uint64_t rowid,
1650 : const struct TALER_Amount *amount_with_fee)
1651 : {
1652 : struct TALER_Amount tmp;
1653 : enum GNUNET_DB_QueryStatus qs;
1654 :
1655 36 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
1656 36 : TALER_ARL_amount_subtract_neg (&tmp,
1657 : &dso->dcd.denom_balance,
1658 : amount_with_fee))
1659 : {
1660 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1661 : "Emergency: failed to reduce balance of denomination `%s' by %s\n",
1662 : GNUNET_h2s (&dso->issue->denom_hash.hash),
1663 : TALER_amount2s (amount_with_fee));
1664 0 : TALER_ARL_amount_add (&dso->dcd.denom_loss,
1665 : &dso->dcd.denom_loss,
1666 : amount_with_fee);
1667 0 : dso->report_emergency = true;
1668 : }
1669 : else
1670 : {
1671 36 : dso->dcd.denom_balance = tmp;
1672 : }
1673 36 : if (-1 == TALER_amount_cmp (&TALER_ARL_USE_AB (total_escrowed),
1674 : amount_with_fee))
1675 : {
1676 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1677 : "Failed to total escrow by %s\n",
1678 : TALER_amount2s (amount_with_fee));
1679 : /* This can theoretically happen if for example the exchange
1680 : never issued any coins (i.e. escrow balance is zero), but
1681 : accepted a forged coin (i.e. emergency situation after
1682 : private key compromise). In that case, we cannot even
1683 : subtract the profit we make from the fee from the escrow
1684 : balance. Tested as part of test-auditor.sh, case #18 */
1685 0 : qs = report_amount_arithmetic_inconsistency (
1686 : "subtracting amount from escrow balance",
1687 : rowid,
1688 : &TALER_ARL_USE_AB (total_escrowed),
1689 : amount_with_fee,
1690 : 0);
1691 0 : if (0 > qs)
1692 : {
1693 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1694 0 : return qs;
1695 : }
1696 : }
1697 : else
1698 : {
1699 36 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_escrowed),
1700 : &TALER_ARL_USE_AB (total_escrowed),
1701 : amount_with_fee);
1702 : }
1703 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1704 : "New balance of denomination `%s' is %s\n",
1705 : GNUNET_h2s (&dso->issue->denom_hash.hash),
1706 : TALER_amount2s (&dso->dcd.denom_balance));
1707 36 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
1708 : }
1709 :
1710 :
1711 : /**
1712 : * Function called with details about coins that were melted, with the
1713 : * goal of auditing the refresh's execution. Verifies the signature
1714 : * and updates our information about coins outstanding (the old coin's
1715 : * denomination has less, the fresh coins increased outstanding
1716 : * balances).
1717 : *
1718 : * @param cc closure
1719 : * @param rowid unique serial ID for the refresh session in our DB
1720 : * @param old_denom_pub denomination public key of @a coin_pub
1721 : * @param coin_pub public key of the coin
1722 : * @param coin_sig signature from the coin
1723 : * @param h_age_commitment hash of the age commitment for the coin
1724 : * @param amount_with_fee amount that was deposited including fee
1725 : * @param num_nds length of the @a new_denom_serials array
1726 : * @param new_denom_serials array of denomination serials of fresh coins
1727 : * @param rc what the refresh commitment
1728 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1729 : */
1730 : static enum GNUNET_GenericReturnValue
1731 2 : refresh_session_cb (struct CoinContext *cc,
1732 : uint64_t rowid,
1733 : const struct TALER_DenominationPublicKey *old_denom_pub,
1734 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
1735 : const struct TALER_CoinSpendSignatureP *coin_sig,
1736 : const struct TALER_AgeCommitmentHashP *h_age_commitment,
1737 : const struct TALER_Amount *amount_with_fee,
1738 : size_t num_nds,
1739 : uint64_t new_denom_serials[static num_nds],
1740 : const struct TALER_RefreshCommitmentP *rc)
1741 2 : {
1742 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
1743 : struct DenominationSummary *dso;
1744 : enum GNUNET_DB_QueryStatus qs;
1745 : struct TALER_DenominationHashP h_denom_pub;
1746 :
1747 2 : GNUNET_assert (rowid >=
1748 : TALER_ARL_USE_PP (coins_melt_serial_id)); /* should be monotonically increasing */
1749 2 : TALER_ARL_USE_PP (coins_melt_serial_id) = rowid + 1;
1750 2 : qs = TALER_ARL_get_denomination_info (old_denom_pub,
1751 : &issue,
1752 : &h_denom_pub);
1753 2 : if (0 > qs)
1754 : {
1755 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1756 0 : cc->qs = qs;
1757 0 : return GNUNET_SYSERR;
1758 : }
1759 2 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1760 : {
1761 0 : qs = report_row_inconsistency ("melt",
1762 : rowid,
1763 : "denomination key not found");
1764 0 : if (0 > qs)
1765 : {
1766 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1767 0 : cc->qs = qs;
1768 0 : return GNUNET_SYSERR;
1769 : }
1770 0 : return GNUNET_OK;
1771 : }
1772 2 : qs = check_known_coin ("melt",
1773 : issue,
1774 : rowid,
1775 : coin_pub,
1776 : old_denom_pub,
1777 : amount_with_fee);
1778 2 : if (0 > qs)
1779 : {
1780 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1781 0 : cc->qs = qs;
1782 0 : return GNUNET_SYSERR;
1783 : }
1784 :
1785 : /* verify melt signature */
1786 2 : if (GNUNET_OK !=
1787 2 : TALER_wallet_melt_verify (amount_with_fee,
1788 2 : &issue->fees.refresh,
1789 : rc,
1790 : &h_denom_pub,
1791 : h_age_commitment,
1792 : coin_pub,
1793 : coin_sig))
1794 : {
1795 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
1796 : .problem_row_id = rowid,
1797 : .operation = (char *) "melt",
1798 : .loss = *amount_with_fee,
1799 : .operation_specific_pub = coin_pub->eddsa_pub
1800 : };
1801 :
1802 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1803 : "Failed to verify coin melt signature in row %llu\n",
1804 : (unsigned long long) rowid);
1805 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
1806 : TALER_ARL_adb,
1807 : &bsl);
1808 0 : if (qs < 0)
1809 : {
1810 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1811 0 : cc->qs = qs;
1812 0 : return GNUNET_SYSERR;
1813 : }
1814 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
1815 : &TALER_ARL_USE_AB (coin_irregular_loss),
1816 : amount_with_fee);
1817 : }
1818 2 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1819 : "Melting coin %s in denomination `%s' of value %s\n",
1820 : TALER_B2S (coin_pub),
1821 : GNUNET_h2s (&issue->denom_hash.hash),
1822 : TALER_amount2s (amount_with_fee));
1823 :
1824 2 : {
1825 : struct TALER_Amount refresh_cost;
1826 : struct TALER_Amount amount_without_fee;
1827 2 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *nis[num_nds];
1828 :
1829 : /* Check that the resulting amounts are consistent with the value being
1830 : refreshed by calculating the total refresh cost */
1831 2 : GNUNET_assert (GNUNET_OK ==
1832 : TALER_amount_set_zero (amount_with_fee->currency,
1833 : &refresh_cost));
1834 10 : for (size_t i = 0; i < num_nds; i++)
1835 : {
1836 8 : qs = TALER_ARL_get_denomination_info_by_serial (new_denom_serials[i],
1837 : &nis[i]);
1838 8 : if (0 > qs)
1839 : {
1840 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1841 0 : cc->qs = qs;
1842 0 : return GNUNET_SYSERR;
1843 : }
1844 8 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1845 : {
1846 : /* nis[i] was not set; we cannot audit this refresh at all */
1847 0 : qs = report_row_inconsistency ("refresh_reveal",
1848 : rowid,
1849 : "denomination key for fresh coin not found");
1850 0 : if (0 > qs)
1851 : {
1852 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1853 0 : cc->qs = qs;
1854 0 : return GNUNET_SYSERR;
1855 : }
1856 0 : return GNUNET_OK;
1857 : }
1858 : /* update cost of refresh */
1859 8 : TALER_ARL_amount_add (&refresh_cost,
1860 : &refresh_cost,
1861 : &nis[i]->fees.withdraw);
1862 8 : TALER_ARL_amount_add (&refresh_cost,
1863 : &refresh_cost,
1864 : &nis[i]->value);
1865 : }
1866 :
1867 : /* compute contribution of old coin */
1868 2 : if (TALER_ARL_SR_POSITIVE !=
1869 2 : TALER_ARL_amount_subtract_neg (&amount_without_fee,
1870 : amount_with_fee,
1871 : &issue->fees.refresh))
1872 : {
1873 : /* Melt fee higher than contribution of melted coin; this makes
1874 : no sense (exchange should never have accepted the operation) */
1875 0 : qs = report_amount_arithmetic_inconsistency ("melt contribution vs. fee",
1876 : rowid,
1877 : amount_with_fee,
1878 0 : &issue->fees.refresh,
1879 : -1);
1880 0 : if (0 > qs)
1881 : {
1882 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1883 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1884 0 : return GNUNET_SYSERR;
1885 : }
1886 : /* To continue, best assumption is the melted coin contributed
1887 : nothing (=> all withdrawal amounts will be counted as losses) */
1888 0 : GNUNET_assert (GNUNET_OK ==
1889 : TALER_amount_set_zero (TALER_ARL_currency,
1890 : &amount_without_fee));
1891 : }
1892 :
1893 : /* check old coin covers complete expenses (of refresh operation) */
1894 2 : if (1 == TALER_amount_cmp (&refresh_cost,
1895 : &amount_without_fee))
1896 : {
1897 : /* refresh_cost > amount_without_fee, which is bad (exchange lost) */
1898 0 : GNUNET_break_op (0);
1899 0 : qs = report_amount_arithmetic_inconsistency ("melt (cost)",
1900 : rowid,
1901 : &amount_without_fee, /* 'exchange' */
1902 : &refresh_cost, /* 'auditor' */
1903 : 1);
1904 0 : if (0 > qs)
1905 : {
1906 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1907 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1908 0 : return GNUNET_SYSERR;
1909 : }
1910 : }
1911 :
1912 : /* update outstanding denomination amounts for fresh coins withdrawn */
1913 10 : for (size_t i = 0; i < num_nds; i++)
1914 : {
1915 8 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *ni
1916 : = nis[i];
1917 : struct DenominationSummary *dsi;
1918 :
1919 8 : dsi = get_denomination_summary (cc,
1920 : ni);
1921 8 : if (NULL == dsi)
1922 : {
1923 0 : qs = report_row_inconsistency ("refresh_reveal",
1924 : rowid,
1925 : "denomination key for fresh coin unknown to auditor");
1926 0 : if (0 > qs)
1927 : {
1928 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1929 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1930 0 : return GNUNET_SYSERR;
1931 : }
1932 : }
1933 : else
1934 : {
1935 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1936 : "Created fresh coin in denomination `%s' of value %s\n",
1937 : GNUNET_h2s (&ni->denom_hash.hash),
1938 : TALER_amount2s (&ni->value));
1939 8 : dsi->dcd.num_issued++;
1940 8 : TALER_ARL_amount_add (&dsi->dcd.denom_balance,
1941 : &dsi->dcd.denom_balance,
1942 : &ni->value);
1943 8 : TALER_ARL_amount_add (&dsi->dcd.denom_risk,
1944 : &dsi->dcd.denom_risk,
1945 : &ni->value);
1946 8 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1947 : "New balance of denomination `%s' after refresh_reveal is %s\n",
1948 : GNUNET_h2s (&ni->denom_hash.hash),
1949 : TALER_amount2s (&dsi->dcd.denom_balance));
1950 8 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
1951 : &TALER_ARL_USE_AB (total_escrowed),
1952 : &ni->value);
1953 8 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
1954 : &TALER_ARL_USE_AB (coin_balance_risk),
1955 : &ni->value);
1956 : }
1957 : }
1958 : }
1959 :
1960 : /* update old coin's denomination balance */
1961 2 : dso = get_denomination_summary (cc,
1962 : issue);
1963 2 : if (NULL == dso)
1964 : {
1965 0 : qs = report_row_inconsistency ("refresh_reveal",
1966 : rowid,
1967 : "denomination key for dirty coin unknown to auditor");
1968 0 : if (0 > qs)
1969 : {
1970 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1971 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1972 0 : return GNUNET_SYSERR;
1973 : }
1974 : }
1975 : else
1976 : {
1977 2 : qs = reduce_denom_balance (dso,
1978 : rowid,
1979 : amount_with_fee);
1980 2 : if (0 > qs)
1981 : {
1982 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1983 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
1984 0 : return GNUNET_SYSERR;
1985 : }
1986 : }
1987 :
1988 : /* update global melt fees */
1989 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_melt_fee_revenue),
1990 : &TALER_ARL_USE_AB (coin_melt_fee_revenue),
1991 : &issue->fees.refresh);
1992 2 : return GNUNET_OK;
1993 : }
1994 :
1995 :
1996 : /**
1997 : * Function called with details about deposits that have been made,
1998 : * with the goal of auditing the deposit's execution.
1999 : *
2000 : * @param cc closure
2001 : * @param rowid unique serial ID for the deposit in our DB
2002 : * @param exchange_timestamp when did the exchange get the deposit
2003 : * @param deposit deposit details
2004 : * @param denom_pub denomination public key of @a coin_pub
2005 : * @param done flag set if the deposit was already executed (or not)
2006 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2007 : */
2008 : static enum GNUNET_GenericReturnValue
2009 34 : deposit_cb (struct CoinContext *cc,
2010 : uint64_t rowid,
2011 : struct GNUNET_TIME_Timestamp exchange_timestamp,
2012 : const struct TALER_EXCHANGEDB_Deposit *deposit,
2013 : const struct TALER_DenominationPublicKey *denom_pub,
2014 : bool done)
2015 : {
2016 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2017 : struct DenominationSummary *ds;
2018 : enum GNUNET_DB_QueryStatus qs;
2019 :
2020 : (void) done;
2021 : (void) exchange_timestamp;
2022 34 : GNUNET_assert (rowid >=
2023 : TALER_ARL_USE_PP (coins_deposit_serial_id)); /* should be monotonically increasing */
2024 34 : TALER_ARL_USE_PP (coins_deposit_serial_id) = rowid + 1;
2025 :
2026 34 : qs = TALER_ARL_get_denomination_info (denom_pub,
2027 : &issue,
2028 : NULL);
2029 34 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2030 : {
2031 0 : qs = report_row_inconsistency ("deposits",
2032 : rowid,
2033 : "denomination key not found");
2034 0 : if (0 > qs)
2035 : {
2036 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2037 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2038 0 : return GNUNET_SYSERR;
2039 : }
2040 0 : return GNUNET_OK;
2041 : }
2042 34 : if (GNUNET_TIME_timestamp_cmp (deposit->refund_deadline,
2043 : >,
2044 : deposit->wire_deadline))
2045 : {
2046 0 : qs = report_row_inconsistency ("deposits",
2047 : rowid,
2048 : "refund deadline past wire deadline");
2049 0 : if (0 > qs)
2050 : {
2051 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2052 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2053 0 : return GNUNET_SYSERR;
2054 : }
2055 : }
2056 :
2057 34 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
2058 : {
2059 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2060 0 : cc->qs = qs;
2061 0 : return GNUNET_SYSERR;
2062 : }
2063 34 : qs = check_known_coin ("deposit",
2064 : issue,
2065 : rowid,
2066 : &deposit->coin.coin_pub,
2067 : denom_pub,
2068 : &deposit->amount_with_fee);
2069 34 : if (0 > qs)
2070 : {
2071 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2072 0 : cc->qs = qs;
2073 0 : return GNUNET_SYSERR;
2074 : }
2075 :
2076 : /* Verify deposit signature */
2077 : {
2078 : struct TALER_MerchantWireHashP h_wire;
2079 : struct TALER_DenominationHashP h_denom_pub;
2080 :
2081 34 : TALER_denom_pub_hash (denom_pub,
2082 : &h_denom_pub);
2083 34 : TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
2084 : &deposit->wire_salt,
2085 : &h_wire);
2086 : /* NOTE: This is one of the operations we might eventually
2087 : want to do in parallel in the background to improve
2088 : auditor performance! */
2089 34 : if (GNUNET_OK !=
2090 34 : TALER_wallet_deposit_verify (&deposit->amount_with_fee,
2091 34 : &issue->fees.deposit,
2092 : &h_wire,
2093 : &deposit->h_contract_terms,
2094 34 : deposit->no_wallet_data_hash
2095 : ? NULL
2096 : : &deposit->wallet_data_hash,
2097 : &deposit->coin.h_age_commitment,
2098 : &deposit->h_policy,
2099 : &h_denom_pub,
2100 : deposit->timestamp,
2101 : &deposit->merchant_pub,
2102 : deposit->refund_deadline,
2103 : &deposit->coin.coin_pub,
2104 : &deposit->csig))
2105 : {
2106 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2107 : .problem_row_id = rowid,
2108 : .operation = (char *) "deposit",
2109 : .loss = deposit->amount_with_fee,
2110 : .operation_specific_pub = deposit->coin.coin_pub.eddsa_pub
2111 : };
2112 :
2113 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2114 : "Failed to verify coin deposit signature in row %llu\n",
2115 : (unsigned long long) rowid);
2116 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2117 : TALER_ARL_adb,
2118 : &bsl);
2119 0 : if (0 > qs)
2120 : {
2121 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2122 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2123 0 : return GNUNET_SYSERR;
2124 : }
2125 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2126 : &TALER_ARL_USE_AB (coin_irregular_loss),
2127 : &deposit->amount_with_fee);
2128 0 : return GNUNET_OK;
2129 : }
2130 : }
2131 34 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2132 : "Deposited coin %s in denomination `%s' of value %s\n",
2133 : TALER_B2S (&deposit->coin.coin_pub),
2134 : GNUNET_h2s (&issue->denom_hash.hash),
2135 : TALER_amount2s (&deposit->amount_with_fee));
2136 :
2137 : /* update old coin's denomination balance */
2138 34 : ds = get_denomination_summary (cc,
2139 : issue);
2140 34 : if (NULL == ds)
2141 : {
2142 0 : qs = report_row_inconsistency ("deposit",
2143 : rowid,
2144 : "denomination key for deposited coin unknown to auditor");
2145 0 : if (0 > qs)
2146 : {
2147 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2148 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2149 0 : return GNUNET_SYSERR;
2150 : }
2151 : }
2152 : else
2153 : {
2154 34 : qs = reduce_denom_balance (ds,
2155 : rowid,
2156 : &deposit->amount_with_fee);
2157 34 : if (0 > qs)
2158 : {
2159 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2160 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2161 0 : return GNUNET_SYSERR;
2162 : }
2163 : }
2164 :
2165 : /* update global deposit fees */
2166 34 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue),
2167 : &TALER_ARL_USE_AB (coin_deposit_fee_revenue),
2168 : &issue->fees.deposit);
2169 34 : return GNUNET_OK;
2170 : }
2171 :
2172 :
2173 : /**
2174 : * Function called with details about coins that were refunding,
2175 : * with the goal of auditing the refund's execution. Adds the
2176 : * refunded amount back to the outstanding balance of the respective
2177 : * denomination.
2178 : *
2179 : * @param cc closure
2180 : * @param rowid unique serial ID for the refund in our DB
2181 : * @param denom_pub denomination public key of @a coin_pub
2182 : * @param coin_pub public key of the coin
2183 : * @param merchant_pub public key of the merchant
2184 : * @param merchant_sig signature of the merchant
2185 : * @param h_contract_terms hash of the proposal data known to merchant and customer
2186 : * @param rtransaction_id refund transaction ID chosen by the merchant
2187 : * @param full_refund true if the refunds total up to the entire deposited value
2188 : * @param amount_with_fee amount that was deposited including fee
2189 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2190 : */
2191 : static enum GNUNET_GenericReturnValue
2192 2 : refund_cb (struct CoinContext *cc,
2193 : uint64_t rowid,
2194 : const struct TALER_DenominationPublicKey *denom_pub,
2195 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2196 : const struct TALER_MerchantPublicKeyP *merchant_pub,
2197 : const struct TALER_MerchantSignatureP *merchant_sig,
2198 : const struct TALER_PrivateContractHashP *h_contract_terms,
2199 : uint64_t rtransaction_id,
2200 : bool full_refund,
2201 : const struct TALER_Amount *amount_with_fee)
2202 : {
2203 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2204 : struct DenominationSummary *ds;
2205 : struct TALER_Amount amount_without_fee;
2206 : enum GNUNET_DB_QueryStatus qs;
2207 :
2208 2 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_refund_serial_id)); /* should be monotonically increasing */
2209 2 : TALER_ARL_USE_PP (coins_refund_serial_id) = rowid + 1;
2210 :
2211 2 : qs = TALER_ARL_get_denomination_info (denom_pub,
2212 : &issue,
2213 : NULL);
2214 2 : if (0 > qs)
2215 : {
2216 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2217 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2218 0 : return GNUNET_SYSERR;
2219 : }
2220 2 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2221 : {
2222 0 : qs = report_row_inconsistency ("refunds",
2223 : rowid,
2224 : "denomination key not found");
2225 0 : if (0 > qs)
2226 : {
2227 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2228 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2229 0 : return GNUNET_SYSERR;
2230 : }
2231 0 : return GNUNET_OK;
2232 : }
2233 :
2234 : /* verify refund signature */
2235 2 : if (GNUNET_OK !=
2236 2 : TALER_merchant_refund_verify (coin_pub,
2237 : h_contract_terms,
2238 : rtransaction_id,
2239 : amount_with_fee,
2240 : merchant_pub,
2241 : merchant_sig))
2242 : {
2243 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2244 : .problem_row_id = rowid,
2245 : .operation = (char *) "refund",
2246 : .loss = *amount_with_fee,
2247 : .operation_specific_pub = coin_pub->eddsa_pub
2248 : };
2249 :
2250 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2251 : "Failed to verify merchant refund signature in row %llu\n",
2252 : (unsigned long long) rowid);
2253 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2254 : TALER_ARL_adb,
2255 : &bsl);
2256 0 : if (0 > qs)
2257 : {
2258 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2259 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2260 0 : return GNUNET_SYSERR;
2261 : }
2262 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2263 : &TALER_ARL_USE_AB (coin_irregular_loss),
2264 : amount_with_fee);
2265 0 : return GNUNET_OK;
2266 : }
2267 :
2268 2 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
2269 2 : TALER_ARL_amount_subtract_neg (&amount_without_fee,
2270 : amount_with_fee,
2271 : &issue->fees.refund))
2272 : {
2273 0 : qs = report_amount_arithmetic_inconsistency ("refund (fee)",
2274 : rowid,
2275 : amount_with_fee,
2276 0 : &issue->fees.refund,
2277 : -1);
2278 0 : if (0 > qs)
2279 : {
2280 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2281 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2282 0 : return GNUNET_SYSERR;
2283 : }
2284 0 : return GNUNET_OK;
2285 : }
2286 :
2287 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2288 : "Refunding coin %s in denomination `%s' value %s\n",
2289 : TALER_B2S (coin_pub),
2290 : GNUNET_h2s (&issue->denom_hash.hash),
2291 : TALER_amount2s (amount_with_fee));
2292 :
2293 : /* update coin's denomination balance */
2294 2 : ds = get_denomination_summary (cc,
2295 : issue);
2296 2 : if (NULL == ds)
2297 : {
2298 0 : qs = report_row_inconsistency ("refund",
2299 : rowid,
2300 : "denomination key for refunded coin unknown to auditor");
2301 0 : if (0 > qs)
2302 : {
2303 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2304 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2305 0 : return GNUNET_SYSERR;
2306 : }
2307 : }
2308 : else
2309 : {
2310 2 : TALER_ARL_amount_add (&ds->dcd.denom_balance,
2311 : &ds->dcd.denom_balance,
2312 : &amount_without_fee);
2313 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
2314 : &TALER_ARL_USE_AB (total_escrowed),
2315 : &amount_without_fee);
2316 : /* A coin of a denomination whose deposit period is over cannot be
2317 : recouped (#TEH_recoup_check_keys() refuses it), so crediting it back
2318 : does not put anything at risk again; see
2319 : #release_denomination_risk(). */
2320 2 : if (! is_past_deposit_grace (issue))
2321 : {
2322 2 : TALER_ARL_amount_add (&ds->dcd.denom_risk,
2323 : &ds->dcd.denom_risk,
2324 : &amount_without_fee);
2325 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
2326 : &TALER_ARL_USE_AB (coin_balance_risk),
2327 : &amount_without_fee);
2328 : }
2329 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2330 : "New balance of denomination `%s' after refund is %s\n",
2331 : GNUNET_h2s (&issue->denom_hash.hash),
2332 : TALER_amount2s (&ds->dcd.denom_balance));
2333 : }
2334 : /* update total refund fee balance */
2335 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_refund_fee_revenue),
2336 : &TALER_ARL_USE_AB (coin_refund_fee_revenue),
2337 : &issue->fees.refund);
2338 2 : if (full_refund)
2339 : {
2340 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss),
2341 : &TALER_ARL_USE_AB (coin_deposit_fee_loss),
2342 : &issue->fees.deposit);
2343 : }
2344 2 : return GNUNET_OK;
2345 : }
2346 :
2347 :
2348 : /**
2349 : * Function called with details about purse refunds that have been made, with
2350 : * the goal of auditing the purse refund's execution.
2351 : *
2352 : * @param cc closure
2353 : * @param rowid row of the purse-refund
2354 : * @param amount_with_fee amount of the deposit into the purse
2355 : * @param coin_pub coin that is to be refunded the @a given amount_with_fee
2356 : * @param denom_pub denomination of @a coin_pub
2357 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2358 : */
2359 : static enum GNUNET_GenericReturnValue
2360 0 : purse_refund_coin_cb (
2361 : struct CoinContext *cc,
2362 : uint64_t rowid,
2363 : const struct TALER_Amount *amount_with_fee,
2364 : const struct TALER_CoinSpendPublicKeyP *coin_pub,
2365 : const struct TALER_DenominationPublicKey *denom_pub)
2366 : {
2367 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2368 : struct DenominationSummary *ds;
2369 : enum GNUNET_DB_QueryStatus qs;
2370 :
2371 0 : qs = TALER_ARL_get_denomination_info (denom_pub,
2372 : &issue,
2373 : NULL);
2374 0 : if (0 > qs)
2375 : {
2376 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2377 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2378 0 : return GNUNET_SYSERR;
2379 : }
2380 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2381 : {
2382 0 : qs = report_row_inconsistency ("purse-refunds",
2383 : rowid,
2384 : "denomination key not found");
2385 0 : if (0 > qs)
2386 : {
2387 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2388 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2389 0 : return GNUNET_SYSERR;
2390 : }
2391 0 : return GNUNET_OK;
2392 : }
2393 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2394 : "Aborted purse-deposit of coin %s in denomination `%s' value %s\n",
2395 : TALER_B2S (coin_pub),
2396 : GNUNET_h2s (&issue->denom_hash.hash),
2397 : TALER_amount2s (amount_with_fee));
2398 :
2399 : /* update coin's denomination balance */
2400 0 : ds = get_denomination_summary (cc,
2401 : issue);
2402 0 : if (NULL == ds)
2403 : {
2404 0 : qs = report_row_inconsistency ("purse-refund",
2405 : rowid,
2406 : "denomination key for purse-refunded coin unknown to auditor");
2407 0 : if (0 > qs)
2408 : {
2409 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2410 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2411 0 : return GNUNET_SYSERR;
2412 : }
2413 : }
2414 : else
2415 : {
2416 0 : TALER_ARL_amount_add (&ds->dcd.denom_balance,
2417 : &ds->dcd.denom_balance,
2418 : amount_with_fee);
2419 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
2420 : &TALER_ARL_USE_AB (total_escrowed),
2421 : amount_with_fee);
2422 : /* A coin of a denomination whose deposit period is over cannot be
2423 : recouped (#TEH_recoup_check_keys() refuses it), so crediting it back
2424 : does not put anything at risk again; see
2425 : #release_denomination_risk(). */
2426 0 : if (! is_past_deposit_grace (issue))
2427 : {
2428 0 : TALER_ARL_amount_add (&ds->dcd.denom_risk,
2429 : &ds->dcd.denom_risk,
2430 : amount_with_fee);
2431 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
2432 : &TALER_ARL_USE_AB (coin_balance_risk),
2433 : amount_with_fee);
2434 : }
2435 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2436 : "New balance of denomination `%s' after purse-refund is %s\n",
2437 : GNUNET_h2s (&issue->denom_hash.hash),
2438 : TALER_amount2s (&ds->dcd.denom_balance));
2439 : }
2440 : /* update total deposit fee balance */
2441 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss),
2442 : &TALER_ARL_USE_AB (coin_deposit_fee_loss),
2443 : &issue->fees.deposit);
2444 :
2445 0 : return GNUNET_OK;
2446 : }
2447 :
2448 :
2449 : /**
2450 : * Function called with details about a purse that was refunded. Adds the
2451 : * refunded amounts back to the outstanding balance of the respective
2452 : * denominations.
2453 : *
2454 : * @param cc closure
2455 : * @param rowid unique serial ID for the refund in our DB
2456 : * @param purse_pub public key of the purse
2457 : * @param reserve_pub public key of the targeted reserve (ignored)
2458 : * @param val targeted amount to be in the reserve (ignored)
2459 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2460 : */
2461 : static enum GNUNET_GenericReturnValue
2462 0 : purse_refund_cb (struct CoinContext *cc,
2463 : uint64_t rowid,
2464 : const struct TALER_PurseContractPublicKeyP *purse_pub,
2465 : const struct TALER_ReservePublicKeyP *reserve_pub,
2466 : const struct TALER_Amount *val)
2467 : {
2468 : enum GNUNET_DB_QueryStatus qs;
2469 :
2470 : (void) val; /* irrelevant on refund */
2471 : (void) reserve_pub; /* irrelevant, may even be NULL */
2472 0 : GNUNET_assert (rowid >=
2473 : TALER_ARL_USE_PP (coins_purse_refunds_serial_id)); /* should be monotonically increasing */
2474 0 : qs = TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (
2475 : TALER_ARL_edb,
2476 : purse_pub,
2477 : &purse_refund_coin_cb,
2478 : cc);
2479 0 : if (qs < 0)
2480 : {
2481 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2482 0 : cc->qs = qs;
2483 0 : return GNUNET_SYSERR;
2484 : }
2485 0 : if (0 > cc->qs)
2486 0 : return GNUNET_SYSERR; /* #purse_refund_coin_cb() failed */
2487 0 : TALER_ARL_USE_PP (coins_purse_refunds_serial_id) = rowid + 1;
2488 0 : return GNUNET_OK;
2489 : }
2490 :
2491 :
2492 : /**
2493 : * Check that the recoup operation was properly initiated by a coin
2494 : * and update the denomination's losses accordingly.
2495 : *
2496 : * @param cc the context with details about the coin
2497 : * @param operation name of the operation matching @a rowid
2498 : * @param rowid row identifier used to uniquely identify the recoup operation
2499 : * @param amount how much should be added back to the reserve
2500 : * @param coin public information about the coin
2501 : * @param denom_pub public key of the denomionation of @a coin
2502 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
2503 : * @param coin_blind blinding factor used to blind the coin
2504 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2505 : */
2506 : static enum GNUNET_GenericReturnValue
2507 4 : check_recoup (struct CoinContext *cc,
2508 : const char *operation,
2509 : uint64_t rowid,
2510 : const struct TALER_Amount *amount,
2511 : const struct TALER_CoinPublicInfo *coin,
2512 : const struct TALER_DenominationPublicKey *denom_pub,
2513 : const struct TALER_CoinSpendSignatureP *coin_sig,
2514 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
2515 : {
2516 : struct DenominationSummary *ds;
2517 : enum GNUNET_DB_QueryStatus qs;
2518 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2519 :
2520 4 : if (GNUNET_OK !=
2521 4 : TALER_wallet_recoup_verify (&coin->denom_pub_hash,
2522 : coin_blind,
2523 : &coin->coin_pub,
2524 : coin_sig))
2525 : {
2526 0 : qs = report_row_inconsistency (operation,
2527 : rowid,
2528 : "recoup signature invalid");
2529 0 : if (0 > qs)
2530 : {
2531 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2532 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2533 0 : return GNUNET_SYSERR;
2534 : }
2535 : }
2536 4 : if (GNUNET_OK !=
2537 4 : TALER_test_coin_valid (coin,
2538 : denom_pub))
2539 : {
2540 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2541 : .problem_row_id = rowid,
2542 : .operation = (char *) operation,
2543 : .loss = *amount,
2544 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2545 : };
2546 :
2547 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2548 : "Failed to verify coin signature in row %llu\n",
2549 : (unsigned long long) rowid);
2550 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2551 : TALER_ARL_adb,
2552 : &bsl);
2553 :
2554 0 : if (0 > qs)
2555 : {
2556 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2557 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2558 0 : return GNUNET_SYSERR;
2559 : }
2560 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2561 : &TALER_ARL_USE_AB (coin_irregular_loss),
2562 : amount);
2563 : }
2564 4 : qs = TALER_ARL_get_denomination_info_by_hash (&coin->denom_pub_hash,
2565 : &issue);
2566 4 : if (0 > qs)
2567 : {
2568 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2569 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2570 0 : return GNUNET_SYSERR;
2571 : }
2572 4 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2573 : {
2574 0 : qs = report_row_inconsistency (operation,
2575 : rowid,
2576 : "denomination key not found");
2577 0 : if (0 > qs)
2578 : {
2579 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2580 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2581 0 : return GNUNET_SYSERR;
2582 : }
2583 0 : return GNUNET_OK;
2584 : }
2585 4 : qs = check_known_coin (operation,
2586 : issue,
2587 : rowid,
2588 : &coin->coin_pub,
2589 : denom_pub,
2590 : amount);
2591 4 : if (0 > qs)
2592 : {
2593 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2594 0 : cc->qs = qs;
2595 0 : return GNUNET_SYSERR;
2596 : }
2597 4 : ds = get_denomination_summary (cc,
2598 : issue);
2599 4 : if (NULL == ds)
2600 : {
2601 0 : qs = report_row_inconsistency ("recoup",
2602 : rowid,
2603 : "denomination key for recouped coin unknown to auditor");
2604 0 : if (0 > qs)
2605 : {
2606 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2607 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
2608 0 : return GNUNET_SYSERR;
2609 : }
2610 : }
2611 : else
2612 : {
2613 4 : if (! ds->was_revoked)
2614 : {
2615 0 : struct TALER_AUDITORDB_BadSigLosses bsldnr = {
2616 : .problem_row_id = rowid,
2617 : .operation = (char *) operation,
2618 : .loss = *amount,
2619 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2620 : };
2621 :
2622 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2623 : "Recoup allowed on non-revoked denomination in row %llu\n",
2624 : (unsigned long long) rowid);
2625 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2626 : TALER_ARL_adb,
2627 : &bsldnr);
2628 :
2629 0 : if (qs < 0)
2630 : {
2631 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2632 0 : cc->qs = qs;
2633 0 : return GNUNET_SYSERR;
2634 : }
2635 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2636 : &TALER_ARL_USE_AB (coin_irregular_loss),
2637 : amount);
2638 : }
2639 4 : TALER_ARL_amount_add (&ds->dcd.recoup_loss,
2640 : &ds->dcd.recoup_loss,
2641 : amount);
2642 4 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_recoup_loss),
2643 : &TALER_ARL_USE_AB (total_recoup_loss),
2644 : amount);
2645 : }
2646 4 : return GNUNET_OK;
2647 : }
2648 :
2649 :
2650 : /**
2651 : * Function called about recoups the exchange has to perform.
2652 : *
2653 : * @param cc a `struct CoinContext *`
2654 : * @param rowid row identifier used to uniquely identify the recoup operation
2655 : * @param timestamp when did we receive the recoup request
2656 : * @param amount how much should be added back to the reserve
2657 : * @param reserve_pub public key of the reserve
2658 : * @param coin public information about the coin
2659 : * @param denom_pub denomination public key of @a coin
2660 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
2661 : * @param coin_blind blinding factor used to blind the coin
2662 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2663 : */
2664 : static enum GNUNET_GenericReturnValue
2665 4 : recoup_cb (struct CoinContext *cc,
2666 : uint64_t rowid,
2667 : struct GNUNET_TIME_Timestamp timestamp,
2668 : const struct TALER_Amount *amount,
2669 : const struct TALER_ReservePublicKeyP *reserve_pub,
2670 : const struct TALER_CoinPublicInfo *coin,
2671 : const struct TALER_DenominationPublicKey *denom_pub,
2672 : const struct TALER_CoinSpendSignatureP *coin_sig,
2673 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
2674 : {
2675 : enum GNUNET_DB_QueryStatus qs;
2676 :
2677 4 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_serial_id)); /* should be monotonically increasing */
2678 4 : TALER_ARL_USE_PP (coins_recoup_serial_id) = rowid + 1;
2679 : (void) timestamp;
2680 : (void) reserve_pub;
2681 4 : if (GNUNET_OK !=
2682 4 : TALER_wallet_recoup_verify (&coin->denom_pub_hash,
2683 : coin_blind,
2684 : &coin->coin_pub,
2685 : coin_sig))
2686 : {
2687 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2688 : .problem_row_id = rowid,
2689 : .operation = (char *) "recoup",
2690 : .loss = *amount,
2691 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2692 : };
2693 :
2694 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2695 : "Failed to verify recoup signature in row %llu\n",
2696 : (unsigned long long) rowid);
2697 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2698 : TALER_ARL_adb,
2699 : &bsl);
2700 0 : if (qs < 0)
2701 : {
2702 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2703 0 : cc->qs = qs;
2704 0 : return GNUNET_SYSERR;
2705 : }
2706 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2707 : &TALER_ARL_USE_AB (coin_irregular_loss),
2708 : amount);
2709 0 : return GNUNET_OK;
2710 : }
2711 4 : return check_recoup (cc,
2712 : "recoup",
2713 : rowid,
2714 : amount,
2715 : coin,
2716 : denom_pub,
2717 : coin_sig,
2718 : coin_blind);
2719 : }
2720 :
2721 :
2722 : /**
2723 : * Function called about recoups on refreshed coins the exchange had to
2724 : * perform. Updates the denomination balance(s). Does not change the
2725 : * coin balances, as those are already updated when we check the coin
2726 : * history.
2727 : *
2728 : * @param cc a `struct CoinContext *`
2729 : * @param rowid row identifier used to uniquely identify the recoup operation
2730 : * @param timestamp when did we receive the recoup request
2731 : * @param amount how much should be added back to the old coin
2732 : * @param old_coin_pub original coin that was refreshed to create @a coin
2733 : * @param old_denom_pub_hash hash of the public key of @a old_coin_pub
2734 : * @param coin public information about the fresh coin
2735 : * @param denom_pub denomination public key of @a coin
2736 : * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
2737 : * @param coin_blind blinding factor used to blind the coin
2738 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2739 : */
2740 : static enum GNUNET_GenericReturnValue
2741 0 : recoup_refresh_cb (struct CoinContext *cc,
2742 : uint64_t rowid,
2743 : struct GNUNET_TIME_Timestamp timestamp,
2744 : const struct TALER_Amount *amount,
2745 : const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
2746 : const struct TALER_DenominationHashP *old_denom_pub_hash,
2747 : const struct TALER_CoinPublicInfo *coin,
2748 : const struct TALER_DenominationPublicKey *denom_pub,
2749 : const struct TALER_CoinSpendSignatureP *coin_sig,
2750 : const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
2751 : {
2752 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2753 : enum GNUNET_DB_QueryStatus qs;
2754 :
2755 : (void) timestamp;
2756 : (void) old_coin_pub;
2757 0 : GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_refresh_serial_id)); /* should be monotonically increasing */
2758 0 : TALER_ARL_USE_PP (coins_recoup_refresh_serial_id) = rowid + 1;
2759 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2760 : "Recoup-refresh amount is %s\n",
2761 : TALER_amount2s (amount));
2762 :
2763 : /* Update old coin's denomination balance summary */
2764 0 : qs = TALER_ARL_get_denomination_info_by_hash (old_denom_pub_hash,
2765 : &issue);
2766 0 : if (qs < 0)
2767 : {
2768 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2769 0 : cc->qs = qs;
2770 0 : return GNUNET_SYSERR;
2771 : }
2772 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2773 : {
2774 0 : qs = report_row_inconsistency ("refresh-recoup",
2775 : rowid,
2776 : "denomination key of old coin not found");
2777 0 : if (qs < 0)
2778 : {
2779 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2780 0 : cc->qs = qs;
2781 0 : return GNUNET_SYSERR;
2782 : }
2783 : /* nothing to update without the denomination */
2784 0 : return GNUNET_OK;
2785 : }
2786 :
2787 : {
2788 : struct DenominationSummary *dso;
2789 :
2790 0 : dso = get_denomination_summary (cc,
2791 : issue);
2792 0 : if (NULL == dso)
2793 : {
2794 0 : qs = report_row_inconsistency ("refresh_reveal",
2795 : rowid,
2796 : "denomination key for old coin unknown to auditor");
2797 0 : if (qs < 0)
2798 : {
2799 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2800 0 : cc->qs = qs;
2801 0 : return GNUNET_SYSERR;
2802 : }
2803 : }
2804 : else
2805 : {
2806 0 : TALER_ARL_amount_add (&dso->dcd.denom_balance,
2807 : &dso->dcd.denom_balance,
2808 : amount);
2809 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2810 : "New balance of denomination `%s' after refresh-recoup is %s\n",
2811 : GNUNET_h2s (&issue->denom_hash.hash),
2812 : TALER_amount2s (&dso->dcd.denom_balance));
2813 : }
2814 : }
2815 :
2816 0 : if (GNUNET_OK !=
2817 0 : TALER_wallet_recoup_refresh_verify (&coin->denom_pub_hash,
2818 : coin_blind,
2819 : &coin->coin_pub,
2820 : coin_sig))
2821 : {
2822 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
2823 : .problem_row_id = rowid,
2824 : .operation = (char *) "recoup-refresh",
2825 : .loss = *amount,
2826 : .operation_specific_pub = coin->coin_pub.eddsa_pub
2827 : };
2828 :
2829 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2830 : "Failed to verify recoup-refresh signature in row %llu\n",
2831 : (unsigned long long) rowid);
2832 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
2833 : TALER_ARL_adb,
2834 : &bsl);
2835 0 : if (qs < 0)
2836 : {
2837 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2838 0 : cc->qs = qs;
2839 0 : return GNUNET_SYSERR;
2840 : }
2841 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
2842 : &TALER_ARL_USE_AB (coin_irregular_loss),
2843 : amount);
2844 0 : return GNUNET_OK;
2845 : }
2846 0 : return check_recoup (cc,
2847 : "recoup-refresh",
2848 : rowid,
2849 : amount,
2850 : coin,
2851 : denom_pub,
2852 : coin_sig,
2853 : coin_blind);
2854 : }
2855 :
2856 :
2857 : /**
2858 : * Function called with the results of iterate_denomination_info(),
2859 : * or directly (!). Used to check that we correctly signed the
2860 : * denomination and to warn if there are denominations not approved
2861 : * by this auditor.
2862 : *
2863 : * @param iqs closure, pointer to `enum GNUNET_DB_QueryStatus`
2864 : * @param denom_serial row ID of the denominations table of the exchange DB
2865 : * @param denom_pub public key, sometimes NULL (!)
2866 : * @param issue issuing information with value, fees and other info about the denomination.
2867 : */
2868 : static void
2869 400 : check_denomination (
2870 : enum GNUNET_DB_QueryStatus *iqs,
2871 : uint64_t denom_serial,
2872 : const struct TALER_DenominationPublicKey *denom_pub,
2873 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
2874 : {
2875 : enum GNUNET_DB_QueryStatus qs;
2876 : struct TALER_AuditorSignatureP auditor_sig;
2877 :
2878 : (void) denom_pub;
2879 400 : qs = TALER_EXCHANGEDB_get_auditor_denom_sig (TALER_ARL_edb,
2880 : &issue->denom_hash,
2881 : &TALER_ARL_auditor_pub,
2882 : &auditor_sig);
2883 400 : if (0 > qs)
2884 : {
2885 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2886 0 : *iqs = qs;
2887 0 : return;
2888 : }
2889 400 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2890 : {
2891 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2892 : "Encountered denomination `%s' (%s) valid from %s (%llu-%llu) that this auditor is not auditing!\n",
2893 : GNUNET_h2s (&issue->denom_hash.hash),
2894 : TALER_amount2s (&issue->value),
2895 : GNUNET_TIME_timestamp2s (issue->start),
2896 : (unsigned long long) issue->start.abs_time.abs_value_us,
2897 : (unsigned long long) issue->expire_legal.abs_time.abs_value_us);
2898 0 : return; /* skip! */
2899 : }
2900 400 : if (GNUNET_OK !=
2901 400 : TALER_auditor_denom_validity_verify (
2902 : TALER_ARL_auditor_url,
2903 : &issue->denom_hash,
2904 : &TALER_ARL_master_pub,
2905 : issue->start,
2906 : issue->expire_withdraw,
2907 : issue->expire_deposit,
2908 : issue->expire_legal,
2909 : &issue->value,
2910 : &issue->fees,
2911 : &TALER_ARL_auditor_pub,
2912 : &auditor_sig))
2913 : {
2914 0 : struct TALER_AUDITORDB_DenominationsWithoutSigs dws = {
2915 : .denompub_h = issue->denom_hash,
2916 : .start_time = issue->start.abs_time,
2917 : .end_time = issue->expire_legal.abs_time,
2918 : .value = issue->value
2919 : };
2920 :
2921 0 : qs = TALER_AUDITORDB_insert_denominations_without_sigs (
2922 : TALER_ARL_adb,
2923 : &dws);
2924 :
2925 0 : if (qs < 0)
2926 : {
2927 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2928 0 : *iqs = qs;
2929 0 : return;
2930 : }
2931 : }
2932 400 : *iqs = qs;
2933 : }
2934 :
2935 :
2936 : /**
2937 : * Function called with details about purse deposits that have been made, with
2938 : * the goal of auditing the deposit's execution.
2939 : *
2940 : * @param cc closure
2941 : * @param rowid unique serial ID for the deposit in our DB
2942 : * @param deposit deposit details
2943 : * @param reserve_pub which reserve is the purse merged into, NULL if unknown
2944 : * @param flags purse flags
2945 : * @param auditor_balance purse balance (according to the
2946 : * auditor during auditing)
2947 : * @param purse_total target amount the purse should reach
2948 : * @param denom_pub denomination public key of @a coin_pub
2949 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
2950 : */
2951 : static enum GNUNET_GenericReturnValue
2952 0 : purse_deposit_cb (
2953 : struct CoinContext *cc,
2954 : uint64_t rowid,
2955 : const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
2956 : const struct TALER_ReservePublicKeyP *reserve_pub,
2957 : enum TALER_WalletAccountMergeFlags flags,
2958 : const struct TALER_Amount *auditor_balance,
2959 : const struct TALER_Amount *purse_total,
2960 : const struct TALER_DenominationPublicKey *denom_pub)
2961 : {
2962 : enum GNUNET_DB_QueryStatus qs;
2963 : struct TALER_DenominationHashP dh;
2964 : const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
2965 : struct DenominationSummary *ds;
2966 :
2967 : (void) flags;
2968 : (void) auditor_balance;
2969 : (void) purse_total;
2970 : (void) reserve_pub;
2971 0 : GNUNET_assert (rowid >=
2972 : TALER_ARL_USE_PP (coins_purse_deposits_serial_id));
2973 0 : TALER_ARL_USE_PP (coins_purse_deposits_serial_id) = rowid + 1;
2974 0 : qs = TALER_ARL_get_denomination_info (denom_pub,
2975 : &issue,
2976 : &dh);
2977 0 : if (0 > qs)
2978 : {
2979 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2980 0 : cc->qs = qs;
2981 0 : return GNUNET_SYSERR;
2982 : }
2983 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
2984 : {
2985 0 : qs = report_row_inconsistency ("purse-deposits",
2986 : rowid,
2987 : "denomination key not found");
2988 0 : if (0 > qs)
2989 : {
2990 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
2991 0 : cc->qs = qs;
2992 0 : return GNUNET_SYSERR;
2993 : }
2994 0 : return GNUNET_OK;
2995 : }
2996 0 : qs = check_known_coin ("purse-deposit",
2997 : issue,
2998 : rowid,
2999 : &deposit->coin_pub,
3000 : denom_pub,
3001 : &deposit->amount);
3002 0 : if (0 > qs)
3003 : {
3004 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3005 0 : cc->qs = qs;
3006 0 : return GNUNET_SYSERR;
3007 : }
3008 :
3009 0 : if (GNUNET_OK !=
3010 0 : TALER_wallet_purse_deposit_verify (
3011 0 : NULL != deposit->exchange_base_url
3012 : ? deposit->exchange_base_url
3013 : : TALER_ARL_exchange_url,
3014 : &deposit->purse_pub,
3015 : &deposit->amount,
3016 : &dh,
3017 : &deposit->h_age_commitment,
3018 : &deposit->coin_pub,
3019 : &deposit->coin_sig))
3020 : {
3021 0 : struct TALER_AUDITORDB_BadSigLosses bsl = {
3022 : .problem_row_id = rowid,
3023 : .operation = (char *) "purse-deposit",
3024 : .loss = deposit->amount,
3025 : .operation_specific_pub = deposit->coin_pub.eddsa_pub
3026 : };
3027 :
3028 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3029 : "Failed to verify purse deposit signature in row %llu\n",
3030 : (unsigned long long) rowid);
3031 0 : qs = TALER_AUDITORDB_insert_bad_sig_losses (
3032 : TALER_ARL_adb,
3033 : &bsl);
3034 0 : if (0 > qs)
3035 : {
3036 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3037 0 : cc->qs = qs;
3038 0 : return GNUNET_SYSERR;
3039 : }
3040 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
3041 : &TALER_ARL_USE_AB (coin_irregular_loss),
3042 : &deposit->amount);
3043 0 : return GNUNET_OK;
3044 : }
3045 :
3046 : /* update coin's denomination balance */
3047 0 : ds = get_denomination_summary (cc,
3048 : issue);
3049 0 : if (NULL == ds)
3050 : {
3051 0 : qs = report_row_inconsistency ("purse-deposit",
3052 : rowid,
3053 : "denomination key for purse-deposited coin unknown to auditor");
3054 0 : if (0 > qs)
3055 : {
3056 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3057 0 : cc->qs = qs;
3058 0 : return GNUNET_SYSERR;
3059 : }
3060 : }
3061 : else
3062 : {
3063 0 : qs = reduce_denom_balance (ds,
3064 : rowid,
3065 : &deposit->amount);
3066 0 : if (0 > qs)
3067 : {
3068 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3069 0 : cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
3070 0 : return GNUNET_SYSERR;
3071 : }
3072 : }
3073 :
3074 : /* update global deposit fees */
3075 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue),
3076 : &TALER_ARL_USE_AB (coin_deposit_fee_revenue),
3077 : &issue->fees.deposit);
3078 0 : return GNUNET_OK;
3079 : }
3080 :
3081 :
3082 : /**
3083 : * Analyze the exchange's processing of coins.
3084 : *
3085 : * @param cls closure
3086 : * @return transaction status code
3087 : */
3088 : static enum GNUNET_DB_QueryStatus
3089 10 : analyze_coins (void *cls)
3090 : {
3091 : struct CoinContext cc;
3092 : enum GNUNET_DB_QueryStatus qs;
3093 : enum GNUNET_DB_QueryStatus iqs;
3094 :
3095 : (void) cls;
3096 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3097 : "Checking denominations...\n");
3098 10 : iqs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
3099 10 : qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
3100 : &check_denomination,
3101 : &iqs);
3102 10 : if (0 > qs)
3103 : {
3104 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3105 0 : return qs;
3106 : }
3107 10 : if (0 > iqs)
3108 : {
3109 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == iqs);
3110 0 : return iqs;
3111 : }
3112 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3113 : "Analyzing coins\n");
3114 10 : qs = TALER_AUDITORDB_get_auditor_progress (
3115 : TALER_ARL_adb,
3116 : TALER_ARL_GET_PP (coins_withdraw_serial_id),
3117 : TALER_ARL_GET_PP (coins_deposit_serial_id),
3118 : TALER_ARL_GET_PP (coins_melt_serial_id),
3119 : TALER_ARL_GET_PP (coins_refund_serial_id),
3120 : TALER_ARL_GET_PP (coins_recoup_serial_id),
3121 : TALER_ARL_GET_PP (coins_recoup_refresh_serial_id),
3122 : TALER_ARL_GET_PP (coins_purse_deposits_serial_id),
3123 : TALER_ARL_GET_PP (coins_purse_refunds_serial_id),
3124 : NULL);
3125 10 : if (0 > qs)
3126 : {
3127 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3128 0 : return qs;
3129 : }
3130 10 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
3131 : {
3132 0 : GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
3133 : "First analysis using this auditor, starting from scratch\n");
3134 : }
3135 : else
3136 : {
3137 10 : GNUNET_log (
3138 : GNUNET_ERROR_TYPE_INFO,
3139 : "Resuming coin audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
3140 : (unsigned long long) TALER_ARL_USE_PP (
3141 : coins_withdraw_serial_id),
3142 : (unsigned long long) TALER_ARL_USE_PP (
3143 : coins_deposit_serial_id),
3144 : (unsigned long long) TALER_ARL_USE_PP (
3145 : coins_melt_serial_id),
3146 : (unsigned long long) TALER_ARL_USE_PP (
3147 : coins_refund_serial_id),
3148 : (unsigned long long) TALER_ARL_USE_PP (
3149 : coins_recoup_serial_id),
3150 : (unsigned long long) TALER_ARL_USE_PP (
3151 : coins_recoup_refresh_serial_id),
3152 : (unsigned long long) TALER_ARL_USE_PP (
3153 : coins_purse_deposits_serial_id),
3154 : (unsigned long long) TALER_ARL_USE_PP (
3155 : coins_purse_refunds_serial_id));
3156 : }
3157 :
3158 : /* setup 'cc' */
3159 10 : cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3160 10 : cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
3161 : GNUNET_NO);
3162 10 : qs = TALER_AUDITORDB_get_balance (
3163 : TALER_ARL_adb,
3164 : TALER_ARL_GET_AB (coin_balance_risk),
3165 : TALER_ARL_GET_AB (total_escrowed),
3166 : TALER_ARL_GET_AB (coin_irregular_loss),
3167 : TALER_ARL_GET_AB (coin_melt_fee_revenue),
3168 : TALER_ARL_GET_AB (coin_deposit_fee_revenue),
3169 : TALER_ARL_GET_AB (coin_deposit_fee_loss),
3170 : TALER_ARL_GET_AB (coin_refund_fee_revenue),
3171 : TALER_ARL_GET_AB (total_recoup_loss),
3172 : TALER_ARL_GET_AB (coins_total_arithmetic_delta_plus),
3173 : TALER_ARL_GET_AB (coins_total_arithmetic_delta_minus),
3174 : TALER_ARL_GET_AB (coins_reported_emergency_risk_by_count),
3175 : TALER_ARL_GET_AB (coins_reported_emergency_risk_by_amount),
3176 : TALER_ARL_GET_AB (coins_emergencies_loss),
3177 : TALER_ARL_GET_AB (coins_emergencies_loss_by_count),
3178 : NULL);
3179 10 : if (0 > qs)
3180 : {
3181 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3182 0 : goto cleanup;
3183 : }
3184 : /* process withdrawals */
3185 10 : if (0 >
3186 10 : (qs = TALER_EXCHANGEDB_iterate_withdrawals_above_serial_id (
3187 : TALER_ARL_edb,
3188 : TALER_ARL_USE_PP (coins_withdraw_serial_id),
3189 : &withdraw_cb,
3190 : &cc)))
3191 : {
3192 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3193 0 : goto cleanup;
3194 : }
3195 10 : if (0 > cc.qs)
3196 : {
3197 0 : qs = cc.qs;
3198 0 : goto cleanup;
3199 : }
3200 : /* Process all operations that credit a denomination balance (refunds,
3201 : recoup-refresh, purse refunds) before those that debit it (melts,
3202 : deposits, purse deposits): within a pass the tables are processed
3203 : one after the other and not in time order, so a coin that spends
3204 : a credit it received in the same pass would otherwise make
3205 : reduce_denom_balance() see a balance that is too low and report a
3206 : false emergency. */
3207 : /* process refunds */
3208 10 : if (0 >
3209 10 : (qs = TALER_EXCHANGEDB_iterate_refunds_above_serial_id (
3210 : TALER_ARL_edb,
3211 : TALER_ARL_USE_PP (coins_refund_serial_id),
3212 : &refund_cb,
3213 : &cc)))
3214 : {
3215 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3216 0 : goto cleanup;
3217 : }
3218 10 : if (0 > cc.qs)
3219 : {
3220 0 : qs = cc.qs;
3221 0 : goto cleanup;
3222 : }
3223 : /* process recoups of refreshed coins (credit the old coin) */
3224 10 : if (0 >
3225 10 : (qs = TALER_EXCHANGEDB_iterate_recoup_refreshes_above_serial_id (
3226 : TALER_ARL_edb,
3227 : TALER_ARL_USE_PP (coins_recoup_refresh_serial_id),
3228 : &recoup_refresh_cb,
3229 : &cc)))
3230 : {
3231 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3232 0 : goto cleanup;
3233 : }
3234 10 : if (0 > cc.qs)
3235 : {
3236 0 : qs = cc.qs;
3237 0 : goto cleanup;
3238 : }
3239 : /* process purse_refunds */
3240 10 : if (0 >
3241 10 : (qs = TALER_EXCHANGEDB_iterate_purse_decisions_above_serial_id (
3242 : TALER_ARL_edb,
3243 : TALER_ARL_USE_PP (coins_purse_refunds_serial_id),
3244 : true, /* only go for refunds! */
3245 : &purse_refund_cb,
3246 : &cc)))
3247 : {
3248 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3249 0 : goto cleanup;
3250 : }
3251 10 : if (0 > cc.qs)
3252 : {
3253 0 : qs = cc.qs;
3254 0 : goto cleanup;
3255 : }
3256 : /* process refreshes */
3257 10 : if (0 >
3258 10 : (qs = TALER_EXCHANGEDB_iterate_refreshes_above_serial_id (
3259 : TALER_ARL_edb,
3260 : TALER_ARL_USE_PP (coins_melt_serial_id),
3261 : &refresh_session_cb,
3262 : &cc)))
3263 : {
3264 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3265 0 : goto cleanup;
3266 : }
3267 10 : if (0 > cc.qs)
3268 : {
3269 0 : qs = cc.qs;
3270 0 : goto cleanup;
3271 : }
3272 : /* process deposits */
3273 10 : if (0 >
3274 10 : (qs = TALER_EXCHANGEDB_iterate_coin_deposits_above_serial_id (
3275 : TALER_ARL_edb,
3276 : TALER_ARL_USE_PP (coins_deposit_serial_id),
3277 : &deposit_cb,
3278 : &cc)))
3279 : {
3280 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3281 0 : goto cleanup;
3282 : }
3283 10 : if (0 > cc.qs)
3284 : {
3285 0 : qs = cc.qs;
3286 0 : goto cleanup;
3287 : }
3288 : /* process purse_deposits */
3289 10 : if (0 >
3290 10 : (qs = TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
3291 : TALER_ARL_edb,
3292 : TALER_ARL_USE_PP (coins_purse_deposits_serial_id),
3293 : &purse_deposit_cb,
3294 : &cc)))
3295 : {
3296 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3297 0 : goto cleanup;
3298 : }
3299 10 : if (0 > cc.qs)
3300 : {
3301 0 : qs = cc.qs;
3302 0 : goto cleanup;
3303 : }
3304 10 : if (0 >
3305 10 : (qs = TALER_EXCHANGEDB_iterate_recoups_above_serial_id (
3306 : TALER_ARL_edb,
3307 : TALER_ARL_USE_PP (coins_recoup_serial_id),
3308 : &recoup_cb,
3309 : &cc)))
3310 : {
3311 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3312 0 : goto cleanup;
3313 : }
3314 10 : if (0 > cc.qs)
3315 : {
3316 0 : qs = cc.qs;
3317 0 : goto cleanup;
3318 : }
3319 : /* sync 'cc' back to disk */
3320 10 : cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3321 10 : GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
3322 : &sync_denomination,
3323 : &cc);
3324 :
3325 10 : if (0 > cc.qs)
3326 : {
3327 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs);
3328 0 : qs = cc.qs;
3329 0 : goto cleanup;
3330 : }
3331 :
3332 : /* settle the denominations that nothing touched in this pass */
3333 10 : qs = sweep_denominations (&cc);
3334 10 : if (0 > qs)
3335 : {
3336 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3337 0 : goto cleanup;
3338 : }
3339 :
3340 10 : qs = TALER_AUDITORDB_insert_balance (
3341 : TALER_ARL_adb,
3342 : TALER_ARL_SET_AB (coin_balance_risk),
3343 : TALER_ARL_SET_AB (total_escrowed),
3344 : TALER_ARL_SET_AB (coin_irregular_loss),
3345 : TALER_ARL_SET_AB (coin_melt_fee_revenue),
3346 : TALER_ARL_SET_AB (coin_deposit_fee_revenue),
3347 : TALER_ARL_SET_AB (coin_deposit_fee_loss),
3348 : TALER_ARL_SET_AB (coin_refund_fee_revenue),
3349 : TALER_ARL_SET_AB (total_recoup_loss),
3350 : TALER_ARL_SET_AB (coins_total_arithmetic_delta_plus),
3351 : TALER_ARL_SET_AB (coins_total_arithmetic_delta_minus),
3352 : TALER_ARL_SET_AB (coins_reported_emergency_risk_by_count),
3353 : TALER_ARL_SET_AB (coins_reported_emergency_risk_by_amount),
3354 : TALER_ARL_SET_AB (coins_emergencies_loss),
3355 : TALER_ARL_SET_AB (coins_emergencies_loss_by_count),
3356 : NULL);
3357 10 : if (0 > qs)
3358 : {
3359 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3360 : "Failed to update auditor DB, not recording progress\n");
3361 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3362 0 : goto cleanup;
3363 : }
3364 :
3365 10 : qs = TALER_AUDITORDB_insert_auditor_progress (
3366 : TALER_ARL_adb,
3367 : TALER_ARL_SET_PP (coins_withdraw_serial_id),
3368 : TALER_ARL_SET_PP (coins_deposit_serial_id),
3369 : TALER_ARL_SET_PP (coins_melt_serial_id),
3370 : TALER_ARL_SET_PP (coins_refund_serial_id),
3371 : TALER_ARL_SET_PP (coins_recoup_serial_id),
3372 : TALER_ARL_SET_PP (coins_recoup_refresh_serial_id),
3373 : TALER_ARL_SET_PP (coins_purse_deposits_serial_id),
3374 : TALER_ARL_SET_PP (coins_purse_refunds_serial_id),
3375 : NULL);
3376 10 : if (0 > qs)
3377 : {
3378 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3379 : "Failed to update auditor DB, not recording progress\n");
3380 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
3381 0 : goto cleanup;
3382 : }
3383 :
3384 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3385 : "Concluded coin audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
3386 : (unsigned long long) TALER_ARL_USE_PP (coins_withdraw_serial_id),
3387 : (unsigned long long) TALER_ARL_USE_PP (coins_deposit_serial_id),
3388 : (unsigned long long) TALER_ARL_USE_PP (coins_melt_serial_id),
3389 : (unsigned long long) TALER_ARL_USE_PP (coins_refund_serial_id),
3390 : (unsigned long long) TALER_ARL_USE_PP (coins_recoup_serial_id),
3391 : (unsigned long long) TALER_ARL_USE_PP (
3392 : coins_recoup_refresh_serial_id),
3393 : (unsigned long long) TALER_ARL_USE_PP (
3394 : coins_purse_deposits_serial_id),
3395 : (unsigned long long) TALER_ARL_USE_PP (
3396 : coins_purse_refunds_serial_id));
3397 10 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
3398 10 : cleanup:
3399 10 : flush_coin_histories ();
3400 10 : GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
3401 : &cleanup_denomination,
3402 : &cc);
3403 10 : GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
3404 10 : return qs;
3405 : }
3406 :
3407 :
3408 : /**
3409 : * Function called on events received from Postgres.
3410 : *
3411 : * @param cls closure, NULL
3412 : * @param extra additional event data provided
3413 : * @param extra_size number of bytes in @a extra
3414 : */
3415 : static void
3416 0 : db_notify (void *cls,
3417 : const void *extra,
3418 : size_t extra_size)
3419 : {
3420 : (void) cls;
3421 : (void) extra;
3422 : (void) extra_size;
3423 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3424 : "Received notification to wake coins helper\n");
3425 0 : if (GNUNET_OK !=
3426 0 : TALER_ARL_setup_sessions_and_run (&analyze_coins,
3427 : NULL))
3428 : {
3429 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3430 : "Audit failed\n");
3431 0 : GNUNET_SCHEDULER_shutdown ();
3432 0 : global_ret = EXIT_FAILURE;
3433 0 : return;
3434 : }
3435 : }
3436 :
3437 :
3438 : /**
3439 : * Function called on shutdown.
3440 : */
3441 : static void
3442 10 : do_shutdown (void *cls)
3443 : {
3444 : (void) cls;
3445 10 : if (NULL != eh)
3446 : {
3447 4 : TALER_AUDITORDB_event_listen_cancel (eh);
3448 4 : eh = NULL;
3449 : }
3450 10 : TALER_ARL_done ();
3451 10 : }
3452 :
3453 :
3454 : /**
3455 : * Main function that will be run.
3456 : *
3457 : * @param cls closure
3458 : * @param args remaining command-line arguments
3459 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
3460 : * @param c configuration
3461 : */
3462 : static void
3463 10 : run (void *cls,
3464 : char *const *args,
3465 : const char *cfgfile,
3466 : const struct GNUNET_CONFIGURATION_Handle *c)
3467 : {
3468 : (void) cls;
3469 : (void) args;
3470 : (void) cfgfile;
3471 10 : cfg = c;
3472 10 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
3473 : NULL);
3474 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3475 : "Launching coins auditor\n");
3476 10 : if (EXIT_SUCCESS !=
3477 10 : (global_ret = TALER_ARL_init (c)))
3478 : {
3479 0 : return;
3480 : }
3481 10 : if (test_mode != 1)
3482 : {
3483 4 : struct GNUNET_DB_EventHeaderP es = {
3484 4 : .size = htons (sizeof (es)),
3485 4 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_COINS)
3486 : };
3487 :
3488 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
3489 : "Running helper indefinitely\n");
3490 4 : eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
3491 : &es,
3492 4 : GNUNET_TIME_UNIT_FOREVER_REL,
3493 : &db_notify,
3494 : NULL);
3495 : }
3496 10 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3497 : "Starting audit\n");
3498 10 : if (GNUNET_OK !=
3499 10 : TALER_ARL_setup_sessions_and_run (&analyze_coins,
3500 : NULL))
3501 : {
3502 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
3503 : "Audit failed\n");
3504 0 : GNUNET_SCHEDULER_shutdown ();
3505 0 : global_ret = EXIT_FAILURE;
3506 0 : return;
3507 : }
3508 : }
3509 :
3510 :
3511 : /**
3512 : * The main function to audit operations on coins.
3513 : *
3514 : * @param argc number of arguments from the command line
3515 : * @param argv command line arguments
3516 : * @return 0 ok, 1 on error
3517 : */
3518 : int
3519 10 : main (int argc,
3520 : char *const *argv)
3521 : {
3522 10 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
3523 10 : GNUNET_GETOPT_option_flag ('i',
3524 : "internal",
3525 : "perform checks only applicable for exchange-internal audits",
3526 : &internal_checks),
3527 10 : GNUNET_GETOPT_option_flag ('t',
3528 : "test",
3529 : "run in test mode and exit when idle",
3530 : &test_mode),
3531 10 : GNUNET_GETOPT_option_timetravel ('T',
3532 : "timetravel"),
3533 : GNUNET_GETOPT_OPTION_END
3534 : };
3535 : enum GNUNET_GenericReturnValue ret;
3536 :
3537 10 : ret = GNUNET_PROGRAM_run (
3538 : TALER_AUDITOR_project_data (),
3539 : argc,
3540 : argv,
3541 : "taler-helper-auditor-coins",
3542 : gettext_noop ("Audit Taler coin processing"),
3543 : options,
3544 : &run,
3545 : NULL);
3546 10 : if (GNUNET_SYSERR == ret)
3547 0 : return EXIT_NOTCONFIGURED;
3548 10 : if (GNUNET_NO == ret)
3549 0 : return EXIT_SUCCESS;
3550 10 : return global_ret;
3551 : }
3552 :
3553 :
3554 : /* end of taler-helper-auditor-coins.c */
|