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