taler-helper-auditor-reserves.c (79634B)
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 == TALER_amount_set_zero (TALER_ARL_currency, 591 &auditor_amount)); 592 GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, 593 &auditor_fee)); 594 GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency, 595 &auditor_amount_with_fee)); 596 597 for (size_t i = 0; i < num_denom_serials; i++) 598 { 599 /* lookup denomination pub data (make sure denom_pub is valid, establish fees); 600 initializes wsrd.h_denomination_pub! */ 601 qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i], 602 &issue); 603 if (0 > qs) 604 { 605 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 606 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 607 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 608 "Hard database error trying to get denomination by serial %llu (%s) from database!\n", 609 (unsigned long long) denom_serials[i], 610 GNUNET_h2s (&h_planchets->hash)); 611 rc->qs = qs; 612 return GNUNET_SYSERR; 613 } 614 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 615 { 616 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 617 "Denomination #%llu not found\n", 618 (unsigned long long) denom_serials[i]); 619 report_row_inconsistency ("withdraw", 620 rowid, 621 "denomination key not found"); 622 if (global_qs < 0) 623 return GNUNET_SYSERR; 624 return GNUNET_OK; 625 } 626 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 627 "Analyzing withdrawn denomination #%llu (%s)\n", 628 (unsigned long long) denom_serials[i], 629 TALER_amount2s (&issue->value)); 630 631 /* check that execution date is within withdraw range for denom_pub */ 632 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 633 "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n", 634 (unsigned long long) issue->start.abs_time.abs_value_us, 635 (unsigned long 636 long) issue->expire_withdraw.abs_time.abs_value_us, 637 (unsigned long long) execution_date.abs_time.abs_value_us); 638 if (GNUNET_TIME_timestamp_cmp (issue->start, 639 >, 640 execution_date) || 641 GNUNET_TIME_timestamp_cmp (issue->expire_withdraw, 642 <, 643 execution_date)) 644 { 645 struct TALER_AUDITORDB_DenominationKeyValidityWithdrawInconsistency 646 dkvwi ={ 647 .problem_row_id = rowid, 648 .execution_date = execution_date.abs_time, 649 .denompub_h = issue->denom_hash, 650 .reserve_pub = *reserve_pub 651 }; 652 653 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 654 "Withdraw outside of denomination #%llu validity period detected\n", 655 (unsigned long long) denom_serials[i]); 656 qs = 657 TALER_AUDITORDB_insert_denomination_key_validity_withdraw_inconsistency 658 ( 659 TALER_ARL_adb, 660 &dkvwi); 661 662 if (qs < 0) 663 { 664 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 665 rc->qs = qs; 666 return GNUNET_SYSERR; 667 } 668 } 669 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 670 "Adding withdraw fee of denomination (%s)\n", 671 TALER_amount2s (&issue->fees.withdraw)); 672 TALER_ARL_amount_add (&auditor_amount, 673 &auditor_amount, 674 &issue->value); 675 TALER_ARL_amount_add (&auditor_fee, 676 &auditor_fee, 677 &issue->fees.withdraw); 678 { 679 struct TALER_Amount issue_amount_with_fee; 680 681 TALER_ARL_amount_add (&issue_amount_with_fee, 682 &issue->value, 683 &issue->fees.withdraw); 684 TALER_ARL_amount_add (&auditor_amount_with_fee, 685 &auditor_amount_with_fee, 686 &issue_amount_with_fee); 687 } 688 } 689 690 /* check reserve_sig (first: setup remaining members of wsrd) */ 691 if (GNUNET_OK != 692 TALER_wallet_withdraw_verify ( 693 &auditor_amount, 694 &auditor_fee, 695 h_planchets, 696 blinding_seed, 697 age_proof_required 698 ? &issue->age_mask 699 : NULL, 700 age_proof_required 701 ? max_age 702 : 0, 703 reserve_pub, 704 reserve_sig)) 705 { 706 struct TALER_AUDITORDB_BadSigLosses bsl = { 707 .problem_row_id = rowid, 708 .operation = (char *) "withdraw", 709 .loss = *amount_with_fee, 710 .operation_specific_pub = reserve_pub->eddsa_pub 711 }; 712 713 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 714 "Withdraw signature invalid (row #%llu)\n", 715 (unsigned long long) rowid); 716 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 717 TALER_ARL_adb, 718 &bsl); 719 if (qs < 0) 720 { 721 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 722 rc->qs = qs; 723 return GNUNET_SYSERR; 724 } 725 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 726 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 727 amount_with_fee); 728 return GNUNET_OK; /* exit function here, we cannot add this to the legitimate withdrawals */ 729 } 730 731 if (0 != 732 TALER_amount_cmp (&auditor_amount_with_fee, 733 amount_with_fee)) 734 { 735 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 736 "Withdraw fee inconsistent (row #%llu)\n", 737 (unsigned long long) rowid); 738 report_row_inconsistency ("withdraw", 739 rowid, 740 "amount with fee from exchange does not match denomination value plus fee"); 741 if (global_qs < 0) 742 return GNUNET_SYSERR; 743 } 744 rs = setup_reserve (rc, 745 reserve_pub); 746 if (NULL == rs) 747 { 748 GNUNET_break (0); 749 return GNUNET_SYSERR; 750 } 751 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 752 "Reserve `%s' reduced by %s from withdraw\n", 753 TALER_B2S (reserve_pub), 754 TALER_amount2s (&auditor_amount_with_fee)); 755 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 756 "Increasing withdraw profits by fee %s\n", 757 TALER_amount2s (&issue->fees.withdraw)); 758 TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance, 759 &rs->curr_balance.withdraw_fee_balance, 760 &issue->fees.withdraw); 761 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_withdraw_fee_revenue), 762 &TALER_ARL_USE_AB (reserves_withdraw_fee_revenue), 763 &issue->fees.withdraw); 764 TALER_ARL_amount_add (&rs->total_out, 765 &rs->total_out, 766 &auditor_amount_with_fee); 767 return GNUNET_OK; 768 } 769 770 771 /** 772 * Function called with details about withdraw operations. Verifies 773 * the signature and updates the reserve's balance. 774 * 775 * @param cls our `struct ReserveContext` 776 * @param rowid unique serial ID for the refresh session in our DB 777 * @param timestamp when did we receive the recoup request 778 * @param amount how much should be added back to the reserve 779 * @param reserve_pub public key of the reserve 780 * @param coin public information about the coin, denomination signature is 781 * already verified in #check_recoup() 782 * @param denom_pub public key of the denomionation of @a coin 783 * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP 784 * @param coin_blind blinding factor used to blind the coin 785 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 786 */ 787 static enum GNUNET_GenericReturnValue 788 handle_recoup_by_reserve ( 789 void *cls, 790 uint64_t rowid, 791 struct GNUNET_TIME_Timestamp timestamp, 792 const struct TALER_Amount *amount, 793 const struct TALER_ReservePublicKeyP *reserve_pub, 794 const struct TALER_CoinPublicInfo *coin, 795 const struct TALER_DenominationPublicKey *denom_pub, 796 const struct TALER_CoinSpendSignatureP *coin_sig, 797 const union GNUNET_CRYPTO_BlindingSecretP *coin_blind) 798 { 799 struct ReserveContext *rc = cls; 800 struct ReserveSummary *rs; 801 struct GNUNET_TIME_Timestamp expiry; 802 struct TALER_MasterSignatureP msig; 803 uint64_t rev_rowid; 804 enum GNUNET_DB_QueryStatus qs; 805 const char *rev; 806 807 (void) denom_pub; 808 /* should be monotonically increasing */ 809 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id)); 810 TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id) = rowid + 1; 811 /* We know that denom_pub matches denom_pub_hash because this 812 is how the SQL statement joined the tables. */ 813 if (GNUNET_OK != 814 TALER_wallet_recoup_verify (&coin->denom_pub_hash, 815 coin_blind, 816 &coin->coin_pub, 817 coin_sig)) 818 { 819 struct TALER_AUDITORDB_BadSigLosses bslr = { 820 .problem_row_id = rowid, 821 .operation = (char *) "recoup", 822 .loss = *amount, 823 .operation_specific_pub = coin->coin_pub.eddsa_pub 824 }; 825 826 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 827 TALER_ARL_adb, 828 &bslr); 829 830 if (qs < 0) 831 { 832 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 833 rc->qs = qs; 834 return GNUNET_SYSERR; 835 } 836 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 837 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 838 amount); 839 } 840 841 /* check that the coin was eligible for recoup!*/ 842 rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked, 843 &coin->denom_pub_hash.hash); 844 if (NULL == rev) 845 { 846 qs = TALER_EXCHANGEDB_get_denomination_revocation (TALER_ARL_edb, 847 &coin->denom_pub_hash, 848 &msig, 849 &rev_rowid); 850 if (0 > qs) 851 { 852 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 853 rc->qs = qs; 854 return GNUNET_SYSERR; 855 } 856 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 857 { 858 report_row_inconsistency ("recoup", 859 rowid, 860 "denomination key not in revocation set"); 861 if (global_qs < 0) 862 return GNUNET_SYSERR; 863 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss), 864 &TALER_ARL_USE_AB (reserves_reserve_loss), 865 amount); 866 } 867 else 868 { 869 if (GNUNET_OK != 870 TALER_exchange_offline_denomination_revoke_verify ( 871 &coin->denom_pub_hash, 872 &TALER_ARL_master_pub, 873 &msig)) 874 { 875 rev = "master signature invalid"; 876 } 877 else 878 { 879 rev = "revoked"; 880 } 881 GNUNET_assert ( 882 GNUNET_OK == 883 GNUNET_CONTAINER_multihashmap_put ( 884 rc->revoked, 885 &coin->denom_pub_hash.hash, 886 (void *) rev, 887 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 888 } 889 } 890 else 891 { 892 rev_rowid = 0; /* reported elsewhere */ 893 } 894 if ((NULL != rev) && 895 (0 == strcmp (rev, 896 "master signature invalid"))) 897 { 898 struct TALER_AUDITORDB_BadSigLosses bslrm = { 899 .problem_row_id = rev_rowid, 900 .operation = (char *) "recoup-master", 901 .loss = *amount, 902 .operation_specific_pub = TALER_ARL_master_pub.eddsa_pub 903 }; 904 905 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 906 TALER_ARL_adb, 907 &bslrm); 908 909 if (qs < 0) 910 { 911 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 912 rc->qs = qs; 913 return GNUNET_SYSERR; 914 } 915 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 916 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 917 amount); 918 } 919 920 rs = setup_reserve (rc, 921 reserve_pub); 922 if (NULL == rs) 923 { 924 GNUNET_break (0); 925 return GNUNET_SYSERR; 926 } 927 TALER_ARL_amount_add (&rs->total_in, 928 &rs->total_in, 929 amount); 930 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 931 "Additional /recoup value to for reserve `%s' of %s\n", 932 TALER_B2S (reserve_pub), 933 TALER_amount2s (amount)); 934 expiry = GNUNET_TIME_absolute_to_timestamp ( 935 GNUNET_TIME_absolute_add (timestamp.abs_time, 936 idle_reserve_expiration_time)); 937 rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date, 938 expiry); 939 return GNUNET_OK; 940 } 941 942 943 /** 944 * Obtain the closing fee for a transfer at @a time for target 945 * @a receiver_account. 946 * 947 * @param receiver_account payto:// URI of the target account 948 * @param atime when was the transfer made 949 * @param[out] fee set to the closing fee 950 * @return #GNUNET_OK on success 951 */ 952 static enum GNUNET_GenericReturnValue 953 get_closing_fee (const struct TALER_FullPayto receiver_account, 954 struct GNUNET_TIME_Timestamp atime, 955 struct TALER_Amount *fee) 956 { 957 struct TALER_MasterSignatureP master_sig; 958 struct GNUNET_TIME_Timestamp start_date; 959 struct GNUNET_TIME_Timestamp end_date; 960 struct TALER_WireFeeSet fees; 961 char *method; 962 uint64_t rowid; 963 964 method = TALER_payto_get_method (receiver_account.full_payto); 965 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 966 "Method is `%s'\n", 967 method); 968 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 969 TALER_EXCHANGEDB_get_wire_fee (TALER_ARL_edb, 970 method, 971 atime, 972 &rowid, 973 &start_date, 974 &end_date, 975 &fees, 976 &master_sig)) 977 { 978 char *diag; 979 980 GNUNET_asprintf (&diag, 981 "closing fee for `%s' unavailable at %s\n", 982 method, 983 GNUNET_TIME_timestamp2s (atime)); 984 report_row_inconsistency ("closing-fee", 985 rowid, 986 diag); 987 GNUNET_free (diag); 988 GNUNET_free (method); 989 return GNUNET_SYSERR; 990 } 991 *fee = fees.closing; 992 GNUNET_free (method); 993 return GNUNET_OK; 994 } 995 996 997 /** 998 * Function called about reserve opening operations. 999 * 1000 * @param cls closure 1001 * @param rowid row identifier used to uniquely identify the reserve closing operation 1002 * @param reserve_payment how much to pay from the 1003 * reserve's own balance for opening the reserve 1004 * @param request_timestamp when was the request created 1005 * @param reserve_expiration desired expiration time for the reserve 1006 * @param purse_limit minimum number of purses the client 1007 * wants to have concurrently open for this reserve 1008 * @param reserve_pub public key of the reserve 1009 * @param reserve_sig signature affirming the operation 1010 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1011 */ 1012 static enum GNUNET_GenericReturnValue 1013 handle_reserve_open ( 1014 void *cls, 1015 uint64_t rowid, 1016 const struct TALER_Amount *reserve_payment, 1017 struct GNUNET_TIME_Timestamp request_timestamp, 1018 struct GNUNET_TIME_Timestamp reserve_expiration, 1019 uint32_t purse_limit, 1020 const struct TALER_ReservePublicKeyP *reserve_pub, 1021 const struct TALER_ReserveSignatureP *reserve_sig) 1022 { 1023 struct ReserveContext *rc = cls; 1024 struct ReserveSummary *rs; 1025 enum GNUNET_DB_QueryStatus qs; 1026 1027 /* should be monotonically increasing */ 1028 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_open_serial_id)); 1029 TALER_ARL_USE_PP (reserves_reserve_open_serial_id) = rowid + 1; 1030 1031 rs = setup_reserve (rc, 1032 reserve_pub); 1033 if (NULL == rs) 1034 { 1035 GNUNET_break (0); 1036 return GNUNET_SYSERR; 1037 } 1038 if (GNUNET_OK != 1039 TALER_wallet_reserve_open_verify (reserve_payment, 1040 request_timestamp, 1041 reserve_expiration, 1042 purse_limit, 1043 reserve_pub, 1044 reserve_sig)) 1045 { 1046 struct TALER_AUDITORDB_BadSigLosses bsl = { 1047 .problem_row_id = rowid, 1048 .operation = (char *) "reserve-open", 1049 .loss = *reserve_payment, 1050 .operation_specific_pub = reserve_pub->eddsa_pub 1051 }; 1052 1053 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1054 TALER_ARL_adb, 1055 &bsl); 1056 1057 if (qs < 0) 1058 { 1059 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1060 rc->qs = qs; 1061 return GNUNET_SYSERR; 1062 } 1063 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1064 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1065 reserve_payment); 1066 return GNUNET_OK; 1067 } 1068 TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance, 1069 &rs->curr_balance.open_fee_balance, 1070 reserve_payment); 1071 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_open_fee_revenue), 1072 &TALER_ARL_USE_AB (reserves_open_fee_revenue), 1073 reserve_payment); 1074 TALER_ARL_amount_add (&rs->total_out, 1075 &rs->total_out, 1076 reserve_payment); 1077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1078 "Additional open operation for reserve `%s' of %s\n", 1079 TALER_B2S (reserve_pub), 1080 TALER_amount2s (reserve_payment)); 1081 return GNUNET_OK; 1082 } 1083 1084 1085 /** 1086 * Function called about reserve closing operations 1087 * the aggregator triggered. 1088 * 1089 * @param cls closure 1090 * @param rowid row identifier used to uniquely identify the reserve closing operation 1091 * @param execution_date when did we execute the close operation 1092 * @param amount_with_fee how much did we debit the reserve 1093 * @param closing_fee how much did we charge for closing the reserve 1094 * @param reserve_pub public key of the reserve 1095 * @param receiver_account where did we send the funds 1096 * @param transfer_details details about the wire transfer 1097 * @param close_request_row which close request triggered the operation? 1098 * 0 if it was a timeout 1099 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1100 */ 1101 static enum GNUNET_GenericReturnValue 1102 handle_reserve_closed ( 1103 void *cls, 1104 uint64_t rowid, 1105 struct GNUNET_TIME_Timestamp execution_date, 1106 const struct TALER_Amount *amount_with_fee, 1107 const struct TALER_Amount *closing_fee, 1108 const struct TALER_ReservePublicKeyP *reserve_pub, 1109 const struct TALER_FullPayto receiver_account, 1110 const struct TALER_WireTransferIdentifierRawP *transfer_details, 1111 uint64_t close_request_row) 1112 { 1113 struct ReserveContext *rc = cls; 1114 struct ReserveSummary *rs; 1115 1116 (void) transfer_details; 1117 /* should be monotonically increasing */ 1118 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_close_serial_id)); 1119 TALER_ARL_USE_PP (reserves_reserve_close_serial_id) = rowid + 1; 1120 1121 rs = setup_reserve (rc, 1122 reserve_pub); 1123 if (NULL == rs) 1124 { 1125 GNUNET_break (0); 1126 return GNUNET_SYSERR; 1127 } 1128 { 1129 struct TALER_Amount expected_fee; 1130 1131 /* verify closing_fee is correct! */ 1132 if (GNUNET_OK != 1133 get_closing_fee (receiver_account, 1134 execution_date, 1135 &expected_fee)) 1136 { 1137 GNUNET_break (0); 1138 } 1139 else if (0 != TALER_amount_cmp (&expected_fee, 1140 closing_fee)) 1141 { 1142 report_amount_arithmetic_inconsistency ( 1143 "closing aggregation fee", 1144 rowid, 1145 closing_fee, 1146 &expected_fee, 1147 1); 1148 if (global_qs < 0) 1149 return GNUNET_SYSERR; 1150 } 1151 } 1152 1153 TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance, 1154 &rs->curr_balance.close_fee_balance, 1155 closing_fee); 1156 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_close_fee_revenue), 1157 &TALER_ARL_USE_AB (reserves_close_fee_revenue), 1158 closing_fee); 1159 TALER_ARL_amount_add (&rs->total_out, 1160 &rs->total_out, 1161 amount_with_fee); 1162 if (0 != close_request_row) 1163 { 1164 struct TALER_ReserveSignatureP reserve_sig; 1165 struct GNUNET_TIME_Timestamp request_timestamp; 1166 struct TALER_Amount close_balance; 1167 struct TALER_Amount close_fee; 1168 struct TALER_FullPayto payto_uri; 1169 enum GNUNET_DB_QueryStatus qs; 1170 1171 qs = TALER_EXCHANGEDB_select_reserve_close_request_info ( 1172 TALER_ARL_edb, 1173 reserve_pub, 1174 close_request_row, 1175 &reserve_sig, 1176 &request_timestamp, 1177 &close_balance, 1178 &close_fee, 1179 &payto_uri); 1180 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1181 { 1182 report_row_inconsistency ("reserves_close", 1183 rowid, 1184 "reserve close request unknown"); 1185 if (global_qs < 0) 1186 return GNUNET_SYSERR; 1187 } 1188 else 1189 { 1190 struct TALER_FullPaytoHashP h_payto; 1191 1192 TALER_full_payto_hash (payto_uri, 1193 &h_payto); 1194 if (GNUNET_OK != 1195 TALER_wallet_reserve_close_verify ( 1196 request_timestamp, 1197 &h_payto, 1198 reserve_pub, 1199 &reserve_sig)) 1200 { 1201 struct TALER_AUDITORDB_BadSigLosses bsl = { 1202 .problem_row_id = close_request_row, 1203 .operation = (char *) "close-request", 1204 .loss = *amount_with_fee, 1205 .operation_specific_pub = reserve_pub->eddsa_pub 1206 }; 1207 1208 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1209 TALER_ARL_adb, 1210 &bsl); 1211 1212 if (qs < 0) 1213 { 1214 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1215 rc->qs = qs; 1216 GNUNET_free (payto_uri.full_payto); 1217 return GNUNET_SYSERR; 1218 } 1219 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1220 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1221 amount_with_fee); 1222 } 1223 } 1224 if ( (NULL == payto_uri.full_payto) && 1225 (NULL == rs->sender_account.full_payto) ) 1226 { 1227 GNUNET_break (! rs->had_ri); 1228 report_row_inconsistency ("reserves_close", 1229 rowid, 1230 "target account not verified, auditor does not know reserve"); 1231 if (global_qs < 0) 1232 return GNUNET_SYSERR; 1233 } 1234 if (NULL == payto_uri.full_payto) 1235 { 1236 if ((NULL == rs->sender_account.full_payto) || 1237 (0 != TALER_full_payto_cmp (rs->sender_account, 1238 receiver_account))) 1239 { 1240 report_row_inconsistency ("reserves_close", 1241 rowid, 1242 "target account does not match origin account"); 1243 if (global_qs < 0) 1244 return GNUNET_SYSERR; 1245 } 1246 } 1247 else 1248 { 1249 if (0 != TALER_full_payto_cmp (payto_uri, 1250 receiver_account)) 1251 { 1252 report_row_inconsistency ("reserves_close", 1253 rowid, 1254 "target account does not match origin account"); 1255 if (global_qs < 0) 1256 { 1257 GNUNET_free (payto_uri.full_payto); 1258 return GNUNET_SYSERR; 1259 } 1260 } 1261 } 1262 GNUNET_free (payto_uri.full_payto); 1263 } 1264 else 1265 { 1266 if (NULL == rs->sender_account.full_payto) 1267 { 1268 GNUNET_break (! rs->had_ri); 1269 report_row_inconsistency ("reserves_close", 1270 rowid, 1271 "target account not verified, auditor does not know reserve"); 1272 if (global_qs < 0) 1273 return GNUNET_SYSERR; 1274 } 1275 else if (0 != TALER_full_payto_cmp (rs->sender_account, 1276 receiver_account)) 1277 { 1278 report_row_inconsistency ("reserves_close", 1279 rowid, 1280 "target account does not match origin account"); 1281 if (global_qs < 0) 1282 return GNUNET_SYSERR; 1283 } 1284 } 1285 1286 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1287 "Additional closing operation for reserve `%s' of %s\n", 1288 TALER_B2S (reserve_pub), 1289 TALER_amount2s (amount_with_fee)); 1290 return GNUNET_OK; 1291 } 1292 1293 1294 /** 1295 * Function called with details about account merge requests that have been 1296 * made, with the goal of accounting for the merge fee paid by the reserve (if 1297 * applicable). 1298 * 1299 * @param cls closure 1300 * @param rowid unique serial ID for the deposit in our DB 1301 * @param reserve_pub reserve affected by the merge 1302 * @param purse_pub purse being merged 1303 * @param h_contract_terms hash over contract of the purse 1304 * @param purse_expiration when would the purse expire 1305 * @param amount total amount in the purse 1306 * @param min_age minimum age of all coins deposited into the purse 1307 * @param flags how was the purse created 1308 * @param purse_fee if a purse fee was paid, how high is it 1309 * @param merge_timestamp when was the merge approved 1310 * @param reserve_sig signature by reserve approving the merge 1311 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1312 */ 1313 static enum GNUNET_GenericReturnValue 1314 handle_account_merged ( 1315 void *cls, 1316 uint64_t rowid, 1317 const struct TALER_ReservePublicKeyP *reserve_pub, 1318 const struct TALER_PurseContractPublicKeyP *purse_pub, 1319 const struct TALER_PrivateContractHashP *h_contract_terms, 1320 struct GNUNET_TIME_Timestamp purse_expiration, 1321 const struct TALER_Amount *amount, 1322 uint32_t min_age, 1323 enum TALER_WalletAccountMergeFlags flags, 1324 const struct TALER_Amount *purse_fee, 1325 struct GNUNET_TIME_Timestamp merge_timestamp, 1326 const struct TALER_ReserveSignatureP *reserve_sig) 1327 { 1328 struct ReserveContext *rc = cls; 1329 struct ReserveSummary *rs; 1330 enum GNUNET_DB_QueryStatus qs; 1331 1332 /* should be monotonically increasing */ 1333 GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_account_merges_serial_id)); 1334 TALER_ARL_USE_PP (reserves_account_merges_serial_id) = rowid + 1; 1335 if (GNUNET_OK != 1336 TALER_wallet_account_merge_verify (merge_timestamp, 1337 purse_pub, 1338 purse_expiration, 1339 h_contract_terms, 1340 amount, 1341 purse_fee, 1342 min_age, 1343 flags, 1344 reserve_pub, 1345 reserve_sig)) 1346 { 1347 struct TALER_AUDITORDB_BadSigLosses bsl = { 1348 .problem_row_id = rowid, 1349 .operation = (char *) "account-merge", 1350 .loss = *purse_fee, 1351 .operation_specific_pub = reserve_pub->eddsa_pub 1352 }; 1353 1354 qs = TALER_AUDITORDB_insert_bad_sig_losses ( 1355 TALER_ARL_adb, 1356 &bsl); 1357 if (qs < 0) 1358 { 1359 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1360 rc->qs = qs; 1361 return GNUNET_SYSERR; 1362 } 1363 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1364 &TALER_ARL_USE_AB (reserves_total_bad_sig_loss), 1365 purse_fee); 1366 return GNUNET_OK; 1367 } 1368 if ((flags & TALER_WAMF_MERGE_MODE_MASK) != 1369 TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE) 1370 return GNUNET_OK; /* no impact on reserve balance */ 1371 rs = setup_reserve (rc, 1372 reserve_pub); 1373 if (NULL == rs) 1374 { 1375 GNUNET_break (0); 1376 return GNUNET_SYSERR; 1377 } 1378 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_purse_fee_revenue), 1379 &TALER_ARL_USE_AB (reserves_purse_fee_revenue), 1380 purse_fee); 1381 TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance, 1382 &rs->curr_balance.purse_fee_balance, 1383 purse_fee); 1384 TALER_ARL_amount_add (&rs->total_out, 1385 &rs->total_out, 1386 purse_fee); 1387 return GNUNET_OK; 1388 } 1389 1390 1391 /** 1392 * Function called with details about a purse that was merged into an account. 1393 * Only updates the reserve balance, the actual verifications are done in the 1394 * purse helper. 1395 * 1396 * @param cls closure 1397 * @param rowid unique serial ID for the refund in our DB 1398 * @param purse_pub public key of the purse 1399 * @param reserve_pub which reserve is the purse credited to 1400 * @param purse_value what is the target value of the purse 1401 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1402 */ 1403 static enum GNUNET_GenericReturnValue 1404 purse_decision_cb (void *cls, 1405 uint64_t rowid, 1406 const struct TALER_PurseContractPublicKeyP *purse_pub, 1407 const struct TALER_ReservePublicKeyP *reserve_pub, 1408 const struct TALER_Amount *purse_value) 1409 { 1410 struct ReserveContext *rc = cls; 1411 struct ReserveSummary *rs; 1412 1413 GNUNET_assert (rowid >= TALER_ARL_USE_PP ( 1414 reserves_purse_decisions_serial_id)); /* should be monotonically increasing */ 1415 TALER_ARL_USE_PP (reserves_purse_decisions_serial_id) = rowid + 1; 1416 rs = setup_reserve (rc, 1417 reserve_pub); 1418 if (NULL == rs) 1419 { 1420 GNUNET_break (0); 1421 return GNUNET_SYSERR; 1422 } 1423 TALER_ARL_amount_add (&rs->total_in, 1424 &rs->total_in, 1425 purse_value); 1426 return GNUNET_OK; 1427 } 1428 1429 1430 /** 1431 * Check that the reserve summary matches what the exchange database 1432 * thinks about the reserve, and update our own state of the reserve. 1433 * 1434 * Remove all reserves that we are happy with from the DB. 1435 * 1436 * @param cls our `struct ReserveContext` 1437 * @param key hash of the reserve public key 1438 * @param value a `struct ReserveSummary` 1439 * @return #GNUNET_OK to process more entries 1440 */ 1441 static enum GNUNET_GenericReturnValue 1442 verify_reserve_balance (void *cls, 1443 const struct GNUNET_HashCode *key, 1444 void *value) 1445 { 1446 struct ReserveContext *rc = cls; 1447 struct ReserveSummary *rs = value; 1448 struct TALER_Amount mbalance; 1449 struct TALER_Amount nbalance; 1450 enum GNUNET_DB_QueryStatus qs; 1451 enum GNUNET_GenericReturnValue ret; 1452 1453 ret = GNUNET_OK; 1454 /* Check our reserve summary balance calculation shows that 1455 the reserve balance is acceptable (i.e. non-negative) */ 1456 TALER_ARL_amount_add (&mbalance, 1457 &rs->total_in, 1458 &rs->prev_balance.reserve_balance); 1459 if (TALER_ARL_SR_INVALID_NEGATIVE == 1460 TALER_ARL_amount_subtract_neg (&nbalance, 1461 &mbalance, 1462 &rs->total_out)) 1463 { 1464 struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiil = { 1465 .reserve_pub = rs->reserve_pub.eddsa_pub, 1466 .inconsistency_gain = false 1467 }; 1468 1469 TALER_ARL_amount_subtract (&rbiil.inconsistency_amount, 1470 &rs->total_out, 1471 &mbalance); 1472 TALER_ARL_amount_add (&rs->curr_balance.reserve_loss, 1473 &rs->prev_balance.reserve_loss, 1474 &rbiil.inconsistency_amount); 1475 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss), 1476 &TALER_ARL_USE_AB (reserves_reserve_loss), 1477 &rbiil.inconsistency_amount); 1478 qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency ( 1479 TALER_ARL_adb, 1480 &rbiil); 1481 1482 if (qs < 0) 1483 { 1484 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1485 rc->qs = qs; 1486 return GNUNET_SYSERR; 1487 } 1488 /* Continue with a reserve balance of zero */ 1489 GNUNET_assert (GNUNET_OK == 1490 TALER_amount_set_zero (TALER_ARL_currency, 1491 &rs->curr_balance.reserve_balance)); 1492 nbalance = rs->curr_balance.reserve_balance; 1493 } 1494 else 1495 { 1496 /* Update remaining reserve balance! */ 1497 rs->curr_balance.reserve_balance = nbalance; 1498 } 1499 1500 if (internal_checks) 1501 { 1502 /* Now check OUR balance calculation vs. the one the exchange has 1503 in its database. This can only be done when we are doing an 1504 internal audit, as otherwise the balance of the 'reserves' table 1505 is not replicated at the auditor. */ 1506 struct TALER_EXCHANGEDB_Reserve reserve = { 1507 .pub = rs->reserve_pub 1508 }; 1509 1510 qs = TALER_EXCHANGEDB_reserves_get (TALER_ARL_edb, 1511 &reserve); 1512 if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) 1513 { 1514 /* If the exchange doesn't have this reserve in the summary, it 1515 is like the exchange 'lost' that amount from its records, 1516 making an illegitimate gain over the amount it dropped. 1517 We don't add the amount to some total simply because it is 1518 not an actualized gain and could be trivially corrected by 1519 restoring the summary. */ 1520 struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = { 1521 .reserve_pub = rs->reserve_pub.eddsa_pub, 1522 .inconsistency_amount = nbalance, 1523 .inconsistency_gain = true 1524 }; 1525 1526 qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency ( 1527 TALER_ARL_adb, 1528 &rbiig); 1529 1530 if (qs < 0) 1531 { 1532 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1533 rc->qs = qs; 1534 return GNUNET_SYSERR; 1535 } 1536 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1537 { 1538 GNUNET_break (0); 1539 qs = GNUNET_DB_STATUS_HARD_ERROR; 1540 } 1541 rc->qs = qs; 1542 } 1543 else 1544 { 1545 /* Check that exchange's balance matches our expected balance for the reserve */ 1546 if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance, 1547 &reserve.balance)) 1548 { 1549 struct TALER_Amount delta; 1550 1551 if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance, 1552 &reserve.balance)) 1553 { 1554 /* balance > reserve.balance */ 1555 TALER_ARL_amount_subtract (&delta, 1556 &rs->curr_balance.reserve_balance, 1557 &reserve.balance); 1558 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1559 total_balance_summary_delta_plus), 1560 &TALER_ARL_USE_AB ( 1561 total_balance_summary_delta_plus), 1562 &delta); 1563 } 1564 else 1565 { 1566 /* balance < reserve.balance */ 1567 TALER_ARL_amount_subtract (&delta, 1568 &reserve.balance, 1569 &rs->curr_balance.reserve_balance); 1570 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1571 total_balance_summary_delta_minus), 1572 &TALER_ARL_USE_AB ( 1573 total_balance_summary_delta_minus), 1574 &delta); 1575 } 1576 1577 { 1578 struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = 1579 { 1580 .reserve_pub = rs->reserve_pub.eddsa_pub, 1581 .inconsistency_amount = nbalance, 1582 .inconsistency_gain = true 1583 }; 1584 1585 qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency 1586 ( 1587 TALER_ARL_adb, 1588 &rbiig); 1589 } 1590 if (qs < 0) 1591 { 1592 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1593 rc->qs = qs; 1594 return GNUNET_SYSERR; 1595 } 1596 1597 { 1598 struct TALER_AUDITORDB_ReserveBalanceSummaryWrongInconsistency rbswi = 1599 { 1600 .exchange_amount = reserve.balance, 1601 .auditor_amount = rs->curr_balance.reserve_balance, 1602 .reserve_pub = rs->reserve_pub 1603 }; 1604 1605 qs = 1606 TALER_AUDITORDB_insert_reserve_balance_summary_wrong_inconsistency 1607 ( 1608 TALER_ARL_adb, 1609 &rbswi); 1610 } 1611 if (qs < 0) 1612 { 1613 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1614 rc->qs = qs; 1615 return GNUNET_SYSERR; 1616 } 1617 } 1618 } 1619 } /* end of 'if (internal_checks)' */ 1620 1621 /* Check that reserve is being closed if it is past its expiration date 1622 (and the closing fee would not exceed the remaining balance) */ 1623 if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD, 1624 <, 1625 GNUNET_TIME_absolute_get_duration ( 1626 rs->a_expiration_date.abs_time))) 1627 { 1628 /* Reserve is expired */ 1629 struct TALER_Amount cfee; 1630 1631 if ( (NULL != rs->sender_account.full_payto) && 1632 (GNUNET_OK == 1633 get_closing_fee (rs->sender_account, 1634 rs->a_expiration_date, 1635 &cfee)) ) 1636 { 1637 /* We got the closing fee */ 1638 if (1 == TALER_amount_cmp (&nbalance, 1639 &cfee)) 1640 { 1641 struct TALER_AUDITORDB_ReserveNotClosedInconsistency rnci = { 1642 .reserve_pub = rs->reserve_pub, 1643 .expiration_time = rs->a_expiration_date.abs_time, 1644 .balance = nbalance, 1645 .diagnostic = rs->sender_account.full_payto 1646 }; 1647 1648 /* remaining balance (according to us) exceeds closing fee */ 1649 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1650 total_balance_reserve_not_closed), 1651 &TALER_ARL_USE_AB ( 1652 total_balance_reserve_not_closed), 1653 &rnci.balance); 1654 qs = TALER_AUDITORDB_insert_reserve_not_closed_inconsistency ( 1655 TALER_ARL_adb, 1656 &rnci); 1657 if (qs < 0) 1658 { 1659 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1660 rc->qs = qs; 1661 return GNUNET_SYSERR; 1662 } 1663 } 1664 } 1665 else 1666 { 1667 /* We failed to determine the closing fee, complain! */ 1668 struct TALER_AUDITORDB_ReserveNotClosedInconsistency rncid = { 1669 .reserve_pub = rs->reserve_pub, 1670 .balance = nbalance, 1671 .expiration_time = rs->a_expiration_date.abs_time, 1672 .diagnostic = (char *) "could not determine closing fee" 1673 }; 1674 1675 /* Even if we don't know the closing fee, update the 1676 total_balance_reserve_not_closed */ 1677 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1678 total_balance_reserve_not_closed), 1679 &TALER_ARL_USE_AB ( 1680 total_balance_reserve_not_closed), 1681 &nbalance); 1682 qs = TALER_AUDITORDB_insert_reserve_not_closed_inconsistency ( 1683 TALER_ARL_adb, 1684 &rncid); 1685 if (qs < 0) 1686 { 1687 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1688 rc->qs = qs; 1689 return GNUNET_SYSERR; 1690 } 1691 } 1692 } 1693 /* We already computed the 'new' balance in 'curr_balance' 1694 to include the previous balance, so this one is just 1695 an assignment, not adding up! */ 1696 rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance; 1697 1698 /* Add up new totals to previous totals */ 1699 TALER_ARL_amount_add (&rs->prev_balance.reserve_loss, 1700 &rs->prev_balance.reserve_loss, 1701 &rs->curr_balance.reserve_loss); 1702 TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance, 1703 &rs->prev_balance.withdraw_fee_balance, 1704 &rs->curr_balance.withdraw_fee_balance); 1705 TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance, 1706 &rs->prev_balance.close_fee_balance, 1707 &rs->curr_balance.close_fee_balance); 1708 TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance, 1709 &rs->prev_balance.purse_fee_balance, 1710 &rs->curr_balance.purse_fee_balance); 1711 TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance, 1712 &rs->prev_balance.open_fee_balance, 1713 &rs->curr_balance.open_fee_balance); 1714 TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance, 1715 &rs->prev_balance.history_fee_balance, 1716 &rs->curr_balance.history_fee_balance); 1717 /* Update global balance: add incoming first, then try 1718 to subtract outgoing... */ 1719 TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_total_balance), 1720 &TALER_ARL_USE_AB (reserves_reserve_total_balance), 1721 &rs->total_in); 1722 { 1723 struct TALER_Amount r; 1724 1725 if (TALER_ARL_SR_INVALID_NEGATIVE == 1726 TALER_ARL_amount_subtract_neg (&r, 1727 &TALER_ARL_USE_AB ( 1728 reserves_reserve_total_balance), 1729 &rs->total_out)) 1730 { 1731 /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!) 1732 to be withdrawn more than it was IN TOTAL ever given (exchange balance 1733 went negative!). Woopsie. Calculate how badly it went and log. */ 1734 report_amount_arithmetic_inconsistency ("global escrow balance", 1735 0, 1736 &TALER_ARL_USE_AB ( 1737 reserves_reserve_total_balance), /* what we had */ 1738 &rs->total_out, /* what we needed */ 1739 0 /* specific profit/loss does not apply to the total summary */ 1740 ); 1741 if (global_qs < 0) 1742 return GNUNET_SYSERR; 1743 /* We unexpectedly went negative, so a sane value to continue from 1744 would be zero. */ 1745 GNUNET_assert (GNUNET_OK == 1746 TALER_amount_set_zero (TALER_ARL_currency, 1747 &TALER_ARL_USE_AB ( 1748 reserves_reserve_total_balance))); 1749 } 1750 else 1751 { 1752 TALER_ARL_USE_AB (reserves_reserve_total_balance) = r; 1753 } 1754 } 1755 if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance)) 1756 { 1757 /* balance is zero, drop reserve details (and then do not update/insert) */ 1758 if (rs->had_ri) 1759 { 1760 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1761 "Final balance of reserve `%s' is zero, dropping it\n", 1762 TALER_B2S (&rs->reserve_pub)); 1763 qs = TALER_AUDITORDB_del_reserve_info (TALER_ARL_adb, 1764 &rs->reserve_pub); 1765 if (0 >= qs) 1766 { 1767 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1768 ret = GNUNET_SYSERR; 1769 rc->qs = qs; 1770 } 1771 } 1772 else 1773 { 1774 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1775 "Final balance of reserve `%s' is zero, no need to remember it\n", 1776 TALER_B2S (&rs->reserve_pub)); 1777 } 1778 } 1779 else 1780 { 1781 /* balance is non-zero, persist for future audits */ 1782 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1783 "Remembering final balance of reserve `%s' as %s\n", 1784 TALER_B2S (&rs->reserve_pub), 1785 TALER_amount2s (&rs->prev_balance.reserve_balance)); 1786 if (rs->had_ri) 1787 qs = TALER_AUDITORDB_update_reserve_info (TALER_ARL_adb, 1788 &rs->reserve_pub, 1789 &rs->prev_balance, 1790 rs->a_expiration_date); 1791 else 1792 qs = TALER_AUDITORDB_insert_reserve_info (TALER_ARL_adb, 1793 &rs->reserve_pub, 1794 &rs->prev_balance, 1795 rs->a_expiration_date, 1796 rs->sender_account); 1797 if (0 >= qs) 1798 { 1799 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1800 ret = GNUNET_SYSERR; 1801 rc->qs = qs; 1802 } 1803 } 1804 /* now we can discard the cached entry */ 1805 GNUNET_assert (GNUNET_YES == 1806 GNUNET_CONTAINER_multihashmap_remove (rc->reserves, 1807 key, 1808 rs)); 1809 GNUNET_free (rs->sender_account.full_payto); 1810 GNUNET_free (rs); 1811 return ret; 1812 } 1813 1814 1815 #define CHECK_DB() do { \ 1816 if (qs < 0) { \ 1817 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); \ 1818 goto cleanup; \ 1819 } \ 1820 if (global_qs < 0) { \ 1821 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == global_qs); \ 1822 qs = global_qs; \ 1823 goto cleanup; \ 1824 } \ 1825 if (rc.qs < 0) { \ 1826 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rc.qs); \ 1827 qs = rc.qs; \ 1828 goto cleanup; \ 1829 } \ 1830 } while (0) 1831 1832 1833 /** 1834 * Analyze reserves for being well-formed. 1835 * 1836 * @param cls NULL 1837 * @return transaction status code 1838 */ 1839 static enum GNUNET_DB_QueryStatus 1840 analyze_reserves (void *cls) 1841 { 1842 struct ReserveContext rc = { 1843 .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT 1844 }; 1845 enum GNUNET_DB_QueryStatus qs; 1846 1847 (void) cls; 1848 global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1849 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1850 "Analyzing reserves\n"); 1851 qs = TALER_AUDITORDB_get_auditor_progress ( 1852 TALER_ARL_adb, 1853 TALER_ARL_GET_PP (reserves_reserve_in_serial_id), 1854 TALER_ARL_GET_PP (reserves_withdraw_serial_id), 1855 TALER_ARL_GET_PP (reserves_reserve_recoup_serial_id), 1856 TALER_ARL_GET_PP (reserves_reserve_open_serial_id), 1857 TALER_ARL_GET_PP (reserves_reserve_close_serial_id), 1858 TALER_ARL_GET_PP (reserves_purse_decisions_serial_id), 1859 TALER_ARL_GET_PP (reserves_account_merges_serial_id), 1860 TALER_ARL_GET_PP (reserves_history_requests_serial_id), 1861 NULL); 1862 if (0 > qs) 1863 { 1864 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1865 return qs; 1866 } 1867 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1868 { 1869 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1870 "First analysis using this auditor, starting audit from scratch\n"); 1871 } 1872 else 1873 { 1874 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1875 "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", 1876 (unsigned long long) TALER_ARL_USE_PP ( 1877 reserves_reserve_in_serial_id), 1878 (unsigned long long) TALER_ARL_USE_PP ( 1879 reserves_withdraw_serial_id), 1880 (unsigned long long) TALER_ARL_USE_PP ( 1881 reserves_reserve_recoup_serial_id), 1882 (unsigned long long) TALER_ARL_USE_PP ( 1883 reserves_reserve_open_serial_id), 1884 (unsigned long long) TALER_ARL_USE_PP ( 1885 reserves_reserve_close_serial_id), 1886 (unsigned long long) TALER_ARL_USE_PP ( 1887 reserves_purse_decisions_serial_id), 1888 (unsigned long long) TALER_ARL_USE_PP ( 1889 reserves_account_merges_serial_id), 1890 (unsigned long long) TALER_ARL_USE_PP ( 1891 reserves_history_requests_serial_id)); 1892 } 1893 qs = TALER_AUDITORDB_get_balance ( 1894 TALER_ARL_adb, 1895 TALER_ARL_GET_AB (reserves_reserve_total_balance), 1896 TALER_ARL_GET_AB (reserves_reserve_loss), 1897 TALER_ARL_GET_AB (reserves_withdraw_fee_revenue), 1898 TALER_ARL_GET_AB (reserves_close_fee_revenue), 1899 TALER_ARL_GET_AB (reserves_purse_fee_revenue), 1900 TALER_ARL_GET_AB (reserves_open_fee_revenue), 1901 TALER_ARL_GET_AB (reserves_history_fee_revenue), 1902 TALER_ARL_GET_AB (reserves_total_bad_sig_loss), 1903 TALER_ARL_GET_AB (total_balance_reserve_not_closed), 1904 TALER_ARL_GET_AB (reserves_total_arithmetic_delta_plus), 1905 TALER_ARL_GET_AB (reserves_total_arithmetic_delta_minus), 1906 TALER_ARL_GET_AB (total_balance_summary_delta_plus), 1907 TALER_ARL_GET_AB (total_balance_summary_delta_minus), 1908 NULL); 1909 if (qs < 0) 1910 { 1911 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1912 return qs; 1913 } 1914 rc.reserves = GNUNET_CONTAINER_multihashmap_create (512, 1915 GNUNET_NO); 1916 rc.revoked = GNUNET_CONTAINER_multihashmap_create (4, 1917 GNUNET_NO); 1918 1919 qs = TALER_EXCHANGEDB_select_reserves_in_above_serial_id ( 1920 TALER_ARL_edb, 1921 TALER_ARL_USE_PP (reserves_reserve_in_serial_id), 1922 &handle_reserve_in, 1923 &rc); 1924 CHECK_DB (); 1925 qs = TALER_EXCHANGEDB_select_withdrawals_above_serial_id ( 1926 TALER_ARL_edb, 1927 TALER_ARL_USE_PP (reserves_withdraw_serial_id), 1928 &handle_withdrawals, 1929 &rc); 1930 CHECK_DB (); 1931 qs = TALER_EXCHANGEDB_select_recoup_above_serial_id ( 1932 TALER_ARL_edb, 1933 TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id), 1934 &handle_recoup_by_reserve, 1935 &rc); 1936 if ( (qs < 0) || 1937 (rc.qs < 0) || 1938 (global_qs < 0) ) 1939 { 1940 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1941 return qs; 1942 } 1943 1944 qs = TALER_EXCHANGEDB_select_reserve_open_above_serial_id ( 1945 TALER_ARL_edb, 1946 TALER_ARL_USE_PP (reserves_reserve_open_serial_id), 1947 &handle_reserve_open, 1948 &rc); 1949 CHECK_DB (); 1950 qs = TALER_EXCHANGEDB_select_reserve_closed_above_serial_id ( 1951 TALER_ARL_edb, 1952 TALER_ARL_USE_PP (reserves_reserve_close_serial_id), 1953 &handle_reserve_closed, 1954 &rc); 1955 CHECK_DB (); 1956 /* process purse_decisions (to credit reserve) */ 1957 qs = TALER_TALER_EXCHANGEDB_select_purse_decisions_above_serial_id ( 1958 TALER_ARL_edb, 1959 TALER_ARL_USE_PP (reserves_purse_decisions_serial_id), 1960 false, /* only go for merged purses! */ 1961 &purse_decision_cb, 1962 &rc); 1963 CHECK_DB (); 1964 /* Charge purse fee! */ 1965 1966 qs = TALER_EXCHANGEDB_select_account_merges_above_serial_id ( 1967 TALER_ARL_edb, 1968 TALER_ARL_USE_PP (reserves_account_merges_serial_id), 1969 &handle_account_merged, 1970 &rc); 1971 CHECK_DB (); 1972 GNUNET_CONTAINER_multihashmap_iterate (rc.reserves, 1973 &verify_reserve_balance, 1974 &rc); 1975 CHECK_DB (); 1976 GNUNET_break (0 == 1977 GNUNET_CONTAINER_multihashmap_size (rc.reserves)); 1978 1979 qs = TALER_AUDITORDB_insert_balance ( 1980 TALER_ARL_adb, 1981 TALER_ARL_SET_AB (reserves_reserve_total_balance), 1982 TALER_ARL_SET_AB (reserves_reserve_loss), 1983 TALER_ARL_SET_AB (reserves_withdraw_fee_revenue), 1984 TALER_ARL_SET_AB (reserves_close_fee_revenue), 1985 TALER_ARL_SET_AB (reserves_purse_fee_revenue), 1986 TALER_ARL_SET_AB (reserves_open_fee_revenue), 1987 TALER_ARL_SET_AB (reserves_history_fee_revenue), 1988 TALER_ARL_SET_AB (reserves_total_bad_sig_loss), 1989 TALER_ARL_SET_AB (total_balance_reserve_not_closed), 1990 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus), 1991 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus), 1992 TALER_ARL_SET_AB (total_balance_summary_delta_plus), 1993 TALER_ARL_SET_AB (total_balance_summary_delta_minus), 1994 NULL); 1995 if (0 > qs) 1996 { 1997 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1998 goto cleanup; 1999 } 2000 2001 qs = TALER_AUDITORDB_update_balance ( 2002 TALER_ARL_adb, 2003 TALER_ARL_SET_AB (reserves_reserve_total_balance), 2004 TALER_ARL_SET_AB (reserves_reserve_loss), 2005 TALER_ARL_SET_AB (reserves_withdraw_fee_revenue), 2006 TALER_ARL_SET_AB (reserves_close_fee_revenue), 2007 TALER_ARL_SET_AB (reserves_purse_fee_revenue), 2008 TALER_ARL_SET_AB (reserves_open_fee_revenue), 2009 TALER_ARL_SET_AB (reserves_history_fee_revenue), 2010 TALER_ARL_SET_AB (reserves_total_bad_sig_loss), 2011 TALER_ARL_SET_AB (total_balance_reserve_not_closed), 2012 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus), 2013 TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus), 2014 TALER_ARL_SET_AB (total_balance_summary_delta_plus), 2015 TALER_ARL_SET_AB (total_balance_summary_delta_minus), 2016 NULL); 2017 if (0 > qs) 2018 { 2019 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2020 goto cleanup; 2021 } 2022 2023 qs = TALER_AUDITORDB_insert_auditor_progress ( 2024 TALER_ARL_adb, 2025 TALER_ARL_SET_PP (reserves_reserve_in_serial_id), 2026 TALER_ARL_SET_PP (reserves_withdraw_serial_id), 2027 TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id), 2028 TALER_ARL_SET_PP (reserves_reserve_open_serial_id), 2029 TALER_ARL_SET_PP (reserves_reserve_close_serial_id), 2030 TALER_ARL_SET_PP (reserves_purse_decisions_serial_id), 2031 TALER_ARL_SET_PP (reserves_account_merges_serial_id), 2032 TALER_ARL_SET_PP (reserves_history_requests_serial_id), 2033 NULL); 2034 if (0 > qs) 2035 { 2036 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2037 "Failed to update auditor DB, not recording progress\n"); 2038 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2039 goto cleanup; 2040 } 2041 qs = TALER_AUDITORDB_update_auditor_progress ( 2042 TALER_ARL_adb, 2043 TALER_ARL_SET_PP (reserves_reserve_in_serial_id), 2044 TALER_ARL_SET_PP (reserves_withdraw_serial_id), 2045 TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id), 2046 TALER_ARL_SET_PP (reserves_reserve_open_serial_id), 2047 TALER_ARL_SET_PP (reserves_reserve_close_serial_id), 2048 TALER_ARL_SET_PP (reserves_purse_decisions_serial_id), 2049 TALER_ARL_SET_PP (reserves_account_merges_serial_id), 2050 TALER_ARL_SET_PP (reserves_history_requests_serial_id), 2051 NULL); 2052 if (0 > qs) 2053 { 2054 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2055 "Failed to update auditor DB, not recording progress\n"); 2056 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 2057 goto cleanup; 2058 } 2059 2060 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2061 "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n", 2062 (unsigned long long) TALER_ARL_USE_PP ( 2063 reserves_reserve_in_serial_id), 2064 (unsigned long long) TALER_ARL_USE_PP ( 2065 reserves_withdraw_serial_id), 2066 (unsigned long long) TALER_ARL_USE_PP ( 2067 reserves_reserve_recoup_serial_id), 2068 (unsigned long long) TALER_ARL_USE_PP ( 2069 reserves_reserve_open_serial_id), 2070 (unsigned long long) TALER_ARL_USE_PP ( 2071 reserves_reserve_close_serial_id), 2072 (unsigned long long) TALER_ARL_USE_PP ( 2073 reserves_purse_decisions_serial_id), 2074 (unsigned long long) TALER_ARL_USE_PP ( 2075 reserves_account_merges_serial_id), 2076 (unsigned long long) TALER_ARL_USE_PP ( 2077 reserves_history_requests_serial_id)); 2078 qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; 2079 cleanup: 2080 GNUNET_CONTAINER_multihashmap_destroy (rc.reserves); 2081 GNUNET_CONTAINER_multihashmap_destroy (rc.revoked); 2082 return qs; 2083 } 2084 2085 2086 #undef CHECK_DB 2087 2088 2089 /** 2090 * Function called on events received from Postgres. 2091 * 2092 * @param cls closure, NULL 2093 * @param extra additional event data provided 2094 * @param extra_size number of bytes in @a extra 2095 */ 2096 static void 2097 db_notify (void *cls, 2098 const void *extra, 2099 size_t extra_size) 2100 { 2101 (void) cls; 2102 (void) extra; 2103 (void) extra_size; 2104 2105 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2106 "Received notification to wake reserves helper\n"); 2107 if (GNUNET_OK != 2108 TALER_ARL_setup_sessions_and_run (&analyze_reserves, 2109 NULL)) 2110 { 2111 GNUNET_SCHEDULER_shutdown (); 2112 global_ret = EXIT_FAILURE; 2113 return; 2114 } 2115 } 2116 2117 2118 /** 2119 * Function called on shutdown. 2120 */ 2121 static void 2122 do_shutdown (void *cls) 2123 { 2124 (void) cls; 2125 if (NULL != eh) 2126 { 2127 TALER_AUDITORDB_event_listen_cancel (eh); 2128 eh = NULL; 2129 } 2130 TALER_ARL_done (); 2131 } 2132 2133 2134 /** 2135 * Main function that will be run. 2136 * 2137 * @param cls closure 2138 * @param args remaining command-line arguments 2139 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 2140 * @param c configuration 2141 */ 2142 static void 2143 run (void *cls, 2144 char *const *args, 2145 const char *cfgfile, 2146 const struct GNUNET_CONFIGURATION_Handle *c) 2147 { 2148 (void) cls; 2149 (void) args; 2150 (void) cfgfile; 2151 2152 cfg = c; 2153 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2154 NULL); 2155 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2156 "Launching reserves auditor\n"); 2157 if (GNUNET_OK != 2158 TALER_ARL_init (c)) 2159 { 2160 global_ret = EXIT_FAILURE; 2161 return; 2162 } 2163 if (GNUNET_OK != 2164 GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg, 2165 "exchangedb", 2166 "IDLE_RESERVE_EXPIRATION_TIME", 2167 &idle_reserve_expiration_time)) 2168 { 2169 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2170 "exchangedb", 2171 "IDLE_RESERVE_EXPIRATION_TIME"); 2172 GNUNET_SCHEDULER_shutdown (); 2173 global_ret = EXIT_FAILURE; 2174 return; 2175 } 2176 if (test_mode != 1) 2177 { 2178 struct GNUNET_DB_EventHeaderP es = { 2179 .size = htons (sizeof (es)), 2180 .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_RESERVES) 2181 }; 2182 2183 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2184 "Running helper indefinitely\n"); 2185 eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb, 2186 &es, 2187 GNUNET_TIME_UNIT_FOREVER_REL, 2188 &db_notify, 2189 NULL); 2190 } 2191 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 2192 "Starting audit\n"); 2193 if (GNUNET_OK != 2194 TALER_ARL_setup_sessions_and_run (&analyze_reserves, 2195 NULL)) 2196 { 2197 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2198 "Audit failed\n"); 2199 GNUNET_SCHEDULER_shutdown (); 2200 global_ret = EXIT_FAILURE; 2201 return; 2202 } 2203 } 2204 2205 2206 /** 2207 * The main function to check the database's handling of reserves. 2208 * 2209 * @param argc number of arguments from the command line 2210 * @param argv command line arguments 2211 * @return 0 ok, 1 on error 2212 */ 2213 int 2214 main (int argc, 2215 char *const *argv) 2216 { 2217 const struct GNUNET_GETOPT_CommandLineOption options[] = { 2218 GNUNET_GETOPT_option_flag ('i', 2219 "internal", 2220 "perform checks only applicable for exchange-internal audits", 2221 &internal_checks), 2222 GNUNET_GETOPT_option_flag ('t', 2223 "test", 2224 "run in test mode and exit when idle", 2225 &test_mode), 2226 GNUNET_GETOPT_option_timetravel ('T', 2227 "timetravel"), 2228 GNUNET_GETOPT_OPTION_END 2229 }; 2230 enum GNUNET_GenericReturnValue ret; 2231 2232 ret = GNUNET_PROGRAM_run ( 2233 TALER_AUDITOR_project_data (), 2234 argc, 2235 argv, 2236 "taler-helper-auditor-reserves", 2237 gettext_noop ("Audit Taler exchange reserve handling"), 2238 options, 2239 &run, 2240 NULL); 2241 if (GNUNET_SYSERR == ret) 2242 return EXIT_INVALIDARGUMENT; 2243 if (GNUNET_NO == ret) 2244 return EXIT_SUCCESS; 2245 return global_ret; 2246 } 2247 2248 2249 /* end of taler-helper-auditor-reserves.c */