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