taler-helper-auditor-reserves.c (78007B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2016-2024 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-reserves.c 18 * @brief audits the reserves of an exchange database 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include "taler/taler_auditordb_plugin.h" 23 #include "report-lib.h" 24 #include "taler/taler_dbevents.h" 25 #include "taler/taler_exchangedb_lib.h" 26 27 28 /** 29 * Use a 1 day grace period to deal with clocks not being perfectly synchronized. 30 */ 31 #define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS 32 33 /** 34 * Return value from main(). 35 */ 36 static int global_ret; 37 38 /** 39 * State of the last database transaction. 40 */ 41 static enum GNUNET_DB_QueryStatus global_qs; 42 43 /** 44 * Run in test mode. Exit when idle instead of 45 * going to sleep and waiting for more work. 46 */ 47 static int test_mode; 48 49 /** 50 * After how long should idle reserves be closed? 51 */ 52 static struct GNUNET_TIME_Relative idle_reserve_expiration_time; 53 54 /** 55 * Checkpointing our progress for reserves. 56 */ 57 static TALER_ARL_DEF_PP (reserves_reserve_in_serial_id); 58 static TALER_ARL_DEF_PP (reserves_withdraw_serial_id); 59 static TALER_ARL_DEF_PP (reserves_reserve_recoup_serial_id); 60 static TALER_ARL_DEF_PP (reserves_reserve_open_serial_id); 61 static TALER_ARL_DEF_PP (reserves_reserve_close_serial_id); 62 static TALER_ARL_DEF_PP (reserves_purse_decisions_serial_id); 63 static TALER_ARL_DEF_PP (reserves_account_merges_serial_id); 64 static TALER_ARL_DEF_PP (reserves_history_requests_serial_id); 65 66 /** 67 * Tracked global reserve balances. 68 */ 69 static TALER_ARL_DEF_AB (reserves_reserve_total_balance); 70 static TALER_ARL_DEF_AB (reserves_reserve_loss); 71 static TALER_ARL_DEF_AB (reserves_withdraw_fee_revenue); 72 static TALER_ARL_DEF_AB (reserves_close_fee_revenue); 73 static TALER_ARL_DEF_AB (reserves_purse_fee_revenue); 74 static TALER_ARL_DEF_AB (reserves_open_fee_revenue); 75 static TALER_ARL_DEF_AB (reserves_history_fee_revenue); 76 77 /** 78 * Total amount lost by operations for which signatures were invalid. 79 */ 80 static TALER_ARL_DEF_AB (reserves_total_bad_sig_loss); 81 82 /** 83 * Total amount affected by reserves not having been closed on time. 84 */ 85 static TALER_ARL_DEF_AB (total_balance_reserve_not_closed); 86 87 /** 88 * Total delta between expected and stored reserve balance summaries, 89 * for positive deltas. Used only when internal checks are 90 * enabled. 91 */ 92 static TALER_ARL_DEF_AB (total_balance_summary_delta_plus); 93 94 /** 95 * Total delta between expected and stored reserve balance summaries, 96 * for negative deltas. Used only when internal checks are 97 * enabled. 98 */ 99 static TALER_ARL_DEF_AB (total_balance_summary_delta_minus); 100 101 /** 102 * Profits the exchange made by bad amount calculations. 103 */ 104 static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_plus); 105 106 /** 107 * Losses the exchange made by bad amount calculations. 108 */ 109 static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_minus); 110 111 /** 112 * Should we run checks that only work for exchange-internal audits? 113 */ 114 static int internal_checks; 115 116 static struct GNUNET_DB_EventHandler *eh; 117 118 /** 119 * The auditors's configuration. 120 */ 121 static const struct GNUNET_CONFIGURATION_Handle *cfg; 122 123 /* ***************************** Report logic **************************** */ 124 125 126 /** 127 * Report a (serious) inconsistency in the exchange's database with 128 * respect to calculations involving amounts. 129 * 130 * @param operation what operation had the inconsistency 131 * @param rowid affected row, 0 if row is missing 132 * @param exchange amount calculated by exchange 133 * @param auditor amount calculated by auditor 134 * @param profitable 1 if @a exchange being larger than @a auditor is 135 * profitable for the exchange for this operation, 136 * -1 if @a exchange being smaller than @a auditor is 137 * profitable for the exchange, and 0 if it is unclear 138 */ 139 static void 140 report_amount_arithmetic_inconsistency ( 141 const char *operation, 142 uint64_t rowid, 143 const struct TALER_Amount *exchange, 144 const struct TALER_Amount *auditor, 145 int profitable) 146 { 147 struct TALER_Amount delta; 148 struct TALER_Amount *target; 149 enum GNUNET_DB_QueryStatus qs; 150 151 if (0 < TALER_amount_cmp (exchange, 152 auditor)) 153 { 154 /* exchange > auditor */ 155 TALER_ARL_amount_subtract (&delta, 156 exchange, 157 auditor); 158 } 159 else 160 { 161 /* auditor < exchange */ 162 profitable = -profitable; 163 TALER_ARL_amount_subtract (&delta, 164 auditor, 165 exchange); 166 } 167 168 { 169 struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = { 170 .problem_row_id = rowid, 171 .profitable = profitable, 172 .operation = (char *) operation, 173 .exchange_amount = *exchange, 174 .auditor_amount = *auditor, 175 }; 176 177 qs = TALER_ARL_adb->insert_amount_arithmetic_inconsistency ( 178 TALER_ARL_adb->cls, 179 &aai); 180 181 if (qs < 0) 182 { 183 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 184 global_qs = qs; 185 return; 186 } 187 } 188 189 if (0 != profitable) 190 { 191 target = (1 == profitable) 192 ? &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_plus) 193 : &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_minus); 194 TALER_ARL_amount_add (target, 195 target, 196 &delta); 197 } 198 } 199 200 201 /** 202 * Report a (serious) inconsistency in the exchange's database. 203 * 204 * @param table affected table 205 * @param rowid affected row, 0 if row is missing 206 * @param diagnostic message explaining the problem 207 */ 208 static void 209 report_row_inconsistency (const char *table, 210 uint64_t rowid, 211 const char *diagnostic) 212 { 213 enum GNUNET_DB_QueryStatus qs; 214 struct TALER_AUDITORDB_RowInconsistency ri = { 215 .diagnostic = (char *) diagnostic, 216 .row_table = (char *) table, 217 .row_id = rowid 218 }; 219 220 qs = TALER_ARL_adb->insert_row_inconsistency ( 221 TALER_ARL_adb->cls, 222 &ri); 223 224 if (qs < 0) 225 { 226 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 227 global_qs = qs; 228 return; 229 } 230 } 231 232 233 /* ***************************** Analyze reserves ************************ */ 234 /* This logic checks the reserves_in, withdraw and reserves-tables */ 235 236 /** 237 * Summary data we keep per reserve. 238 */ 239 struct ReserveSummary 240 { 241 /** 242 * Public key of the reserve. 243 * Always set when the struct is first initialized. 244 */ 245 struct TALER_ReservePublicKeyP reserve_pub; 246 247 /** 248 * Sum of all incoming transfers during this transaction. 249 * Updated only in #handle_reserve_in(). 250 */ 251 struct TALER_Amount total_in; 252 253 /** 254 * Sum of all outgoing transfers during this transaction (includes fees). 255 * Updated only in #handle_withdrawals(). 256 */ 257 struct TALER_Amount total_out; 258 259 /** 260 * Sum of balance and fees encountered during this transaction. 261 */ 262 struct TALER_AUDITORDB_ReserveFeeBalance curr_balance; 263 264 /** 265 * Previous balances of the reserve as remembered by the auditor. 266 * (updated based on @e total_in and @e total_out at the end). 267 */ 268 struct TALER_AUDITORDB_ReserveFeeBalance prev_balance; 269 270 /** 271 * Previous reserve expiration data, as remembered by the auditor. 272 * (updated on-the-fly in #handle_reserve_in()). 273 */ 274 struct GNUNET_TIME_Timestamp a_expiration_date; 275 276 /** 277 * Which account did originally put money into the reserve? 278 */ 279 struct TALER_FullPayto sender_account; 280 281 /** 282 * Did we have a previous reserve info? Used to decide between 283 * UPDATE and INSERT later. Initialized in 284 * #load_auditor_reserve_summary() together with the a-* values 285 * (if available). 286 */ 287 bool had_ri; 288 289 }; 290 291 292 /** 293 * Load the auditor's remembered state about the reserve into @a rs. 294 * The "total_in" and "total_out" amounts of @a rs must already be 295 * initialized (so we can determine the currency). 296 * 297 * @param[in,out] rs reserve summary to (fully) initialize 298 * @return transaction status code 299 */ 300 static enum GNUNET_DB_QueryStatus 301 load_auditor_reserve_summary (struct ReserveSummary *rs) 302 { 303 enum GNUNET_DB_QueryStatus qs; 304 uint64_t rowid; 305 306 qs = TALER_ARL_adb->get_reserve_info (TALER_ARL_adb->cls, 307 &rs->reserve_pub, 308 &rowid, 309 &rs->prev_balance, 310 &rs->a_expiration_date, 311 &rs->sender_account); 312 if (0 > qs) 313 { 314 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 315 return qs; 316 } 317 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 318 { 319 rs->had_ri = false; 320 GNUNET_assert (GNUNET_OK == 321 TALER_amount_set_zero (rs->total_in.currency, 322 &rs->prev_balance.reserve_balance)); 323 GNUNET_assert (GNUNET_OK == 324 TALER_amount_set_zero (rs->total_in.currency, 325 &rs->prev_balance.reserve_loss)); 326 GNUNET_assert (GNUNET_OK == 327 TALER_amount_set_zero (rs->total_in.currency, 328 &rs->prev_balance.withdraw_fee_balance 329 )); 330 GNUNET_assert (GNUNET_OK == 331 TALER_amount_set_zero (rs->total_in.currency, 332 &rs->prev_balance.close_fee_balance)); 333 GNUNET_assert (GNUNET_OK == 334 TALER_amount_set_zero (rs->total_in.currency, 335 &rs->prev_balance.purse_fee_balance)); 336 GNUNET_assert (GNUNET_OK == 337 TALER_amount_set_zero (rs->total_in.currency, 338 &rs->prev_balance.open_fee_balance)); 339 GNUNET_assert (GNUNET_OK == 340 TALER_amount_set_zero (rs->total_in.currency, 341 &rs->prev_balance.history_fee_balance) 342 ); 343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 344 "Creating fresh reserve `%s'\n", 345 TALER_B2S (&rs->reserve_pub)); 346 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 347 } 348 rs->had_ri = true; 349 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 350 "Auditor remembers reserve `%s' has balance %s\n", 351 TALER_B2S (&rs->reserve_pub), 352 TALER_amount2s (&rs->prev_balance.reserve_balance)); 353 return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 354 } 355 356 357 /** 358 * Closure to the various callbacks we make while checking a reserve. 359 */ 360 struct ReserveContext 361 { 362 /** 363 * Map from hash of reserve's public key to a `struct ReserveSummary`. 364 */ 365 struct GNUNET_CONTAINER_MultiHashMap *reserves; 366 367 /** 368 * Map from hash of denomination's public key to a 369 * static string "revoked" for keys that have been revoked, 370 * or "master signature invalid" in case the revocation is 371 * there but bogus. 372 */ 373 struct GNUNET_CONTAINER_MultiHashMap *revoked; 374 375 /** 376 * Transaction status code, set to error codes if applicable. 377 */ 378 enum GNUNET_DB_QueryStatus qs; 379 380 }; 381 382 383 /** 384 * Create a new reserve for @a reserve_pub in @a rc. 385 * 386 * @param[in,out] rc context to update 387 * @param reserve_pub key for which to create a reserve 388 * @return NULL on error 389 */ 390 static struct ReserveSummary * 391 setup_reserve (struct ReserveContext *rc, 392 const struct TALER_ReservePublicKeyP *reserve_pub) 393 { 394 struct ReserveSummary *rs; 395 struct GNUNET_HashCode key; 396 enum GNUNET_DB_QueryStatus qs; 397 398 GNUNET_CRYPTO_hash (reserve_pub, 399 sizeof (*reserve_pub), 400 &key); 401 rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves, 402 &key); 403 if (NULL != rs) 404 return rs; 405 rs = GNUNET_new (struct ReserveSummary); 406 rs->reserve_pub = *reserve_pub; 407 GNUNET_assert (GNUNET_OK == 408 TALER_amount_set_zero (TALER_ARL_currency, 409 &rs->total_in)); 410 GNUNET_assert (GNUNET_OK == 411 TALER_amount_set_zero (TALER_ARL_currency, 412 &rs->total_out)); 413 GNUNET_assert (GNUNET_OK == 414 TALER_amount_set_zero (TALER_ARL_currency, 415 &rs->curr_balance.reserve_balance)); 416 GNUNET_assert (GNUNET_OK == 417 TALER_amount_set_zero (TALER_ARL_currency, 418 &rs->curr_balance.reserve_loss)); 419 GNUNET_assert (GNUNET_OK == 420 TALER_amount_set_zero (TALER_ARL_currency, 421 &rs->curr_balance.withdraw_fee_balance)) 422 ; 423 GNUNET_assert (GNUNET_OK == 424 TALER_amount_set_zero (TALER_ARL_currency, 425 &rs->curr_balance.close_fee_balance)); 426 GNUNET_assert (GNUNET_OK == 427 TALER_amount_set_zero (TALER_ARL_currency, 428 &rs->curr_balance.purse_fee_balance)); 429 GNUNET_assert (GNUNET_OK == 430 TALER_amount_set_zero (TALER_ARL_currency, 431 &rs->curr_balance.open_fee_balance)); 432 GNUNET_assert (GNUNET_OK == 433 TALER_amount_set_zero (TALER_ARL_currency, 434 &rs->curr_balance.history_fee_balance)); 435 if (0 > (qs = load_auditor_reserve_summary (rs))) 436 { 437 GNUNET_free (rs); 438 rc->qs = qs; 439 return NULL; 440 } 441 GNUNET_assert (GNUNET_OK == 442 GNUNET_CONTAINER_multihashmap_put (rc->reserves, 443 &key, 444 rs, 445 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 446 return rs; 447 } 448 449 450 /** 451 * Function called with details about incoming wire transfers. 452 * 453 * @param cls our `struct ReserveContext` 454 * @param rowid unique serial ID for the refresh session in our DB 455 * @param reserve_pub public key of the reserve (also the WTID) 456 * @param credit amount that was received 457 * @param sender_account_details information about the sender's bank account 458 * @param wire_reference unique reference identifying the wire transfer 459 * @param execution_date when did we receive the funds 460 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 461 */ 462 static enum GNUNET_GenericReturnValue 463 handle_reserve_in ( 464 void *cls, 465 uint64_t rowid, 466 const struct TALER_ReservePublicKeyP *reserve_pub, 467 const struct TALER_Amount *credit, 468 const struct TALER_FullPayto sender_account_details, 469 uint64_t wire_reference, 470 struct GNUNET_TIME_Timestamp execution_date) 471 { 472 struct ReserveContext *rc = cls; 473 struct ReserveSummary *rs; 474 struct GNUNET_TIME_Timestamp expiry; 475 476 (void) wire_reference; 477 /* should be monotonically increasing */ 478 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_in_serial_id)); 479 TALER_ARL_USE_PP (reserves_reserve_in_serial_id) = rowid + 1; 480 rs = setup_reserve (rc, 481 reserve_pub); 482 if (NULL == rs) 483 { 484 GNUNET_break (0); 485 return GNUNET_SYSERR; 486 } 487 if (NULL == rs->sender_account.full_payto) 488 rs->sender_account.full_payto 489 = GNUNET_strdup (sender_account_details.full_payto); 490 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 491 "Additional incoming wire transfer for reserve `%s' of %s\n", 492 TALER_B2S (reserve_pub), 493 TALER_amount2s (credit)); 494 expiry = GNUNET_TIME_absolute_to_timestamp ( 495 GNUNET_TIME_absolute_add (execution_date.abs_time, 496 idle_reserve_expiration_time)); 497 rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date, 498 expiry); 499 TALER_ARL_amount_add (&rs->total_in, 500 &rs->total_in, 501 credit); 502 return GNUNET_OK; 503 } 504 505 506 /** 507 * Function called with details about withdraw operations. Verifies 508 * the signature and updates the reserve's balance. 509 * 510 * @param cls our `struct ReserveContext` 511 * @param rowid unique serial ID for the refresh session in our DB 512 * @param num_denom_serials number of elements in @e denom_serials array 513 * @param denom_serials array with length @e num_denom_serials of serial ID's of denominations in our DB 514 * @param selected_h hash over the gamma-selected planchets 515 * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request 516 * @param blinding_seed the blinding seed for CS denominations that was provided during withdraw; might be NULL 517 * @param age_proof_required true if the withdraw request required an age proof. 518 * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins. 519 * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase. 520 * @param reserve_pub public key of the reserve 521 * @param reserve_sig signature over the withdraw operation 522 * @param execution_date when did the wallet withdraw the coin 523 * @param amount_with_fee amount that was withdrawn 524 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 525 */ 526 static enum GNUNET_GenericReturnValue 527 handle_withdrawals ( 528 void *cls, 529 uint64_t rowid, 530 size_t num_denom_serials, 531 const uint64_t *denom_serials, 532 const struct TALER_HashBlindedPlanchetsP *selected_h, 533 const struct TALER_HashBlindedPlanchetsP *h_planchets, 534 const struct TALER_BlindingMasterSeedP *blinding_seed, 535 bool age_proof_required, 536 uint8_t max_age, 537 uint8_t noreveal_index, 538 const struct TALER_ReservePublicKeyP *reserve_pub, 539 const struct TALER_ReserveSignatureP *reserve_sig, 540 struct GNUNET_TIME_Timestamp execution_date, 541 const struct TALER_Amount *amount_with_fee) 542 { 543 struct ReserveContext *rc = cls; 544 struct ReserveSummary *rs; 545 const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue; 546 struct TALER_Amount auditor_amount; 547 struct TALER_Amount auditor_fee; 548 struct TALER_Amount auditor_amount_with_fee; 549 enum GNUNET_DB_QueryStatus qs; 550 551 /* should be monotonically increasing */ 552 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 553 "Analyzing withdrawal row %llu\n", 554 (unsigned long long) rowid); 555 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_withdraw_serial_id)); 556 TALER_ARL_USE_PP (reserves_withdraw_serial_id) = rowid + 1; 557 558 GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, 559 &auditor_amount)); 560 GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, 561 &auditor_fee)); 562 GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, 563 &auditor_amount_with_fee)); 564 565 for (size_t i = 0; i < num_denom_serials; i++) 566 { 567 /* lookup denomination pub data (make sure denom_pub is valid, establish fees); 568 initializes wsrd.h_denomination_pub! */ 569 qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i], 570 &issue); 571 if (0 > qs) 572 { 573 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 574 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 575 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 576 "Hard database error trying to get denomination by serial %llu (%s) from database!\n", 577 (unsigned long long) denom_serials[i], 578 GNUNET_h2s (&h_planchets->hash)); 579 rc->qs = qs; 580 return GNUNET_SYSERR; 581 } 582 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 583 { 584 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 585 "Denomination #%llu not found\n", 586 (unsigned long long) denom_serials[i]); 587 report_row_inconsistency ("withdraw", 588 rowid, 589 "denomination key not found"); 590 if (global_qs < 0) 591 return GNUNET_SYSERR; 592 return GNUNET_OK; 593 } 594 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 595 "Analyzing withdrawn denomination #%llu (%s)\n", 596 (unsigned long long) denom_serials[i], 597 TALER_amount2s (&issue->value)); 598 599 /* check that execution date is within withdraw range for denom_pub */ 600 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 601 "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n", 602 (unsigned long long) issue->start.abs_time.abs_value_us, 603 (unsigned long 604 long) issue->expire_withdraw.abs_time.abs_value_us, 605 (unsigned long long) execution_date.abs_time.abs_value_us); 606 if (GNUNET_TIME_timestamp_cmp (issue->start, 607 >, 608 execution_date) || 609 GNUNET_TIME_timestamp_cmp (issue->expire_withdraw, 610 <, 611 execution_date)) 612 { 613 struct TALER_AUDITORDB_DenominationKeyValidityWithdrawInconsistency 614 dkvwi ={ 615 .problem_row_id = rowid, 616 .execution_date = execution_date.abs_time, 617 .denompub_h = issue->denom_hash, 618 .reserve_pub = *reserve_pub 619 }; 620 621 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 622 "Withdraw outside of denomination #%llu validity period detected\n", 623 (unsigned long long) denom_serials[i]); 624 qs = 625 TALER_ARL_adb->insert_denomination_key_validity_withdraw_inconsistency ( 626 TALER_ARL_adb->cls, 627 &dkvwi); 628 629 if (qs < 0) 630 { 631 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 632 rc->qs = qs; 633 return GNUNET_SYSERR; 634 } 635 } 636 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 637 "Adding withdraw fee of denomination (%s)\n", 638 TALER_amount2s (&issue->fees.withdraw)); 639 TALER_ARL_amount_add (&auditor_amount, 640 &auditor_amount, 641 &issue->value); 642 TALER_ARL_amount_add (&auditor_fee, 643 &auditor_fee, 644 &issue->fees.withdraw); 645 { 646 struct TALER_Amount issue_amount_with_fee; 647 648 TALER_ARL_amount_add (&issue_amount_with_fee, 649 &issue->value, 650 &issue->fees.withdraw); 651 TALER_ARL_amount_add (&auditor_amount_with_fee, 652 &auditor_amount_with_fee, 653 &issue_amount_with_fee); 654 } 655 } 656 657 /* check reserve_sig (first: setup remaining members of wsrd) */ 658 if (GNUNET_OK != 659 TALER_wallet_withdraw_verify ( 660 &auditor_amount, 661 &auditor_fee, 662 h_planchets, 663 blinding_seed, 664 age_proof_required 665 ? &issue->age_mask 666 : NULL, 667 age_proof_required 668 ? max_age 669 : 0, 670 reserve_pub, 671 reserve_sig)) 672 { 673 struct TALER_AUDITORDB_BadSigLosses bsl = { 674 .problem_row_id = rowid, 675 .operation = (char *) "withdraw", 676 .loss = *amount_with_fee, 677 .operation_specific_pub = reserve_pub->eddsa_pub 678 }; 679 680 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 681 "Withdraw signature invalid (row #%llu)\n", 682 (unsigned long long) rowid); 683 qs = TALER_ARL_adb->insert_bad_sig_losses ( 684 TALER_ARL_adb->cls, 685 &bsl); 686 if (qs < 0) 687 { 688 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 689 rc->qs = qs; 690 return GNUNET_SYSERR; 691 } 692 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 693 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 694 amount_with_fee); 695 return GNUNET_OK; /* exit function here, we cannot add this to the legitimate withdrawals */ 696 } 697 698 if (0 != 699 TALER_amount_cmp (&auditor_amount_with_fee, 700 amount_with_fee)) 701 { 702 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 703 "Withdraw fee inconsistent (row #%llu)\n", 704 (unsigned long long) rowid); 705 report_row_inconsistency ("withdraw", 706 rowid, 707 "amount with fee from exchange does not match denomination value plus fee"); 708 if (global_qs < 0) 709 return GNUNET_SYSERR; 710 } 711 rs = setup_reserve (rc, 712 reserve_pub); 713 if (NULL == rs) 714 { 715 GNUNET_break (0); 716 return GNUNET_SYSERR; 717 } 718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 719 "Reserve `%s' reduced by %s from withdraw\n", 720 TALER_B2S (reserve_pub), 721 TALER_amount2s (&auditor_amount_with_fee)); 722 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 723 "Increasing withdraw profits by fee %s\n", 724 TALER_amount2s (&issue->fees.withdraw)); 725 TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance, 726 &rs->curr_balance.withdraw_fee_balance, 727 &issue->fees.withdraw); 728 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_withdraw_fee_revenue), 729 &TALER_ARL_USE_AB (reserves_withdraw_fee_revenue), 730 &issue->fees.withdraw); 731 TALER_ARL_amount_add (&rs->total_out, 732 &rs->total_out, 733 &auditor_amount_with_fee); 734 return GNUNET_OK; 735 } 736 737 738 /** 739 * Function called with details about withdraw operations. Verifies 740 * the signature and updates the reserve's balance. 741 * 742 * @param cls our `struct ReserveContext` 743 * @param rowid unique serial ID for the refresh session in our DB 744 * @param timestamp when did we receive the recoup request 745 * @param amount how much should be added back to the reserve 746 * @param reserve_pub public key of the reserve 747 * @param coin public information about the coin, denomination signature is 748 * already verified in #check_recoup() 749 * @param denom_pub public key of the denomionation of @a coin 750 * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP 751 * @param coin_blind blinding factor used to blind the coin 752 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 753 */ 754 static enum GNUNET_GenericReturnValue 755 handle_recoup_by_reserve ( 756 void *cls, 757 uint64_t rowid, 758 struct GNUNET_TIME_Timestamp timestamp, 759 const struct TALER_Amount *amount, 760 const struct TALER_ReservePublicKeyP *reserve_pub, 761 const struct TALER_CoinPublicInfo *coin, 762 const struct TALER_DenominationPublicKey *denom_pub, 763 const struct TALER_CoinSpendSignatureP *coin_sig, 764 const union GNUNET_CRYPTO_BlindingSecretP *coin_blind) 765 { 766 struct ReserveContext *rc = cls; 767 struct ReserveSummary *rs; 768 struct GNUNET_TIME_Timestamp expiry; 769 struct TALER_MasterSignatureP msig; 770 uint64_t rev_rowid; 771 enum GNUNET_DB_QueryStatus qs; 772 const char *rev; 773 774 (void) denom_pub; 775 /* should be monotonically increasing */ 776 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id)); 777 TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id) = rowid + 1; 778 /* We know that denom_pub matches denom_pub_hash because this 779 is how the SQL statement joined the tables. */ 780 if (GNUNET_OK != 781 TALER_wallet_recoup_verify (&coin->denom_pub_hash, 782 coin_blind, 783 &coin->coin_pub, 784 coin_sig)) 785 { 786 struct TALER_AUDITORDB_BadSigLosses bslr = { 787 .problem_row_id = rowid, 788 .operation = (char *) "recoup", 789 .loss = *amount, 790 .operation_specific_pub = coin->coin_pub.eddsa_pub 791 }; 792 793 qs = TALER_ARL_adb->insert_bad_sig_losses ( 794 TALER_ARL_adb->cls, 795 &bslr); 796 797 if (qs < 0) 798 { 799 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 800 rc->qs = qs; 801 return GNUNET_SYSERR; 802 } 803 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 804 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 805 amount); 806 } 807 808 /* check that the coin was eligible for recoup!*/ 809 rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked, 810 &coin->denom_pub_hash.hash); 811 if (NULL == rev) 812 { 813 qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls, 814 &coin->denom_pub_hash, 815 &msig, 816 &rev_rowid); 817 if (0 > qs) 818 { 819 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 820 rc->qs = qs; 821 return GNUNET_SYSERR; 822 } 823 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 824 { 825 report_row_inconsistency ("recoup", 826 rowid, 827 "denomination key not in revocation set"); 828 if (global_qs < 0) 829 return GNUNET_SYSERR; 830 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss), 831 &TALER_ARL_USE_AB (reserves_reserve_loss), 832 amount); 833 } 834 else 835 { 836 if (GNUNET_OK != 837 TALER_exchange_offline_denomination_revoke_verify ( 838 &coin->denom_pub_hash, 839 &TALER_ARL_master_pub, 840 &msig)) 841 { 842 rev = "master signature invalid"; 843 } 844 else 845 { 846 rev = "revoked"; 847 } 848 GNUNET_assert ( 849 GNUNET_OK == 850 GNUNET_CONTAINER_multihashmap_put ( 851 rc->revoked, 852 &coin->denom_pub_hash.hash, 853 (void *) rev, 854 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 855 } 856 } 857 else 858 { 859 rev_rowid = 0; /* reported elsewhere */ 860 } 861 if ((NULL != rev) && 862 (0 == strcmp (rev, 863 "master signature invalid"))) 864 { 865 struct TALER_AUDITORDB_BadSigLosses bslrm = { 866 .problem_row_id = rev_rowid, 867 .operation = (char *) "recoup-master", 868 .loss = *amount, 869 .operation_specific_pub = TALER_ARL_master_pub.eddsa_pub 870 }; 871 872 qs = TALER_ARL_adb->insert_bad_sig_losses ( 873 TALER_ARL_adb->cls, 874 &bslrm); 875 876 if (qs < 0) 877 { 878 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 879 rc->qs = qs; 880 return GNUNET_SYSERR; 881 } 882 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 883 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 884 amount); 885 } 886 887 rs = setup_reserve (rc, 888 reserve_pub); 889 if (NULL == rs) 890 { 891 GNUNET_break (0); 892 return GNUNET_SYSERR; 893 } 894 TALER_ARL_amount_add (&rs->total_in, 895 &rs->total_in, 896 amount); 897 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 898 "Additional /recoup value to for reserve `%s' of %s\n", 899 TALER_B2S (reserve_pub), 900 TALER_amount2s (amount)); 901 expiry = GNUNET_TIME_absolute_to_timestamp ( 902 GNUNET_TIME_absolute_add (timestamp.abs_time, 903 idle_reserve_expiration_time)); 904 rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date, 905 expiry); 906 return GNUNET_OK; 907 } 908 909 910 /** 911 * Obtain the closing fee for a transfer at @a time for target 912 * @a receiver_account. 913 * 914 * @param receiver_account payto:// URI of the target account 915 * @param atime when was the transfer made 916 * @param[out] fee set to the closing fee 917 * @return #GNUNET_OK on success 918 */ 919 static enum GNUNET_GenericReturnValue 920 get_closing_fee (const struct TALER_FullPayto receiver_account, 921 struct GNUNET_TIME_Timestamp atime, 922 struct TALER_Amount *fee) 923 { 924 struct TALER_MasterSignatureP master_sig; 925 struct GNUNET_TIME_Timestamp start_date; 926 struct GNUNET_TIME_Timestamp end_date; 927 struct TALER_WireFeeSet fees; 928 char *method; 929 uint64_t rowid; 930 931 method = TALER_payto_get_method (receiver_account.full_payto); 932 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 933 "Method is `%s'\n", 934 method); 935 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 936 TALER_ARL_edb->get_wire_fee (TALER_ARL_edb->cls, 937 method, 938 atime, 939 &rowid, 940 &start_date, 941 &end_date, 942 &fees, 943 &master_sig)) 944 { 945 char *diag; 946 947 GNUNET_asprintf (&diag, 948 "closing fee for `%s' unavailable at %s\n", 949 method, 950 GNUNET_TIME_timestamp2s (atime)); 951 report_row_inconsistency ("closing-fee", 952 rowid, 953 diag); 954 GNUNET_free (diag); 955 GNUNET_free (method); 956 return GNUNET_SYSERR; 957 } 958 *fee = fees.closing; 959 GNUNET_free (method); 960 return GNUNET_OK; 961 } 962 963 964 /** 965 * Function called about reserve opening operations. 966 * 967 * @param cls closure 968 * @param rowid row identifier used to uniquely identify the reserve closing operation 969 * @param reserve_payment how much to pay from the 970 * reserve's own balance for opening the reserve 971 * @param request_timestamp when was the request created 972 * @param reserve_expiration desired expiration time for the reserve 973 * @param purse_limit minimum number of purses the client 974 * wants to have concurrently open for this reserve 975 * @param reserve_pub public key of the reserve 976 * @param reserve_sig signature affirming the operation 977 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 978 */ 979 static enum GNUNET_GenericReturnValue 980 handle_reserve_open ( 981 void *cls, 982 uint64_t rowid, 983 const struct TALER_Amount *reserve_payment, 984 struct GNUNET_TIME_Timestamp request_timestamp, 985 struct GNUNET_TIME_Timestamp reserve_expiration, 986 uint32_t purse_limit, 987 const struct TALER_ReservePublicKeyP *reserve_pub, 988 const struct TALER_ReserveSignatureP *reserve_sig) 989 { 990 struct ReserveContext *rc = cls; 991 struct ReserveSummary *rs; 992 enum GNUNET_DB_QueryStatus qs; 993 994 /* should be monotonically increasing */ 995 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_open_serial_id)); 996 TALER_ARL_USE_PP (reserves_reserve_open_serial_id) = rowid + 1; 997 998 rs = setup_reserve (rc, 999 reserve_pub); 1000 if (NULL == rs) 1001 { 1002 GNUNET_break (0); 1003 return GNUNET_SYSERR; 1004 } 1005 if (GNUNET_OK != 1006 TALER_wallet_reserve_open_verify (reserve_payment, 1007 request_timestamp, 1008 reserve_expiration, 1009 purse_limit, 1010 reserve_pub, 1011 reserve_sig)) 1012 { 1013 struct TALER_AUDITORDB_BadSigLosses bsl = { 1014 .problem_row_id = rowid, 1015 .operation = (char *) "reserve-open", 1016 .loss = *reserve_payment, 1017 .operation_specific_pub = reserve_pub->eddsa_pub 1018 }; 1019 1020 qs = TALER_ARL_adb->insert_bad_sig_losses ( 1021 TALER_ARL_adb->cls, 1022 &bsl); 1023 1024 if (qs < 0) 1025 { 1026 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1027 rc->qs = qs; 1028 return GNUNET_SYSERR; 1029 } 1030 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1031 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1032 reserve_payment); 1033 return GNUNET_OK; 1034 } 1035 TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance, 1036 &rs->curr_balance.open_fee_balance, 1037 reserve_payment); 1038 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_open_fee_revenue), 1039 &TALER_ARL_USE_AB (reserves_open_fee_revenue), 1040 reserve_payment); 1041 TALER_ARL_amount_add (&rs->total_out, 1042 &rs->total_out, 1043 reserve_payment); 1044 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1045 "Additional open operation for reserve `%s' of %s\n", 1046 TALER_B2S (reserve_pub), 1047 TALER_amount2s (reserve_payment)); 1048 return GNUNET_OK; 1049 } 1050 1051 1052 /** 1053 * Function called about reserve closing operations 1054 * the aggregator triggered. 1055 * 1056 * @param cls closure 1057 * @param rowid row identifier used to uniquely identify the reserve closing operation 1058 * @param execution_date when did we execute the close operation 1059 * @param amount_with_fee how much did we debit the reserve 1060 * @param closing_fee how much did we charge for closing the reserve 1061 * @param reserve_pub public key of the reserve 1062 * @param receiver_account where did we send the funds 1063 * @param transfer_details details about the wire transfer 1064 * @param close_request_row which close request triggered the operation? 1065 * 0 if it was a timeout 1066 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1067 */ 1068 static enum GNUNET_GenericReturnValue 1069 handle_reserve_closed ( 1070 void *cls, 1071 uint64_t rowid, 1072 struct GNUNET_TIME_Timestamp execution_date, 1073 const struct TALER_Amount *amount_with_fee, 1074 const struct TALER_Amount *closing_fee, 1075 const struct TALER_ReservePublicKeyP *reserve_pub, 1076 const struct TALER_FullPayto receiver_account, 1077 const struct TALER_WireTransferIdentifierRawP *transfer_details, 1078 uint64_t close_request_row) 1079 { 1080 struct ReserveContext *rc = cls; 1081 struct ReserveSummary *rs; 1082 1083 (void) transfer_details; 1084 /* should be monotonically increasing */ 1085 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_close_serial_id)); 1086 TALER_ARL_USE_PP (reserves_reserve_close_serial_id) = rowid + 1; 1087 1088 rs = setup_reserve (rc, 1089 reserve_pub); 1090 if (NULL == rs) 1091 { 1092 GNUNET_break (0); 1093 return GNUNET_SYSERR; 1094 } 1095 { 1096 struct TALER_Amount expected_fee; 1097 1098 /* verify closing_fee is correct! */ 1099 if (GNUNET_OK != 1100 get_closing_fee (receiver_account, 1101 execution_date, 1102 &expected_fee)) 1103 { 1104 GNUNET_break (0); 1105 } 1106 else if (0 != TALER_amount_cmp (&expected_fee, 1107 closing_fee)) 1108 { 1109 report_amount_arithmetic_inconsistency ( 1110 "closing aggregation fee", 1111 rowid, 1112 closing_fee, 1113 &expected_fee, 1114 1); 1115 if (global_qs < 0) 1116 return GNUNET_SYSERR; 1117 } 1118 } 1119 1120 TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance, 1121 &rs->curr_balance.close_fee_balance, 1122 closing_fee); 1123 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_close_fee_revenue), 1124 &TALER_ARL_USE_AB (reserves_close_fee_revenue), 1125 closing_fee); 1126 TALER_ARL_amount_add (&rs->total_out, 1127 &rs->total_out, 1128 amount_with_fee); 1129 if (0 != close_request_row) 1130 { 1131 struct TALER_ReserveSignatureP reserve_sig; 1132 struct GNUNET_TIME_Timestamp request_timestamp; 1133 struct TALER_Amount close_balance; 1134 struct TALER_Amount close_fee; 1135 struct TALER_FullPayto payto_uri; 1136 enum GNUNET_DB_QueryStatus qs; 1137 1138 qs = TALER_ARL_edb->select_reserve_close_request_info ( 1139 TALER_ARL_edb->cls, 1140 reserve_pub, 1141 close_request_row, 1142 &reserve_sig, 1143 &request_timestamp, 1144 &close_balance, 1145 &close_fee, 1146 &payto_uri); 1147 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1148 { 1149 report_row_inconsistency ("reserves_close", 1150 rowid, 1151 "reserve close request unknown"); 1152 if (global_qs < 0) 1153 return GNUNET_SYSERR; 1154 } 1155 else 1156 { 1157 struct TALER_FullPaytoHashP h_payto; 1158 1159 TALER_full_payto_hash (payto_uri, 1160 &h_payto); 1161 if (GNUNET_OK != 1162 TALER_wallet_reserve_close_verify ( 1163 request_timestamp, 1164 &h_payto, 1165 reserve_pub, 1166 &reserve_sig)) 1167 { 1168 struct TALER_AUDITORDB_BadSigLosses bsl = { 1169 .problem_row_id = close_request_row, 1170 .operation = (char *) "close-request", 1171 .loss = *amount_with_fee, 1172 .operation_specific_pub = reserve_pub->eddsa_pub 1173 }; 1174 1175 qs = TALER_ARL_adb->insert_bad_sig_losses ( 1176 TALER_ARL_adb->cls, 1177 &bsl); 1178 1179 if (qs < 0) 1180 { 1181 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1182 rc->qs = qs; 1183 GNUNET_free (payto_uri.full_payto); 1184 return GNUNET_SYSERR; 1185 } 1186 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1187 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1188 amount_with_fee); 1189 } 1190 } 1191 if ( (NULL == payto_uri.full_payto) && 1192 (NULL == rs->sender_account.full_payto) ) 1193 { 1194 GNUNET_break (! rs->had_ri); 1195 report_row_inconsistency ("reserves_close", 1196 rowid, 1197 "target account not verified, auditor does not know reserve"); 1198 if (global_qs < 0) 1199 return GNUNET_SYSERR; 1200 } 1201 if (NULL == payto_uri.full_payto) 1202 { 1203 if ((NULL == rs->sender_account.full_payto) || 1204 (0 != TALER_full_payto_cmp (rs->sender_account, 1205 receiver_account))) 1206 { 1207 report_row_inconsistency ("reserves_close", 1208 rowid, 1209 "target account does not match origin account"); 1210 if (global_qs < 0) 1211 return GNUNET_SYSERR; 1212 } 1213 } 1214 else 1215 { 1216 if (0 != TALER_full_payto_cmp (payto_uri, 1217 receiver_account)) 1218 { 1219 report_row_inconsistency ("reserves_close", 1220 rowid, 1221 "target account does not match origin account"); 1222 if (global_qs < 0) 1223 { 1224 GNUNET_free (payto_uri.full_payto); 1225 return GNUNET_SYSERR; 1226 } 1227 } 1228 } 1229 GNUNET_free (payto_uri.full_payto); 1230 } 1231 else 1232 { 1233 if (NULL == rs->sender_account.full_payto) 1234 { 1235 GNUNET_break (! rs->had_ri); 1236 report_row_inconsistency ("reserves_close", 1237 rowid, 1238 "target account not verified, auditor does not know reserve"); 1239 if (global_qs < 0) 1240 return GNUNET_SYSERR; 1241 } 1242 else if (0 != TALER_full_payto_cmp (rs->sender_account, 1243 receiver_account)) 1244 { 1245 report_row_inconsistency ("reserves_close", 1246 rowid, 1247 "target account does not match origin account"); 1248 if (global_qs < 0) 1249 return GNUNET_SYSERR; 1250 } 1251 } 1252 1253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1254 "Additional closing operation for reserve `%s' of %s\n", 1255 TALER_B2S (reserve_pub), 1256 TALER_amount2s (amount_with_fee)); 1257 return GNUNET_OK; 1258 } 1259 1260 1261 /** 1262 * Function called with details about account merge requests that have been 1263 * made, with the goal of accounting for the merge fee paid by the reserve (if 1264 * applicable). 1265 * 1266 * @param cls closure 1267 * @param rowid unique serial ID for the deposit in our DB 1268 * @param reserve_pub reserve affected by the merge 1269 * @param purse_pub purse being merged 1270 * @param h_contract_terms hash over contract of the purse 1271 * @param purse_expiration when would the purse expire 1272 * @param amount total amount in the purse 1273 * @param min_age minimum age of all coins deposited into the purse 1274 * @param flags how was the purse created 1275 * @param purse_fee if a purse fee was paid, how high is it 1276 * @param merge_timestamp when was the merge approved 1277 * @param reserve_sig signature by reserve approving the merge 1278 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1279 */ 1280 static enum GNUNET_GenericReturnValue 1281 handle_account_merged ( 1282 void *cls, 1283 uint64_t rowid, 1284 const struct TALER_ReservePublicKeyP *reserve_pub, 1285 const struct TALER_PurseContractPublicKeyP *purse_pub, 1286 const struct TALER_PrivateContractHashP *h_contract_terms, 1287 struct GNUNET_TIME_Timestamp purse_expiration, 1288 const struct TALER_Amount *amount, 1289 uint32_t min_age, 1290 enum TALER_WalletAccountMergeFlags flags, 1291 const struct TALER_Amount *purse_fee, 1292 struct GNUNET_TIME_Timestamp merge_timestamp, 1293 const struct TALER_ReserveSignatureP *reserve_sig) 1294 { 1295 struct ReserveContext *rc = cls; 1296 struct ReserveSummary *rs; 1297 enum GNUNET_DB_QueryStatus qs; 1298 1299 /* should be monotonically increasing */ 1300 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_account_merges_serial_id)); 1301 TALER_ARL_USE_PP (reserves_account_merges_serial_id) = rowid + 1; 1302 if (GNUNET_OK != 1303 TALER_wallet_account_merge_verify (merge_timestamp, 1304 purse_pub, 1305 purse_expiration, 1306 h_contract_terms, 1307 amount, 1308 purse_fee, 1309 min_age, 1310 flags, 1311 reserve_pub, 1312 reserve_sig)) 1313 { 1314 struct TALER_AUDITORDB_BadSigLosses bsl = { 1315 .problem_row_id = rowid, 1316 .operation = (char *) "account-merge", 1317 .loss = *purse_fee, 1318 .operation_specific_pub = reserve_pub->eddsa_pub 1319 }; 1320 1321 qs = TALER_ARL_adb->insert_bad_sig_losses ( 1322 TALER_ARL_adb->cls, 1323 &bsl); 1324 if (qs < 0) 1325 { 1326 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1327 rc->qs = qs; 1328 return GNUNET_SYSERR; 1329 } 1330 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1331 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1332 purse_fee); 1333 return GNUNET_OK; 1334 } 1335 if ((flags & TALER_WAMF_MERGE_MODE_MASK) != 1336 TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE) 1337 return GNUNET_OK; /* no impact on reserve balance */ 1338 rs = setup_reserve (rc, 1339 reserve_pub); 1340 if (NULL == rs) 1341 { 1342 GNUNET_break (0); 1343 return GNUNET_SYSERR; 1344 } 1345 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_purse_fee_revenue), 1346 &TALER_ARL_USE_AB (reserves_purse_fee_revenue), 1347 purse_fee); 1348 TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance, 1349 &rs->curr_balance.purse_fee_balance, 1350 purse_fee); 1351 TALER_ARL_amount_add (&rs->total_out, 1352 &rs->total_out, 1353 purse_fee); 1354 return GNUNET_OK; 1355 } 1356 1357 1358 /** 1359 * Function called with details about a purse that was merged into an account. 1360 * Only updates the reserve balance, the actual verifications are done in the 1361 * purse helper. 1362 * 1363 * @param cls closure 1364 * @param rowid unique serial ID for the refund in our DB 1365 * @param purse_pub public key of the purse 1366 * @param reserve_pub which reserve is the purse credited to 1367 * @param purse_value what is the target value of the purse 1368 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1369 */ 1370 static enum GNUNET_GenericReturnValue 1371 purse_decision_cb (void *cls, 1372 uint64_t rowid, 1373 const struct TALER_PurseContractPublicKeyP *purse_pub, 1374 const struct TALER_ReservePublicKeyP *reserve_pub, 1375 const struct TALER_Amount *purse_value) 1376 { 1377 struct ReserveContext *rc = cls; 1378 struct ReserveSummary *rs; 1379 1380 GNUNET_assert (rowid >= TALER_ARL_USE_PP ( 1381 reserves_purse_decisions_serial_id)); /* should be monotonically increasing */ 1382 TALER_ARL_USE_PP (reserves_purse_decisions_serial_id) = rowid + 1; 1383 rs = setup_reserve (rc, 1384 reserve_pub); 1385 if (NULL == rs) 1386 { 1387 GNUNET_break (0); 1388 return GNUNET_SYSERR; 1389 } 1390 TALER_ARL_amount_add (&rs->total_in, 1391 &rs->total_in, 1392 purse_value); 1393 return GNUNET_OK; 1394 } 1395 1396 1397 /** 1398 * Check that the reserve summary matches what the exchange database 1399 * thinks about the reserve, and update our own state of the reserve. 1400 * 1401 * Remove all reserves that we are happy with from the DB. 1402 * 1403 * @param cls our `struct ReserveContext` 1404 * @param key hash of the reserve public key 1405 * @param value a `struct ReserveSummary` 1406 * @return #GNUNET_OK to process more entries 1407 */ 1408 static enum GNUNET_GenericReturnValue 1409 verify_reserve_balance (void *cls, 1410 const struct GNUNET_HashCode *key, 1411 void *value) 1412 { 1413 struct ReserveContext *rc = cls; 1414 struct ReserveSummary *rs = value; 1415 struct TALER_Amount mbalance; 1416 struct TALER_Amount nbalance; 1417 enum GNUNET_DB_QueryStatus qs; 1418 enum GNUNET_GenericReturnValue ret; 1419 1420 ret = GNUNET_OK; 1421 /* Check our reserve summary balance calculation shows that 1422 the reserve balance is acceptable (i.e. non-negative) */ 1423 TALER_ARL_amount_add (&mbalance, 1424 &rs->total_in, 1425 &rs->prev_balance.reserve_balance); 1426 if (TALER_ARL_SR_INVALID_NEGATIVE == 1427 TALER_ARL_amount_subtract_neg (&nbalance, 1428 &mbalance, 1429 &rs->total_out)) 1430 { 1431 struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiil = { 1432 .reserve_pub = rs->reserve_pub.eddsa_pub, 1433 .inconsistency_gain = false 1434 }; 1435 1436 TALER_ARL_amount_subtract (&rbiil.inconsistency_amount, 1437 &rs->total_out, 1438 &mbalance); 1439 TALER_ARL_amount_add (&rs->curr_balance.reserve_loss, 1440 &rs->prev_balance.reserve_loss, 1441 &rbiil.inconsistency_amount); 1442 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss), 1443 &TALER_ARL_USE_AB (reserves_reserve_loss), 1444 &rbiil.inconsistency_amount); 1445 qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency ( 1446 TALER_ARL_adb->cls, 1447 &rbiil); 1448 1449 if (qs < 0) 1450 { 1451 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1452 rc->qs = qs; 1453 return GNUNET_SYSERR; 1454 } 1455 /* Continue with a reserve balance of zero */ 1456 GNUNET_assert (GNUNET_OK == 1457 TALER_amount_set_zero (TALER_ARL_currency, 1458 &rs->curr_balance.reserve_balance)); 1459 nbalance = rs->curr_balance.reserve_balance; 1460 } 1461 else 1462 { 1463 /* Update remaining reserve balance! */ 1464 rs->curr_balance.reserve_balance = nbalance; 1465 } 1466 1467 if (internal_checks) 1468 { 1469 /* Now check OUR balance calculation vs. the one the exchange has 1470 in its database. This can only be done when we are doing an 1471 internal audit, as otherwise the balance of the 'reserves' table 1472 is not replicated at the auditor. */ 1473 struct TALER_EXCHANGEDB_Reserve reserve = { 1474 .pub = rs->reserve_pub 1475 }; 1476 1477 qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls, 1478 &reserve); 1479 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1480 { 1481 /* If the exchange doesn't have this reserve in the summary, it 1482 is like the exchange 'lost' that amount from its records, 1483 making an illegitimate gain over the amount it dropped. 1484 We don't add the amount to some total simply because it is 1485 not an actualized gain and could be trivially corrected by 1486 restoring the summary. */ 1487 struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = { 1488 .reserve_pub = rs->reserve_pub.eddsa_pub, 1489 .inconsistency_amount = nbalance, 1490 .inconsistency_gain = true 1491 }; 1492 1493 qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency ( 1494 TALER_ARL_adb->cls, 1495 &rbiig); 1496 1497 if (qs < 0) 1498 { 1499 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1500 rc->qs = qs; 1501 return GNUNET_SYSERR; 1502 } 1503 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1504 { 1505 GNUNET_break (0); 1506 qs = GNUNET_DB_STATUS_HARD_ERROR; 1507 } 1508 rc->qs = qs; 1509 } 1510 else 1511 { 1512 /* Check that exchange's balance matches our expected balance for the reserve */ 1513 if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance, 1514 &reserve.balance)) 1515 { 1516 struct TALER_Amount delta; 1517 1518 if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance, 1519 &reserve.balance)) 1520 { 1521 /* balance > reserve.balance */ 1522 TALER_ARL_amount_subtract (&delta, 1523 &rs->curr_balance.reserve_balance, 1524 &reserve.balance); 1525 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1526 total_balance_summary_delta_plus), 1527 &TALER_ARL_USE_AB ( 1528 total_balance_summary_delta_plus), 1529 &delta); 1530 } 1531 else 1532 { 1533 /* balance < reserve.balance */ 1534 TALER_ARL_amount_subtract (&delta, 1535 &reserve.balance, 1536 &rs->curr_balance.reserve_balance); 1537 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1538 total_balance_summary_delta_minus), 1539 &TALER_ARL_USE_AB ( 1540 total_balance_summary_delta_minus), 1541 &delta); 1542 } 1543 1544 { 1545 struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = 1546 { 1547 .reserve_pub = rs->reserve_pub.eddsa_pub, 1548 .inconsistency_amount = nbalance, 1549 .inconsistency_gain = true 1550 }; 1551 1552 qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency 1553 ( 1554 TALER_ARL_adb->cls, 1555 &rbiig); 1556 } 1557 if (qs < 0) 1558 { 1559 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1560 rc->qs = qs; 1561 return GNUNET_SYSERR; 1562 } 1563 1564 { 1565 struct TALER_AUDITORDB_ReserveBalanceSummaryWrongInconsistency rbswi = 1566 { 1567 .exchange_amount = reserve.balance, 1568 .auditor_amount = rs->curr_balance.reserve_balance, 1569 .reserve_pub = rs->reserve_pub 1570 }; 1571 1572 qs = TALER_ARL_adb->insert_reserve_balance_summary_wrong_inconsistency 1573 ( 1574 TALER_ARL_adb->cls, 1575 &rbswi); 1576 } 1577 if (qs < 0) 1578 { 1579 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1580 rc->qs = qs; 1581 return GNUNET_SYSERR; 1582 } 1583 } 1584 } 1585 } /* end of 'if (internal_checks)' */ 1586 1587 /* Check that reserve is being closed if it is past its expiration date 1588 (and the closing fee would not exceed the remaining balance) */ 1589 if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD, 1590 <, 1591 GNUNET_TIME_absolute_get_duration ( 1592 rs->a_expiration_date.abs_time))) 1593 { 1594 /* Reserve is expired */ 1595 struct TALER_Amount cfee; 1596 1597 if ( (NULL != rs->sender_account.full_payto) && 1598 (GNUNET_OK == 1599 get_closing_fee (rs->sender_account, 1600 rs->a_expiration_date, 1601 &cfee)) ) 1602 { 1603 /* We got the closing fee */ 1604 if (1 == TALER_amount_cmp (&nbalance, 1605 &cfee)) 1606 { 1607 struct TALER_AUDITORDB_ReserveNotClosedInconsistency rnci = { 1608 .reserve_pub = rs->reserve_pub, 1609 .expiration_time = rs->a_expiration_date.abs_time, 1610 .balance = nbalance, 1611 .diagnostic = rs->sender_account.full_payto 1612 }; 1613 1614 /* remaining balance (according to us) exceeds closing fee */ 1615 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1616 total_balance_reserve_not_closed), 1617 &TALER_ARL_USE_AB ( 1618 total_balance_reserve_not_closed), 1619 &rnci.balance); 1620 qs = TALER_ARL_adb->insert_reserve_not_closed_inconsistency ( 1621 TALER_ARL_adb->cls, 1622 &rnci); 1623 if (qs < 0) 1624 { 1625 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1626 rc->qs = qs; 1627 return GNUNET_SYSERR; 1628 } 1629 } 1630 } 1631 else 1632 { 1633 /* We failed to determine the closing fee, complain! */ 1634 struct TALER_AUDITORDB_ReserveNotClosedInconsistency rncid = { 1635 .reserve_pub = rs->reserve_pub, 1636 .balance = nbalance, 1637 .expiration_time = rs->a_expiration_date.abs_time, 1638 .diagnostic = (char *) "could not determine closing fee" 1639 }; 1640 1641 /* Even if we don't know the closing fee, update the 1642 total_balance_reserve_not_closed */ 1643 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1644 total_balance_reserve_not_closed), 1645 &TALER_ARL_USE_AB ( 1646 total_balance_reserve_not_closed), 1647 &nbalance); 1648 qs = TALER_ARL_adb->insert_reserve_not_closed_inconsistency ( 1649 TALER_ARL_adb->cls, 1650 &rncid); 1651 if (qs < 0) 1652 { 1653 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1654 rc->qs = qs; 1655 return GNUNET_SYSERR; 1656 } 1657 } 1658 } 1659 /* We already computed the 'new' balance in 'curr_balance' 1660 to include the previous balance, so this one is just 1661 an assignment, not adding up! */ 1662 rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance; 1663 1664 /* Add up new totals to previous totals */ 1665 TALER_ARL_amount_add (&rs->prev_balance.reserve_loss, 1666 &rs->prev_balance.reserve_loss, 1667 &rs->curr_balance.reserve_loss); 1668 TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance, 1669 &rs->prev_balance.withdraw_fee_balance, 1670 &rs->curr_balance.withdraw_fee_balance); 1671 TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance, 1672 &rs->prev_balance.close_fee_balance, 1673 &rs->curr_balance.close_fee_balance); 1674 TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance, 1675 &rs->prev_balance.purse_fee_balance, 1676 &rs->curr_balance.purse_fee_balance); 1677 TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance, 1678 &rs->prev_balance.open_fee_balance, 1679 &rs->curr_balance.open_fee_balance); 1680 TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance, 1681 &rs->prev_balance.history_fee_balance, 1682 &rs->curr_balance.history_fee_balance); 1683 /* Update global balance: add incoming first, then try 1684 to subtract outgoing... */ 1685 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_total_balance), 1686 &TALER_ARL_USE_AB (reserves_reserve_total_balance), 1687 &rs->total_in); 1688 { 1689 struct TALER_Amount r; 1690 1691 if (TALER_ARL_SR_INVALID_NEGATIVE == 1692 TALER_ARL_amount_subtract_neg (&r, 1693 &TALER_ARL_USE_AB ( 1694 reserves_reserve_total_balance), 1695 &rs->total_out)) 1696 { 1697 /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!) 1698 to be withdrawn more than it was IN TOTAL ever given (exchange balance 1699 went negative!). Woopsie. Calculate how badly it went and log. */ 1700 report_amount_arithmetic_inconsistency ("global escrow balance", 1701 0, 1702 &TALER_ARL_USE_AB ( 1703 reserves_reserve_total_balance), /* what we had */ 1704 &rs->total_out, /* what we needed */ 1705 0 /* specific profit/loss does not apply to the total summary */ 1706 ); 1707 if (global_qs < 0) 1708 return GNUNET_SYSERR; 1709 /* We unexpectedly went negative, so a sane value to continue from 1710 would be zero. */ 1711 GNUNET_assert (GNUNET_OK == 1712 TALER_amount_set_zero (TALER_ARL_currency, 1713 &TALER_ARL_USE_AB ( 1714 reserves_reserve_total_balance))); 1715 } 1716 else 1717 { 1718 TALER_ARL_USE_AB (reserves_reserve_total_balance) = r; 1719 } 1720 } 1721 if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance)) 1722 { 1723 /* balance is zero, drop reserve details (and then do not update/insert) */ 1724 if (rs->had_ri) 1725 { 1726 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1727 "Final balance of reserve `%s' is zero, dropping it\n", 1728 TALER_B2S (&rs->reserve_pub)); 1729 qs = TALER_ARL_adb->del_reserve_info (TALER_ARL_adb->cls, 1730 &rs->reserve_pub); 1731 if (0 >= qs) 1732 { 1733 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1734 ret = GNUNET_SYSERR; 1735 rc->qs = qs; 1736 } 1737 } 1738 else 1739 { 1740 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1741 "Final balance of reserve `%s' is zero, no need to remember it\n", 1742 TALER_B2S (&rs->reserve_pub)); 1743 } 1744 } 1745 else 1746 { 1747 /* balance is non-zero, persist for future audits */ 1748 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1749 "Remembering final balance of reserve `%s' as %s\n", 1750 TALER_B2S (&rs->reserve_pub), 1751 TALER_amount2s (&rs->prev_balance.reserve_balance)); 1752 if (rs->had_ri) 1753 qs = TALER_ARL_adb->update_reserve_info (TALER_ARL_adb->cls, 1754 &rs->reserve_pub, 1755 &rs->prev_balance, 1756 rs->a_expiration_date); 1757 else 1758 qs = TALER_ARL_adb->insert_reserve_info (TALER_ARL_adb->cls, 1759 &rs->reserve_pub, 1760 &rs->prev_balance, 1761 rs->a_expiration_date, 1762 rs->sender_account); 1763 if (0 >= qs) 1764 { 1765 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1766 ret = GNUNET_SYSERR; 1767 rc->qs = qs; 1768 } 1769 } 1770 /* now we can discard the cached entry */ 1771 GNUNET_assert (GNUNET_YES == 1772 GNUNET_CONTAINER_multihashmap_remove (rc->reserves, 1773 key, 1774 rs)); 1775 GNUNET_free (rs->sender_account.full_payto); 1776 GNUNET_free (rs); 1777 return ret; 1778 } 1779 1780 1781 #define CHECK_DB() do { \ 1782 if (qs < 0) { \ 1783 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); \ 1784 goto cleanup; \ 1785 } \ 1786 if (global_qs < 0) { \ 1787 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == global_qs); \ 1788 qs = global_qs; \ 1789 goto cleanup; \ 1790 } \ 1791 if (rc.qs < 0) { \ 1792 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rc.qs); \ 1793 qs = rc.qs; \ 1794 goto cleanup; \ 1795 } \ 1796 } while (0) 1797 1798 1799 /** 1800 * Analyze reserves for being well-formed. 1801 * 1802 * @param cls NULL 1803 * @return transaction status code 1804 */ 1805 static enum GNUNET_DB_QueryStatus 1806 analyze_reserves (void *cls) 1807 { 1808 struct ReserveContext rc = { 1809 .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT 1810 }; 1811 enum GNUNET_DB_QueryStatus qs; 1812 1813 (void) cls; 1814 global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1815 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1816 "Analyzing reserves\n"); 1817 qs = TALER_ARL_adb->get_auditor_progress ( 1818 TALER_ARL_adb->cls, 1819 TALER_ARL_GET_PP (reserves_reserve_in_serial_id), 1820 TALER_ARL_GET_PP (reserves_withdraw_serial_id), 1821 TALER_ARL_GET_PP (reserves_reserve_recoup_serial_id), 1822 TALER_ARL_GET_PP (reserves_reserve_open_serial_id), 1823 TALER_ARL_GET_PP (reserves_reserve_close_serial_id), 1824 TALER_ARL_GET_PP (reserves_purse_decisions_serial_id), 1825 TALER_ARL_GET_PP (reserves_account_merges_serial_id), 1826 TALER_ARL_GET_PP (reserves_history_requests_serial_id), 1827 NULL); 1828 if (0 > qs) 1829 { 1830 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1831 return qs; 1832 } 1833 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1834 { 1835 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1836 "First analysis using this auditor, starting audit from scratch\n"); 1837 } 1838 else 1839 { 1840 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1841 "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", 1842 (unsigned long long) TALER_ARL_USE_PP ( 1843 reserves_reserve_in_serial_id), 1844 (unsigned long long) TALER_ARL_USE_PP ( 1845 reserves_withdraw_serial_id), 1846 (unsigned long long) TALER_ARL_USE_PP ( 1847 reserves_reserve_recoup_serial_id), 1848 (unsigned long long) TALER_ARL_USE_PP ( 1849 reserves_reserve_open_serial_id), 1850 (unsigned long long) TALER_ARL_USE_PP ( 1851 reserves_reserve_close_serial_id), 1852 (unsigned long long) TALER_ARL_USE_PP ( 1853 reserves_purse_decisions_serial_id), 1854 (unsigned long long) TALER_ARL_USE_PP ( 1855 reserves_account_merges_serial_id), 1856 (unsigned long long) TALER_ARL_USE_PP ( 1857 reserves_history_requests_serial_id)); 1858 } 1859 qs = TALER_ARL_adb->get_balance ( 1860 TALER_ARL_adb->cls, 1861 TALER_ARL_GET_AB (reserves_reserve_total_balance), 1862 TALER_ARL_GET_AB (reserves_reserve_loss), 1863 TALER_ARL_GET_AB (reserves_withdraw_fee_revenue), 1864 TALER_ARL_GET_AB (reserves_close_fee_revenue), 1865 TALER_ARL_GET_AB (reserves_purse_fee_revenue), 1866 TALER_ARL_GET_AB (reserves_open_fee_revenue), 1867 TALER_ARL_GET_AB (reserves_history_fee_revenue), 1868 TALER_ARL_GET_AB (reserves_total_bad_sig_loss), 1869 TALER_ARL_GET_AB (total_balance_reserve_not_closed), 1870 TALER_ARL_GET_AB (reserves_total_arithmetic_delta_plus), 1871 TALER_ARL_GET_AB (reserves_total_arithmetic_delta_minus), 1872 TALER_ARL_GET_AB (total_balance_summary_delta_plus), 1873 TALER_ARL_GET_AB (total_balance_summary_delta_minus), 1874 NULL); 1875 if (qs < 0) 1876 { 1877 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1878 return qs; 1879 } 1880 rc.reserves = GNUNET_CONTAINER_multihashmap_create (512, 1881 GNUNET_NO); 1882 rc.revoked = GNUNET_CONTAINER_multihashmap_create (4, 1883 GNUNET_NO); 1884 1885 qs = TALER_ARL_edb->select_reserves_in_above_serial_id ( 1886 TALER_ARL_edb->cls, 1887 TALER_ARL_USE_PP (reserves_reserve_in_serial_id), 1888 &handle_reserve_in, 1889 &rc); 1890 CHECK_DB (); 1891 qs = TALER_ARL_edb->select_withdrawals_above_serial_id ( 1892 TALER_ARL_edb->cls, 1893 TALER_ARL_USE_PP (reserves_withdraw_serial_id), 1894 &handle_withdrawals, 1895 &rc); 1896 CHECK_DB (); 1897 qs = TALER_ARL_edb->select_recoup_above_serial_id ( 1898 TALER_ARL_edb->cls, 1899 TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id), 1900 &handle_recoup_by_reserve, 1901 &rc); 1902 if ( (qs < 0) || 1903 (rc.qs < 0) || 1904 (global_qs < 0) ) 1905 { 1906 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1907 return qs; 1908 } 1909 1910 qs = TALER_ARL_edb->select_reserve_open_above_serial_id ( 1911 TALER_ARL_edb->cls, 1912 TALER_ARL_USE_PP (reserves_reserve_open_serial_id), 1913 &handle_reserve_open, 1914 &rc); 1915 CHECK_DB (); 1916 qs = TALER_ARL_edb->select_reserve_closed_above_serial_id ( 1917 TALER_ARL_edb->cls, 1918 TALER_ARL_USE_PP (reserves_reserve_close_serial_id), 1919 &handle_reserve_closed, 1920 &rc); 1921 CHECK_DB (); 1922 /* process purse_decisions (to credit reserve) */ 1923 qs = TALER_ARL_edb->select_purse_decisions_above_serial_id ( 1924 TALER_ARL_edb->cls, 1925 TALER_ARL_USE_PP (reserves_purse_decisions_serial_id), 1926 false, /* only go for merged purses! */ 1927 &purse_decision_cb, 1928 &rc); 1929 CHECK_DB (); 1930 /* Charge purse fee! */ 1931 1932 qs = TALER_ARL_edb->select_account_merges_above_serial_id ( 1933 TALER_ARL_edb->cls, 1934 TALER_ARL_USE_PP (reserves_account_merges_serial_id), 1935 &handle_account_merged, 1936 &rc); 1937 CHECK_DB (); 1938 GNUNET_CONTAINER_multihashmap_iterate (rc.reserves, 1939 &verify_reserve_balance, 1940 &rc); 1941 CHECK_DB (); 1942 GNUNET_break (0 == 1943 GNUNET_CONTAINER_multihashmap_size (rc.reserves)); 1944 1945 qs = TALER_ARL_adb->insert_balance ( 1946 TALER_ARL_adb->cls, 1947 TALER_ARL_SET_AB (reserves_reserve_total_balance), 1948 TALER_ARL_SET_AB (reserves_reserve_loss), 1949 TALER_ARL_SET_AB (reserves_withdraw_fee_revenue), 1950 TALER_ARL_SET_AB (reserves_close_fee_revenue), 1951 TALER_ARL_SET_AB (reserves_purse_fee_revenue), 1952 TALER_ARL_SET_AB (reserves_open_fee_revenue), 1953 TALER_ARL_SET_AB (reserves_history_fee_revenue), 1954 TALER_ARL_SET_AB (reserves_total_bad_sig_loss), 1955 TALER_ARL_SET_AB (total_balance_reserve_not_closed), 1956 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus), 1957 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus), 1958 TALER_ARL_SET_AB (total_balance_summary_delta_plus), 1959 TALER_ARL_SET_AB (total_balance_summary_delta_minus), 1960 NULL); 1961 if (0 > qs) 1962 { 1963 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1964 goto cleanup; 1965 } 1966 1967 qs = TALER_ARL_adb->update_balance ( 1968 TALER_ARL_adb->cls, 1969 TALER_ARL_SET_AB (reserves_reserve_total_balance), 1970 TALER_ARL_SET_AB (reserves_reserve_loss), 1971 TALER_ARL_SET_AB (reserves_withdraw_fee_revenue), 1972 TALER_ARL_SET_AB (reserves_close_fee_revenue), 1973 TALER_ARL_SET_AB (reserves_purse_fee_revenue), 1974 TALER_ARL_SET_AB (reserves_open_fee_revenue), 1975 TALER_ARL_SET_AB (reserves_history_fee_revenue), 1976 TALER_ARL_SET_AB (reserves_total_bad_sig_loss), 1977 TALER_ARL_SET_AB (total_balance_reserve_not_closed), 1978 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus), 1979 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus), 1980 TALER_ARL_SET_AB (total_balance_summary_delta_plus), 1981 TALER_ARL_SET_AB (total_balance_summary_delta_minus), 1982 NULL); 1983 if (0 > qs) 1984 { 1985 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1986 goto cleanup; 1987 } 1988 1989 qs = TALER_ARL_adb->insert_auditor_progress ( 1990 TALER_ARL_adb->cls, 1991 TALER_ARL_SET_PP (reserves_reserve_in_serial_id), 1992 TALER_ARL_SET_PP (reserves_withdraw_serial_id), 1993 TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id), 1994 TALER_ARL_SET_PP (reserves_reserve_open_serial_id), 1995 TALER_ARL_SET_PP (reserves_reserve_close_serial_id), 1996 TALER_ARL_SET_PP (reserves_purse_decisions_serial_id), 1997 TALER_ARL_SET_PP (reserves_account_merges_serial_id), 1998 TALER_ARL_SET_PP (reserves_history_requests_serial_id), 1999 NULL); 2000 if (0 > qs) 2001 { 2002 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2003 "Failed to update auditor DB, not recording progress\n"); 2004 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2005 goto cleanup; 2006 } 2007 qs = TALER_ARL_adb->update_auditor_progress ( 2008 TALER_ARL_adb->cls, 2009 TALER_ARL_SET_PP (reserves_reserve_in_serial_id), 2010 TALER_ARL_SET_PP (reserves_withdraw_serial_id), 2011 TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id), 2012 TALER_ARL_SET_PP (reserves_reserve_open_serial_id), 2013 TALER_ARL_SET_PP (reserves_reserve_close_serial_id), 2014 TALER_ARL_SET_PP (reserves_purse_decisions_serial_id), 2015 TALER_ARL_SET_PP (reserves_account_merges_serial_id), 2016 TALER_ARL_SET_PP (reserves_history_requests_serial_id), 2017 NULL); 2018 if (0 > qs) 2019 { 2020 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2021 "Failed to update auditor DB, not recording progress\n"); 2022 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2023 goto cleanup; 2024 } 2025 2026 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2027 "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", 2028 (unsigned long long) TALER_ARL_USE_PP ( 2029 reserves_reserve_in_serial_id), 2030 (unsigned long long) TALER_ARL_USE_PP ( 2031 reserves_withdraw_serial_id), 2032 (unsigned long long) TALER_ARL_USE_PP ( 2033 reserves_reserve_recoup_serial_id), 2034 (unsigned long long) TALER_ARL_USE_PP ( 2035 reserves_reserve_open_serial_id), 2036 (unsigned long long) TALER_ARL_USE_PP ( 2037 reserves_reserve_close_serial_id), 2038 (unsigned long long) TALER_ARL_USE_PP ( 2039 reserves_purse_decisions_serial_id), 2040 (unsigned long long) TALER_ARL_USE_PP ( 2041 reserves_account_merges_serial_id), 2042 (unsigned long long) TALER_ARL_USE_PP ( 2043 reserves_history_requests_serial_id)); 2044 qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 2045 cleanup: 2046 GNUNET_CONTAINER_multihashmap_destroy (rc.reserves); 2047 GNUNET_CONTAINER_multihashmap_destroy (rc.revoked); 2048 return qs; 2049 } 2050 2051 2052 #undef CHECK_DB 2053 2054 2055 /** 2056 * Function called on events received from Postgres. 2057 * 2058 * @param cls closure, NULL 2059 * @param extra additional event data provided 2060 * @param extra_size number of bytes in @a extra 2061 */ 2062 static void 2063 db_notify (void *cls, 2064 const void *extra, 2065 size_t extra_size) 2066 { 2067 (void) cls; 2068 (void) extra; 2069 (void) extra_size; 2070 2071 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2072 "Received notification to wake reserves helper\n"); 2073 if (GNUNET_OK != 2074 TALER_ARL_setup_sessions_and_run (&analyze_reserves, 2075 NULL)) 2076 { 2077 GNUNET_SCHEDULER_shutdown (); 2078 global_ret = EXIT_FAILURE; 2079 return; 2080 } 2081 } 2082 2083 2084 /** 2085 * Function called on shutdown. 2086 */ 2087 static void 2088 do_shutdown (void *cls) 2089 { 2090 (void) cls; 2091 if (NULL != eh) 2092 { 2093 TALER_ARL_adb->event_listen_cancel (eh); 2094 eh = NULL; 2095 } 2096 TALER_ARL_done (); 2097 } 2098 2099 2100 /** 2101 * Main function that will be run. 2102 * 2103 * @param cls closure 2104 * @param args remaining command-line arguments 2105 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 2106 * @param c configuration 2107 */ 2108 static void 2109 run (void *cls, 2110 char *const *args, 2111 const char *cfgfile, 2112 const struct GNUNET_CONFIGURATION_Handle *c) 2113 { 2114 (void) cls; 2115 (void) args; 2116 (void) cfgfile; 2117 2118 cfg = c; 2119 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2120 NULL); 2121 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2122 "Launching reserves auditor\n"); 2123 if (GNUNET_OK != 2124 TALER_ARL_init (c)) 2125 { 2126 global_ret = EXIT_FAILURE; 2127 return; 2128 } 2129 if (GNUNET_OK != 2130 GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg, 2131 "exchangedb", 2132 "IDLE_RESERVE_EXPIRATION_TIME", 2133 &idle_reserve_expiration_time)) 2134 { 2135 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2136 "exchangedb", 2137 "IDLE_RESERVE_EXPIRATION_TIME"); 2138 GNUNET_SCHEDULER_shutdown (); 2139 global_ret = EXIT_FAILURE; 2140 return; 2141 } 2142 if (test_mode != 1) 2143 { 2144 struct GNUNET_DB_EventHeaderP es = { 2145 .size = htons (sizeof (es)), 2146 .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_RESERVES) 2147 }; 2148 2149 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2150 "Running helper indefinitely\n"); 2151 eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls, 2152 &es, 2153 GNUNET_TIME_UNIT_FOREVER_REL, 2154 &db_notify, 2155 NULL); 2156 } 2157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2158 "Starting audit\n"); 2159 if (GNUNET_OK != 2160 TALER_ARL_setup_sessions_and_run (&analyze_reserves, 2161 NULL)) 2162 { 2163 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2164 "Audit failed\n"); 2165 GNUNET_SCHEDULER_shutdown (); 2166 global_ret = EXIT_FAILURE; 2167 return; 2168 } 2169 } 2170 2171 2172 /** 2173 * The main function to check the database's handling of reserves. 2174 * 2175 * @param argc number of arguments from the command line 2176 * @param argv command line arguments 2177 * @return 0 ok, 1 on error 2178 */ 2179 int 2180 main (int argc, 2181 char *const *argv) 2182 { 2183 const struct GNUNET_GETOPT_CommandLineOption options[] = { 2184 GNUNET_GETOPT_option_flag ('i', 2185 "internal", 2186 "perform checks only applicable for exchange-internal audits", 2187 &internal_checks), 2188 GNUNET_GETOPT_option_flag ('t', 2189 "test", 2190 "run in test mode and exit when idle", 2191 &test_mode), 2192 GNUNET_GETOPT_option_timetravel ('T', 2193 "timetravel"), 2194 GNUNET_GETOPT_OPTION_END 2195 }; 2196 enum GNUNET_GenericReturnValue ret; 2197 2198 ret = GNUNET_PROGRAM_run ( 2199 TALER_AUDITOR_project_data (), 2200 argc, 2201 argv, 2202 "taler-helper-auditor-reserves", 2203 gettext_noop ("Audit Taler exchange reserve handling"), 2204 options, 2205 &run, 2206 NULL); 2207 if (GNUNET_SYSERR == ret) 2208 return EXIT_INVALIDARGUMENT; 2209 if (GNUNET_NO == ret) 2210 return EXIT_SUCCESS; 2211 return global_ret; 2212 } 2213 2214 2215 /* end of taler-helper-auditor-reserves.c */