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