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