taler-helper-auditor-wire-credit.c (67061B)
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/iterate_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/delete_kycauth_in_inconsistency.h" 43 #include "auditor-database/get_kycauth_in_inconsistency.h" 44 #include "auditor-database/insert_kycauth_in_inconsistency.h" 45 #include "auditor-database/insert_misattribution_in_inconsistency.h" 46 #include "auditor-database/insert_reserve_in_inconsistency.h" 47 #include "auditor-database/insert_row_inconsistency.h" 48 #include "auditor-database/insert_row_minor_inconsistencies.h" 49 #include "auditor-database/preflight.h" 50 #include "auditor-database/get_reserve_in_inconsistency.h" 51 #include "auditor-database/start.h" 52 #include "exchange-database/preflight.h" 53 #include "exchange-database/rollback.h" 54 #include \ 55 "exchange-database/iterate_kycauth_in_above_serial_id_by_account.h" 56 #include \ 57 "exchange-database/iterate_reserves_in_above_serial_id_by_account.h" 58 #include "exchange-database/start_read_only.h" 59 60 /** 61 * How much time do we allow the aggregator to lag behind? If 62 * wire transfers should have been made more than #GRACE_PERIOD 63 * before, we issue warnings. 64 */ 65 #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS 66 67 /** 68 * Maximum number of wire transfers we process per 69 * (database) transaction. 70 */ 71 #define MAX_PER_TRANSACTION 1024 72 73 /** 74 * How much do we allow the bank and the exchange to disagree about 75 * timestamps? Should be sufficiently large to avoid bogus reports from deltas 76 * created by imperfect clock synchronization and network delay. 77 */ 78 #define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \ 79 15) 80 81 82 /** 83 * Run in test mode. Exit when idle instead of 84 * going to sleep and waiting for more work. 85 */ 86 static int test_mode; 87 88 /** 89 * Information we keep for each supported account. 90 */ 91 struct WireAccount 92 { 93 /** 94 * Accounts are kept in a DLL. 95 */ 96 struct WireAccount *next; 97 98 /** 99 * Plugins are kept in a DLL. 100 */ 101 struct WireAccount *prev; 102 103 /** 104 * Account details. 105 */ 106 const struct TALER_EXCHANGEDB_AccountInfo *ai; 107 108 /** 109 * Active wire request for the transaction history. 110 */ 111 struct TALER_BANK_CreditHistoryHandle *chh; 112 113 /** 114 * Progress point for this account. 115 */ 116 uint64_t last_reserve_in_serial_id; 117 118 /** 119 * Initial progress point for this account. 120 */ 121 uint64_t start_reserve_in_serial_id; 122 123 /** 124 * Progress point for this account in the kycauths_in table. 125 */ 126 uint64_t last_kycauth_in_serial_id; 127 128 /** 129 * Where we are in the inbound transaction history. 130 */ 131 uint64_t wire_off_in; 132 133 /** 134 * Label under which we store our pp's reserve_in_serial_id. 135 */ 136 char *label_reserve_in_serial_id; 137 138 /** 139 * Label under which we store our pp's kycauth_in_serial_id. 140 */ 141 char *label_kycauth_in_serial_id; 142 143 /** 144 * Label under which we store our wire_off_in. 145 */ 146 char *label_wire_off_in; 147 148 }; 149 150 151 /** 152 * Return value from main(). 153 */ 154 static int global_ret; 155 156 /** 157 * State of the current database transaction with 158 * the auditor DB. 159 */ 160 static enum GNUNET_DB_QueryStatus global_qs; 161 162 /** 163 * Map with information about incoming wire transfers. 164 * Maps hashes of the wire offsets to `struct ReserveInInfo`s. 165 */ 166 static struct GNUNET_CONTAINER_MultiHashMap *in_map; 167 168 /** 169 * Map with information about incoming KYCAUTH wire transfers. 170 * Maps hashes of the wire offsets to `struct KycauthInInfo`s. 171 */ 172 static struct GNUNET_CONTAINER_MultiHashMap *kycauth_map; 173 174 /** 175 * Head of list of wire accounts we still need to look at. 176 */ 177 static struct WireAccount *wa_head; 178 179 /** 180 * Tail of list of wire accounts we still need to look at. 181 */ 182 static struct WireAccount *wa_tail; 183 184 /** 185 * Amount that is considered "tiny" 186 */ 187 static struct TALER_Amount tiny_amount; 188 189 /** 190 * Total amount that was transferred too much to the exchange. 191 */ 192 static TALER_ARL_DEF_AB (total_bad_amount_in_plus); 193 194 /** 195 * Total amount that was transferred too little to the exchange. 196 */ 197 static TALER_ARL_DEF_AB (total_bad_amount_in_minus); 198 199 /** 200 * Total amount where the exchange has the wrong sender account 201 * for incoming funds and may thus wire funds to the wrong 202 * destination when closing the reserve. 203 */ 204 static TALER_ARL_DEF_AB (total_misattribution_in); 205 206 /** 207 * Total amount credited to exchange accounts, as claimed by the exchange. 208 * Includes #total_kycauth_revenue, so that this is the credit side of the 209 * balance the exchange's bank account is expected to have. 210 */ 211 static TALER_ARL_DEF_AB (total_wire_in); 212 213 /** 214 * Total amount credited to exchange accounts via KYCAUTH, as reported by 215 * the bank. 216 */ 217 static TALER_ARL_DEF_AB (total_kycauth_in); 218 219 /** 220 * Total amount credited to exchange accounts via KYCAUTH, as booked by the 221 * exchange in its `kycauths_in` table. KYCAUTH funds are not credited to 222 * any reserve and are never paid back, so they are revenue of the exchange 223 * operator, like wire fees and closing fees, and may be drained together 224 * with those. Counted towards #total_wire_in for that reason. 225 */ 226 static TALER_ARL_DEF_AB (total_kycauth_revenue); 227 228 /** 229 * Total wire credit fees charged to the exchange account. 230 */ 231 static TALER_ARL_DEF_AB (total_wire_credit_fees); 232 233 /** 234 * Amount of zero in our currency. 235 */ 236 static struct TALER_Amount zero; 237 238 /** 239 * Handle to the context for interacting with the bank. 240 */ 241 static struct GNUNET_CURL_Context *ctx; 242 243 /** 244 * Scheduler context for running the @e ctx. 245 */ 246 static struct GNUNET_CURL_RescheduleContext *rc; 247 248 /** 249 * Should we run checks that only work for exchange-internal audits? 250 */ 251 static int internal_checks; 252 253 /** 254 * Should we ignore if the bank does not know our bank 255 * account? 256 */ 257 static int ignore_account_404; 258 259 /** 260 * Database event handler to wake us up again. 261 */ 262 static struct GNUNET_DB_EventHandler *eh; 263 264 /** 265 * The auditors's configuration. 266 */ 267 static const struct GNUNET_CONFIGURATION_Handle *cfg; 268 269 /* ***************************** Shutdown **************************** */ 270 271 /** 272 * Entry in map with wire information we expect to obtain from the 273 * bank later. 274 */ 275 struct ReserveInInfo 276 { 277 278 /** 279 * Hash of expected row offset. 280 */ 281 struct GNUNET_HashCode row_off_hash; 282 283 /** 284 * Expected details about the wire transfer. 285 * The member "account_url" is to be allocated 286 * at the end of this struct! 287 */ 288 struct TALER_BANK_CreditDetails credit_details; 289 290 /** 291 * RowID in reserves_in table. 292 */ 293 uint64_t rowid; 294 295 }; 296 297 298 /** 299 * Entry in #kycauth_map with the KYCAUTH transfer the exchange claims to 300 * have received, waiting to be matched against the bank's credit history. 301 */ 302 struct KycauthInInfo 303 { 304 305 /** 306 * Hash of expected row offset. 307 */ 308 struct GNUNET_HashCode row_off_hash; 309 310 /** 311 * RowID in the kycauths_in table. 312 */ 313 uint64_t rowid; 314 315 /** 316 * Public key the transfer associated with the debited account. 317 */ 318 union TALER_AccountPublicKeyP account_pub; 319 320 /** 321 * Amount the exchange says it received. 322 */ 323 struct TALER_Amount credit; 324 325 /** 326 * When the exchange says it received the funds. 327 */ 328 struct GNUNET_TIME_Timestamp execution_date; 329 330 /** 331 * payto://-URL of the debited account, allocated at the end of this 332 * struct. NULL if the exchange has no `wire_targets` row for the 333 * account, which it only creates the first time it sees one. 334 */ 335 struct TALER_FullPayto sender_account_details; 336 337 }; 338 339 340 /** 341 * Free entry in #in_map. 342 * 343 * @param cls NULL 344 * @param key unused key 345 * @param value the `struct ReserveInInfo` to free 346 * @return #GNUNET_OK 347 */ 348 static enum GNUNET_GenericReturnValue 349 free_rii (void *cls, 350 const struct GNUNET_HashCode *key, 351 void *value) 352 { 353 struct ReserveInInfo *rii = value; 354 355 (void) cls; 356 GNUNET_assert (GNUNET_YES == 357 GNUNET_CONTAINER_multihashmap_remove (in_map, 358 key, 359 rii)); 360 GNUNET_free (rii); 361 return GNUNET_OK; 362 } 363 364 365 /** 366 * Free entry in #kycauth_map. 367 * 368 * @param cls NULL 369 * @param key unused key 370 * @param value the `struct KycauthInInfo` to free 371 * @return #GNUNET_OK 372 */ 373 static enum GNUNET_GenericReturnValue 374 free_kii (void *cls, 375 const struct GNUNET_HashCode *key, 376 void *value) 377 { 378 struct KycauthInInfo *kii = value; 379 380 (void) cls; 381 GNUNET_assert (GNUNET_YES == 382 GNUNET_CONTAINER_multihashmap_remove (kycauth_map, 383 key, 384 kii)); 385 GNUNET_free (kii); 386 return GNUNET_OK; 387 } 388 389 390 /** 391 * Task run on shutdown. 392 * 393 * @param cls NULL 394 */ 395 static void 396 do_shutdown (void *cls) 397 { 398 struct WireAccount *wa; 399 400 (void) cls; 401 if (NULL != eh) 402 { 403 TALER_AUDITORDB_event_listen_cancel (eh); 404 eh = NULL; 405 } 406 TALER_ARL_done (); 407 if (NULL != in_map) 408 { 409 GNUNET_CONTAINER_multihashmap_iterate (in_map, 410 &free_rii, 411 NULL); 412 GNUNET_CONTAINER_multihashmap_destroy (in_map); 413 in_map = NULL; 414 } 415 if (NULL != kycauth_map) 416 { 417 GNUNET_CONTAINER_multihashmap_iterate (kycauth_map, 418 &free_kii, 419 NULL); 420 GNUNET_CONTAINER_multihashmap_destroy (kycauth_map); 421 kycauth_map = NULL; 422 } 423 while (NULL != (wa = wa_head)) 424 { 425 if (NULL != wa->chh) 426 { 427 TALER_BANK_credit_history_cancel (wa->chh); 428 wa->chh = NULL; 429 } 430 GNUNET_CONTAINER_DLL_remove (wa_head, 431 wa_tail, 432 wa); 433 GNUNET_free (wa->label_reserve_in_serial_id); 434 GNUNET_free (wa->label_kycauth_in_serial_id); 435 GNUNET_free (wa->label_wire_off_in); 436 GNUNET_free (wa); 437 } 438 if (NULL != ctx) 439 { 440 GNUNET_CURL_fini (ctx); 441 ctx = NULL; 442 } 443 if (NULL != rc) 444 { 445 GNUNET_CURL_gnunet_rc_destroy (rc); 446 rc = NULL; 447 } 448 TALER_EXCHANGEDB_unload_accounts (); 449 TALER_ARL_cfg = NULL; 450 } 451 452 453 /** 454 * Start the database transactions and begin the audit. 455 * 456 * @return transaction status code 457 */ 458 static enum GNUNET_DB_QueryStatus 459 begin_transaction (void); 460 461 462 /** 463 * Rollback the current transaction, reset our state and try 464 * again (we had a serialization error). 465 */ 466 static void 467 rollback_and_reset (void) 468 { 469 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 470 "Serialization issue, trying again\n"); 471 TALER_AUDITORDB_rollback (TALER_ARL_adb); 472 for (unsigned int max_retries = 3; max_retries>0; max_retries--) 473 { 474 enum GNUNET_DB_QueryStatus qs; 475 476 if (NULL != in_map) 477 { 478 GNUNET_CONTAINER_multihashmap_iterate (in_map, 479 &free_rii, 480 NULL); 481 GNUNET_CONTAINER_multihashmap_destroy (in_map); 482 in_map = NULL; 483 } 484 if (NULL != kycauth_map) 485 { 486 GNUNET_CONTAINER_multihashmap_iterate (kycauth_map, 487 &free_kii, 488 NULL); 489 GNUNET_CONTAINER_multihashmap_destroy (kycauth_map); 490 kycauth_map = NULL; 491 } 492 qs = begin_transaction (); 493 if (GNUNET_DB_STATUS_HARD_ERROR == qs) 494 break; 495 } 496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 497 "Hard database error, terminating\n"); 498 GNUNET_SCHEDULER_shutdown (); 499 } 500 501 502 /** 503 * Commit the transaction, checkpointing our progress in the auditor DB. 504 * 505 * @param qs transaction status so far 506 */ 507 static void 508 commit (enum GNUNET_DB_QueryStatus qs) 509 { 510 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 511 "Transaction logic ended with status %d\n", 512 qs); 513 TALER_EXCHANGEDB_rollback (TALER_ARL_edb); 514 if (qs < 0) 515 goto handle_db_error; 516 qs = TALER_AUDITORDB_insert_balance ( 517 TALER_ARL_adb, 518 TALER_ARL_SET_AB (total_wire_in), 519 TALER_ARL_SET_AB (total_kycauth_in), 520 TALER_ARL_SET_AB (total_kycauth_revenue), 521 TALER_ARL_SET_AB (total_wire_credit_fees), 522 TALER_ARL_SET_AB (total_bad_amount_in_plus), 523 TALER_ARL_SET_AB (total_bad_amount_in_minus), 524 TALER_ARL_SET_AB (total_misattribution_in), 525 NULL); 526 if (0 > qs) 527 goto handle_db_error; 528 for (struct WireAccount *wa = wa_head; 529 NULL != wa; 530 wa = wa->next) 531 { 532 qs = TALER_AUDITORDB_insert_auditor_progress ( 533 TALER_ARL_adb, 534 wa->label_reserve_in_serial_id, 535 wa->last_reserve_in_serial_id, 536 wa->label_kycauth_in_serial_id, 537 wa->last_kycauth_in_serial_id, 538 wa->label_wire_off_in, 539 wa->wire_off_in, 540 NULL); 541 if (0 > qs) 542 goto handle_db_error; 543 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 544 "Transaction ends at %s=%llu for account `%s'\n", 545 wa->label_reserve_in_serial_id, 546 (unsigned long long) wa->last_reserve_in_serial_id, 547 wa->ai->section_name); 548 } 549 qs = TALER_AUDITORDB_commit (TALER_ARL_adb); 550 if (0 > qs) 551 { 552 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 553 goto handle_db_error; 554 } 555 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 556 "Transaction concluded!\n"); 557 if (1 == test_mode) 558 GNUNET_SCHEDULER_shutdown (); 559 return; 560 handle_db_error: 561 rollback_and_reset (); 562 } 563 564 565 /** 566 * Conclude the credit history check by logging entries that 567 * were not found and freeing resources. Then move on to 568 * processing debits. 569 */ 570 static void 571 conclude_credit_history (void) 572 { 573 if (NULL != in_map) 574 { 575 GNUNET_assert (0 == 576 GNUNET_CONTAINER_multihashmap_size (in_map)); 577 GNUNET_CONTAINER_multihashmap_destroy (in_map); 578 in_map = NULL; 579 } 580 if (NULL != kycauth_map) 581 { 582 GNUNET_assert (0 == 583 GNUNET_CONTAINER_multihashmap_size (kycauth_map)); 584 GNUNET_CONTAINER_multihashmap_destroy (kycauth_map); 585 kycauth_map = NULL; 586 } 587 commit (global_qs); 588 } 589 590 591 /** 592 * Check if the given wire transfers are equivalent. 593 * 594 * @param credit amount that was received 595 * @param credit2 2nd amount that was received 596 * @param reserve_pub public key of the reserve (also the WTID) 597 * @param reserve_pub2 2nd public key of the reserve (also the WTID) 598 * @param sender_account_details payto://-URL of the sender's bank account 599 * @param sender_account_details2 2nd payto://-URL of the sender's bank account 600 * @param execution_date when did we receive the funds 601 * @param execution_date2 2nd when did we receive the funds 602 * @return #GNUNET_YES if so, 603 * #GNUNET_NO if not 604 * #GNUNET_SYSERR on internal error 605 */ 606 static enum GNUNET_GenericReturnValue 607 check_equality (const struct TALER_Amount *credit, 608 const struct TALER_Amount *credit2, 609 const struct TALER_ReservePublicKeyP *reserve_pub, 610 const struct TALER_ReservePublicKeyP *reserve_pub2, 611 const struct TALER_FullPayto sender_account_details, 612 const struct TALER_FullPayto sender_account_details2, 613 struct GNUNET_TIME_Timestamp execution_date, 614 struct GNUNET_TIME_Timestamp execution_date2) 615 { 616 if (0 != TALER_amount_cmp (credit, 617 credit2)) 618 return GNUNET_NO; 619 if (0 != GNUNET_memcmp (reserve_pub, 620 reserve_pub2)) 621 return GNUNET_NO; 622 { 623 struct TALER_NormalizedPayto np; 624 struct TALER_NormalizedPayto np2; 625 bool fail; 626 627 np = TALER_payto_normalize (sender_account_details); 628 np2 = TALER_payto_normalize (sender_account_details2); 629 fail = (0 != TALER_normalized_payto_cmp (np, 630 np2)); 631 GNUNET_free (np.normalized_payto); 632 GNUNET_free (np2.normalized_payto); 633 if (fail) 634 return GNUNET_NO; 635 } 636 if (GNUNET_TIME_timestamp_cmp (execution_date, 637 !=, 638 execution_date2)) 639 return GNUNET_NO; 640 return GNUNET_YES; 641 } 642 643 644 /** 645 * Check if two payto://-URLs designate the same bank account. 646 * 647 * @param a first account, may be NULL 648 * @param b second account, may be NULL 649 * @return true if they match, or if either side is unknown 650 */ 651 static bool 652 same_account (const struct TALER_FullPayto a, 653 const struct TALER_FullPayto b) 654 { 655 struct TALER_NormalizedPayto na; 656 struct TALER_NormalizedPayto nb; 657 bool ret; 658 659 /* The exchange only creates the wire_targets row for an account the 660 first time it sees one, so it legitimately may not know the debited 661 account of a KYCAUTH transfer. Do not turn that into a finding. */ 662 if ( (NULL == a.full_payto) || 663 (NULL == b.full_payto) ) 664 return true; 665 na = TALER_payto_normalize (a); 666 nb = TALER_payto_normalize (b); 667 ret = (0 == TALER_normalized_payto_cmp (na, 668 nb)); 669 GNUNET_free (na.normalized_payto); 670 GNUNET_free (nb.normalized_payto); 671 return ret; 672 } 673 674 675 /** 676 * Check if the two views of a KYCAUTH wire transfer agree in every 677 * respect. 678 * 679 * @param credit amount of the first view 680 * @param credit2 amount of the second view 681 * @param account_pub account key of the first view 682 * @param account_pub2 account key of the second view 683 * @param sender_account_details debited account of the first view 684 * @param sender_account_details2 debited account of the second view 685 * @param execution_date execution time of the first view 686 * @param execution_date2 execution time of the second view 687 * @return true if the two agree 688 */ 689 static bool 690 check_kycauth_equality ( 691 const struct TALER_Amount *credit, 692 const struct TALER_Amount *credit2, 693 const union TALER_AccountPublicKeyP *account_pub, 694 const union TALER_AccountPublicKeyP *account_pub2, 695 const struct TALER_FullPayto sender_account_details, 696 const struct TALER_FullPayto sender_account_details2, 697 struct GNUNET_TIME_Timestamp execution_date, 698 struct GNUNET_TIME_Timestamp execution_date2) 699 { 700 return (0 == TALER_amount_cmp (credit, 701 credit2)) && 702 (0 == GNUNET_memcmp (account_pub, 703 account_pub2)) && 704 same_account (sender_account_details, 705 sender_account_details2) && 706 GNUNET_TIME_timestamp_cmp (execution_date, 707 ==, 708 execution_date2); 709 } 710 711 712 /** 713 * Function called with details about incoming wire transfers that 714 * established an account key for KYC authentication, as claimed by the 715 * exchange DB. 716 * 717 * Such a transfer is not credited to a reserve and is never paid back, so 718 * the money is simply revenue of the exchange operator. We book it as 719 * such: it is part of the balance the exchange's bank account is expected 720 * to have, and the exchange may drain it together with its fee income. 721 * 722 * The row is then remembered in #kycauth_map, so that the walk over the 723 * bank's credit history that follows can confirm the transfer actually 724 * happened; whatever is left in that map afterwards is money the exchange 725 * claims to have received and the bank knows nothing about. 726 * 727 * @param cls a `struct WireAccount` we are processing 728 * @param rowid unique serial ID for the entry in our DB 729 * @param account_pub public key the transfer associated with the account 730 * @param credit amount that was received 731 * @param sender_account_details payto://-URL of the sender's bank account, 732 * NULL if the exchange has no `wire_targets` row for it 733 * @param wire_reference unique identifier for the wire transfer 734 * @param execution_date when did we receive the funds 735 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 736 */ 737 static enum GNUNET_GenericReturnValue 738 kycauth_in_cb (void *cls, 739 uint64_t rowid, 740 const union TALER_AccountPublicKeyP *account_pub, 741 const struct TALER_Amount *credit, 742 const struct TALER_FullPayto sender_account_details, 743 uint64_t wire_reference, 744 struct GNUNET_TIME_Timestamp execution_date) 745 { 746 struct WireAccount *wa = cls; 747 struct KycauthInInfo *kii; 748 size_t slen; 749 750 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 751 "Analyzing exchange KYCAUTH IN (%llu) at %s of %s\n", 752 (unsigned long long) rowid, 753 GNUNET_TIME_timestamp2s (execution_date), 754 TALER_amount2s (credit)); 755 /* Advance the cursor up front: every path below has fully accounted for 756 this row, and reading it a second time would count the revenue twice. */ 757 wa->last_kycauth_in_serial_id = rowid + 1; 758 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_revenue), 759 &TALER_ARL_USE_AB (total_kycauth_revenue), 760 credit); 761 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in), 762 &TALER_ARL_USE_AB (total_wire_in), 763 credit); 764 { 765 enum GNUNET_DB_QueryStatus qs; 766 struct TALER_AUDITORDB_KycauthInInconsistency dc; 767 768 qs = TALER_AUDITORDB_get_kycauth_in_inconsistency ( 769 TALER_ARL_adb, 770 wire_reference, 771 &dc); 772 switch (qs) 773 { 774 case GNUNET_DB_STATUS_HARD_ERROR: 775 case GNUNET_DB_STATUS_SOFT_ERROR: 776 global_qs = qs; 777 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 778 return GNUNET_SYSERR; 779 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 780 break; 781 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 782 { 783 bool healed = false; 784 785 if (TALER_amount_is_zero (&dc.amount_exchange_expected)) 786 { 787 /* An earlier run saw this transfer at the bank while the 788 exchange had not booked it yet. If the two now agree, that 789 finding was a transient and we retract it. */ 790 healed = check_kycauth_equality (&dc.amount_wired, 791 credit, 792 &dc.account_pub, 793 account_pub, 794 dc.account, 795 sender_account_details, 796 GNUNET_TIME_absolute_to_timestamp ( 797 dc.timestamp), 798 execution_date); 799 } 800 GNUNET_free (dc.account.full_payto); 801 GNUNET_free (dc.diagnostic); 802 if (healed) 803 { 804 qs = TALER_AUDITORDB_delete_kycauth_in_inconsistency ( 805 TALER_ARL_adb, 806 dc.serial_id); 807 if (qs < 0) 808 { 809 global_qs = qs; 810 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 811 return GNUNET_SYSERR; 812 } 813 /* We are retracting the finding, so the excess credit we booked 814 when we made it has to go as well -- otherwise a transfer the 815 exchange merely booked late inflates this balance forever. */ 816 TALER_ARL_amount_subtract ( 817 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 818 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 819 &dc.amount_wired); 820 /* The bank side of this transfer was accounted for when the 821 finding was made, so do not expect it again. */ 822 return GNUNET_OK; 823 } 824 break; 825 } 826 } 827 } 828 slen = (NULL == sender_account_details.full_payto) 829 ? 0 830 : strlen (sender_account_details.full_payto) + 1; 831 kii = GNUNET_malloc (sizeof (struct KycauthInInfo) + slen); 832 kii->rowid = rowid; 833 kii->account_pub = *account_pub; 834 kii->credit = *credit; 835 kii->execution_date = execution_date; 836 if (0 != slen) 837 { 838 kii->sender_account_details.full_payto = (char *) &kii[1]; 839 GNUNET_memcpy (&kii[1], 840 sender_account_details.full_payto, 841 slen); 842 } 843 GNUNET_CRYPTO_hash (&wire_reference, 844 sizeof (uint64_t), 845 &kii->row_off_hash); 846 if (GNUNET_OK != 847 GNUNET_CONTAINER_multihashmap_put ( 848 kycauth_map, 849 &kii->row_off_hash, 850 kii, 851 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) 852 { 853 struct TALER_AUDITORDB_RowInconsistency ri = { 854 .row_id = rowid, 855 .row_table = (char *) "kycauths_in", 856 .diagnostic = (char *) "duplicate wire offset" 857 }; 858 enum GNUNET_DB_QueryStatus qs; 859 860 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 861 "Duplicate wire offset\n"); 862 qs = TALER_AUDITORDB_insert_row_inconsistency ( 863 TALER_ARL_adb, 864 &ri); 865 GNUNET_free (kii); 866 if (qs < 0) 867 { 868 global_qs = qs; 869 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 870 return GNUNET_SYSERR; 871 } 872 } 873 return GNUNET_OK; 874 } 875 876 877 /** 878 * Function called with details about incoming wire transfers 879 * as claimed by the exchange DB. 880 * 881 * @param cls a `struct WireAccount` we are processing 882 * @param rowid unique serial ID for the entry in our DB 883 * @param reserve_pub public key of the reserve (also the WTID) 884 * @param credit amount that was received 885 * @param sender_account_details payto://-URL of the sender's bank account 886 * @param wire_reference unique identifier for the wire transfer 887 * @param execution_date when did we receive the funds 888 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 889 */ 890 static enum GNUNET_GenericReturnValue 891 reserve_in_cb (void *cls, 892 uint64_t rowid, 893 const struct TALER_ReservePublicKeyP *reserve_pub, 894 const struct TALER_Amount *credit, 895 const struct TALER_FullPayto sender_account_details, 896 uint64_t wire_reference, 897 struct GNUNET_TIME_Timestamp execution_date) 898 { 899 struct WireAccount *wa = cls; 900 struct ReserveInInfo *rii; 901 size_t slen; 902 903 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 904 "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n", 905 (unsigned long long) rowid, 906 GNUNET_TIME_timestamp2s (execution_date), 907 TALER_amount2s (credit), 908 TALER_B2S (reserve_pub)); 909 /* Advance the cursor up front: every path below has fully accounted for 910 this row, and reading it a second time would count the credit twice. */ 911 wa->last_reserve_in_serial_id = rowid + 1; 912 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in), 913 &TALER_ARL_USE_AB (total_wire_in), 914 credit); 915 { 916 enum GNUNET_DB_QueryStatus qs; 917 struct TALER_AUDITORDB_ReserveInInconsistency dc; 918 919 qs = TALER_AUDITORDB_get_reserve_in_inconsistency ( 920 TALER_ARL_adb, 921 wire_reference, 922 &dc); 923 switch (qs) 924 { 925 case GNUNET_DB_STATUS_HARD_ERROR: 926 case GNUNET_DB_STATUS_SOFT_ERROR: 927 global_qs = qs; 928 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 929 return GNUNET_SYSERR; 930 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 931 break; 932 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 933 { 934 enum GNUNET_GenericReturnValue ret = GNUNET_NO; 935 936 if (TALER_amount_is_zero (&dc.amount_exchange_expected)) 937 { 938 /* database entry indicates unmatched transaction */ 939 ret = check_equality (&dc.amount_wired, 940 credit, 941 &dc.reserve_pub, 942 reserve_pub, 943 dc.account, 944 sender_account_details, 945 GNUNET_TIME_absolute_to_timestamp (dc.timestamp) 946 , 947 execution_date); 948 } 949 GNUNET_free (dc.account.full_payto); 950 GNUNET_free (dc.diagnostic); 951 if (GNUNET_SYSERR == ret) 952 return GNUNET_SYSERR; 953 if (GNUNET_YES == ret) 954 { 955 qs = TALER_AUDITORDB_delete_reserve_in_inconsistency ( 956 TALER_ARL_adb, 957 dc.serial_id); 958 if (qs < 0) 959 { 960 global_qs = qs; 961 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 962 return GNUNET_SYSERR; 963 } 964 /* We are retracting the finding, so the excess credit we booked 965 when we made it has to go as well -- otherwise a transfer the 966 exchange merely booked late inflates this balance forever. */ 967 TALER_ARL_amount_subtract ( 968 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 969 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 970 &dc.amount_wired); 971 return GNUNET_OK; 972 } 973 break; 974 } 975 } 976 } 977 slen = strlen (sender_account_details.full_payto) + 1; 978 rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen); 979 rii->rowid = rowid; 980 rii->credit_details.type = TALER_BANK_CT_RESERVE; 981 rii->credit_details.amount = *credit; 982 rii->credit_details.execution_date = execution_date; 983 rii->credit_details.details.reserve.reserve_pub = *reserve_pub; 984 rii->credit_details.debit_account_uri.full_payto = (char *) &rii[1]; 985 GNUNET_memcpy (&rii[1], 986 sender_account_details.full_payto, 987 slen); 988 GNUNET_CRYPTO_hash (&wire_reference, 989 sizeof (uint64_t), 990 &rii->row_off_hash); 991 if (GNUNET_OK != 992 GNUNET_CONTAINER_multihashmap_put (in_map, 993 &rii->row_off_hash, 994 rii, 995 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) 996 { 997 struct TALER_AUDITORDB_RowInconsistency ri = { 998 .row_id = rowid, 999 .row_table = (char *) "reserves_in", 1000 .diagnostic = (char *) "duplicate wire offset" 1001 }; 1002 enum GNUNET_DB_QueryStatus qs; 1003 1004 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1005 "Duplicate wire offset\n"); 1006 qs = TALER_AUDITORDB_insert_row_inconsistency ( 1007 TALER_ARL_adb, 1008 &ri); 1009 GNUNET_free (rii); 1010 if (qs < 0) 1011 { 1012 global_qs = qs; 1013 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1014 return GNUNET_SYSERR; 1015 } 1016 return GNUNET_OK; 1017 } 1018 return GNUNET_OK; 1019 } 1020 1021 1022 /** 1023 * Complain that we failed to match an entry from #in_map. 1024 * 1025 * @param cls a `struct WireAccount` 1026 * @param key unused key 1027 * @param value the `struct ReserveInInfo` to free 1028 * @return #GNUNET_OK 1029 */ 1030 static enum GNUNET_GenericReturnValue 1031 complain_in_not_found (void *cls, 1032 const struct GNUNET_HashCode *key, 1033 void *value) 1034 { 1035 struct WireAccount *wa = cls; 1036 struct ReserveInInfo *rii = value; 1037 enum GNUNET_DB_QueryStatus qs; 1038 struct TALER_AUDITORDB_ReserveInInconsistency riiDb = { 1039 .bank_row_id = rii->rowid, 1040 .diagnostic = (char *) 1041 "incoming wire transfer claimed by exchange not found", 1042 .account = wa->ai->payto_uri, 1043 .amount_exchange_expected = rii->credit_details.amount, 1044 .amount_wired = zero, 1045 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 1046 .timestamp = rii->credit_details.execution_date.abs_time 1047 }; 1048 1049 (void) key; 1050 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1051 "Incoming wire transfer #%llu claimed by exchange not found\n", 1052 (unsigned long long) rii->rowid); 1053 GNUNET_assert (TALER_BANK_CT_RESERVE == 1054 rii->credit_details.type); 1055 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency ( 1056 TALER_ARL_adb, 1057 &riiDb); 1058 if (qs < 0) 1059 { 1060 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1061 global_qs = qs; 1062 return GNUNET_SYSERR; 1063 } 1064 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 1065 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 1066 &rii->credit_details.amount); 1067 return GNUNET_OK; 1068 } 1069 1070 1071 /** 1072 * Complain that we failed to match an entry from #kycauth_map: the 1073 * exchange booked a KYCAUTH transfer the bank never made. Beyond the 1074 * missing money, this means the exchange let an account authenticate 1075 * without anyone ever having proven that they control it. 1076 * 1077 * @param cls a `struct WireAccount` 1078 * @param key unused key 1079 * @param value the `struct KycauthInInfo` we could not match 1080 * @return #GNUNET_OK 1081 */ 1082 static enum GNUNET_GenericReturnValue 1083 complain_kycauth_in_not_found (void *cls, 1084 const struct GNUNET_HashCode *key, 1085 void *value) 1086 { 1087 struct WireAccount *wa = cls; 1088 struct KycauthInInfo *kii = value; 1089 enum GNUNET_DB_QueryStatus qs; 1090 struct TALER_AUDITORDB_KycauthInInconsistency kiiDb = { 1091 .bank_row_id = kii->rowid, 1092 .diagnostic = (char *) 1093 "KYCAUTH wire transfer claimed by exchange not found", 1094 .account = (NULL == kii->sender_account_details.full_payto) 1095 ? wa->ai->payto_uri 1096 : kii->sender_account_details, 1097 .amount_exchange_expected = kii->credit, 1098 .amount_wired = zero, 1099 .account_pub = kii->account_pub, 1100 .timestamp = kii->execution_date.abs_time 1101 }; 1102 1103 (void) key; 1104 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1105 "KYCAUTH wire transfer #%llu claimed by exchange not found\n", 1106 (unsigned long long) kii->rowid); 1107 qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency ( 1108 TALER_ARL_adb, 1109 &kiiDb); 1110 if (qs < 0) 1111 { 1112 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1113 global_qs = qs; 1114 return GNUNET_SYSERR; 1115 } 1116 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 1117 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 1118 &kii->credit); 1119 return GNUNET_OK; 1120 } 1121 1122 1123 /** 1124 * Start processing the next wire account. 1125 * Shuts down if we are done. 1126 * 1127 * @param cls `struct WireAccount` with a wire account list to process 1128 */ 1129 static void 1130 process_credits (void *cls); 1131 1132 1133 /** 1134 * We got all of the incoming transactions for @a wa, 1135 * finish processing the account. 1136 * 1137 * @param[in,out] wa wire account to process 1138 */ 1139 static void 1140 conclude_account (struct WireAccount *wa) 1141 { 1142 GNUNET_assert (NULL == wa->chh); 1143 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1144 "Reconciling CREDIT processing of account `%s'\n", 1145 wa->ai->section_name); 1146 if (NULL != in_map) 1147 { 1148 GNUNET_CONTAINER_multihashmap_iterate (in_map, 1149 &complain_in_not_found, 1150 wa); 1151 /* clean up before 2nd phase */ 1152 GNUNET_CONTAINER_multihashmap_iterate (in_map, 1153 &free_rii, 1154 NULL); 1155 if (global_qs < 0) 1156 { 1157 commit (global_qs); 1158 return; 1159 } 1160 } 1161 if (NULL != kycauth_map) 1162 { 1163 GNUNET_CONTAINER_multihashmap_iterate (kycauth_map, 1164 &complain_kycauth_in_not_found, 1165 wa); 1166 /* clean up before 2nd phase */ 1167 GNUNET_CONTAINER_multihashmap_iterate (kycauth_map, 1168 &free_kii, 1169 NULL); 1170 if (global_qs < 0) 1171 { 1172 commit (global_qs); 1173 return; 1174 } 1175 } 1176 process_credits (wa->next); 1177 } 1178 1179 1180 /** 1181 * Analyze a KYCAUTH credit the bank reports against what the exchange 1182 * booked in its `kycauths_in` table (collected in #kycauth_map). 1183 * 1184 * @param[in,out] wa account that received the transfer 1185 * @param credit_details transfer details as reported by the bank 1186 * @return true on success, false to stop loop at this point 1187 */ 1188 static bool 1189 analyze_kycauth_credit ( 1190 struct WireAccount *wa, 1191 const struct TALER_BANK_CreditDetails *credit_details) 1192 { 1193 struct KycauthInInfo *kii; 1194 struct GNUNET_HashCode key; 1195 1196 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1197 "Analyzing bank KYCAUTH CREDIT #%llu at %s of %s\n", 1198 (unsigned long long) credit_details->serial_id, 1199 GNUNET_TIME_timestamp2s (credit_details->execution_date), 1200 TALER_amount2s (&credit_details->amount)); 1201 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_in), 1202 &TALER_ARL_USE_AB (total_kycauth_in), 1203 &credit_details->amount); 1204 /* the bank charges its credit fee on this transfer just like on 1205 any other, so it must be part of the total either way */ 1206 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees), 1207 &TALER_ARL_USE_AB (total_wire_credit_fees), 1208 &credit_details->credit_fee); 1209 GNUNET_CRYPTO_hash (&credit_details->serial_id, 1210 sizeof (credit_details->serial_id), 1211 &key); 1212 kii = GNUNET_CONTAINER_multihashmap_get (kycauth_map, 1213 &key); 1214 if (NULL == kii) 1215 { 1216 struct TALER_AUDITORDB_KycauthInInconsistency dc = { 1217 .bank_row_id = credit_details->serial_id, 1218 .amount_exchange_expected = zero, 1219 .amount_wired = credit_details->amount, 1220 .account_pub = credit_details->details.kycauth.account_pub, 1221 .timestamp = credit_details->execution_date.abs_time, 1222 .account = credit_details->debit_account_uri, 1223 .diagnostic = (char *) "unknown to exchange" 1224 }; 1225 enum GNUNET_DB_QueryStatus qs; 1226 1227 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1228 "Failed to find KYCAUTH transfer at `%s' in exchange database.\n", 1229 GNUNET_TIME_timestamp2s (credit_details->execution_date)); 1230 qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (TALER_ARL_adb, 1231 &dc); 1232 if (qs <= 0) 1233 { 1234 global_qs = qs; 1235 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1236 return false; 1237 } 1238 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 1239 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 1240 &credit_details->amount); 1241 /* We have fully processed this credit: the finding is on file, and 1242 kycauth_in_cb() retracts it if the exchange books the transfer 1243 later. Looking at it again would only duplicate the finding. */ 1244 wa->wire_off_in = credit_details->serial_id; 1245 return true; 1246 } 1247 1248 /* Update offset */ 1249 wa->wire_off_in = credit_details->serial_id; 1250 if (0 != TALER_amount_cmp (&kii->credit, 1251 &credit_details->amount)) 1252 { 1253 struct TALER_AUDITORDB_KycauthInInconsistency dc = { 1254 .bank_row_id = credit_details->serial_id, 1255 .amount_exchange_expected = kii->credit, 1256 .amount_wired = credit_details->amount, 1257 .account_pub = kii->account_pub, 1258 .timestamp = kii->execution_date.abs_time, 1259 .account = wa->ai->payto_uri, 1260 .diagnostic = (char *) "wire amount does not match" 1261 }; 1262 enum GNUNET_DB_QueryStatus qs; 1263 1264 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1265 "KYCAUTH transfer amount differs\n"); 1266 qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency ( 1267 TALER_ARL_adb, 1268 &dc); 1269 if (qs <= 0) 1270 { 1271 global_qs = qs; 1272 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1273 return false; 1274 } 1275 if (0 < TALER_amount_cmp (&credit_details->amount, 1276 &kii->credit)) 1277 { 1278 struct TALER_Amount delta; 1279 1280 TALER_ARL_amount_subtract (&delta, 1281 &credit_details->amount, 1282 &kii->credit); 1283 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 1284 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 1285 &delta); 1286 } 1287 else 1288 { 1289 struct TALER_Amount delta; 1290 1291 TALER_ARL_amount_subtract (&delta, 1292 &kii->credit, 1293 &credit_details->amount); 1294 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 1295 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 1296 &delta); 1297 } 1298 } 1299 if (0 != GNUNET_memcmp (&kii->account_pub, 1300 &credit_details->details.kycauth.account_pub)) 1301 { 1302 /* No money is missing, but the exchange associated a different key 1303 with the account than the one the sender asked for: the wrong party 1304 can now authenticate as the owner of that bank account. */ 1305 struct TALER_AUDITORDB_KycauthInInconsistency dc = { 1306 .bank_row_id = credit_details->serial_id, 1307 .amount_exchange_expected = kii->credit, 1308 .amount_wired = credit_details->amount, 1309 .account_pub = kii->account_pub, 1310 .timestamp = kii->execution_date.abs_time, 1311 .account = wa->ai->payto_uri, 1312 .diagnostic = (char *) "account public key does not match" 1313 }; 1314 enum GNUNET_DB_QueryStatus qs; 1315 1316 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1317 "KYCAUTH account public key differs\n"); 1318 qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency ( 1319 TALER_ARL_adb, 1320 &dc); 1321 if (qs <= 0) 1322 { 1323 global_qs = qs; 1324 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1325 return false; 1326 } 1327 } 1328 if (! same_account (kii->sender_account_details, 1329 credit_details->debit_account_uri)) 1330 { 1331 /* Again no money is missing: the exchange authorised the wrong bank 1332 account. There is no reserve to close towards it either, so this 1333 does not feed into #total_misattribution_in. */ 1334 struct TALER_AUDITORDB_KycauthInInconsistency dc = { 1335 .bank_row_id = credit_details->serial_id, 1336 .amount_exchange_expected = kii->credit, 1337 .amount_wired = credit_details->amount, 1338 .account_pub = kii->account_pub, 1339 .timestamp = kii->execution_date.abs_time, 1340 .account = credit_details->debit_account_uri, 1341 .diagnostic = (char *) "debited account does not match" 1342 }; 1343 enum GNUNET_DB_QueryStatus qs; 1344 1345 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1346 "Origin bank account of KYCAUTH transfer differs\n"); 1347 qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency ( 1348 TALER_ARL_adb, 1349 &dc); 1350 if (qs <= 0) 1351 { 1352 global_qs = qs; 1353 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1354 return false; 1355 } 1356 } 1357 if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date, 1358 !=, 1359 kii->execution_date)) 1360 { 1361 struct TALER_AUDITORDB_RowMinorInconsistencies rmi = { 1362 .problem_row = kii->rowid, 1363 .diagnostic = (char *) "execution date mismatch", 1364 .row_table = (char *) "kycauths_in" 1365 }; 1366 enum GNUNET_DB_QueryStatus qs; 1367 1368 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1369 "Execution date of KYCAUTH transfer differs\n"); 1370 qs = TALER_AUDITORDB_insert_row_minor_inconsistencies ( 1371 TALER_ARL_adb, 1372 &rmi); 1373 if (qs < 0) 1374 { 1375 global_qs = qs; 1376 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1377 return false; 1378 } 1379 } 1380 GNUNET_assert (GNUNET_OK == 1381 free_kii (NULL, 1382 &key, 1383 kii)); 1384 return true; 1385 } 1386 1387 1388 /** 1389 * Analyze credit transaction @a details into @a wa. 1390 * 1391 * @param[in,out] wa account that received the transfer 1392 * @param credit_details transfer details 1393 * @return true on success, false to stop loop at this point 1394 */ 1395 static bool 1396 analyze_credit ( 1397 struct WireAccount *wa, 1398 const struct TALER_BANK_CreditDetails *credit_details) 1399 { 1400 struct ReserveInInfo *rii; 1401 struct GNUNET_HashCode key; 1402 1403 switch (credit_details->type) 1404 { 1405 case TALER_BANK_CT_RESERVE: 1406 break; 1407 case TALER_BANK_CT_KYCAUTH: 1408 return analyze_kycauth_credit (wa, 1409 credit_details); 1410 case TALER_BANK_CT_WAD: 1411 GNUNET_break (0); /* FIXME: Wad not yet supported */ 1412 return false; 1413 } 1414 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1415 "Analyzing bank CREDIT #%llu at %s of %s with Reserve-pub %s\n", 1416 (unsigned long long) credit_details->serial_id, 1417 GNUNET_TIME_timestamp2s (credit_details->execution_date), 1418 TALER_amount2s (&credit_details->amount), 1419 TALER_B2S (&credit_details->details.reserve.reserve_pub)); 1420 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees), 1421 &TALER_ARL_USE_AB (total_wire_credit_fees), 1422 &credit_details->credit_fee); 1423 GNUNET_CRYPTO_hash (&credit_details->serial_id, 1424 sizeof (credit_details->serial_id), 1425 &key); 1426 rii = GNUNET_CONTAINER_multihashmap_get (in_map, 1427 &key); 1428 if (NULL == rii) 1429 { 1430 struct TALER_AUDITORDB_ReserveInInconsistency dc = { 1431 .bank_row_id = credit_details->serial_id, 1432 .amount_exchange_expected = zero, 1433 .amount_wired = credit_details->amount, 1434 .reserve_pub = credit_details->details.reserve.reserve_pub, 1435 .timestamp = credit_details->execution_date.abs_time, 1436 .account = credit_details->debit_account_uri, 1437 .diagnostic = (char *) "unknown to exchange" 1438 }; 1439 enum GNUNET_DB_QueryStatus qs; 1440 1441 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1442 "Failed to find wire transfer at `%s' in exchange database.\n", 1443 GNUNET_TIME_timestamp2s (credit_details->execution_date)); 1444 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (TALER_ARL_adb, 1445 &dc); 1446 if (qs <= 0) 1447 { 1448 global_qs = qs; 1449 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1450 return false; 1451 } 1452 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 1453 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 1454 &credit_details->amount); 1455 /* We have fully processed this credit: the finding is on file, and 1456 reserve_in_cb() retracts it if the exchange books the transfer 1457 later. Looking at it again would only duplicate the finding. */ 1458 wa->wire_off_in = credit_details->serial_id; 1459 return true; 1460 } 1461 1462 /* Update offset */ 1463 wa->wire_off_in = credit_details->serial_id; 1464 /* compare records with expected data */ 1465 if (0 != GNUNET_memcmp (&credit_details->details.reserve.reserve_pub, 1466 &rii->credit_details.details.reserve.reserve_pub)) 1467 { 1468 struct TALER_AUDITORDB_ReserveInInconsistency riiDb = { 1469 .bank_row_id = credit_details->serial_id, 1470 .amount_exchange_expected = rii->credit_details.amount, 1471 .amount_wired = zero, 1472 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 1473 .timestamp = rii->credit_details.execution_date.abs_time, 1474 .account = wa->ai->payto_uri, 1475 .diagnostic = (char *) "wire subject does not match" 1476 }; 1477 enum GNUNET_DB_QueryStatus qs; 1478 1479 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1480 "Reserve public key differs\n"); 1481 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency ( 1482 TALER_ARL_adb, 1483 &riiDb); 1484 if (qs <= 0) 1485 { 1486 global_qs = qs; 1487 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1488 return false; 1489 } 1490 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 1491 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 1492 &rii->credit_details.amount); 1493 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 1494 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 1495 &credit_details->amount); 1496 GNUNET_assert (GNUNET_OK == 1497 free_rii (NULL, 1498 &key, 1499 rii)); 1500 return true; 1501 } 1502 if (0 != TALER_amount_cmp (&rii->credit_details.amount, 1503 &credit_details->amount)) 1504 { 1505 struct TALER_AUDITORDB_ReserveInInconsistency riiDb = { 1506 .diagnostic = (char *) "wire amount does not match", 1507 .account = wa->ai->payto_uri, 1508 .bank_row_id = credit_details->serial_id, 1509 .amount_exchange_expected = rii->credit_details.amount, 1510 .amount_wired = credit_details->amount, 1511 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 1512 .timestamp = rii->credit_details.execution_date.abs_time 1513 }; 1514 enum GNUNET_DB_QueryStatus qs; 1515 1516 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1517 "Wire transfer amount differs\n"); 1518 qs = TALER_AUDITORDB_insert_reserve_in_inconsistency ( 1519 TALER_ARL_adb, 1520 &riiDb); 1521 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); 1522 if (qs < 0) 1523 { 1524 global_qs = qs; 1525 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1526 return false; 1527 } 1528 if (0 < TALER_amount_cmp (&credit_details->amount, 1529 &rii->credit_details.amount)) 1530 { 1531 /* details->amount > rii->details.amount: wire transfer was larger than it should have been */ 1532 struct TALER_Amount delta; 1533 1534 TALER_ARL_amount_subtract (&delta, 1535 &credit_details->amount, 1536 &rii->credit_details.amount); 1537 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus), 1538 &TALER_ARL_USE_AB (total_bad_amount_in_plus), 1539 &delta); 1540 } 1541 else 1542 { 1543 /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */ 1544 struct TALER_Amount delta; 1545 1546 TALER_ARL_amount_subtract (&delta, 1547 &rii->credit_details.amount, 1548 &credit_details->amount); 1549 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus), 1550 &TALER_ARL_USE_AB (total_bad_amount_in_minus), 1551 &delta); 1552 } 1553 } 1554 1555 { 1556 struct TALER_NormalizedPayto np; 1557 struct TALER_NormalizedPayto np2; 1558 1559 np = TALER_payto_normalize (credit_details->debit_account_uri); 1560 np2 = TALER_payto_normalize (rii->credit_details.debit_account_uri); 1561 if (0 != TALER_normalized_payto_cmp (np, 1562 np2)) 1563 { 1564 struct TALER_AUDITORDB_MisattributionInInconsistency mii = { 1565 .reserve_pub = rii->credit_details.details.reserve.reserve_pub, 1566 .amount = rii->credit_details.amount, 1567 .bank_row = credit_details->serial_id 1568 }; 1569 enum GNUNET_DB_QueryStatus qs; 1570 1571 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1572 "Origin bank account differs\n"); 1573 qs = TALER_AUDITORDB_insert_misattribution_in_inconsistency ( 1574 TALER_ARL_adb, 1575 &mii); 1576 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs); 1577 if (qs < 0) 1578 { 1579 global_qs = qs; 1580 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1581 GNUNET_free (np.normalized_payto); 1582 GNUNET_free (np2.normalized_payto); 1583 return false; 1584 } 1585 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_misattribution_in), 1586 &TALER_ARL_USE_AB (total_misattribution_in), 1587 &rii->credit_details.amount); 1588 } 1589 GNUNET_free (np.normalized_payto); 1590 GNUNET_free (np2.normalized_payto); 1591 } 1592 if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date, 1593 !=, 1594 rii->credit_details.execution_date)) 1595 { 1596 struct TALER_AUDITORDB_RowMinorInconsistencies rmi = { 1597 .problem_row = rii->rowid, 1598 .diagnostic = (char *) "execution date mismatch", 1599 .row_table = (char *) "reserves_in" 1600 }; 1601 enum GNUNET_DB_QueryStatus qs; 1602 1603 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1604 "Execution date differs\n"); 1605 qs = TALER_AUDITORDB_insert_row_minor_inconsistencies ( 1606 TALER_ARL_adb, 1607 &rmi); 1608 1609 if (qs < 0) 1610 { 1611 global_qs = qs; 1612 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1613 return false; 1614 } 1615 } 1616 GNUNET_assert (GNUNET_OK == 1617 free_rii (NULL, 1618 &key, 1619 rii)); 1620 return true; 1621 } 1622 1623 1624 /** 1625 * This function is called for all transactions that 1626 * are credited to the exchange's account (incoming 1627 * transactions). 1628 * 1629 * @param cls `struct WireAccount` we are processing 1630 * @param chr HTTP response returned by the bank 1631 */ 1632 static void 1633 history_credit_cb (void *cls, 1634 const struct TALER_BANK_CreditHistoryResponse *chr) 1635 { 1636 struct WireAccount *wa = cls; 1637 1638 wa->chh = NULL; 1639 switch (chr->http_status) 1640 { 1641 case MHD_HTTP_OK: 1642 for (unsigned int i = 0; i < chr->details.ok.details_length; i++) 1643 { 1644 const struct TALER_BANK_CreditDetails *cd 1645 = &chr->details.ok.details[i]; 1646 1647 if (! analyze_credit (wa, 1648 cd)) 1649 { 1650 switch (global_qs) 1651 { 1652 case GNUNET_DB_STATUS_SOFT_ERROR: 1653 rollback_and_reset (); 1654 return; 1655 case GNUNET_DB_STATUS_HARD_ERROR: 1656 GNUNET_SCHEDULER_shutdown (); 1657 return; 1658 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1659 /* perfectly fine */ 1660 break; 1661 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1662 /* perfectly fine */ 1663 break; 1664 } 1665 break; 1666 } 1667 } 1668 conclude_account (wa); 1669 return; 1670 case MHD_HTTP_NO_CONTENT: 1671 conclude_account (wa); 1672 return; 1673 case MHD_HTTP_NOT_FOUND: 1674 if (ignore_account_404) 1675 { 1676 conclude_account (wa); 1677 return; 1678 } 1679 break; 1680 default: 1681 break; 1682 } 1683 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1684 "Error fetching credit history of account %s: %u (%s)\n", 1685 wa->ai->section_name, 1686 chr->http_status, 1687 TALER_ErrorCode_get_hint (chr->ec)); 1688 commit (GNUNET_DB_STATUS_HARD_ERROR); 1689 global_ret = EXIT_FAILURE; 1690 GNUNET_SCHEDULER_shutdown (); 1691 } 1692 1693 1694 /* ***************************** Setup logic ************************ */ 1695 1696 1697 /** 1698 * Start processing the next wire account. 1699 * Shuts down if we are done. 1700 * 1701 * @param cls `struct WireAccount` with a wire account list to process 1702 */ 1703 static void 1704 process_credits (void *cls) 1705 { 1706 struct WireAccount *wa = cls; 1707 enum GNUNET_DB_QueryStatus qs; 1708 1709 /* skip accounts where CREDIT is not enabled */ 1710 while ( (NULL != wa) && 1711 (GNUNET_NO == wa->ai->credit_enabled) ) 1712 wa = wa->next; 1713 if (NULL == wa) 1714 { 1715 /* done with all accounts, conclude check */ 1716 conclude_credit_history (); 1717 return; 1718 } 1719 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1720 "Analyzing exchange's wire IN table for account `%s'\n", 1721 wa->ai->section_name); 1722 qs = TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account ( 1723 TALER_ARL_edb, 1724 wa->ai->section_name, 1725 wa->last_reserve_in_serial_id, 1726 &reserve_in_cb, 1727 wa); 1728 if (0 > qs) 1729 { 1730 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1731 global_ret = EXIT_FAILURE; 1732 GNUNET_SCHEDULER_shutdown (); 1733 return; 1734 } 1735 1736 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1737 "Analyzing exchange's KYCAUTH IN table for account `%s'\n", 1738 wa->ai->section_name); 1739 qs = TALER_EXCHANGEDB_iterate_kycauth_in_above_serial_id_by_account ( 1740 TALER_ARL_edb, 1741 wa->ai->section_name, 1742 wa->last_kycauth_in_serial_id, 1743 &kycauth_in_cb, 1744 wa); 1745 if (0 > qs) 1746 { 1747 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1748 global_ret = EXIT_FAILURE; 1749 GNUNET_SCHEDULER_shutdown (); 1750 return; 1751 } 1752 1753 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1754 "Starting bank CREDIT history of account `%s'\n", 1755 wa->ai->section_name); 1756 GNUNET_assert (NULL == wa->chh); 1757 wa->chh = TALER_BANK_credit_history (ctx, 1758 wa->ai->auth, 1759 wa->wire_off_in, 1760 MAX_PER_TRANSACTION, 1761 GNUNET_TIME_UNIT_ZERO, 1762 &history_credit_cb, 1763 wa); 1764 if (NULL == wa->chh) 1765 { 1766 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1767 "Failed to obtain bank transaction history\n"); 1768 commit (GNUNET_DB_STATUS_HARD_ERROR); 1769 global_ret = EXIT_FAILURE; 1770 GNUNET_SCHEDULER_shutdown (); 1771 return; 1772 } 1773 } 1774 1775 1776 /** 1777 * Begin audit of CREDITs to the exchange. 1778 */ 1779 static void 1780 begin_credit_audit (void) 1781 { 1782 GNUNET_assert (NULL == in_map); 1783 in_map = GNUNET_CONTAINER_multihashmap_create (1024, 1784 GNUNET_YES); 1785 GNUNET_assert (NULL == kycauth_map); 1786 kycauth_map = GNUNET_CONTAINER_multihashmap_create (1024, 1787 GNUNET_YES); 1788 /* now go over all bank accounts and check delta with in_map */ 1789 process_credits (wa_head); 1790 } 1791 1792 1793 static enum GNUNET_DB_QueryStatus 1794 begin_transaction (void) 1795 { 1796 enum GNUNET_DB_QueryStatus qs; 1797 1798 if (GNUNET_SYSERR == 1799 TALER_EXCHANGEDB_preflight (TALER_ARL_edb)) 1800 { 1801 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1802 "Failed to initialize exchange database connection.\n"); 1803 return GNUNET_DB_STATUS_HARD_ERROR; 1804 } 1805 if (GNUNET_SYSERR == 1806 TALER_AUDITORDB_preflight (TALER_ARL_adb)) 1807 { 1808 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1809 "Failed to initialize auditor database session.\n"); 1810 return GNUNET_DB_STATUS_HARD_ERROR; 1811 } 1812 global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1813 if (GNUNET_OK != 1814 TALER_AUDITORDB_start (TALER_ARL_adb, 1815 "auditor-wire-credit")) 1816 { 1817 GNUNET_break (0); 1818 return GNUNET_DB_STATUS_HARD_ERROR; 1819 } 1820 if (GNUNET_OK != 1821 TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb, 1822 "wire credit auditor")) 1823 { 1824 GNUNET_break (0); 1825 return GNUNET_DB_STATUS_HARD_ERROR; 1826 } 1827 qs = TALER_AUDITORDB_get_balance ( 1828 TALER_ARL_adb, 1829 TALER_ARL_GET_AB (total_wire_in), 1830 TALER_ARL_GET_AB (total_kycauth_in), 1831 TALER_ARL_GET_AB (total_kycauth_revenue), 1832 TALER_ARL_GET_AB (total_wire_credit_fees), 1833 TALER_ARL_GET_AB (total_bad_amount_in_plus), 1834 TALER_ARL_GET_AB (total_bad_amount_in_minus), 1835 TALER_ARL_GET_AB (total_misattribution_in), 1836 NULL); 1837 switch (qs) 1838 { 1839 case GNUNET_DB_STATUS_HARD_ERROR: 1840 GNUNET_break (0); 1841 return qs; 1842 case GNUNET_DB_STATUS_SOFT_ERROR: 1843 GNUNET_break (0); 1844 return qs; 1845 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1846 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1847 break; 1848 } 1849 for (struct WireAccount *wa = wa_head; 1850 NULL != wa; 1851 wa = wa->next) 1852 { 1853 /* we are called again by rollback_and_reset() after a serialization 1854 failure, so the labels may already have been computed */ 1855 GNUNET_free (wa->label_reserve_in_serial_id); 1856 GNUNET_free (wa->label_kycauth_in_serial_id); 1857 GNUNET_free (wa->label_wire_off_in); 1858 GNUNET_asprintf (&wa->label_reserve_in_serial_id, 1859 "wire-%s-%s", 1860 wa->ai->section_name, 1861 "reserve_in_serial_id"); 1862 GNUNET_asprintf (&wa->label_kycauth_in_serial_id, 1863 "wire-%s-%s", 1864 wa->ai->section_name, 1865 "kycauth_in_serial_id"); 1866 GNUNET_asprintf (&wa->label_wire_off_in, 1867 "wire-%s-%s", 1868 wa->ai->section_name, 1869 "wire_off_in"); 1870 qs = TALER_AUDITORDB_get_auditor_progress ( 1871 TALER_ARL_adb, 1872 wa->label_reserve_in_serial_id, 1873 &wa->last_reserve_in_serial_id, 1874 wa->label_kycauth_in_serial_id, 1875 &wa->last_kycauth_in_serial_id, 1876 wa->label_wire_off_in, 1877 &wa->wire_off_in, 1878 NULL); 1879 if (0 > qs) 1880 { 1881 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1882 return qs; 1883 } 1884 wa->start_reserve_in_serial_id = wa->last_reserve_in_serial_id; 1885 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1886 "Starting from reserve_in at %s=%llu for account `%s'\n", 1887 wa->label_reserve_in_serial_id, 1888 (unsigned long long) wa->start_reserve_in_serial_id, 1889 wa->ai->section_name); 1890 } 1891 1892 begin_credit_audit (); 1893 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1894 } 1895 1896 1897 /** 1898 * Function called with information about a wire account. Adds the 1899 * account to our list for processing (if it is enabled and we can 1900 * load the plugin). 1901 * 1902 * @param cls closure, NULL 1903 * @param ai account information 1904 */ 1905 static void 1906 process_account_cb (void *cls, 1907 const struct TALER_EXCHANGEDB_AccountInfo *ai) 1908 { 1909 struct WireAccount *wa; 1910 1911 (void) cls; 1912 if ((! ai->debit_enabled) && 1913 (! ai->credit_enabled)) 1914 return; /* not an active exchange account */ 1915 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1916 "Found exchange account `%s'\n", 1917 ai->section_name); 1918 wa = GNUNET_new (struct WireAccount); 1919 wa->ai = ai; 1920 GNUNET_CONTAINER_DLL_insert (wa_head, 1921 wa_tail, 1922 wa); 1923 } 1924 1925 1926 /** 1927 * Function called on events received from Postgres. 1928 * 1929 * @param cls closure, NULL 1930 * @param extra additional event data provided 1931 * @param extra_size number of bytes in @a extra 1932 */ 1933 static void 1934 db_notify (void *cls, 1935 const void *extra, 1936 size_t extra_size) 1937 { 1938 (void) cls; 1939 (void) extra; 1940 (void) extra_size; 1941 1942 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1943 "Received notification to wake wire helper\n"); 1944 /* If there are accounts we are still processing, abort 1945 the HTTP requests so we can start afresh. */ 1946 for (struct WireAccount *wa = wa_head; 1947 NULL != wa; 1948 wa = wa->next) 1949 { 1950 if (NULL != wa->chh) 1951 { 1952 TALER_BANK_credit_history_cancel (wa->chh); 1953 wa->chh = NULL; 1954 } 1955 conclude_account (wa); 1956 } 1957 1958 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 1959 begin_transaction ()) 1960 { 1961 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1962 "Audit failed\n"); 1963 GNUNET_break (0); 1964 global_ret = EXIT_FAILURE; 1965 GNUNET_SCHEDULER_shutdown (); 1966 } 1967 } 1968 1969 1970 /** 1971 * Main function that will be run. 1972 * 1973 * @param cls closure 1974 * @param args remaining command-line arguments 1975 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 1976 * @param c configuration 1977 */ 1978 static void 1979 run (void *cls, 1980 char *const *args, 1981 const char *cfgfile, 1982 const struct GNUNET_CONFIGURATION_Handle *c) 1983 { 1984 (void) cls; 1985 (void) args; 1986 (void) cfgfile; 1987 cfg = c; 1988 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1989 "Launching wire-credit auditor\n"); 1990 if (GNUNET_OK != 1991 TALER_ARL_init (c)) 1992 { 1993 global_ret = EXIT_FAILURE; 1994 return; 1995 } 1996 if (GNUNET_OK != 1997 TALER_config_get_amount (TALER_ARL_cfg, 1998 "auditor", 1999 "TINY_AMOUNT", 2000 &tiny_amount)) 2001 { 2002 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2003 "auditor", 2004 "TINY_AMOUNT"); 2005 global_ret = EXIT_NOTCONFIGURED; 2006 return; 2007 } 2008 GNUNET_assert (GNUNET_OK == 2009 TALER_amount_set_zero (TALER_ARL_currency, 2010 &zero)); 2011 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 2012 NULL); 2013 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 2014 &rc); 2015 rc = GNUNET_CURL_gnunet_rc_create (ctx); 2016 if (NULL == ctx) 2017 { 2018 GNUNET_break (0); 2019 global_ret = EXIT_FAILURE; 2020 return; 2021 } 2022 if (GNUNET_OK != 2023 TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg, 2024 TALER_EXCHANGEDB_ALO_CREDIT 2025 | TALER_EXCHANGEDB_ALO_AUTHDATA)) 2026 { 2027 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2028 "No bank accounts configured\n"); 2029 global_ret = EXIT_NOTCONFIGURED; 2030 GNUNET_SCHEDULER_shutdown (); 2031 return; 2032 } 2033 TALER_EXCHANGEDB_find_accounts (&process_account_cb, 2034 NULL); 2035 2036 if (0 == test_mode) 2037 { 2038 struct GNUNET_DB_EventHeaderP es = { 2039 .size = htons (sizeof (es)), 2040 .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE) 2041 }; 2042 2043 eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb, 2044 &es, 2045 GNUNET_TIME_UNIT_FOREVER_REL, 2046 &db_notify, 2047 NULL); 2048 GNUNET_assert (NULL != eh); 2049 } 2050 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 2051 begin_transaction ()) 2052 { 2053 GNUNET_break (0); 2054 global_ret = EXIT_FAILURE; 2055 GNUNET_SCHEDULER_shutdown (); 2056 return; 2057 } 2058 } 2059 2060 2061 /** 2062 * The main function of the wire auditing tool. Checks that 2063 * the exchange's records of wire transfers match that of 2064 * the wire gateway. 2065 * 2066 * @param argc number of arguments from the command line 2067 * @param argv command line arguments 2068 * @return 0 ok, 1 on error 2069 */ 2070 int 2071 main (int argc, 2072 char *const *argv) 2073 { 2074 const struct GNUNET_GETOPT_CommandLineOption options[] = { 2075 GNUNET_GETOPT_option_flag ('i', 2076 "internal", 2077 "perform checks only applicable for exchange-internal audits", 2078 &internal_checks), 2079 GNUNET_GETOPT_option_flag ('I', 2080 "ignore-not-found", 2081 "continue, even if the bank account of the exchange was not found", 2082 &ignore_account_404), 2083 GNUNET_GETOPT_option_flag ('t', 2084 "test", 2085 "run in test mode and exit when idle", 2086 &test_mode), 2087 GNUNET_GETOPT_option_timetravel ('T', 2088 "timetravel"), 2089 GNUNET_GETOPT_OPTION_END 2090 }; 2091 enum GNUNET_GenericReturnValue ret; 2092 2093 ret = GNUNET_PROGRAM_run ( 2094 TALER_AUDITOR_project_data (), 2095 argc, 2096 argv, 2097 "taler-helper-auditor-wire-credit", 2098 gettext_noop ( 2099 "Audit exchange database for consistency with the bank's wire transfers"), 2100 options, 2101 &run, 2102 NULL); 2103 if (GNUNET_SYSERR == ret) 2104 return EXIT_INVALIDARGUMENT; 2105 if (GNUNET_NO == ret) 2106 return EXIT_SUCCESS; 2107 return global_ret; 2108 } 2109 2110 2111 /* end of taler-helper-auditor-wire-credit.c */