taler-helper-auditor-coins.c (105092B)
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 1136 */ 1137 static enum GNUNET_DB_QueryStatus 1138 check_known_coin ( 1139 const char *operation, 1140 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue, 1141 uint64_t rowid, 1142 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1143 const struct TALER_DenominationPublicKey *denom_pub, 1144 const struct TALER_Amount *loss_potential) 1145 { 1146 struct TALER_CoinPublicInfo ci; 1147 enum GNUNET_DB_QueryStatus qs; 1148 1149 if (NULL == get_cached_history (coin_pub)) 1150 { 1151 qs = check_coin_history (coin_pub, 1152 rowid, 1153 operation, 1154 &issue->value); 1155 if (0 > qs) 1156 { 1157 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1158 return qs; 1159 } 1160 GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); 1161 } 1162 1163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1164 "Checking denomination signature on %s\n", 1165 TALER_B2S (coin_pub)); 1166 qs = TALER_EXCHANGEDB_get_known_coin (TALER_ARL_edb, 1167 coin_pub, 1168 &ci); 1169 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1170 { 1171 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1172 return qs; 1173 } 1174 if (GNUNET_YES != 1175 TALER_test_coin_valid (&ci, 1176 denom_pub)) 1177 { 1178 struct TALER_AUDITORDB_BadSigLosses bsl = { 1179 .problem_row_id = rowid, 1180 .operation = (char *) operation, 1181 .loss = *loss_potential, 1182 .operation_specific_pub = coin_pub->eddsa_pub 1183 }; 1184 1185 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1186 "Failed to verify coin denomination signature in row %llu\n", 1187 (unsigned long long) rowid); 1188 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1189 TALER_ARL_adb, 1190 &bsl); 1191 if (qs < 0) 1192 { 1193 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1194 return qs; 1195 } 1196 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 1197 &TALER_ARL_USE_AB (coin_irregular_loss), 1198 loss_potential); 1199 } 1200 TALER_denom_sig_free (&ci.denom_sig); 1201 return qs; 1202 } 1203 1204 1205 /** 1206 * Update the denom balance in @a dso reducing it by 1207 * @a amount_with_fee. If this is not possible, report 1208 * an emergency. Also updates the balance. 1209 * 1210 * @param dso denomination summary to update 1211 * @param rowid responsible row (for logging) 1212 * @param amount_with_fee amount to subtract 1213 * @return transaction status 1214 */ 1215 static enum GNUNET_DB_QueryStatus 1216 reduce_denom_balance (struct DenominationSummary *dso, 1217 uint64_t rowid, 1218 const struct TALER_Amount *amount_with_fee) 1219 { 1220 struct TALER_Amount tmp; 1221 enum GNUNET_DB_QueryStatus qs; 1222 1223 if (TALER_ARL_SR_INVALID_NEGATIVE == 1224 TALER_ARL_amount_subtract_neg (&tmp, 1225 &dso->dcd.denom_balance, 1226 amount_with_fee)) 1227 { 1228 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1229 "Emergency: failed to reduce balance of denomination `%s' by %s\n", 1230 GNUNET_h2s (&dso->issue->denom_hash.hash), 1231 TALER_amount2s (amount_with_fee)); 1232 TALER_ARL_amount_add (&dso->dcd.denom_loss, 1233 &dso->dcd.denom_loss, 1234 amount_with_fee); 1235 dso->report_emergency = true; 1236 } 1237 else 1238 { 1239 dso->dcd.denom_balance = tmp; 1240 } 1241 if (-1 == TALER_amount_cmp (&TALER_ARL_USE_AB (total_escrowed), 1242 amount_with_fee)) 1243 { 1244 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1245 "Failed to total escrow by %s\n", 1246 TALER_amount2s (amount_with_fee)); 1247 /* This can theoretically happen if for example the exchange 1248 never issued any coins (i.e. escrow balance is zero), but 1249 accepted a forged coin (i.e. emergency situation after 1250 private key compromise). In that case, we cannot even 1251 subtract the profit we make from the fee from the escrow 1252 balance. Tested as part of test-auditor.sh, case #18 */ 1253 qs = report_amount_arithmetic_inconsistency ( 1254 "subtracting amount from escrow balance", 1255 rowid, 1256 &TALER_ARL_USE_AB (total_escrowed), 1257 amount_with_fee, 1258 0); 1259 if (0 > qs) 1260 { 1261 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1262 return qs; 1263 } 1264 } 1265 else 1266 { 1267 TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_escrowed), 1268 &TALER_ARL_USE_AB (total_escrowed), 1269 amount_with_fee); 1270 } 1271 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1272 "New balance of denomination `%s' is %s\n", 1273 GNUNET_h2s (&dso->issue->denom_hash.hash), 1274 TALER_amount2s (&dso->dcd.denom_balance)); 1275 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 1276 } 1277 1278 1279 /** 1280 * Function called with details about coins that were melted, with the 1281 * goal of auditing the refresh's execution. Verifies the signature 1282 * and updates our information about coins outstanding (the old coin's 1283 * denomination has less, the fresh coins increased outstanding 1284 * balances). 1285 * 1286 * @param cc closure 1287 * @param rowid unique serial ID for the refresh session in our DB 1288 * @param old_denom_pub denomination public key of @a coin_pub 1289 * @param coin_pub public key of the coin 1290 * @param coin_sig signature from the coin 1291 * @param h_age_commitment hash of the age commitment for the coin 1292 * @param amount_with_fee amount that was deposited including fee 1293 * @param num_nds length of the @a new_denom_serials array 1294 * @param new_denom_serials array of denomination serials of fresh coins 1295 * @param rc what the refresh commitment 1296 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1297 */ 1298 static enum GNUNET_GenericReturnValue 1299 refresh_session_cb (struct CoinContext *cc, 1300 uint64_t rowid, 1301 const struct TALER_DenominationPublicKey *old_denom_pub, 1302 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1303 const struct TALER_CoinSpendSignatureP *coin_sig, 1304 const struct TALER_AgeCommitmentHashP *h_age_commitment, 1305 const struct TALER_Amount *amount_with_fee, 1306 size_t num_nds, 1307 uint64_t new_denom_serials[static num_nds], 1308 const struct TALER_RefreshCommitmentP *rc) 1309 { 1310 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 1311 struct DenominationSummary *dso; 1312 enum GNUNET_DB_QueryStatus qs; 1313 struct TALER_DenominationHashP h_denom_pub; 1314 1315 GNUNET_assert (rowid >= 1316 TALER_ARL_USE_PP (coins_melt_serial_id)); /* should be monotonically increasing */ 1317 TALER_ARL_USE_PP (coins_melt_serial_id) = rowid + 1; 1318 qs = TALER_ARL_get_denomination_info (old_denom_pub, 1319 &issue, 1320 &h_denom_pub); 1321 if (0 > qs) 1322 { 1323 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1324 cc->qs = qs; 1325 return GNUNET_SYSERR; 1326 } 1327 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1328 { 1329 qs = report_row_inconsistency ("melt", 1330 rowid, 1331 "denomination key not found"); 1332 if (0 > qs) 1333 { 1334 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1335 cc->qs = qs; 1336 return GNUNET_SYSERR; 1337 } 1338 return GNUNET_OK; 1339 } 1340 qs = check_known_coin ("melt", 1341 issue, 1342 rowid, 1343 coin_pub, 1344 old_denom_pub, 1345 amount_with_fee); 1346 if (0 > qs) 1347 { 1348 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1349 cc->qs = qs; 1350 return GNUNET_SYSERR; 1351 } 1352 1353 /* verify melt signature */ 1354 if (GNUNET_OK != 1355 TALER_wallet_melt_verify (amount_with_fee, 1356 &issue->fees.refresh, 1357 rc, 1358 &h_denom_pub, 1359 h_age_commitment, 1360 coin_pub, 1361 coin_sig)) 1362 { 1363 struct TALER_AUDITORDB_BadSigLosses bsl = { 1364 .problem_row_id = rowid, 1365 .operation = (char *) "melt", 1366 .loss = *amount_with_fee, 1367 .operation_specific_pub = coin_pub->eddsa_pub 1368 }; 1369 1370 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1371 "Failed to verify coin melt signature in row %llu\n", 1372 (unsigned long long) rowid); 1373 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1374 TALER_ARL_adb, 1375 &bsl); 1376 if (qs < 0) 1377 { 1378 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1379 cc->qs = qs; 1380 return GNUNET_SYSERR; 1381 } 1382 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 1383 &TALER_ARL_USE_AB (coin_irregular_loss), 1384 amount_with_fee); 1385 } 1386 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1387 "Melting coin %s in denomination `%s' of value %s\n", 1388 TALER_B2S (coin_pub), 1389 GNUNET_h2s (&issue->denom_hash.hash), 1390 TALER_amount2s (amount_with_fee)); 1391 1392 { 1393 struct TALER_Amount refresh_cost; 1394 struct TALER_Amount amount_without_fee; 1395 const struct TALER_EXCHANGEDB_DenominationKeyInformation *nis[num_nds]; 1396 1397 /* Check that the resulting amounts are consistent with the value being 1398 refreshed by calculating the total refresh cost */ 1399 GNUNET_assert (GNUNET_OK == 1400 TALER_amount_set_zero (amount_with_fee->currency, 1401 &refresh_cost)); 1402 for (size_t i = 0; i < num_nds; i++) 1403 { 1404 qs = TALER_ARL_get_denomination_info_by_serial (new_denom_serials[i], 1405 &nis[i]); 1406 if (0 > qs) 1407 { 1408 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1409 cc->qs = qs; 1410 return GNUNET_SYSERR; 1411 } 1412 /* update cost of refresh */ 1413 TALER_ARL_amount_add (&refresh_cost, 1414 &refresh_cost, 1415 &nis[i]->fees.withdraw); 1416 TALER_ARL_amount_add (&refresh_cost, 1417 &refresh_cost, 1418 &nis[i]->value); 1419 } 1420 1421 /* compute contribution of old coin */ 1422 if (TALER_ARL_SR_POSITIVE != 1423 TALER_ARL_amount_subtract_neg (&amount_without_fee, 1424 amount_with_fee, 1425 &issue->fees.refresh)) 1426 { 1427 /* Melt fee higher than contribution of melted coin; this makes 1428 no sense (exchange should never have accepted the operation) */ 1429 qs = report_amount_arithmetic_inconsistency ("melt contribution vs. fee", 1430 rowid, 1431 amount_with_fee, 1432 &issue->fees.refresh, 1433 -1); 1434 if (0 > qs) 1435 { 1436 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1437 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1438 return GNUNET_SYSERR; 1439 } 1440 /* To continue, best assumption is the melted coin contributed 1441 nothing (=> all withdrawal amounts will be counted as losses) */ 1442 GNUNET_assert (GNUNET_OK == 1443 TALER_amount_set_zero (TALER_ARL_currency, 1444 &amount_without_fee)); 1445 } 1446 1447 /* check old coin covers complete expenses (of refresh operation) */ 1448 if (1 == TALER_amount_cmp (&refresh_cost, 1449 &amount_without_fee)) 1450 { 1451 /* refresh_cost > amount_without_fee, which is bad (exchange lost) */ 1452 GNUNET_break_op (0); 1453 qs = report_amount_arithmetic_inconsistency ("melt (cost)", 1454 rowid, 1455 &amount_without_fee, /* 'exchange' */ 1456 &refresh_cost, /* 'auditor' */ 1457 1); 1458 if (0 > qs) 1459 { 1460 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1461 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1462 return GNUNET_SYSERR; 1463 } 1464 } 1465 1466 /* update outstanding denomination amounts for fresh coins withdrawn */ 1467 for (size_t i = 0; i < num_nds; i++) 1468 { 1469 const struct TALER_EXCHANGEDB_DenominationKeyInformation *ni 1470 = nis[i]; 1471 struct DenominationSummary *dsi; 1472 1473 dsi = get_denomination_summary (cc, 1474 ni); 1475 if (NULL == dsi) 1476 { 1477 qs = report_row_inconsistency ("refresh_reveal", 1478 rowid, 1479 "denomination key for fresh coin unknown to auditor"); 1480 if (0 > qs) 1481 { 1482 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1483 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1484 return GNUNET_SYSERR; 1485 } 1486 } 1487 else 1488 { 1489 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1490 "Created fresh coin in denomination `%s' of value %s\n", 1491 GNUNET_h2s (&ni->denom_hash.hash), 1492 TALER_amount2s (&ni->value)); 1493 dsi->dcd.num_issued++; 1494 TALER_ARL_amount_add (&dsi->dcd.denom_balance, 1495 &dsi->dcd.denom_balance, 1496 &ni->value); 1497 TALER_ARL_amount_add (&dsi->dcd.denom_risk, 1498 &dsi->dcd.denom_risk, 1499 &ni->value); 1500 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1501 "New balance of denomination `%s' after refresh_reveal is %s\n", 1502 GNUNET_h2s (&ni->denom_hash.hash), 1503 TALER_amount2s (&dsi->dcd.denom_balance)); 1504 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed), 1505 &TALER_ARL_USE_AB (total_escrowed), 1506 &ni->value); 1507 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk), 1508 &TALER_ARL_USE_AB (coin_balance_risk), 1509 &ni->value); 1510 } 1511 } 1512 } 1513 1514 /* update old coin's denomination balance */ 1515 dso = get_denomination_summary (cc, 1516 issue); 1517 if (NULL == dso) 1518 { 1519 qs = report_row_inconsistency ("refresh_reveal", 1520 rowid, 1521 "denomination key for dirty coin unknown to auditor"); 1522 if (0 > qs) 1523 { 1524 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1525 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1526 return GNUNET_SYSERR; 1527 } 1528 } 1529 else 1530 { 1531 qs = reduce_denom_balance (dso, 1532 rowid, 1533 amount_with_fee); 1534 if (0 > qs) 1535 { 1536 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1537 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1538 return GNUNET_SYSERR; 1539 } 1540 } 1541 1542 /* update global melt fees */ 1543 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_melt_fee_revenue), 1544 &TALER_ARL_USE_AB (coin_melt_fee_revenue), 1545 &issue->fees.refresh); 1546 return GNUNET_OK; 1547 } 1548 1549 1550 /** 1551 * Function called with details about deposits that have been made, 1552 * with the goal of auditing the deposit's execution. 1553 * 1554 * @param cc closure 1555 * @param rowid unique serial ID for the deposit in our DB 1556 * @param exchange_timestamp when did the exchange get the deposit 1557 * @param deposit deposit details 1558 * @param denom_pub denomination public key of @a coin_pub 1559 * @param done flag set if the deposit was already executed (or not) 1560 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1561 */ 1562 static enum GNUNET_GenericReturnValue 1563 deposit_cb (struct CoinContext *cc, 1564 uint64_t rowid, 1565 struct GNUNET_TIME_Timestamp exchange_timestamp, 1566 const struct TALER_EXCHANGEDB_Deposit *deposit, 1567 const struct TALER_DenominationPublicKey *denom_pub, 1568 bool done) 1569 { 1570 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 1571 struct DenominationSummary *ds; 1572 enum GNUNET_DB_QueryStatus qs; 1573 1574 (void) done; 1575 (void) exchange_timestamp; 1576 GNUNET_assert (rowid >= 1577 TALER_ARL_USE_PP (coins_deposit_serial_id)); /* should be monotonically increasing */ 1578 TALER_ARL_USE_PP (coins_deposit_serial_id) = rowid + 1; 1579 1580 qs = TALER_ARL_get_denomination_info (denom_pub, 1581 &issue, 1582 NULL); 1583 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1584 { 1585 qs = report_row_inconsistency ("deposits", 1586 rowid, 1587 "denomination key not found"); 1588 if (0 > qs) 1589 { 1590 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1591 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1592 return GNUNET_SYSERR; 1593 } 1594 return GNUNET_OK; 1595 } 1596 if (GNUNET_TIME_timestamp_cmp (deposit->refund_deadline, 1597 >, 1598 deposit->wire_deadline)) 1599 { 1600 qs = report_row_inconsistency ("deposits", 1601 rowid, 1602 "refund deadline past wire deadline"); 1603 if (0 > qs) 1604 { 1605 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1606 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1607 return GNUNET_SYSERR; 1608 } 1609 } 1610 1611 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1612 { 1613 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1614 cc->qs = qs; 1615 return GNUNET_SYSERR; 1616 } 1617 qs = check_known_coin ("deposit", 1618 issue, 1619 rowid, 1620 &deposit->coin.coin_pub, 1621 denom_pub, 1622 &deposit->amount_with_fee); 1623 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1624 { 1625 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1626 cc->qs = qs; 1627 return GNUNET_SYSERR; 1628 } 1629 1630 /* Verify deposit signature */ 1631 { 1632 struct TALER_MerchantWireHashP h_wire; 1633 struct TALER_DenominationHashP h_denom_pub; 1634 1635 TALER_denom_pub_hash (denom_pub, 1636 &h_denom_pub); 1637 TALER_merchant_wire_signature_hash (deposit->receiver_wire_account, 1638 &deposit->wire_salt, 1639 &h_wire); 1640 /* NOTE: This is one of the operations we might eventually 1641 want to do in parallel in the background to improve 1642 auditor performance! */ 1643 if (GNUNET_OK != 1644 TALER_wallet_deposit_verify (&deposit->amount_with_fee, 1645 &issue->fees.deposit, 1646 &h_wire, 1647 &deposit->h_contract_terms, 1648 deposit->no_wallet_data_hash 1649 ? NULL 1650 : &deposit->wallet_data_hash, 1651 &deposit->coin.h_age_commitment, 1652 &deposit->h_policy, 1653 &h_denom_pub, 1654 deposit->timestamp, 1655 &deposit->merchant_pub, 1656 deposit->refund_deadline, 1657 &deposit->coin.coin_pub, 1658 &deposit->csig)) 1659 { 1660 struct TALER_AUDITORDB_BadSigLosses bsl = { 1661 .problem_row_id = rowid, 1662 .operation = (char *) "deposit", 1663 .loss = deposit->amount_with_fee, 1664 .operation_specific_pub = deposit->coin.coin_pub.eddsa_pub 1665 }; 1666 1667 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1668 "Failed to verify coin deposit signature in row %llu\n", 1669 (unsigned long long) rowid); 1670 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1671 TALER_ARL_adb, 1672 &bsl); 1673 if (0 > qs) 1674 { 1675 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1676 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1677 return GNUNET_SYSERR; 1678 } 1679 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 1680 &TALER_ARL_USE_AB (coin_irregular_loss), 1681 &deposit->amount_with_fee); 1682 return GNUNET_OK; 1683 } 1684 } 1685 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1686 "Deposited coin %s in denomination `%s' of value %s\n", 1687 TALER_B2S (&deposit->coin.coin_pub), 1688 GNUNET_h2s (&issue->denom_hash.hash), 1689 TALER_amount2s (&deposit->amount_with_fee)); 1690 1691 /* update old coin's denomination balance */ 1692 ds = get_denomination_summary (cc, 1693 issue); 1694 if (NULL == ds) 1695 { 1696 qs = report_row_inconsistency ("deposit", 1697 rowid, 1698 "denomination key for deposited coin unknown to auditor"); 1699 if (0 > qs) 1700 { 1701 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1702 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1703 return GNUNET_SYSERR; 1704 } 1705 } 1706 else 1707 { 1708 qs = reduce_denom_balance (ds, 1709 rowid, 1710 &deposit->amount_with_fee); 1711 if (0 > qs) 1712 { 1713 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1714 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1715 return GNUNET_SYSERR; 1716 } 1717 } 1718 1719 /* update global deposit fees */ 1720 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue), 1721 &TALER_ARL_USE_AB (coin_deposit_fee_revenue), 1722 &issue->fees.deposit); 1723 return GNUNET_OK; 1724 } 1725 1726 1727 /** 1728 * Function called with details about coins that were refunding, 1729 * with the goal of auditing the refund's execution. Adds the 1730 * refunded amount back to the outstanding balance of the respective 1731 * denomination. 1732 * 1733 * @param cc closure 1734 * @param rowid unique serial ID for the refund in our DB 1735 * @param denom_pub denomination public key of @a coin_pub 1736 * @param coin_pub public key of the coin 1737 * @param merchant_pub public key of the merchant 1738 * @param merchant_sig signature of the merchant 1739 * @param h_contract_terms hash of the proposal data known to merchant and customer 1740 * @param rtransaction_id refund transaction ID chosen by the merchant 1741 * @param full_refund true if the refunds total up to the entire deposited value 1742 * @param amount_with_fee amount that was deposited including fee 1743 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1744 */ 1745 static enum GNUNET_GenericReturnValue 1746 refund_cb (struct CoinContext *cc, 1747 uint64_t rowid, 1748 const struct TALER_DenominationPublicKey *denom_pub, 1749 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1750 const struct TALER_MerchantPublicKeyP *merchant_pub, 1751 const struct TALER_MerchantSignatureP *merchant_sig, 1752 const struct TALER_PrivateContractHashP *h_contract_terms, 1753 uint64_t rtransaction_id, 1754 bool full_refund, 1755 const struct TALER_Amount *amount_with_fee) 1756 { 1757 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 1758 struct DenominationSummary *ds; 1759 struct TALER_Amount amount_without_fee; 1760 enum GNUNET_DB_QueryStatus qs; 1761 1762 GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_refund_serial_id)); /* should be monotonically increasing */ 1763 TALER_ARL_USE_PP (coins_refund_serial_id) = rowid + 1; 1764 1765 qs = TALER_ARL_get_denomination_info (denom_pub, 1766 &issue, 1767 NULL); 1768 if (0 > qs) 1769 { 1770 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1771 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1772 return GNUNET_SYSERR; 1773 } 1774 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1775 { 1776 qs = report_row_inconsistency ("refunds", 1777 rowid, 1778 "denomination key not found"); 1779 if (0 > qs) 1780 { 1781 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1782 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1783 return GNUNET_SYSERR; 1784 } 1785 return GNUNET_OK; 1786 } 1787 1788 /* verify refund signature */ 1789 if (GNUNET_OK != 1790 TALER_merchant_refund_verify (coin_pub, 1791 h_contract_terms, 1792 rtransaction_id, 1793 amount_with_fee, 1794 merchant_pub, 1795 merchant_sig)) 1796 { 1797 struct TALER_AUDITORDB_BadSigLosses bsl = { 1798 .problem_row_id = rowid, 1799 .operation = (char *) "refund", 1800 .loss = *amount_with_fee, 1801 .operation_specific_pub = coin_pub->eddsa_pub 1802 }; 1803 1804 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1805 "Failed to verify merchant refund signature in row %llu\n", 1806 (unsigned long long) rowid); 1807 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1808 TALER_ARL_adb, 1809 &bsl); 1810 if (0 > qs) 1811 { 1812 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1813 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1814 return GNUNET_SYSERR; 1815 } 1816 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 1817 &TALER_ARL_USE_AB (coin_irregular_loss), 1818 amount_with_fee); 1819 return GNUNET_OK; 1820 } 1821 1822 if (TALER_ARL_SR_INVALID_NEGATIVE == 1823 TALER_ARL_amount_subtract_neg (&amount_without_fee, 1824 amount_with_fee, 1825 &issue->fees.refund)) 1826 { 1827 qs = report_amount_arithmetic_inconsistency ("refund (fee)", 1828 rowid, 1829 amount_with_fee, 1830 &issue->fees.refund, 1831 -1); 1832 if (0 > qs) 1833 { 1834 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1835 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1836 return GNUNET_SYSERR; 1837 } 1838 return GNUNET_OK; 1839 } 1840 1841 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1842 "Refunding coin %s in denomination `%s' value %s\n", 1843 TALER_B2S (coin_pub), 1844 GNUNET_h2s (&issue->denom_hash.hash), 1845 TALER_amount2s (amount_with_fee)); 1846 1847 /* update coin's denomination balance */ 1848 ds = get_denomination_summary (cc, 1849 issue); 1850 if (NULL == ds) 1851 { 1852 qs = report_row_inconsistency ("refund", 1853 rowid, 1854 "denomination key for refunded coin unknown to auditor"); 1855 if (0 > qs) 1856 { 1857 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1858 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1859 return GNUNET_SYSERR; 1860 } 1861 } 1862 else 1863 { 1864 TALER_ARL_amount_add (&ds->dcd.denom_balance, 1865 &ds->dcd.denom_balance, 1866 &amount_without_fee); 1867 TALER_ARL_amount_add (&ds->dcd.denom_risk, 1868 &ds->dcd.denom_risk, 1869 &amount_without_fee); 1870 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed), 1871 &TALER_ARL_USE_AB (total_escrowed), 1872 &amount_without_fee); 1873 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk), 1874 &TALER_ARL_USE_AB (coin_balance_risk), 1875 &amount_without_fee); 1876 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1877 "New balance of denomination `%s' after refund is %s\n", 1878 GNUNET_h2s (&issue->denom_hash.hash), 1879 TALER_amount2s (&ds->dcd.denom_balance)); 1880 } 1881 /* update total refund fee balance */ 1882 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_refund_fee_revenue), 1883 &TALER_ARL_USE_AB (coin_refund_fee_revenue), 1884 &issue->fees.refund); 1885 if (full_refund) 1886 { 1887 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss), 1888 &TALER_ARL_USE_AB (coin_deposit_fee_loss), 1889 &issue->fees.deposit); 1890 } 1891 return GNUNET_OK; 1892 } 1893 1894 1895 /** 1896 * Function called with details about purse refunds that have been made, with 1897 * the goal of auditing the purse refund's execution. 1898 * 1899 * @param cc closure 1900 * @param rowid row of the purse-refund 1901 * @param amount_with_fee amount of the deposit into the purse 1902 * @param coin_pub coin that is to be refunded the @a given amount_with_fee 1903 * @param denom_pub denomination of @a coin_pub 1904 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1905 */ 1906 static enum GNUNET_GenericReturnValue 1907 purse_refund_coin_cb ( 1908 struct CoinContext *cc, 1909 uint64_t rowid, 1910 const struct TALER_Amount *amount_with_fee, 1911 const struct TALER_CoinSpendPublicKeyP *coin_pub, 1912 const struct TALER_DenominationPublicKey *denom_pub) 1913 { 1914 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 1915 struct DenominationSummary *ds; 1916 enum GNUNET_DB_QueryStatus qs; 1917 1918 qs = TALER_ARL_get_denomination_info (denom_pub, 1919 &issue, 1920 NULL); 1921 if (0 > qs) 1922 { 1923 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1924 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1925 return GNUNET_SYSERR; 1926 } 1927 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1928 { 1929 qs = report_row_inconsistency ("purse-refunds", 1930 rowid, 1931 "denomination key not found"); 1932 if (0 > qs) 1933 { 1934 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1935 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1936 return GNUNET_SYSERR; 1937 } 1938 return GNUNET_OK; 1939 } 1940 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1941 "Aborted purse-deposit of coin %s in denomination `%s' value %s\n", 1942 TALER_B2S (coin_pub), 1943 GNUNET_h2s (&issue->denom_hash.hash), 1944 TALER_amount2s (amount_with_fee)); 1945 1946 /* update coin's denomination balance */ 1947 ds = get_denomination_summary (cc, 1948 issue); 1949 if (NULL == ds) 1950 { 1951 qs = report_row_inconsistency ("purse-refund", 1952 rowid, 1953 "denomination key for purse-refunded coin unknown to auditor"); 1954 if (0 > qs) 1955 { 1956 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1957 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 1958 return GNUNET_SYSERR; 1959 } 1960 } 1961 else 1962 { 1963 TALER_ARL_amount_add (&ds->dcd.denom_balance, 1964 &ds->dcd.denom_balance, 1965 amount_with_fee); 1966 TALER_ARL_amount_add (&ds->dcd.denom_risk, 1967 &ds->dcd.denom_risk, 1968 amount_with_fee); 1969 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed), 1970 &TALER_ARL_USE_AB (total_escrowed), 1971 amount_with_fee); 1972 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk), 1973 &TALER_ARL_USE_AB (coin_balance_risk), 1974 amount_with_fee); 1975 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1976 "New balance of denomination `%s' after purse-refund is %s\n", 1977 GNUNET_h2s (&issue->denom_hash.hash), 1978 TALER_amount2s (&ds->dcd.denom_balance)); 1979 } 1980 /* update total deposit fee balance */ 1981 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss), 1982 &TALER_ARL_USE_AB (coin_deposit_fee_loss), 1983 &issue->fees.deposit); 1984 1985 return GNUNET_OK; 1986 } 1987 1988 1989 /** 1990 * Function called with details about a purse that was refunded. Adds the 1991 * refunded amounts back to the outstanding balance of the respective 1992 * denominations. 1993 * 1994 * @param cc closure 1995 * @param rowid unique serial ID for the refund in our DB 1996 * @param purse_pub public key of the purse 1997 * @param reserve_pub public key of the targeted reserve (ignored) 1998 * @param val targeted amount to be in the reserve (ignored) 1999 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 2000 */ 2001 static enum GNUNET_GenericReturnValue 2002 purse_refund_cb (struct CoinContext *cc, 2003 uint64_t rowid, 2004 const struct TALER_PurseContractPublicKeyP *purse_pub, 2005 const struct TALER_ReservePublicKeyP *reserve_pub, 2006 const struct TALER_Amount *val) 2007 { 2008 enum GNUNET_DB_QueryStatus qs; 2009 2010 (void) val; /* irrelevant on refund */ 2011 (void) reserve_pub; /* irrelevant, may even be NULL */ 2012 GNUNET_assert (rowid >= 2013 TALER_ARL_USE_PP (coins_purse_refunds_serial_id)); /* should be monotonically increasing */ 2014 TALER_ARL_USE_PP (coins_purse_refunds_serial_id) = rowid + 1; 2015 qs = TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (TALER_ARL_edb, 2016 purse_pub, 2017 & 2018 purse_refund_coin_cb, 2019 cc); 2020 if (qs < 0) 2021 { 2022 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2023 return GNUNET_SYSERR; 2024 } 2025 return GNUNET_OK; 2026 } 2027 2028 2029 /** 2030 * Check that the recoup operation was properly initiated by a coin 2031 * and update the denomination's losses accordingly. 2032 * 2033 * @param cc the context with details about the coin 2034 * @param operation name of the operation matching @a rowid 2035 * @param rowid row identifier used to uniquely identify the recoup operation 2036 * @param amount how much should be added back to the reserve 2037 * @param coin public information about the coin 2038 * @param denom_pub public key of the denomionation of @a coin 2039 * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP 2040 * @param coin_blind blinding factor used to blind the coin 2041 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 2042 */ 2043 static enum GNUNET_GenericReturnValue 2044 check_recoup (struct CoinContext *cc, 2045 const char *operation, 2046 uint64_t rowid, 2047 const struct TALER_Amount *amount, 2048 const struct TALER_CoinPublicInfo *coin, 2049 const struct TALER_DenominationPublicKey *denom_pub, 2050 const struct TALER_CoinSpendSignatureP *coin_sig, 2051 const union GNUNET_CRYPTO_BlindingSecretP *coin_blind) 2052 { 2053 struct DenominationSummary *ds; 2054 enum GNUNET_DB_QueryStatus qs; 2055 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 2056 2057 if (GNUNET_OK != 2058 TALER_wallet_recoup_verify (&coin->denom_pub_hash, 2059 coin_blind, 2060 &coin->coin_pub, 2061 coin_sig)) 2062 { 2063 qs = report_row_inconsistency (operation, 2064 rowid, 2065 "recoup signature invalid"); 2066 if (0 > qs) 2067 { 2068 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2069 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 2070 return GNUNET_SYSERR; 2071 } 2072 } 2073 if (GNUNET_OK != 2074 TALER_test_coin_valid (coin, 2075 denom_pub)) 2076 { 2077 struct TALER_AUDITORDB_BadSigLosses bsl = { 2078 .problem_row_id = rowid, 2079 .operation = (char *) operation, 2080 .loss = *amount, 2081 .operation_specific_pub = coin->coin_pub.eddsa_pub 2082 }; 2083 2084 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2085 "Failed to verify coin signature in row %llu\n", 2086 (unsigned long long) rowid); 2087 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 2088 TALER_ARL_adb, 2089 &bsl); 2090 2091 if (0 > qs) 2092 { 2093 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2094 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 2095 return GNUNET_SYSERR; 2096 } 2097 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 2098 &TALER_ARL_USE_AB (coin_irregular_loss), 2099 amount); 2100 } 2101 qs = TALER_ARL_get_denomination_info_by_hash (&coin->denom_pub_hash, 2102 &issue); 2103 if (0 > qs) 2104 { 2105 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2106 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 2107 return GNUNET_SYSERR; 2108 } 2109 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 2110 { 2111 qs = report_row_inconsistency (operation, 2112 rowid, 2113 "denomination key not found"); 2114 if (0 > qs) 2115 { 2116 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2117 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 2118 return GNUNET_SYSERR; 2119 } 2120 return GNUNET_OK; 2121 } 2122 qs = check_known_coin (operation, 2123 issue, 2124 rowid, 2125 &coin->coin_pub, 2126 denom_pub, 2127 amount); 2128 if (0 > qs) 2129 { 2130 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2131 cc->qs = qs; 2132 return GNUNET_SYSERR; 2133 } 2134 ds = get_denomination_summary (cc, 2135 issue); 2136 if (NULL == ds) 2137 { 2138 qs = report_row_inconsistency ("recoup", 2139 rowid, 2140 "denomination key for recouped coin unknown to auditor"); 2141 if (0 > qs) 2142 { 2143 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2144 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 2145 return GNUNET_SYSERR; 2146 } 2147 } 2148 else 2149 { 2150 if (! ds->was_revoked) 2151 { 2152 struct TALER_AUDITORDB_BadSigLosses bsldnr = { 2153 .problem_row_id = rowid, 2154 .operation = (char *) operation, 2155 .loss = *amount, 2156 .operation_specific_pub = coin->coin_pub.eddsa_pub 2157 }; 2158 2159 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2160 "Recoup allowed on non-revoked denomination in row %llu\n", 2161 (unsigned long long) rowid); 2162 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 2163 TALER_ARL_adb, 2164 &bsldnr); 2165 2166 if (qs < 0) 2167 { 2168 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2169 cc->qs = qs; 2170 return GNUNET_SYSERR; 2171 } 2172 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 2173 &TALER_ARL_USE_AB (coin_irregular_loss), 2174 amount); 2175 } 2176 TALER_ARL_amount_add (&ds->dcd.recoup_loss, 2177 &ds->dcd.recoup_loss, 2178 amount); 2179 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_recoup_loss), 2180 &TALER_ARL_USE_AB (total_recoup_loss), 2181 amount); 2182 } 2183 return GNUNET_OK; 2184 } 2185 2186 2187 /** 2188 * Function called about recoups the exchange has to perform. 2189 * 2190 * @param cc a `struct CoinContext *` 2191 * @param rowid row identifier used to uniquely identify the recoup operation 2192 * @param timestamp when did we receive the recoup request 2193 * @param amount how much should be added back to the reserve 2194 * @param reserve_pub public key of the reserve 2195 * @param coin public information about the coin 2196 * @param denom_pub denomination public key of @a coin 2197 * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP 2198 * @param coin_blind blinding factor used to blind the coin 2199 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 2200 */ 2201 static enum GNUNET_GenericReturnValue 2202 recoup_cb (struct CoinContext *cc, 2203 uint64_t rowid, 2204 struct GNUNET_TIME_Timestamp timestamp, 2205 const struct TALER_Amount *amount, 2206 const struct TALER_ReservePublicKeyP *reserve_pub, 2207 const struct TALER_CoinPublicInfo *coin, 2208 const struct TALER_DenominationPublicKey *denom_pub, 2209 const struct TALER_CoinSpendSignatureP *coin_sig, 2210 const union GNUNET_CRYPTO_BlindingSecretP *coin_blind) 2211 { 2212 enum GNUNET_DB_QueryStatus qs; 2213 2214 GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_serial_id)); /* should be monotonically increasing */ 2215 TALER_ARL_USE_PP (coins_recoup_serial_id) = rowid + 1; 2216 (void) timestamp; 2217 (void) reserve_pub; 2218 if (GNUNET_OK != 2219 TALER_wallet_recoup_verify (&coin->denom_pub_hash, 2220 coin_blind, 2221 &coin->coin_pub, 2222 coin_sig)) 2223 { 2224 struct TALER_AUDITORDB_BadSigLosses bsl = { 2225 .problem_row_id = rowid, 2226 .operation = (char *) "recoup", 2227 .loss = *amount, 2228 .operation_specific_pub = coin->coin_pub.eddsa_pub 2229 }; 2230 2231 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2232 "Failed to verify recoup signature in row %llu\n", 2233 (unsigned long long) rowid); 2234 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 2235 TALER_ARL_adb, 2236 &bsl); 2237 if (qs < 0) 2238 { 2239 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2240 cc->qs = qs; 2241 return GNUNET_SYSERR; 2242 } 2243 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 2244 &TALER_ARL_USE_AB (coin_irregular_loss), 2245 amount); 2246 return GNUNET_OK; 2247 } 2248 return check_recoup (cc, 2249 "recoup", 2250 rowid, 2251 amount, 2252 coin, 2253 denom_pub, 2254 coin_sig, 2255 coin_blind); 2256 } 2257 2258 2259 #if FIXME_9828 2260 /** 2261 * Function called about recoups on refreshed coins the exchange had to 2262 * perform. Updates the denomination balance(s). Does not change the 2263 * coin balances, as those are already updated when we check the coin 2264 * history. 2265 * 2266 * @param cc a `struct CoinContext *` 2267 * @param rowid row identifier used to uniquely identify the recoup operation 2268 * @param timestamp when did we receive the recoup request 2269 * @param amount how much should be added back to the old coin 2270 * @param old_coin_pub original coin that was refreshed to create @a coin 2271 * @param old_denom_pub_hash hash of the public key of @a old_coin_pub 2272 * @param coin public information about the fresh coin 2273 * @param denom_pub denomination public key of @a coin 2274 * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP 2275 * @param coin_blind blinding factor used to blind the coin 2276 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 2277 */ 2278 static enum GNUNET_GenericReturnValue 2279 recoup_refresh_cb (struct CoinContext *cc, 2280 uint64_t rowid, 2281 struct GNUNET_TIME_Timestamp timestamp, 2282 const struct TALER_Amount *amount, 2283 const struct TALER_CoinSpendPublicKeyP *old_coin_pub, 2284 const struct TALER_DenominationHashP *old_denom_pub_hash, 2285 const struct TALER_CoinPublicInfo *coin, 2286 const struct TALER_DenominationPublicKey *denom_pub, 2287 const struct TALER_CoinSpendSignatureP *coin_sig, 2288 const union GNUNET_CRYPTO_BlindingSecretP *coin_blind) 2289 { 2290 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 2291 enum GNUNET_DB_QueryStatus qs; 2292 2293 (void) timestamp; 2294 (void) old_coin_pub; 2295 GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_refresh_serial_id)); /* should be monotonically increasing */ 2296 TALER_ARL_USE_PP (coins_recoup_refresh_serial_id) = rowid + 1; 2297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2298 "Recoup-refresh amount is %s\n", 2299 TALER_amount2s (amount)); 2300 2301 /* Update old coin's denomination balance summary */ 2302 qs = TALER_ARL_get_denomination_info_by_hash (old_denom_pub_hash, 2303 &issue); 2304 if (qs < 0) 2305 { 2306 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2307 cc->qs = qs; 2308 return GNUNET_SYSERR; 2309 } 2310 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 2311 { 2312 qs = report_row_inconsistency ("refresh-recoup", 2313 rowid, 2314 "denomination key of old coin not found"); 2315 if (qs < 0) 2316 { 2317 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2318 cc->qs = qs; 2319 return GNUNET_SYSERR; 2320 } 2321 } 2322 2323 { 2324 struct DenominationSummary *dso; 2325 2326 dso = get_denomination_summary (cc, 2327 issue); 2328 if (NULL == dso) 2329 { 2330 qs = report_row_inconsistency ("refresh_reveal", 2331 rowid, 2332 "denomination key for old coin unknown to auditor"); 2333 if (qs < 0) 2334 { 2335 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2336 cc->qs = qs; 2337 return GNUNET_SYSERR; 2338 } 2339 } 2340 else 2341 { 2342 TALER_ARL_amount_add (&dso->dcd.denom_balance, 2343 &dso->dcd.denom_balance, 2344 amount); 2345 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2346 "New balance of denomination `%s' after refresh-recoup is %s\n", 2347 GNUNET_h2s (&issue->denom_hash.hash), 2348 TALER_amount2s (&dso->dcd.denom_balance)); 2349 } 2350 } 2351 2352 if (GNUNET_OK != 2353 TALER_wallet_recoup_refresh_verify (&coin->denom_pub_hash, 2354 coin_blind, 2355 &coin->coin_pub, 2356 coin_sig)) 2357 { 2358 struct TALER_AUDITORDB_BadSigLosses bsl = { 2359 .problem_row_id = rowid, 2360 .operation = (char *) "recoup-refresh", 2361 .loss = *amount, 2362 .operation_specific_pub = coin->coin_pub.eddsa_pub 2363 }; 2364 2365 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2366 "Failed to verify recoup-refresh signature in row %llu\n", 2367 (unsigned long long) rowid); 2368 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 2369 TALER_ARL_adb, 2370 &bsl); 2371 if (qs < 0) 2372 { 2373 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2374 cc->qs = qs; 2375 return GNUNET_SYSERR; 2376 } 2377 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 2378 &TALER_ARL_USE_AB (coin_irregular_loss), 2379 amount); 2380 return GNUNET_OK; 2381 } 2382 return check_recoup (cc, 2383 "recoup-refresh", 2384 rowid, 2385 amount, 2386 coin, 2387 denom_pub, 2388 coin_sig, 2389 coin_blind); 2390 } 2391 2392 2393 #endif 2394 2395 2396 /** 2397 * Function called with the results of iterate_denomination_info(), 2398 * or directly (!). Used to check that we correctly signed the 2399 * denomination and to warn if there are denominations not approved 2400 * by this auditor. 2401 * 2402 * @param iqs closure, pointer to `enum GNUNET_DB_QueryStatus` 2403 * @param denom_serial row ID of the denominations table of the exchange DB 2404 * @param denom_pub public key, sometimes NULL (!) 2405 * @param issue issuing information with value, fees and other info about the denomination. 2406 */ 2407 static void 2408 check_denomination ( 2409 enum GNUNET_DB_QueryStatus *iqs, 2410 uint64_t denom_serial, 2411 const struct TALER_DenominationPublicKey *denom_pub, 2412 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue) 2413 { 2414 enum GNUNET_DB_QueryStatus qs; 2415 struct TALER_AuditorSignatureP auditor_sig; 2416 2417 (void) denom_pub; 2418 qs = TALER_EXCHANGEDB_get_auditor_denom_sig (TALER_ARL_edb, 2419 &issue->denom_hash, 2420 &TALER_ARL_auditor_pub, 2421 &auditor_sig); 2422 if (0 > qs) 2423 { 2424 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2425 *iqs = qs; 2426 return; 2427 } 2428 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 2429 { 2430 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2431 "Encountered denomination `%s' (%s) valid from %s (%llu-%llu) that this auditor is not auditing!\n", 2432 GNUNET_h2s (&issue->denom_hash.hash), 2433 TALER_amount2s (&issue->value), 2434 GNUNET_TIME_timestamp2s (issue->start), 2435 (unsigned long long) issue->start.abs_time.abs_value_us, 2436 (unsigned long long) issue->expire_legal.abs_time.abs_value_us); 2437 return; /* skip! */ 2438 } 2439 if (GNUNET_OK != 2440 TALER_auditor_denom_validity_verify ( 2441 TALER_ARL_auditor_url, 2442 &issue->denom_hash, 2443 &TALER_ARL_master_pub, 2444 issue->start, 2445 issue->expire_withdraw, 2446 issue->expire_deposit, 2447 issue->expire_legal, 2448 &issue->value, 2449 &issue->fees, 2450 &TALER_ARL_auditor_pub, 2451 &auditor_sig)) 2452 { 2453 struct TALER_AUDITORDB_DenominationsWithoutSigs dws = { 2454 .denompub_h = issue->denom_hash, 2455 .start_time = issue->start.abs_time, 2456 .end_time = issue->expire_legal.abs_time, 2457 .value = issue->value 2458 }; 2459 2460 qs = TALER_AUDITORDB_insert_denominations_without_sigs ( 2461 TALER_ARL_adb, 2462 &dws); 2463 2464 if (qs < 0) 2465 { 2466 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2467 *iqs = qs; 2468 return; 2469 } 2470 } 2471 *iqs = qs; 2472 } 2473 2474 2475 /** 2476 * Function called with details about purse deposits that have been made, with 2477 * the goal of auditing the deposit's execution. 2478 * 2479 * @param cc closure 2480 * @param rowid unique serial ID for the deposit in our DB 2481 * @param deposit deposit details 2482 * @param reserve_pub which reserve is the purse merged into, NULL if unknown 2483 * @param flags purse flags 2484 * @param auditor_balance purse balance (according to the 2485 * auditor during auditing) 2486 * @param purse_total target amount the purse should reach 2487 * @param denom_pub denomination public key of @a coin_pub 2488 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 2489 */ 2490 static enum GNUNET_GenericReturnValue 2491 purse_deposit_cb ( 2492 struct CoinContext *cc, 2493 uint64_t rowid, 2494 const struct TALER_EXCHANGEDB_PurseDeposit *deposit, 2495 const struct TALER_ReservePublicKeyP *reserve_pub, 2496 enum TALER_WalletAccountMergeFlags flags, 2497 const struct TALER_Amount *auditor_balance, 2498 const struct TALER_Amount *purse_total, 2499 const struct TALER_DenominationPublicKey *denom_pub) 2500 { 2501 enum GNUNET_DB_QueryStatus qs; 2502 struct TALER_DenominationHashP dh; 2503 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 2504 struct DenominationSummary *ds; 2505 2506 (void) flags; 2507 (void) auditor_balance; 2508 (void) purse_total; 2509 (void) reserve_pub; 2510 GNUNET_assert (rowid >= 2511 TALER_ARL_USE_PP (coins_purse_deposits_serial_id)); 2512 TALER_ARL_USE_PP (coins_purse_deposits_serial_id) = rowid + 1; 2513 qs = TALER_ARL_get_denomination_info (denom_pub, 2514 &issue, 2515 &dh); 2516 if (0 > qs) 2517 { 2518 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2519 cc->qs = qs; 2520 return GNUNET_SYSERR; 2521 } 2522 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 2523 { 2524 qs = report_row_inconsistency ("purse-deposits", 2525 rowid, 2526 "denomination key not found"); 2527 if (0 > qs) 2528 { 2529 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2530 cc->qs = qs; 2531 return GNUNET_SYSERR; 2532 } 2533 return GNUNET_OK; 2534 } 2535 qs = check_known_coin ("purse-deposit", 2536 issue, 2537 rowid, 2538 &deposit->coin_pub, 2539 denom_pub, 2540 &deposit->amount); 2541 if (0 > qs) 2542 { 2543 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2544 cc->qs = qs; 2545 return GNUNET_SYSERR; 2546 } 2547 2548 if (GNUNET_OK != 2549 TALER_wallet_purse_deposit_verify ( 2550 NULL != deposit->exchange_base_url 2551 ? deposit->exchange_base_url 2552 : TALER_ARL_exchange_url, 2553 &deposit->purse_pub, 2554 &deposit->amount, 2555 &dh, 2556 &deposit->h_age_commitment, 2557 &deposit->coin_pub, 2558 &deposit->coin_sig)) 2559 { 2560 struct TALER_AUDITORDB_BadSigLosses bsl = { 2561 .problem_row_id = rowid, 2562 .operation = (char *) "purse-deposit", 2563 .loss = deposit->amount, 2564 .operation_specific_pub = deposit->coin_pub.eddsa_pub 2565 }; 2566 2567 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2568 "Failed to verify purse deposit signature in row %llu\n", 2569 (unsigned long long) rowid); 2570 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 2571 TALER_ARL_adb, 2572 &bsl); 2573 if (0 > qs) 2574 { 2575 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2576 cc->qs = qs; 2577 return GNUNET_SYSERR; 2578 } 2579 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss), 2580 &TALER_ARL_USE_AB (coin_irregular_loss), 2581 &deposit->amount); 2582 return GNUNET_OK; 2583 } 2584 2585 /* update coin's denomination balance */ 2586 ds = get_denomination_summary (cc, 2587 issue); 2588 if (NULL == ds) 2589 { 2590 qs = report_row_inconsistency ("purse-deposit", 2591 rowid, 2592 "denomination key for purse-deposited coin unknown to auditor"); 2593 if (0 > qs) 2594 { 2595 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2596 cc->qs = qs; 2597 return GNUNET_SYSERR; 2598 } 2599 } 2600 else 2601 { 2602 qs = reduce_denom_balance (ds, 2603 rowid, 2604 &deposit->amount); 2605 if (0 > qs) 2606 { 2607 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2608 cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 2609 return GNUNET_SYSERR; 2610 } 2611 } 2612 2613 /* update global deposit fees */ 2614 TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue), 2615 &TALER_ARL_USE_AB (coin_deposit_fee_revenue), 2616 &issue->fees.deposit); 2617 return GNUNET_OK; 2618 } 2619 2620 2621 /** 2622 * Analyze the exchange's processing of coins. 2623 * 2624 * @param cls closure 2625 * @return transaction status code 2626 */ 2627 static enum GNUNET_DB_QueryStatus 2628 analyze_coins (void *cls) 2629 { 2630 struct CoinContext cc; 2631 enum GNUNET_DB_QueryStatus qs; 2632 enum GNUNET_DB_QueryStatus iqs; 2633 2634 (void) cls; 2635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2636 "Checking denominations...\n"); 2637 iqs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 2638 qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb, 2639 &check_denomination, 2640 &iqs); 2641 if (0 > qs) 2642 { 2643 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2644 return qs; 2645 } 2646 if (0 > iqs) 2647 { 2648 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == iqs); 2649 return iqs; 2650 } 2651 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2652 "Analyzing coins\n"); 2653 qs = TALER_AUDITORDB_get_auditor_progress ( 2654 TALER_ARL_adb, 2655 TALER_ARL_GET_PP (coins_withdraw_serial_id), 2656 TALER_ARL_GET_PP (coins_deposit_serial_id), 2657 TALER_ARL_GET_PP (coins_melt_serial_id), 2658 TALER_ARL_GET_PP (coins_refund_serial_id), 2659 TALER_ARL_GET_PP (coins_recoup_serial_id), 2660 TALER_ARL_GET_PP (coins_recoup_refresh_serial_id), 2661 TALER_ARL_GET_PP (coins_purse_deposits_serial_id), 2662 TALER_ARL_GET_PP (coins_purse_refunds_serial_id), 2663 NULL); 2664 if (0 > qs) 2665 { 2666 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2667 return qs; 2668 } 2669 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 2670 { 2671 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 2672 "First analysis using this auditor, starting from scratch\n"); 2673 } 2674 else 2675 { 2676 GNUNET_log ( 2677 GNUNET_ERROR_TYPE_INFO, 2678 "Resuming coin audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", 2679 (unsigned long long) TALER_ARL_USE_PP ( 2680 coins_withdraw_serial_id), 2681 (unsigned long long) TALER_ARL_USE_PP ( 2682 coins_deposit_serial_id), 2683 (unsigned long long) TALER_ARL_USE_PP ( 2684 coins_melt_serial_id), 2685 (unsigned long long) TALER_ARL_USE_PP ( 2686 coins_refund_serial_id), 2687 (unsigned long long) TALER_ARL_USE_PP ( 2688 coins_recoup_serial_id), 2689 (unsigned long long) TALER_ARL_USE_PP ( 2690 coins_recoup_refresh_serial_id), 2691 (unsigned long long) TALER_ARL_USE_PP ( 2692 coins_purse_deposits_serial_id), 2693 (unsigned long long) TALER_ARL_USE_PP ( 2694 coins_purse_refunds_serial_id)); 2695 } 2696 2697 /* setup 'cc' */ 2698 cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 2699 cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256, 2700 GNUNET_NO); 2701 qs = TALER_AUDITORDB_get_balance ( 2702 TALER_ARL_adb, 2703 TALER_ARL_GET_AB (coin_balance_risk), 2704 TALER_ARL_GET_AB (total_escrowed), 2705 TALER_ARL_GET_AB (coin_irregular_loss), 2706 TALER_ARL_GET_AB (coin_melt_fee_revenue), 2707 TALER_ARL_GET_AB (coin_deposit_fee_revenue), 2708 TALER_ARL_GET_AB (coin_deposit_fee_loss), 2709 TALER_ARL_GET_AB (coin_refund_fee_revenue), 2710 TALER_ARL_GET_AB (total_recoup_loss), 2711 TALER_ARL_GET_AB (coins_total_arithmetic_delta_plus), 2712 TALER_ARL_GET_AB (coins_total_arithmetic_delta_minus), 2713 TALER_ARL_GET_AB (coins_reported_emergency_risk_by_count), 2714 TALER_ARL_GET_AB (coins_reported_emergency_risk_by_amount), 2715 TALER_ARL_GET_AB (coins_emergencies_loss), 2716 TALER_ARL_GET_AB (coins_emergencies_loss_by_count), 2717 NULL); 2718 if (0 > qs) 2719 { 2720 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2721 goto cleanup; 2722 } 2723 /* process withdrawals */ 2724 if (0 > 2725 (qs = TALER_EXCHANGEDB_iterate_withdrawals_above_serial_id ( 2726 TALER_ARL_edb, 2727 TALER_ARL_USE_PP (coins_withdraw_serial_id), 2728 &withdraw_cb, 2729 &cc))) 2730 { 2731 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2732 goto cleanup; 2733 } 2734 if (0 > cc.qs) 2735 { 2736 qs = cc.qs; 2737 goto cleanup; 2738 } 2739 /* process refreshes */ 2740 if (0 > 2741 (qs = TALER_EXCHANGEDB_iterate_refreshes_above_serial_id ( 2742 TALER_ARL_edb, 2743 TALER_ARL_USE_PP (coins_melt_serial_id), 2744 &refresh_session_cb, 2745 &cc))) 2746 { 2747 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2748 goto cleanup; 2749 } 2750 if (0 > cc.qs) 2751 { 2752 qs = cc.qs; 2753 goto cleanup; 2754 } 2755 /* process refunds */ 2756 if (0 > 2757 (qs = TALER_EXCHANGEDB_iterate_refunds_above_serial_id ( 2758 TALER_ARL_edb, 2759 TALER_ARL_USE_PP (coins_refund_serial_id), 2760 &refund_cb, 2761 &cc))) 2762 { 2763 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2764 goto cleanup; 2765 } 2766 if (0 > cc.qs) 2767 { 2768 qs = cc.qs; 2769 goto cleanup; 2770 } 2771 #if FIXME_9828 2772 /* process recoups */ 2773 if (0 > 2774 (qs = TALER_EXCHANGEDB_iterate_recoup_refreshes_above_serial_id ( 2775 TALER_ARL_edb, 2776 TALER_ARL_USE_PP (coins_recoup_refresh_serial_id), 2777 &recoup_refresh_cb, 2778 &cc))) 2779 { 2780 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2781 goto cleanup; 2782 } 2783 if (0 > cc.qs) 2784 { 2785 qs = cc.qs; 2786 goto cleanup; 2787 } 2788 #endif 2789 /* process deposits */ 2790 if (0 > 2791 (qs = TALER_EXCHANGEDB_iterate_coin_deposits_above_serial_id ( 2792 TALER_ARL_edb, 2793 TALER_ARL_USE_PP (coins_deposit_serial_id), 2794 &deposit_cb, 2795 &cc))) 2796 { 2797 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2798 goto cleanup; 2799 } 2800 if (0 > cc.qs) 2801 { 2802 qs = cc.qs; 2803 goto cleanup; 2804 } 2805 /* process purse_deposits */ 2806 if (0 > 2807 (qs = TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id ( 2808 TALER_ARL_edb, 2809 TALER_ARL_USE_PP (coins_purse_deposits_serial_id), 2810 &purse_deposit_cb, 2811 &cc))) 2812 { 2813 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2814 goto cleanup; 2815 } 2816 if (0 > cc.qs) 2817 { 2818 qs = cc.qs; 2819 goto cleanup; 2820 } 2821 /* process purse_refunds */ 2822 if (0 > 2823 (qs = TALER_EXCHANGEDB_iterate_purse_decisions_above_serial_id ( 2824 TALER_ARL_edb, 2825 TALER_ARL_USE_PP (coins_purse_refunds_serial_id), 2826 true, /* only go for refunds! */ 2827 &purse_refund_cb, 2828 &cc))) 2829 { 2830 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2831 goto cleanup; 2832 } 2833 if (0 > cc.qs) 2834 { 2835 qs = cc.qs; 2836 goto cleanup; 2837 } 2838 if (0 > 2839 (qs = TALER_EXCHANGEDB_iterate_recoups_above_serial_id ( 2840 TALER_ARL_edb, 2841 TALER_ARL_USE_PP (coins_recoup_serial_id), 2842 &recoup_cb, 2843 &cc))) 2844 { 2845 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2846 goto cleanup; 2847 } 2848 if (0 > cc.qs) 2849 { 2850 qs = cc.qs; 2851 goto cleanup; 2852 } 2853 /* sync 'cc' back to disk */ 2854 cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 2855 GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries, 2856 &sync_denomination, 2857 &cc); 2858 2859 if (0 > cc.qs) 2860 { 2861 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs); 2862 qs = cc.qs; 2863 goto cleanup; 2864 } 2865 2866 qs = TALER_AUDITORDB_insert_balance ( 2867 TALER_ARL_adb, 2868 TALER_ARL_SET_AB (coin_balance_risk), 2869 TALER_ARL_SET_AB (total_escrowed), 2870 TALER_ARL_SET_AB (coin_irregular_loss), 2871 TALER_ARL_SET_AB (coin_melt_fee_revenue), 2872 TALER_ARL_SET_AB (coin_deposit_fee_revenue), 2873 TALER_ARL_SET_AB (coin_deposit_fee_loss), 2874 TALER_ARL_SET_AB (coin_refund_fee_revenue), 2875 TALER_ARL_SET_AB (total_recoup_loss), 2876 TALER_ARL_SET_AB (coins_total_arithmetic_delta_plus), 2877 TALER_ARL_SET_AB (coins_total_arithmetic_delta_minus), 2878 TALER_ARL_SET_AB (coins_reported_emergency_risk_by_count), 2879 TALER_ARL_SET_AB (coins_reported_emergency_risk_by_amount), 2880 TALER_ARL_SET_AB (coins_emergencies_loss), 2881 TALER_ARL_SET_AB (coins_emergencies_loss_by_count), 2882 NULL); 2883 if (0 > qs) 2884 { 2885 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2886 "Failed to update auditor DB, not recording progress\n"); 2887 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2888 goto cleanup; 2889 } 2890 2891 qs = TALER_AUDITORDB_insert_auditor_progress ( 2892 TALER_ARL_adb, 2893 TALER_ARL_SET_PP (coins_withdraw_serial_id), 2894 TALER_ARL_SET_PP (coins_deposit_serial_id), 2895 TALER_ARL_SET_PP (coins_melt_serial_id), 2896 TALER_ARL_SET_PP (coins_refund_serial_id), 2897 TALER_ARL_SET_PP (coins_recoup_serial_id), 2898 TALER_ARL_SET_PP (coins_recoup_refresh_serial_id), 2899 TALER_ARL_SET_PP (coins_purse_deposits_serial_id), 2900 TALER_ARL_SET_PP (coins_purse_refunds_serial_id), 2901 NULL); 2902 if (0 > qs) 2903 { 2904 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2905 "Failed to update auditor DB, not recording progress\n"); 2906 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2907 goto cleanup; 2908 } 2909 2910 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2911 "Concluded coin audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", 2912 (unsigned long long) TALER_ARL_USE_PP (coins_withdraw_serial_id), 2913 (unsigned long long) TALER_ARL_USE_PP (coins_deposit_serial_id), 2914 (unsigned long long) TALER_ARL_USE_PP (coins_melt_serial_id), 2915 (unsigned long long) TALER_ARL_USE_PP (coins_refund_serial_id), 2916 (unsigned long long) TALER_ARL_USE_PP (coins_recoup_serial_id), 2917 (unsigned long long) TALER_ARL_USE_PP ( 2918 coins_recoup_refresh_serial_id), 2919 (unsigned long long) TALER_ARL_USE_PP ( 2920 coins_purse_deposits_serial_id), 2921 (unsigned long long) TALER_ARL_USE_PP ( 2922 coins_purse_refunds_serial_id)); 2923 qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 2924 cleanup: 2925 GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries, 2926 &cleanup_denomination, 2927 &cc); 2928 GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries); 2929 return qs; 2930 } 2931 2932 2933 /** 2934 * Function called on events received from Postgres. 2935 * 2936 * @param cls closure, NULL 2937 * @param extra additional event data provided 2938 * @param extra_size number of bytes in @a extra 2939 */ 2940 static void 2941 db_notify (void *cls, 2942 const void *extra, 2943 size_t extra_size) 2944 { 2945 (void) cls; 2946 (void) extra; 2947 (void) extra_size; 2948 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2949 "Received notification to wake coins helper\n"); 2950 if (GNUNET_OK != 2951 TALER_ARL_setup_sessions_and_run (&analyze_coins, 2952 NULL)) 2953 { 2954 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2955 "Audit failed\n"); 2956 GNUNET_SCHEDULER_shutdown (); 2957 global_ret = EXIT_FAILURE; 2958 return; 2959 } 2960 } 2961 2962 2963 /** 2964 * Function called on shutdown. 2965 */ 2966 static void 2967 do_shutdown (void *cls) 2968 { 2969 (void) cls; 2970 if (NULL != eh) 2971 { 2972 TALER_AUDITORDB_event_listen_cancel (eh); 2973 eh = NULL; 2974 } 2975 TALER_ARL_done (); 2976 } 2977 2978 2979 /** 2980 * Main function that will be run. 2981 * 2982 * @param cls closure 2983 * @param args remaining command-line arguments 2984 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 2985 * @param c configuration 2986 */ 2987 static void 2988 run (void *cls, 2989 char *const *args, 2990 const char *cfgfile, 2991 const struct GNUNET_CONFIGURATION_Handle *c) 2992 { 2993 (void) cls; 2994 (void) args; 2995 (void) cfgfile; 2996 cfg = c; 2997 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2998 NULL); 2999 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3000 "Launching coins auditor\n"); 3001 if (GNUNET_OK != TALER_ARL_init (c)) 3002 { 3003 global_ret = EXIT_FAILURE; 3004 return; 3005 } 3006 if (test_mode != 1) 3007 { 3008 struct GNUNET_DB_EventHeaderP es = { 3009 .size = htons (sizeof (es)), 3010 .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_COINS) 3011 }; 3012 3013 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 3014 "Running helper indefinitely\n"); 3015 eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb, 3016 &es, 3017 GNUNET_TIME_UNIT_FOREVER_REL, 3018 &db_notify, 3019 NULL); 3020 } 3021 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 3022 "Starting audit\n"); 3023 if (GNUNET_OK != 3024 TALER_ARL_setup_sessions_and_run (&analyze_coins, 3025 NULL)) 3026 { 3027 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3028 "Audit failed\n"); 3029 GNUNET_SCHEDULER_shutdown (); 3030 global_ret = EXIT_FAILURE; 3031 return; 3032 } 3033 } 3034 3035 3036 /** 3037 * The main function to audit operations on coins. 3038 * 3039 * @param argc number of arguments from the command line 3040 * @param argv command line arguments 3041 * @return 0 ok, 1 on error 3042 */ 3043 int 3044 main (int argc, 3045 char *const *argv) 3046 { 3047 const struct GNUNET_GETOPT_CommandLineOption options[] = { 3048 GNUNET_GETOPT_option_flag ('i', 3049 "internal", 3050 "perform checks only applicable for exchange-internal audits", 3051 &internal_checks), 3052 GNUNET_GETOPT_option_flag ('t', 3053 "test", 3054 "run in test mode and exit when idle", 3055 &test_mode), 3056 GNUNET_GETOPT_option_timetravel ('T', 3057 "timetravel"), 3058 GNUNET_GETOPT_OPTION_END 3059 }; 3060 enum GNUNET_GenericReturnValue ret; 3061 3062 ret = GNUNET_PROGRAM_run ( 3063 TALER_AUDITOR_project_data (), 3064 argc, 3065 argv, 3066 "taler-helper-auditor-coins", 3067 gettext_noop ("Audit Taler coin processing"), 3068 options, 3069 &run, 3070 NULL); 3071 if (GNUNET_SYSERR == ret) 3072 return EXIT_INVALIDARGUMENT; 3073 if (GNUNET_NO == ret) 3074 return EXIT_SUCCESS; 3075 return global_ret; 3076 } 3077 3078 3079 /* end of taler-helper-auditor-coins.c */