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