taler-helper-auditor-wire-credit.c (43386B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2017-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 General 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 General Public License for more details. 12 13 You should have received a copy of the GNU General 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-wire-credit.c 18 * @brief audits that wire transfers match those from an exchange database. 19 * @author Christian Grothoff 20 * 21 * This auditor verifies that 'reserves_in' actually matches 22 * the incoming wire transfers from the bank. 23 */ 24 #include "platform.h" 25 #include <gnunet/gnunet_util_lib.h> 26 #include <gnunet/gnunet_curl_lib.h> 27 #include "auditordb_lib.h" 28 #include "exchangedb_lib.h" 29 #include "taler/taler_json_lib.h" 30 #include "taler/taler_bank_service.h" 31 #include "taler/taler_signatures.h" 32 #include "report-lib.h" 33 #include "taler/taler_dbevents.h" 34 #include \ 35 "exchange-database/select_reserves_in_above_serial_id_by_account.h" 36 #include "auditor-database/delete_reserve_in_inconsistency.h" 37 #include "auditor-database/event_listen.h" 38 #include "auditor-database/get_auditor_progress.h" 39 #include "auditor-database/get_balance.h" 40 #include "auditor-database/insert_auditor_progress.h" 41 #include "auditor-database/insert_balance.h" 42 #include "auditor-database/insert_misattribution_in_inconsistency.h" 43 #include "auditor-database/insert_reserve_in_inconsistency.h" 44 #include "auditor-database/insert_row_inconsistency.h" 45 #include "auditor-database/insert_row_minor_inconsistencies.h" 46 #include "auditor-database/preflight.h" 47 #include "auditor-database/select_reserve_in_inconsistency.h" 48 #include "auditor-database/start.h" 49 #include "auditor-database/update_auditor_progress.h" 50 #include "auditor-database/update_balance.h" 51 #include "exchange-database/preflight.h" 52 #include "exchange-database/rollback.h" 53 #include \ 54 "exchange-database/select_reserves_in_above_serial_id_by_account.h" 55 #include "exchange-database/start_read_only.h" 56 57 /** 58 * How much time do we allow the aggregator to lag behind? If 59 * wire transfers should have been made more than #GRACE_PERIOD 60 * before, we issue warnings. 61 */ 62 #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS 63 64 /** 65 * Maximum number of wire transfers we process per 66 * (database) transaction. 67 */ 68 #define MAX_PER_TRANSACTION 1024 69 70 /** 71 * How much do we allow the bank and the exchange to disagree about 72 * timestamps? Should be sufficiently large to avoid bogus reports from deltas 73 * created by imperfect clock synchronization and network delay. 74 */ 75 #define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \ 76 15) 77 78 79 /** 80 * Run in test mode. Exit when idle instead of 81 * going to sleep and waiting for more work. 82 */ 83 static int test_mode; 84 85 /** 86 * Information we keep for each supported account. 87 */ 88 struct WireAccount 89 { 90 /** 91 * Accounts are kept in a DLL. 92 */ 93 struct WireAccount *next; 94 95 /** 96 * Plugins are kept in a DLL. 97 */ 98 struct WireAccount *prev; 99 100 /** 101 * Account details. 102 */ 103 const struct TALER_EXCHANGEDB_AccountInfo *ai; 104 105 /** 106 * Active wire request for the transaction history. 107 */ 108 struct TALER_BANK_CreditHistoryHandle *chh; 109 110 /** 111 * Progress point for this account. 112 */ 113 uint64_t last_reserve_in_serial_id; 114 115 /** 116 * Initial progress point for this account. 117 */ 118 uint64_t start_reserve_in_serial_id; 119 120 /** 121 * Where we are in the inbound transaction history. 122 */ 123 uint64_t wire_off_in; 124 125 /** 126 * Label under which we store our pp's reserve_in_serial_id. 127 */ 128 char *label_reserve_in_serial_id; 129 130 /** 131 * Label under which we store our wire_off_in. 132 */ 133 char *label_wire_off_in; 134 135 }; 136 137 138 /** 139 * Return value from main(). 140 */ 141 static int global_ret; 142 143 /** 144 * State of the current database transaction with 145 * the auditor DB. 146 */ 147 static enum GNUNET_DB_QueryStatus global_qs; 148 149 /** 150 * Map with information about incoming wire transfers. 151 * Maps hashes of the wire offsets to `struct ReserveInInfo`s. 152 */ 153 static struct GNUNET_CONTAINER_MultiHashMap *in_map; 154 155 /** 156 * Head of list of wire accounts we still need to look at. 157 */ 158 static struct WireAccount *wa_head; 159 160 /** 161 * Tail of list of wire accounts we still need to look at. 162 */ 163 static struct WireAccount *wa_tail; 164 165 /** 166 * Amount that is considered "tiny" 167 */ 168 static struct TALER_Amount tiny_amount; 169 170 /** 171 * Total amount that was transferred too much to the exchange. 172 */ 173 static TALER_ARL_DEF_AB (total_bad_amount_in_plus); 174 175 /** 176 * Total amount that was transferred too little to the exchange. 177 */ 178 static TALER_ARL_DEF_AB (total_bad_amount_in_minus); 179 180 /** 181 * Total amount where the exchange has the wrong sender account 182 * for incoming funds and may thus wire funds to the wrong 183 * destination when closing the reserve. 184 */ 185 static TALER_ARL_DEF_AB (total_misattribution_in); 186 187 /** 188 * Total amount credited to exchange accounts. 189 */ 190 static TALER_ARL_DEF_AB (total_wire_in); 191 192 /** 193 * Total amount credited to exchange accounts via KYCAUTH 194 */ 195 static TALER_ARL_DEF_AB (total_kycauth_in); 196 197 /** 198 * Total wire credit fees charged to the exchange account. 199 */ 200 static TALER_ARL_DEF_AB (total_wire_credit_fees); 201 202 /** 203 * Amount of zero in our currency. 204 */ 205 static struct TALER_Amount zero; 206 207 /** 208 * Handle to the context for interacting with the bank. 209 */ 210 static struct GNUNET_CURL_Context *ctx; 211 212 /** 213 * Scheduler context for running the @e ctx. 214 */ 215 static struct GNUNET_CURL_RescheduleContext *rc; 216 217 /** 218 * Should we run checks that only work for exchange-internal audits? 219 */ 220 static int internal_checks; 221 222 /** 223 * Should we ignore if the bank does not know our bank 224 * account? 225 */ 226 static int ignore_account_404; 227 228 /** 229 * Database event handler to wake us up again. 230 */ 231 static struct GNUNET_DB_EventHandler *eh; 232 233 /** 234 * The auditors's configuration. 235 */ 236 static const struct GNUNET_CONFIGURATION_Handle *cfg; 237 238 /* ***************************** Shutdown **************************** */ 239 240 /** 241 * Entry in map with wire information we expect to obtain from the 242 * bank later. 243 */ 244 struct ReserveInInfo 245 { 246 247 /** 248 * Hash of expected row offset. 249 */ 250 struct GNUNET_HashCode row_off_hash; 251 252 /** 253 * Expected details about the wire transfer. 254 * The member "account_url" is to be allocated 255 * at the end of this struct! 256 */ 257 struct TALER_BANK_CreditDetails credit_details; 258 259 /** 260 * RowID in reserves_in table. 261 */ 262 uint64_t rowid; 263 264 }; 265 266 267 /** 268 * Free entry in #in_map. 269 * 270 * @param cls NULL 271 * @param key unused key 272 * @param value the `struct ReserveInInfo` to free 273 * @return #GNUNET_OK 274 */ 275 static enum GNUNET_GenericReturnValue 276 free_rii (void *cls, 277 const struct GNUNET_HashCode *key, 278 void *value) 279 { 280 struct ReserveInInfo *rii = value; 281 282 (void) cls; 283 GNUNET_assert (GNUNET_YES == 284 GNUNET_CONTAINER_multihashmap_remove (in_map, 285 key, 286 rii)); 287 GNUNET_free (rii); 288 return GNUNET_OK; 289 } 290 291 292 /** 293 * Task run on shutdown. 294 * 295 * @param cls NULL 296 */ 297 static void 298 do_shutdown (void *cls) 299 { 300 struct WireAccount *wa; 301 302 (void) cls; 303 if (NULL != eh) 304 { 305 TALER_AUDITORDB_event_listen_cancel (eh); 306 eh = NULL; 307 } 308 TALER_ARL_done (); 309 if (NULL != in_map) 310 { 311 GNUNET_CONTAINER_multihashmap_iterate (in_map, 312 &free_rii, 313 NULL); 314 GNUNET_CONTAINER_multihashmap_destroy (in_map); 315 in_map = NULL; 316 } 317 while (NULL != (wa = wa_head)) 318 { 319 if (NULL != wa->chh) 320 { 321 TALER_BANK_credit_history_cancel (wa->chh); 322 wa->chh = NULL; 323 } 324 GNUNET_CONTAINER_DLL_remove (wa_head, 325 wa_tail, 326 wa); 327 GNUNET_free (wa->label_reserve_in_serial_id); 328 GNUNET_free (wa->label_wire_off_in); 329 GNUNET_free (wa); 330 } 331 if (NULL != ctx) 332 { 333 GNUNET_CURL_fini (ctx); 334 ctx = NULL; 335 } 336 if (NULL != rc) 337 { 338 GNUNET_CURL_gnunet_rc_destroy (rc); 339 rc = NULL; 340 } 341 TALER_EXCHANGEDB_unload_accounts (); 342 TALER_ARL_cfg = NULL; 343 } 344 345 346 /** 347 * Start the database transactions and begin the audit. 348 * 349 * @return transaction status code 350 */ 351 static enum GNUNET_DB_QueryStatus 352 begin_transaction (void); 353 354 355 /** 356 * Rollback the current transaction, reset our state and try 357 * again (we had a serialization error). 358 */ 359 static void 360 rollback_and_reset (void) 361 { 362 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 363 "Serialization issue, trying again\n"); 364 TALER_AUDITORDB_rollback (TALER_ARL_adb); 365 for (unsigned int max_retries = 3; max_retries>0; max_retries--) 366 { 367 enum GNUNET_DB_QueryStatus qs; 368 369 if (NULL != in_map) 370 { 371 GNUNET_CONTAINER_multihashmap_iterate (in_map, 372 &free_rii, 373 NULL); 374 GNUNET_CONTAINER_multihashmap_destroy (in_map); 375 in_map = NULL; 376 } 377 qs = begin_transaction (); 378 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 379 break; 380 } 381 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 382 "Hard database error, terminating\n"); 383 GNUNET_SCHEDULER_shutdown (); 384 } 385 386 387 /** 388 * Commit the transaction, checkpointing our progress in the auditor DB. 389 * 390 * @param qs transaction status so far 391 */ 392 static void 393 commit (enum GNUNET_DB_QueryStatus qs) 394 { 395 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 396 "Transaction logic ended with status %d\n", 397 qs); 398 TALER_EXCHANGEDB_rollback (TALER_ARL_edb); 399 if (qs < 0) 400 goto handle_db_error; 401 qs = TALER_AUDITORDB_update_balance ( 402 TALER_ARL_adb, 403 TALER_ARL_SET_AB (total_wire_in), 404 TALER_ARL_SET_AB (total_kycauth_in), 405 TALER_ARL_SET_AB (total_wire_credit_fees), 406 TALER_ARL_SET_AB (total_bad_amount_in_plus), 407 TALER_ARL_SET_AB (total_bad_amount_in_minus), 408 TALER_ARL_SET_AB (total_misattribution_in), 409 NULL); 410 if (0 > qs) 411 goto handle_db_error; 412 qs = TALER_AUDITORDB_insert_balance ( 413 TALER_ARL_adb, 414 TALER_ARL_SET_AB (total_wire_in), 415 TALER_ARL_SET_AB (total_kycauth_in), 416 TALER_ARL_SET_AB (total_wire_credit_fees), 417 TALER_ARL_SET_AB (total_bad_amount_in_plus), 418 TALER_ARL_SET_AB (total_bad_amount_in_minus), 419 TALER_ARL_SET_AB (total_misattribution_in), 420 NULL); 421 if (0 > qs) 422 goto handle_db_error; 423 for (struct WireAccount *wa = wa_head; 424 NULL != wa; 425 wa = wa->next) 426 { 427 qs = TALER_AUDITORDB_update_auditor_progress ( 428 TALER_ARL_adb, 429 wa->label_reserve_in_serial_id, 430 wa->last_reserve_in_serial_id, 431 wa->label_wire_off_in, 432 wa->wire_off_in, 433 NULL); 434 if (0 > qs) 435 goto handle_db_error; 436 qs = TALER_AUDITORDB_insert_auditor_progress ( 437 TALER_ARL_adb, 438 wa->label_reserve_in_serial_id, 439 wa->last_reserve_in_serial_id, 440 wa->label_wire_off_in, 441 wa->wire_off_in, 442 NULL); 443 if (0 > qs) 444 goto handle_db_error; 445 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 446 "Transaction ends at %s=%llu for account `%s'\n", 447 wa->label_reserve_in_serial_id, 448 (unsigned long long) wa->last_reserve_in_serial_id, 449 wa->ai->section_name); 450 } 451 qs = TALER_AUDITORDB_commit (TALER_ARL_adb); 452 if (0 > qs) 453 { 454 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 455 goto handle_db_error; 456 } 457 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 458 "Transaction concluded!\n"); 459 if (1 == test_mode) 460 GNUNET_SCHEDULER_shutdown (); 461 return; 462 handle_db_error: 463 rollback_and_reset (); 464 } 465 466 467 /** 468 * Conclude the credit history check by logging entries that 469 * were not found and freeing resources. Then move on to 470 * processing debits. 471 */ 472 static void 473 conclude_credit_history (void) 474 { 475 if (NULL != in_map) 476 { 477 GNUNET_assert (0 == 478 GNUNET_CONTAINER_multihashmap_size (in_map)); 479 GNUNET_CONTAINER_multihashmap_destroy (in_map); 480 in_map = NULL; 481 } 482 commit (global_qs); 483 } 484 485 486 /** 487 * Check if the given wire transfers are equivalent. 488 * 489 * @param credit amount that was received 490 * @param credit2 2nd amount that was received 491 * @param reserve_pub public key of the reserve (also the WTID) 492 * @param reserve_pub2 2nd public key of the reserve (also the WTID) 493 * @param sender_account_details payto://-URL of the sender's bank account 494 * @param sender_account_details2 2nd payto://-URL of the sender's bank account 495 * @param execution_date when did we receive the funds 496 * @param execution_date2 2nd when did we receive the funds 497 * @return #GNUNET_YES if so, 498 * #GNUNET_NO if not 499 * #GNUNET_SYSERR on internal error 500 */ 501 static enum GNUNET_GenericReturnValue 502 check_equality (const struct TALER_Amount *credit, 503 const struct TALER_Amount *credit2, 504 const struct TALER_ReservePublicKeyP *reserve_pub, 505 const struct TALER_ReservePublicKeyP *reserve_pub2, 506 const struct TALER_FullPayto sender_account_details, 507 const struct TALER_FullPayto sender_account_details2, 508 struct GNUNET_TIME_Timestamp execution_date, 509 struct GNUNET_TIME_Timestamp execution_date2) 510 { 511 if (0 != TALER_amount_cmp (credit, 512 credit2)) 513 return GNUNET_NO; 514 if (0 != GNUNET_memcmp (reserve_pub, 515 reserve_pub2)) 516 return GNUNET_NO; 517 { 518 struct TALER_NormalizedPayto np; 519 struct TALER_NormalizedPayto np2; 520 bool fail; 521 522 np = TALER_payto_normalize (sender_account_details); 523 np2 = TALER_payto_normalize (sender_account_details2); 524 fail = (0 != TALER_normalized_payto_cmp (np, 525 np2)); 526 GNUNET_free (np.normalized_payto); 527 GNUNET_free (np2.normalized_payto); 528 if (fail) 529 return GNUNET_NO; 530 } 531 if (GNUNET_TIME_timestamp_cmp (execution_date, 532 !=, 533 execution_date2)) 534 return GNUNET_NO; 535 return GNUNET_YES; 536 } 537 538 539 /** 540 * Function called with details about incoming wire transfers 541 * as claimed by the exchange DB. 542 * 543 * @param cls a `struct WireAccount` we are processing 544 * @param rowid unique serial ID for the entry in our DB 545 * @param reserve_pub public key of the reserve (also the WTID) 546 * @param credit amount that was received 547 * @param sender_account_details payto://-URL of the sender's bank account 548 * @param wire_reference unique identifier for the wire transfer 549 * @param execution_date when did we receive the funds 550 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 551 */ 552 static enum GNUNET_GenericReturnValue 553 reserve_in_cb (void *cls, 554 uint64_t rowid, 555 const struct TALER_ReservePublicKeyP *reserve_pub, 556 const struct TALER_Amount *credit, 557 const struct TALER_FullPayto sender_account_details, 558 uint64_t wire_reference, 559 struct GNUNET_TIME_Timestamp execution_date) 560 { 561 struct WireAccount *wa = cls; 562 struct ReserveInInfo *rii; 563 size_t slen; 564 565 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 566 "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n", 567 (unsigned long long) rowid, 568 GNUNET_TIME_timestamp2s (execution_date), 569 TALER_amount2s (credit), 570 TALER_B2S (reserve_pub)); 571 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in), 572 &TALER_ARL_USE_AB (total_wire_in), 573 credit); 574 { 575 enum GNUNET_DB_QueryStatus qs; 576 struct TALER_AUDITORDB_ReserveInInconsistency dc; 577 578 qs = TALER_AUDITORDB_select_reserve_in_inconsistency ( 579 TALER_ARL_adb, 580 wire_reference, 581 &dc); 582 switch (qs) 583 { 584 case GNUNET_DB_STATUS_HARD_ERROR: 585 case GNUNET_DB_STATUS_SOFT_ERROR: 586 global_qs = qs; 587 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 588 return GNUNET_SYSERR; 589 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 590 break; 591 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 592 if (TALER_amount_is_zero (&dc.amount_exchange_expected)) 593 { 594 /* database entry indicates unmatched transaction */ 595 enum GNUNET_GenericReturnValue ret; 596 597 ret = check_equality (&dc.amount_wired, 598 credit, 599 &dc.reserve_pub, 600 reserve_pub, 601 dc.account, 602 sender_account_details, 603 GNUNET_TIME_absolute_to_timestamp (dc.timestamp), 604 execution_date); 605 if (GNUNET_SYSERR == ret) 606 return GNUNET_SYSERR; 607 if (GNUNET_YES == ret) 608 { 609 qs = TALER_AUDITORDB_delete_reserve_in_inconsistency ( 610 TALER_ARL_adb, 611 dc.serial_id); 612 if (qs < 0) 613 { 614 global_qs = qs; 615 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 616 return GNUNET_SYSERR; 617 } 618 return GNUNET_OK; 619 } 620 } 621 break; 622 } 623 } 624 slen = strlen (sender_account_details.full_payto) + 1; 625 rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen); 626 rii->rowid = rowid; 627 rii->credit_details.type = TALER_BANK_CT_RESERVE; 628 rii->credit_details.amount = *credit; 629 rii->credit_details.execution_date = execution_date; 630 rii->credit_details.details.reserve.reserve_pub = *reserve_pub; 631 rii->credit_details.debit_account_uri.full_payto = (char *) &rii[1]; 632 GNUNET_memcpy (&rii[1], 633 sender_account_details.full_payto, 634 slen); 635 GNUNET_CRYPTO_hash (&wire_reference, 636 sizeof (uint64_t), 637 &rii->row_off_hash); 638 if (GNUNET_OK != 639 GNUNET_CONTAINER_multihashmap_put (in_map, 640 &rii->row_off_hash, 641 rii, 642 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) 643 { 644 struct TALER_AUDITORDB_RowInconsistency ri = { 645 .row_id = rowid, 646 .row_table = (char *) "reserves_in", 647 .diagnostic = (char *) "duplicate wire offset" 648 }; 649 enum GNUNET_DB_QueryStatus qs; 650 651 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 652 "Duplicate wire offset\n"); 653 qs = TALER_AUDITORDB_insert_row_inconsistency ( 654 TALER_ARL_adb, 655 &ri); 656 GNUNET_free (rii); 657 if (qs < 0) 658 { 659 global_qs = qs; 660 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 661 return GNUNET_SYSERR; 662 } 663 return GNUNET_OK; 664 } 665 wa->last_reserve_in_serial_id = rowid + 1; 666 return GNUNET_OK; 667 } 668 669 670 /** 671 * Complain that we failed to match an entry from #in_map. 672 * 673 * @param cls a `struct WireAccount` 674 * @param key unused key 675 * @param value the `struct ReserveInInfo` to free 676 * @return #GNUNET_OK 677 */ 678 static enum GNUNET_GenericReturnValue 679 complain_in_not_found (void *cls, 680 const struct GNUNET_HashCode *key, 681 void *value) 682 { 683 struct WireAccount *wa = cls; 684 struct ReserveInInfo *rii = value; 685 enum GNUNET_DB_QueryStatus qs; 686 struct TALER_AUDITORDB_ReserveInInconsistency riiDb = { 687 .bank_row_id = rii->rowid, 688 .diagnostic = (char *) 689 "incoming wire transfer claimed by exchange not found", 690 .account = wa->ai->payto_uri, 691 .amount_exchange_expected = rii->credit_details.amount, 692 .amount_wired = zero, 693 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 694 .timestamp = rii->credit_details.execution_date.abs_time 695 }; 696 697 (void) key; 698 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 699 "Incoming wire transfer #%llu claimed by exchange not found\n", 700 (unsigned long long) rii->rowid); 701 GNUNET_assert (TALER_BANK_CT_RESERVE == 702 rii->credit_details.type); 703 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency ( 704 TALER_ARL_adb, 705 &riiDb); 706 if (qs < 0) 707 { 708 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 709 global_qs = qs; 710 return GNUNET_SYSERR; 711 } 712 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 713 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 714 &rii->credit_details.amount); 715 return GNUNET_OK; 716 } 717 718 719 /** 720 * Start processing the next wire account. 721 * Shuts down if we are done. 722 * 723 * @param cls `struct WireAccount` with a wire account list to process 724 */ 725 static void 726 process_credits (void *cls); 727 728 729 /** 730 * We got all of the incoming transactions for @a wa, 731 * finish processing the account. 732 * 733 * @param[in,out] wa wire account to process 734 */ 735 static void 736 conclude_account (struct WireAccount *wa) 737 { 738 GNUNET_assert (NULL == wa->chh); 739 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 740 "Reconciling CREDIT processing of account `%s'\n", 741 wa->ai->section_name); 742 if (NULL != in_map) 743 { 744 GNUNET_CONTAINER_multihashmap_iterate (in_map, 745 &complain_in_not_found, 746 wa); 747 /* clean up before 2nd phase */ 748 GNUNET_CONTAINER_multihashmap_iterate (in_map, 749 &free_rii, 750 NULL); 751 if (global_qs < 0) 752 { 753 commit (global_qs); 754 return; 755 } 756 } 757 process_credits (wa->next); 758 } 759 760 761 /** 762 * Analyze credit transaction @a details into @a wa. 763 * 764 * @param[in,out] wa account that received the transfer 765 * @param credit_details transfer details 766 * @return true on success, false to stop loop at this point 767 */ 768 static bool 769 analyze_credit ( 770 struct WireAccount *wa, 771 const struct TALER_BANK_CreditDetails *credit_details) 772 { 773 struct ReserveInInfo *rii; 774 struct GNUNET_HashCode key; 775 776 switch (credit_details->type) 777 { 778 case TALER_BANK_CT_RESERVE: 779 break; 780 case TALER_BANK_CT_KYCAUTH: 781 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_in), 782 &TALER_ARL_USE_AB (total_kycauth_in), 783 &credit_details->amount); 784 return true; 785 case TALER_BANK_CT_WAD: 786 GNUNET_break (0); /* FIXME: Wad not yet supported */ 787 return false; 788 } 789 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 790 "Analyzing bank CREDIT #%llu at %s of %s with Reserve-pub %s\n", 791 (unsigned long long) credit_details->serial_id, 792 GNUNET_TIME_timestamp2s (credit_details->execution_date), 793 TALER_amount2s (&credit_details->amount), 794 TALER_B2S (&credit_details->details.reserve.reserve_pub)); 795 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees), 796 &TALER_ARL_USE_AB (total_wire_credit_fees), 797 &credit_details->credit_fee); 798 GNUNET_CRYPTO_hash (&credit_details->serial_id, 799 sizeof (credit_details->serial_id), 800 &key); 801 rii = GNUNET_CONTAINER_multihashmap_get (in_map, 802 &key); 803 if (NULL == rii) 804 { 805 struct TALER_AUDITORDB_ReserveInInconsistency dc = { 806 .bank_row_id = credit_details->serial_id, 807 .amount_exchange_expected = zero, 808 .amount_wired = credit_details->amount, 809 .reserve_pub = credit_details->details.reserve.reserve_pub, 810 .timestamp = credit_details->execution_date.abs_time, 811 .account = credit_details->debit_account_uri, 812 .diagnostic = (char *) "unknown to exchange" 813 }; 814 enum GNUNET_DB_QueryStatus qs; 815 816 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 817 "Failed to find wire transfer at `%s' in exchange database.\n", 818 GNUNET_TIME_timestamp2s (credit_details->execution_date)); 819 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (TALER_ARL_adb, 820 &dc); 821 if (qs <= 0) 822 { 823 global_qs = qs; 824 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 825 return false; 826 } 827 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 828 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 829 &credit_details->amount); 830 return true; 831 } 832 833 /* Update offset */ 834 wa->wire_off_in = credit_details->serial_id; 835 /* compare records with expected data */ 836 if (0 != GNUNET_memcmp (&credit_details->details.reserve.reserve_pub, 837 &rii->credit_details.details.reserve.reserve_pub)) 838 { 839 struct TALER_AUDITORDB_ReserveInInconsistency riiDb = { 840 .bank_row_id = credit_details->serial_id, 841 .amount_exchange_expected = rii->credit_details.amount, 842 .amount_wired = zero, 843 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 844 .timestamp = rii->credit_details.execution_date.abs_time, 845 .account = wa->ai->payto_uri, 846 .diagnostic = (char *) "wire subject does not match" 847 }; 848 enum GNUNET_DB_QueryStatus qs; 849 850 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 851 "Reserve public key differs\n"); 852 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency ( 853 TALER_ARL_adb, 854 &riiDb); 855 if (qs <= 0) 856 { 857 global_qs = qs; 858 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 859 return false; 860 } 861 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 862 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 863 &rii->credit_details.amount); 864 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 865 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 866 &credit_details->amount); 867 GNUNET_assert (GNUNET_OK == 868 free_rii (NULL, 869 &key, 870 rii)); 871 return true; 872 } 873 if (0 != TALER_amount_cmp (&rii->credit_details.amount, 874 &credit_details->amount)) 875 { 876 struct TALER_AUDITORDB_ReserveInInconsistency riiDb = { 877 .diagnostic = (char *) "wire amount does not match", 878 .account = wa->ai->payto_uri, 879 .bank_row_id = credit_details->serial_id, 880 .amount_exchange_expected = rii->credit_details.amount, 881 .amount_wired = credit_details->amount, 882 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 883 .timestamp = rii->credit_details.execution_date.abs_time 884 }; 885 enum GNUNET_DB_QueryStatus qs; 886 887 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 888 "Wire transfer amount differs\n"); 889 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency ( 890 TALER_ARL_adb, 891 &riiDb); 892 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); 893 if (qs < 0) 894 { 895 global_qs = qs; 896 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 897 return false; 898 } 899 if (0 < TALER_amount_cmp (&credit_details->amount, 900 &rii->credit_details.amount)) 901 { 902 /* details->amount > rii->details.amount: wire transfer was larger than it should have been */ 903 struct TALER_Amount delta; 904 905 TALER_ARL_amount_subtract (&delta, 906 &credit_details->amount, 907 &rii->credit_details.amount); 908 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 909 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 910 &delta); 911 } 912 else 913 { 914 /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */ 915 struct TALER_Amount delta; 916 917 TALER_ARL_amount_subtract (&delta, 918 &rii->credit_details.amount, 919 &credit_details->amount); 920 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 921 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 922 &delta); 923 } 924 } 925 926 { 927 struct TALER_NormalizedPayto np; 928 struct TALER_NormalizedPayto np2; 929 930 np = TALER_payto_normalize (credit_details->debit_account_uri); 931 np2 = TALER_payto_normalize (rii->credit_details.debit_account_uri); 932 if (0 != TALER_normalized_payto_cmp (np, 933 np2)) 934 { 935 struct TALER_AUDITORDB_MisattributionInInconsistency mii = { 936 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 937 .amount = rii->credit_details.amount, 938 .bank_row = credit_details->serial_id 939 }; 940 enum GNUNET_DB_QueryStatus qs; 941 942 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 943 "Origin bank account differs\n"); 944 qs = TALER_AUDITORDB_insert_misattribution_in_inconsistency ( 945 TALER_ARL_adb, 946 &mii); 947 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); 948 if (qs < 0) 949 { 950 global_qs = qs; 951 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 952 GNUNET_free (np.normalized_payto); 953 GNUNET_free (np2.normalized_payto); 954 return false; 955 } 956 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_misattribution_in), 957 &TALER_ARL_USE_AB (total_misattribution_in), 958 &rii->credit_details.amount); 959 } 960 GNUNET_free (np.normalized_payto); 961 GNUNET_free (np2.normalized_payto); 962 } 963 if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date, 964 !=, 965 rii->credit_details.execution_date)) 966 { 967 struct TALER_AUDITORDB_RowMinorInconsistencies rmi = { 968 .problem_row = rii->rowid, 969 .diagnostic = (char *) "execution date mismatch", 970 .row_table = (char *) "reserves_in" 971 }; 972 enum GNUNET_DB_QueryStatus qs; 973 974 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 975 "Execution date differs\n"); 976 qs = TALER_AUDITORDB_insert_row_minor_inconsistencies ( 977 TALER_ARL_adb, 978 &rmi); 979 980 if (qs < 0) 981 { 982 global_qs = qs; 983 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 984 return false; 985 } 986 } 987 GNUNET_assert (GNUNET_OK == 988 free_rii (NULL, 989 &key, 990 rii)); 991 return true; 992 } 993 994 995 /** 996 * This function is called for all transactions that 997 * are credited to the exchange's account (incoming 998 * transactions). 999 * 1000 * @param cls `struct WireAccount` we are processing 1001 * @param chr HTTP response returned by the bank 1002 */ 1003 static void 1004 history_credit_cb (void *cls, 1005 const struct TALER_BANK_CreditHistoryResponse *chr) 1006 { 1007 struct WireAccount *wa = cls; 1008 1009 wa->chh = NULL; 1010 switch (chr->http_status) 1011 { 1012 case MHD_HTTP_OK: 1013 for (unsigned int i = 0; i < chr->details.ok.details_length; i++) 1014 { 1015 const struct TALER_BANK_CreditDetails *cd 1016 = &chr->details.ok.details[i]; 1017 1018 if (! analyze_credit (wa, 1019 cd)) 1020 { 1021 switch (global_qs) 1022 { 1023 case GNUNET_DB_STATUS_SOFT_ERROR: 1024 rollback_and_reset (); 1025 return; 1026 case GNUNET_DB_STATUS_HARD_ERROR: 1027 GNUNET_SCHEDULER_shutdown (); 1028 return; 1029 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1030 /* perfectly fine */ 1031 break; 1032 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1033 /* perfectly fine */ 1034 break; 1035 } 1036 break; 1037 } 1038 } 1039 conclude_account (wa); 1040 return; 1041 case MHD_HTTP_NO_CONTENT: 1042 conclude_account (wa); 1043 return; 1044 case MHD_HTTP_NOT_FOUND: 1045 if (ignore_account_404) 1046 { 1047 conclude_account (wa); 1048 return; 1049 } 1050 break; 1051 default: 1052 break; 1053 } 1054 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1055 "Error fetching credit history of account %s: %u (%s)\n", 1056 wa->ai->section_name, 1057 chr->http_status, 1058 TALER_ErrorCode_get_hint (chr->ec)); 1059 commit (GNUNET_DB_STATUS_HARD_ERROR); 1060 global_ret = EXIT_FAILURE; 1061 GNUNET_SCHEDULER_shutdown (); 1062 } 1063 1064 1065 /* ***************************** Setup logic ************************ */ 1066 1067 1068 /** 1069 * Start processing the next wire account. 1070 * Shuts down if we are done. 1071 * 1072 * @param cls `struct WireAccount` with a wire account list to process 1073 */ 1074 static void 1075 process_credits (void *cls) 1076 { 1077 struct WireAccount *wa = cls; 1078 enum GNUNET_DB_QueryStatus qs; 1079 1080 /* skip accounts where CREDIT is not enabled */ 1081 while ( (NULL != wa) && 1082 (GNUNET_NO == wa->ai->credit_enabled) ) 1083 wa = wa->next; 1084 if (NULL == wa) 1085 { 1086 /* done with all accounts, conclude check */ 1087 conclude_credit_history (); 1088 return; 1089 } 1090 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1091 "Analyzing exchange's wire IN table for account `%s'\n", 1092 wa->ai->section_name); 1093 qs = TALER_TALER_EXCHANGEDB_select_reserves_in_above_serial_id_by_account ( 1094 TALER_ARL_edb, 1095 wa->ai->section_name, 1096 wa->last_reserve_in_serial_id, 1097 &reserve_in_cb, 1098 wa); 1099 if (0 > qs) 1100 { 1101 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1102 global_ret = EXIT_FAILURE; 1103 GNUNET_SCHEDULER_shutdown (); 1104 return; 1105 } 1106 1107 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1108 "Starting bank CREDIT history of account `%s'\n", 1109 wa->ai->section_name); 1110 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1111 "user `%s'\n", 1112 wa->ai->auth->details.basic.username); 1113 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1114 "pass `%s'\n", 1115 wa->ai->auth->details.basic.password); 1116 GNUNET_assert (NULL == wa->chh); 1117 wa->chh = TALER_BANK_credit_history (ctx, 1118 wa->ai->auth, 1119 wa->wire_off_in, 1120 MAX_PER_TRANSACTION, 1121 GNUNET_TIME_UNIT_ZERO, 1122 &history_credit_cb, 1123 wa); 1124 if (NULL == wa->chh) 1125 { 1126 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1127 "Failed to obtain bank transaction history\n"); 1128 commit (GNUNET_DB_STATUS_HARD_ERROR); 1129 global_ret = EXIT_FAILURE; 1130 GNUNET_SCHEDULER_shutdown (); 1131 return; 1132 } 1133 } 1134 1135 1136 /** 1137 * Begin audit of CREDITs to the exchange. 1138 */ 1139 static void 1140 begin_credit_audit (void) 1141 { 1142 GNUNET_assert (NULL == in_map); 1143 in_map = GNUNET_CONTAINER_multihashmap_create (1024, 1144 GNUNET_YES); 1145 /* now go over all bank accounts and check delta with in_map */ 1146 process_credits (wa_head); 1147 } 1148 1149 1150 static enum GNUNET_DB_QueryStatus 1151 begin_transaction (void) 1152 { 1153 enum GNUNET_DB_QueryStatus qs; 1154 1155 if (GNUNET_SYSERR == 1156 TALER_EXCHANGEDB_preflight (TALER_ARL_edb)) 1157 { 1158 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1159 "Failed to initialize exchange database connection.\n"); 1160 return GNUNET_DB_STATUS_HARD_ERROR; 1161 } 1162 if (GNUNET_SYSERR == 1163 TALER_AUDITORDB_preflight (TALER_ARL_adb)) 1164 { 1165 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1166 "Failed to initialize auditor database session.\n"); 1167 return GNUNET_DB_STATUS_HARD_ERROR; 1168 } 1169 global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1170 if (GNUNET_OK != 1171 TALER_AUDITORDB_start (TALER_ARL_adb, 1172 "auditor-wire-credit")) 1173 { 1174 GNUNET_break (0); 1175 return GNUNET_DB_STATUS_HARD_ERROR; 1176 } 1177 if (GNUNET_OK != 1178 TALER_TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb, 1179 "wire credit auditor")) 1180 { 1181 GNUNET_break (0); 1182 return GNUNET_DB_STATUS_HARD_ERROR; 1183 } 1184 qs = TALER_AUDITORDB_get_balance ( 1185 TALER_ARL_adb, 1186 TALER_ARL_GET_AB (total_wire_in), 1187 TALER_ARL_GET_AB (total_kycauth_in), 1188 TALER_ARL_GET_AB (total_wire_credit_fees), 1189 TALER_ARL_GET_AB (total_bad_amount_in_plus), 1190 TALER_ARL_GET_AB (total_bad_amount_in_minus), 1191 TALER_ARL_GET_AB (total_misattribution_in), 1192 NULL); 1193 switch (qs) 1194 { 1195 case GNUNET_DB_STATUS_HARD_ERROR: 1196 GNUNET_break (0); 1197 return qs; 1198 case GNUNET_DB_STATUS_SOFT_ERROR: 1199 GNUNET_break (0); 1200 return qs; 1201 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1202 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1203 break; 1204 } 1205 for (struct WireAccount *wa = wa_head; 1206 NULL != wa; 1207 wa = wa->next) 1208 { 1209 GNUNET_asprintf (&wa->label_reserve_in_serial_id, 1210 "wire-%s-%s", 1211 wa->ai->section_name, 1212 "reserve_in_serial_id"); 1213 GNUNET_asprintf (&wa->label_wire_off_in, 1214 "wire-%s-%s", 1215 wa->ai->section_name, 1216 "wire_off_in"); 1217 qs = TALER_AUDITORDB_get_auditor_progress ( 1218 TALER_ARL_adb, 1219 wa->label_reserve_in_serial_id, 1220 &wa->last_reserve_in_serial_id, 1221 wa->label_wire_off_in, 1222 &wa->wire_off_in, 1223 NULL); 1224 if (0 > qs) 1225 { 1226 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1227 return qs; 1228 } 1229 wa->start_reserve_in_serial_id = wa->last_reserve_in_serial_id; 1230 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1231 "Starting from reserve_in at %s=%llu for account `%s'\n", 1232 wa->label_reserve_in_serial_id, 1233 (unsigned long long) wa->start_reserve_in_serial_id, 1234 wa->ai->section_name); 1235 } 1236 1237 begin_credit_audit (); 1238 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1239 } 1240 1241 1242 /** 1243 * Function called with information about a wire account. Adds the 1244 * account to our list for processing (if it is enabled and we can 1245 * load the plugin). 1246 * 1247 * @param cls closure, NULL 1248 * @param ai account information 1249 */ 1250 static void 1251 process_account_cb (void *cls, 1252 const struct TALER_EXCHANGEDB_AccountInfo *ai) 1253 { 1254 struct WireAccount *wa; 1255 1256 (void) cls; 1257 if ((! ai->debit_enabled) && 1258 (! ai->credit_enabled)) 1259 return; /* not an active exchange account */ 1260 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1261 "Found exchange account `%s'\n", 1262 ai->section_name); 1263 wa = GNUNET_new (struct WireAccount); 1264 wa->ai = ai; 1265 GNUNET_CONTAINER_DLL_insert (wa_head, 1266 wa_tail, 1267 wa); 1268 } 1269 1270 1271 /** 1272 * Function called on events received from Postgres. 1273 * 1274 * @param cls closure, NULL 1275 * @param extra additional event data provided 1276 * @param extra_size number of bytes in @a extra 1277 */ 1278 static void 1279 db_notify (void *cls, 1280 const void *extra, 1281 size_t extra_size) 1282 { 1283 (void) cls; 1284 (void) extra; 1285 (void) extra_size; 1286 1287 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1288 "Received notification to wake wire helper\n"); 1289 /* If there are accounts we are still processing, abort 1290 the HTTP requests so we can start afresh. */ 1291 for (struct WireAccount *wa = wa_head; 1292 NULL != wa; 1293 wa = wa->next) 1294 { 1295 if (NULL != wa->chh) 1296 { 1297 TALER_BANK_credit_history_cancel (wa->chh); 1298 wa->chh = NULL; 1299 } 1300 conclude_account (wa); 1301 } 1302 1303 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 1304 begin_transaction ()) 1305 { 1306 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1307 "Audit failed\n"); 1308 GNUNET_break (0); 1309 global_ret = EXIT_FAILURE; 1310 GNUNET_SCHEDULER_shutdown (); 1311 } 1312 } 1313 1314 1315 /** 1316 * Main function that will be run. 1317 * 1318 * @param cls closure 1319 * @param args remaining command-line arguments 1320 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 1321 * @param c configuration 1322 */ 1323 static void 1324 run (void *cls, 1325 char *const *args, 1326 const char *cfgfile, 1327 const struct GNUNET_CONFIGURATION_Handle *c) 1328 { 1329 (void) cls; 1330 (void) args; 1331 (void) cfgfile; 1332 cfg = c; 1333 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1334 "Launching wire-credit auditor\n"); 1335 if (GNUNET_OK != 1336 TALER_ARL_init (c)) 1337 { 1338 global_ret = EXIT_FAILURE; 1339 return; 1340 } 1341 if (GNUNET_OK != 1342 TALER_config_get_amount (TALER_ARL_cfg, 1343 "auditor", 1344 "TINY_AMOUNT", 1345 &tiny_amount)) 1346 { 1347 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1348 "auditor", 1349 "TINY_AMOUNT"); 1350 global_ret = EXIT_NOTCONFIGURED; 1351 return; 1352 } 1353 GNUNET_assert (GNUNET_OK == 1354 TALER_amount_set_zero (TALER_ARL_currency, 1355 &zero)); 1356 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1357 NULL); 1358 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1359 &rc); 1360 rc = GNUNET_CURL_gnunet_rc_create (ctx); 1361 if (NULL == ctx) 1362 { 1363 GNUNET_break (0); 1364 global_ret = EXIT_FAILURE; 1365 return; 1366 } 1367 if (GNUNET_OK != 1368 TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg, 1369 TALER_EXCHANGEDB_ALO_CREDIT 1370 | TALER_EXCHANGEDB_ALO_AUTHDATA)) 1371 { 1372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1373 "No bank accounts configured\n"); 1374 global_ret = EXIT_NOTCONFIGURED; 1375 GNUNET_SCHEDULER_shutdown (); 1376 return; 1377 } 1378 TALER_EXCHANGEDB_find_accounts (&process_account_cb, 1379 NULL); 1380 1381 if (0 == test_mode) 1382 { 1383 struct GNUNET_DB_EventHeaderP es = { 1384 .size = htons (sizeof (es)), 1385 .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE) 1386 }; 1387 1388 eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb, 1389 &es, 1390 GNUNET_TIME_UNIT_FOREVER_REL, 1391 &db_notify, 1392 NULL); 1393 GNUNET_assert (NULL != eh); 1394 } 1395 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 1396 begin_transaction ()) 1397 { 1398 GNUNET_break (0); 1399 global_ret = EXIT_FAILURE; 1400 GNUNET_SCHEDULER_shutdown (); 1401 return; 1402 } 1403 } 1404 1405 1406 /** 1407 * The main function of the wire auditing tool. Checks that 1408 * the exchange's records of wire transfers match that of 1409 * the wire gateway. 1410 * 1411 * @param argc number of arguments from the command line 1412 * @param argv command line arguments 1413 * @return 0 ok, 1 on error 1414 */ 1415 int 1416 main (int argc, 1417 char *const *argv) 1418 { 1419 const struct GNUNET_GETOPT_CommandLineOption options[] = { 1420 GNUNET_GETOPT_option_flag ('i', 1421 "internal", 1422 "perform checks only applicable for exchange-internal audits", 1423 &internal_checks), 1424 GNUNET_GETOPT_option_flag ('I', 1425 "ignore-not-found", 1426 "continue, even if the bank account of the exchange was not found", 1427 &ignore_account_404), 1428 GNUNET_GETOPT_option_flag ('t', 1429 "test", 1430 "run in test mode and exit when idle", 1431 &test_mode), 1432 GNUNET_GETOPT_option_timetravel ('T', 1433 "timetravel"), 1434 GNUNET_GETOPT_OPTION_END 1435 }; 1436 enum GNUNET_GenericReturnValue ret; 1437 1438 ret = GNUNET_PROGRAM_run ( 1439 TALER_AUDITOR_project_data (), 1440 argc, 1441 argv, 1442 "taler-helper-auditor-wire-credit", 1443 gettext_noop ( 1444 "Audit exchange database for consistency with the bank's wire transfers"), 1445 options, 1446 &run, 1447 NULL); 1448 if (GNUNET_SYSERR == ret) 1449 return EXIT_INVALIDARGUMENT; 1450 if (GNUNET_NO == ret) 1451 return EXIT_SUCCESS; 1452 return global_ret; 1453 } 1454 1455 1456 /* end of taler-helper-auditor-wire-credit.c */