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