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