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