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