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