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