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