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