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