taler-helper-auditor-wire-debit.c (58754B)
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-debit.c 18 * @brief audits that wire outgoing transfers match those from an exchange 19 * database. 20 * @author Christian Grothoff 21 * @author Özgür Kesim 22 * 23 * - We check that the outgoing wire transfers match those 24 * given in the 'wire_out' and 'reserve_closures' tables; 25 * any outgoing transfer MUST have a prior justification, 26 * so if one is missing we flag it (and never remove it). 27 * - We check that all wire transfers that should 28 * have been made, were actually made. If any were not made, 29 * we flag those, but may remove those flags if we later 30 * find that the wire transfers were made (wire transfers 31 * could be delayed due to AML/KYC or core-banking issues). 32 */ 33 #include "platform.h" 34 #include <gnunet/gnunet_util_lib.h> 35 #include <gnunet/gnunet_curl_lib.h> 36 #include "auditordb_lib.h" 37 /* WIRE_TRANSFER_OUT's callback typedef also lives in exchangedb_lib.h, so its 38 closure override must be established before that header is included. */ 39 struct WireAccount; 40 #define TALER_EXCHANGEDB_WIRE_TRANSFER_OUT_RESULT_CLOSURE struct WireAccount 41 #include "exchangedb_lib.h" 42 #include "taler/taler_json_lib.h" 43 #include "taler/taler_bank_service.h" 44 #include "taler/taler_signatures.h" 45 #include "report-lib.h" 46 #include "taler/taler_dbevents.h" 47 #include "auditor-database/delete_auditor_closure_lag.h" 48 #include "auditor-database/delete_wire_out_inconsistency_if_matching.h" 49 #include "auditor-database/event_listen.h" 50 #include "auditor-database/get_auditor_progress.h" 51 #include "auditor-database/get_balance.h" 52 #include "auditor-database/insert_auditor_closure_lags.h" 53 #include "auditor-database/insert_auditor_progress.h" 54 #include "auditor-database/insert_balance.h" 55 #include "auditor-database/insert_row_inconsistency.h" 56 #include "auditor-database/insert_row_minor_inconsistencies.h" 57 #include "auditor-database/insert_wire_format_inconsistency.h" 58 #include "auditor-database/insert_wire_out_inconsistency.h" 59 #include "auditor-database/preflight.h" 60 #include "auditor-database/start.h" 61 #include "exchange-database/get_profit_drain.h" 62 #include "exchange-database/preflight.h" 63 #include "exchange-database/rollback.h" 64 #include "exchange-database/iterate_reserve_closed_above_serial_id.h" 65 #include "exchange-database/iterate_wire_outs_above_serial_id_by_account.h" 66 #include "exchange-database/start_read_only.h" 67 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 ( \ 81 GNUNET_TIME_UNIT_MINUTES, \ 82 15) 83 84 85 /** 86 * How long do we try to long-poll for bank wire transfers? 87 */ 88 #define MAX_LONGPOLL_DELAY GNUNET_TIME_relative_multiply ( \ 89 GNUNET_TIME_UNIT_HOURS, \ 90 1) 91 92 93 /** 94 * How long do we wait between polling for bank wire transfers at the minimum? 95 */ 96 #define MIN_LONGPOLL_DELAY GNUNET_TIME_relative_multiply ( \ 97 GNUNET_TIME_UNIT_MINUTES, \ 98 5) 99 100 101 /** 102 * Run in test mode. Exit when idle instead of 103 * going to sleep and waiting for more work. 104 */ 105 static int test_mode; 106 107 108 /** 109 * Information we keep for each supported account. 110 */ 111 struct WireAccount 112 { 113 /** 114 * Accounts are kept in a DLL. 115 */ 116 struct WireAccount *next; 117 118 /** 119 * Plugins are kept in a DLL. 120 */ 121 struct WireAccount *prev; 122 123 /** 124 * Account details. 125 */ 126 const struct TALER_EXCHANGEDB_AccountInfo *ai; 127 128 /** 129 * Active wire request for the transaction history. 130 */ 131 struct TALER_BANK_DebitHistoryHandle *dhh; 132 133 /** 134 * Task to trigger @e dhh long-polling. 135 */ 136 struct GNUNET_SCHEDULER_Task *dhh_task; 137 138 /** 139 * Time when we expect the current @e dhh long-poll 140 * to finish and we thus could begin another one. 141 */ 142 struct GNUNET_TIME_Absolute dhh_next; 143 144 /** 145 * Progress point for this account. 146 */ 147 uint64_t last_wire_out_serial_id; 148 149 /** 150 * Initial progress point for this account. 151 */ 152 uint64_t start_wire_out_serial_id; 153 154 /** 155 * Where we are in the outbound transaction history. 156 */ 157 uint64_t wire_off_out; 158 159 /** 160 * Label under which we store our pp's reserve_in_serial_id. 161 */ 162 char *label_wire_out_serial_id; 163 164 /** 165 * Label under which we store our wire_off_out. 166 */ 167 char *label_wire_off_out; 168 }; 169 170 171 /** 172 * Information we track for a reserve being closed. 173 */ 174 struct ReserveClosure 175 { 176 /** 177 * Row in the reserves_closed table for this action. 178 */ 179 uint64_t rowid; 180 181 /** 182 * When was the reserve closed? 183 */ 184 struct GNUNET_TIME_Timestamp execution_date; 185 186 /** 187 * Amount transferred (amount remaining minus fee). 188 */ 189 struct TALER_Amount amount; 190 191 /** 192 * Target account where the money was sent. 193 */ 194 struct TALER_FullPayto receiver_account; 195 196 /** 197 * Wire transfer subject used. 198 */ 199 struct TALER_WireTransferIdentifierRawP wtid; 200 }; 201 202 203 /** 204 * Map from H(wtid,receiver_account) to `struct ReserveClosure` entries. 205 */ 206 static struct GNUNET_CONTAINER_MultiHashMap *reserve_closures; 207 208 /** 209 * How often do we retry the transaction after a serialization 210 * failure before we give up? 211 */ 212 #define MAX_RETRIES 3 213 214 /** 215 * Return value from main(). 216 */ 217 static int global_ret; 218 219 /** 220 * Number of serialization failures we have retried since the last 221 * successfully committed transaction. 222 */ 223 static unsigned int retries; 224 225 /** 226 * State of the current database transaction with 227 * the auditor DB. 228 */ 229 static enum GNUNET_DB_QueryStatus global_qs; 230 231 /** 232 * Map with information about outgoing wire transfers. 233 * Maps hashes of the wire subjects (in binary encoding) 234 * to `struct ReserveOutInfo`s. 235 */ 236 static struct GNUNET_CONTAINER_MultiHashMap *out_map; 237 238 /** 239 * Head of list of wire accounts we still need to look at. 240 */ 241 static struct WireAccount *wa_head; 242 243 /** 244 * Tail of list of wire accounts we still need to look at. 245 */ 246 static struct WireAccount *wa_tail; 247 248 /** 249 * Last reserve_out / wire_out serial IDs seen. 250 */ 251 static TALER_ARL_DEF_PP (wire_reserve_close_id); 252 253 /** 254 * Total amount that was transferred too much from the exchange. 255 */ 256 static TALER_ARL_DEF_AB (total_bad_amount_out_plus); 257 258 /** 259 * Total amount that was transferred too little from the exchange. 260 */ 261 static TALER_ARL_DEF_AB (total_bad_amount_out_minus); 262 263 /** 264 * Total amount of reserve closures which the exchange did not transfer in time. 265 */ 266 static TALER_ARL_DEF_AB (total_closure_amount_lag); 267 268 /** 269 * Total amount affected by duplicate wire transfer 270 * subjects. 271 */ 272 static TALER_ARL_DEF_AB (wire_debit_duplicate_transfer_subject_total); 273 274 /** 275 * Total amount debited to exchange accounts. 276 */ 277 static TALER_ARL_DEF_AB (total_wire_out); 278 279 /** 280 * Total fees the bank charged the exchange for outgoing wire 281 * transfers, on top of the amounts transferred. 282 */ 283 static TALER_ARL_DEF_AB (total_wire_debit_fees); 284 285 /** 286 * Total amount of profits drained. 287 */ 288 static TALER_ARL_DEF_AB (total_drained); 289 290 /** 291 * Amount of zero in our currency. 292 */ 293 static struct TALER_Amount zero; 294 295 /** 296 * Handle to the context for interacting with the bank. 297 */ 298 static struct GNUNET_CURL_Context *ctx; 299 300 /** 301 * Scheduler context for running the @e ctx. 302 */ 303 static struct GNUNET_CURL_RescheduleContext *rctx; 304 305 /** 306 * Should we run checks that only work for exchange-internal audits? 307 */ 308 static int internal_checks; 309 310 /** 311 * Should we ignore if the bank does not know our bank 312 * account? 313 */ 314 static int ignore_account_404; 315 316 /** 317 * Database event handler to wake us up again. 318 */ 319 static struct GNUNET_DB_EventHandler *eh; 320 321 /** 322 * The auditors's configuration. 323 */ 324 static const struct GNUNET_CONFIGURATION_Handle *cfg; 325 326 327 /** 328 * Entry in map with wire information we expect to obtain from the 329 * #TALER_ARL_edb later. 330 */ 331 struct WireTransferOutInfo 332 { 333 334 /** 335 * Hash of the wire transfer subject. 336 */ 337 struct GNUNET_HashCode subject_hash; 338 339 /** 340 * Expected details about the wire transfer. 341 */ 342 struct TALER_BANK_DebitDetails details; 343 344 }; 345 346 347 /** 348 * Free entry in #out_map. 349 * 350 * @param cls NULL 351 * @param key unused key 352 * @param value the `struct WireTransferOutInfo` to free 353 * @return #GNUNET_OK 354 */ 355 static enum GNUNET_GenericReturnValue 356 free_roi (void *cls, 357 const struct GNUNET_HashCode *key, 358 void *value) 359 { 360 struct WireTransferOutInfo *roi = value; 361 362 (void) cls; 363 GNUNET_assert (GNUNET_YES == 364 GNUNET_CONTAINER_multihashmap_remove (out_map, 365 key, 366 roi)); 367 GNUNET_free (roi); 368 return GNUNET_OK; 369 } 370 371 372 /** 373 * Free entry in #reserve_closures. 374 * 375 * @param cls NULL 376 * @param key unused key 377 * @param value the `struct ReserveClosure` to free 378 * @return #GNUNET_OK 379 */ 380 static enum GNUNET_GenericReturnValue 381 free_rc (void *cls, 382 const struct GNUNET_HashCode *key, 383 void *value) 384 { 385 struct ReserveClosure *rc = value; 386 387 (void) cls; 388 GNUNET_assert (GNUNET_YES == 389 GNUNET_CONTAINER_multihashmap_remove (reserve_closures, 390 key, 391 rc)); 392 GNUNET_free (rc->receiver_account.full_payto); 393 GNUNET_free (rc); 394 return GNUNET_OK; 395 } 396 397 398 /** 399 * Task run on shutdown. 400 * 401 * @param cls NULL 402 */ 403 static void 404 do_shutdown (void *cls) 405 { 406 struct WireAccount *wa; 407 408 (void) cls; 409 if (NULL != eh) 410 { 411 TALER_AUDITORDB_event_listen_cancel (eh); 412 eh = NULL; 413 } 414 TALER_ARL_done (); 415 if (NULL != reserve_closures) 416 { 417 GNUNET_CONTAINER_multihashmap_iterate (reserve_closures, 418 &free_rc, 419 NULL); 420 GNUNET_CONTAINER_multihashmap_destroy (reserve_closures); 421 reserve_closures = NULL; 422 } 423 if (NULL != out_map) 424 { 425 GNUNET_CONTAINER_multihashmap_iterate (out_map, 426 &free_roi, 427 NULL); 428 GNUNET_CONTAINER_multihashmap_destroy (out_map); 429 out_map = NULL; 430 } 431 while (NULL != (wa = wa_head)) 432 { 433 if (NULL != wa->dhh_task) 434 { 435 GNUNET_SCHEDULER_cancel (wa->dhh_task); 436 wa->dhh_task = NULL; 437 } 438 if (NULL != wa->dhh) 439 { 440 TALER_BANK_debit_history_cancel (wa->dhh); 441 wa->dhh = NULL; 442 } 443 GNUNET_CONTAINER_DLL_remove (wa_head, 444 wa_tail, 445 wa); 446 GNUNET_free (wa->label_wire_out_serial_id); 447 GNUNET_free (wa->label_wire_off_out); 448 GNUNET_free (wa); 449 } 450 if (NULL != ctx) 451 { 452 GNUNET_CURL_fini (ctx); 453 ctx = NULL; 454 } 455 if (NULL != rctx) 456 { 457 GNUNET_CURL_gnunet_rc_destroy (rctx); 458 rctx = NULL; 459 } 460 TALER_EXCHANGEDB_unload_accounts (); 461 TALER_ARL_cfg = NULL; 462 } 463 464 465 /** 466 * Detect any entries in #reserve_closures that were not yet 467 * observed on the wire transfer side and update the progress 468 * point accordingly. 469 * 470 * @param cls NULL 471 * @param key unused key 472 * @param value the `struct ReserveClosure` to free 473 * @return #GNUNET_OK 474 */ 475 static enum GNUNET_GenericReturnValue 476 check_pending_rc (void *cls, 477 const struct GNUNET_HashCode *key, 478 void *value) 479 { 480 struct ReserveClosure *rc = value; 481 482 (void) cls; 483 (void) key; 484 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 485 "Missing wire transfer for closed reserve with balance %s\n", 486 TALER_amount2s (&rc->amount)); 487 if (! TALER_amount_is_zero (&rc->amount)) 488 { 489 struct TALER_AUDITORDB_ClosureLags cl = { 490 .problem_row_id = rc->rowid, 491 .account = rc->receiver_account, 492 .amount = rc->amount, 493 .deadline = rc->execution_date.abs_time, 494 .wtid = rc->wtid 495 }; 496 enum GNUNET_DB_QueryStatus qs; 497 498 qs = TALER_AUDITORDB_insert_auditor_closure_lags ( 499 TALER_ARL_adb, 500 &cl); 501 if (qs < 0) 502 { 503 global_qs = qs; 504 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 505 return GNUNET_SYSERR; 506 } 507 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_closure_amount_lag), 508 &TALER_ARL_USE_AB (total_closure_amount_lag), 509 &rc->amount); 510 } 511 return GNUNET_OK; 512 } 513 514 515 /** 516 * Compute the key under which a reserve closure for a given 517 * @a receiver_account and @a wtid would be stored. 518 * 519 * @param receiver_account payto://-URI of the account 520 * @param wtid wire transfer identifier used 521 * @param[out] key set to the key 522 */ 523 static void 524 hash_rc (const struct TALER_FullPayto receiver_account, 525 const struct TALER_WireTransferIdentifierRawP *wtid, 526 struct GNUNET_HashCode *key) 527 { 528 struct TALER_NormalizedPayto npto 529 = TALER_payto_normalize (receiver_account); 530 size_t slen = strlen (npto.normalized_payto); 531 char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen]; 532 533 GNUNET_memcpy (buf, 534 wtid, 535 sizeof (*wtid)); 536 GNUNET_memcpy (&buf[sizeof (*wtid)], 537 npto.normalized_payto, 538 slen); 539 GNUNET_CRYPTO_hash (buf, 540 sizeof (buf), 541 key); 542 GNUNET_free (npto.normalized_payto); 543 } 544 545 546 /** 547 * Start the database transactions and begin the audit. 548 * 549 * @return transaction status code 550 */ 551 static enum GNUNET_DB_QueryStatus 552 begin_transaction (void); 553 554 555 /** 556 * Commit the transaction, checkpointing our progress in the auditor DB. 557 * 558 * @param qs transaction status so far 559 */ 560 static void 561 commit (enum GNUNET_DB_QueryStatus qs) 562 { 563 GNUNET_CONTAINER_multihashmap_iterate (reserve_closures, 564 &check_pending_rc, 565 NULL); 566 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 567 "Transaction logic ended with status %d\n", 568 qs); 569 TALER_EXCHANGEDB_rollback (TALER_ARL_edb); 570 qs = TALER_AUDITORDB_insert_balance ( 571 TALER_ARL_adb, 572 TALER_ARL_SET_AB (total_drained), 573 TALER_ARL_SET_AB (total_wire_out), 574 TALER_ARL_SET_AB (total_bad_amount_out_plus), 575 TALER_ARL_SET_AB (total_bad_amount_out_minus), 576 TALER_ARL_SET_AB (total_closure_amount_lag), 577 TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total), 578 TALER_ARL_SET_AB (total_wire_out), 579 TALER_ARL_SET_AB (total_wire_debit_fees), 580 NULL); 581 if (0 > qs) 582 goto handle_db_error; 583 for (struct WireAccount *wa = wa_head; 584 NULL != wa; 585 wa = wa->next) 586 { 587 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 588 "Transaction of account %s ends at %llu/%llu\n", 589 wa->ai->section_name, 590 (unsigned long long) wa->last_wire_out_serial_id, 591 (unsigned long long) wa->wire_off_out); 592 qs = TALER_AUDITORDB_insert_auditor_progress ( 593 TALER_ARL_adb, 594 wa->label_wire_out_serial_id, 595 wa->last_wire_out_serial_id, 596 wa->label_wire_off_out, 597 wa->wire_off_out, 598 NULL); 599 if (0 > qs) 600 goto handle_db_error; 601 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 602 "Transaction ends at %s=%llu for account `%s'\n", 603 wa->label_wire_out_serial_id, 604 (unsigned long long) wa->last_wire_out_serial_id, 605 wa->ai->section_name); 606 } 607 qs = TALER_AUDITORDB_insert_auditor_progress ( 608 TALER_ARL_adb, 609 TALER_ARL_SET_PP (wire_reserve_close_id), 610 NULL); 611 if (0 > qs) 612 goto handle_db_error; 613 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 614 "Concluded audit step at %llu\n", 615 (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id)); 616 qs = TALER_AUDITORDB_commit (TALER_ARL_adb); 617 if (0 > qs) 618 goto handle_db_error; 619 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 620 "Transaction concluded!\n"); 621 retries = 0; 622 if (1 == test_mode) 623 GNUNET_SCHEDULER_shutdown (); 624 return; 625 handle_db_error: 626 TALER_AUDITORDB_rollback (TALER_ARL_adb); 627 if (GNUNET_DB_STATUS_SOFT_ERROR == qs) 628 { 629 if (MAX_RETRIES < ++retries) 630 { 631 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 632 "Failed to commit the transaction after %u retries, terminating\n", 633 (unsigned int) MAX_RETRIES); 634 global_ret = EXIT_FAILURE; 635 GNUNET_SCHEDULER_shutdown (); 636 return; 637 } 638 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 639 "Serialization issue, trying again\n"); 640 /* Any bank request still in flight belongs to the attempt we are 641 abandoning. #process_debits() would leave it alone and start 642 nothing, so the restarted audit would never make progress. */ 643 for (struct WireAccount *wa = wa_head; 644 NULL != wa; 645 wa = wa->next) 646 { 647 if (NULL != wa->dhh) 648 { 649 TALER_BANK_debit_history_cancel (wa->dhh); 650 wa->dhh = NULL; 651 } 652 if (NULL != wa->dhh_task) 653 { 654 GNUNET_SCHEDULER_cancel (wa->dhh_task); 655 wa->dhh_task = NULL; 656 } 657 } 658 if (GNUNET_DB_STATUS_HARD_ERROR != begin_transaction ()) 659 return; /* the retry continues asynchronously from here */ 660 } 661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 662 "Hard database error, terminating\n"); 663 global_ret = EXIT_FAILURE; 664 GNUNET_SCHEDULER_shutdown (); 665 } 666 667 668 /** 669 * Check that @a want is within #TIME_TOLERANCE of @a have. 670 * Otherwise report an inconsistency in row @a rowid of @a table. 671 * 672 * @param table where is the inconsistency (if any) 673 * @param rowid what is the row 674 * @param want what is the expected time 675 * @param have what is the time we got 676 * @return true on success, false to abort 677 */ 678 static bool 679 check_time_difference (const char *table, 680 uint64_t rowid, 681 struct GNUNET_TIME_Timestamp want, 682 struct GNUNET_TIME_Timestamp have) 683 { 684 struct GNUNET_TIME_Relative delta; 685 char *details; 686 687 if (GNUNET_TIME_timestamp_cmp (have, >, want)) 688 delta = GNUNET_TIME_absolute_get_difference (want.abs_time, 689 have.abs_time); 690 else 691 delta = GNUNET_TIME_absolute_get_difference (have.abs_time, 692 want.abs_time); 693 if (GNUNET_TIME_relative_cmp (delta, 694 <=, 695 TIME_TOLERANCE)) 696 return true; 697 698 GNUNET_asprintf (&details, 699 "execution date mismatch (%s)", 700 GNUNET_TIME_relative2s (delta, 701 true)); 702 { 703 struct TALER_AUDITORDB_RowMinorInconsistencies rmi = { 704 .row_table = (char *) table, 705 .problem_row = rowid, 706 .diagnostic = details 707 }; 708 enum GNUNET_DB_QueryStatus qs; 709 710 qs = TALER_AUDITORDB_insert_row_minor_inconsistencies ( 711 TALER_ARL_adb, 712 &rmi); 713 714 if (qs < 0) 715 { 716 global_qs = qs; 717 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 718 GNUNET_free (details); 719 return false; 720 } 721 } 722 GNUNET_free (details); 723 return true; 724 } 725 726 727 /** 728 * Closure for #check_rc_matches 729 */ 730 struct CheckMatchContext 731 { 732 733 /** 734 * Reserve operation looking for a match 735 */ 736 const struct WireTransferOutInfo *roi; 737 738 /** 739 * Set to true if we found a match. 740 */ 741 bool found; 742 }; 743 744 745 /** 746 * Check if any of the reserve closures match the given wire transfer. 747 * 748 * @param[in,out] cls a `struct CheckMatchContext` 749 * @param key key of @a value in #reserve_closures 750 * @param value a `struct ReserveClosure` 751 */ 752 static enum GNUNET_GenericReturnValue 753 check_rc_matches (void *cls, 754 const struct GNUNET_HashCode *key, 755 void *value) 756 { 757 struct CheckMatchContext *cmx = cls; 758 struct ReserveClosure *rc = value; 759 760 if ((0 == GNUNET_memcmp (&cmx->roi->details.wtid, 761 &rc->wtid)) && 762 (0 == TALER_full_payto_cmp (rc->receiver_account, 763 cmx->roi->details.credit_account_uri)) && 764 (0 == TALER_amount_cmp (&rc->amount, 765 &cmx->roi->details.amount))) 766 { 767 if (! check_time_difference ("reserves_closures", 768 rc->rowid, 769 rc->execution_date, 770 cmx->roi->details.execution_date)) 771 { 772 free_rc (NULL, 773 key, 774 rc); 775 return GNUNET_SYSERR; 776 } 777 cmx->found = true; 778 free_rc (NULL, 779 key, 780 rc); 781 return GNUNET_NO; 782 } 783 return GNUNET_OK; 784 } 785 786 787 /** 788 * Maximum required length for make_missing_diag(). 789 */ 790 #define MAX_DIAG_LEN 128 791 792 /** 793 * Make diagnostic string for missing wire transfer. 794 * 795 * @param[out] diag where to write the diagnostic string 796 * @param wtid wire transfer ID to include 797 */ 798 static void 799 make_missing_diag (char diag[MAX_DIAG_LEN], 800 const struct TALER_WireTransferIdentifierRawP *wtid) 801 { 802 char *wtid_s; 803 804 wtid_s = GNUNET_STRINGS_data_to_string_alloc (wtid, 805 sizeof (*wtid)); 806 GNUNET_snprintf (diag, 807 MAX_DIAG_LEN, 808 "expected outgoing wire transfer %s missing", 809 wtid_s); 810 GNUNET_free (wtid_s); 811 } 812 813 814 /** 815 * Check if an existing report on a missing wire 816 * out operation justified the @a roi. If so, 817 * clear the existing report. 818 * 819 * @param roi reserve out operation to check 820 * @return #GNUNET_YES if @a roi was justified by a previous report, 821 * #GNUNET_NO of @a roi was not justified by a previous missing report 822 * #GNUNET_SYSERR on database trouble 823 */ 824 static enum GNUNET_GenericReturnValue 825 check_reported_inconsistency (struct WireTransferOutInfo *roi) 826 { 827 char diag[MAX_DIAG_LEN]; 828 struct TALER_AUDITORDB_WireOutInconsistency woi = { 829 .wire_out_row_id = roi->details.serial_id, 830 .destination_account = roi->details.credit_account_uri, 831 .diagnostic = diag, 832 .expected = roi->details.amount, 833 .claimed = zero, 834 }; 835 enum GNUNET_DB_QueryStatus qs; 836 837 make_missing_diag (diag, 838 &roi->details.wtid); 839 qs = TALER_AUDITORDB_delete_wire_out_inconsistency_if_matching ( 840 TALER_ARL_adb, 841 &woi); 842 if (qs < 0) 843 { 844 global_qs = qs; 845 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 846 return GNUNET_SYSERR; 847 } 848 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 849 { 850 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 851 "Deletion of wire out inconsistency %llu (%s, %s, %s) failed: not reported missing!\n", 852 (unsigned long long) roi->details.serial_id, 853 roi->details.credit_account_uri.full_payto, 854 diag, 855 TALER_amount2s (&roi->details.amount)); 856 return GNUNET_NO; 857 } 858 TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_bad_amount_out_minus), 859 &TALER_ARL_USE_AB (total_bad_amount_out_minus), 860 &roi->details.amount); 861 return GNUNET_YES; 862 } 863 864 865 /** 866 * Check if a profit drain operation justified the @a roi 867 * 868 * @param roi reserve out operation to check 869 * @return #GNUNET_YES if @a roi was justified by a profit drain, 870 * #GNUNET_NO of @a roi was not justified by a proft drain 871 * #GNUNET_SYSERR on database trouble 872 */ 873 static enum GNUNET_GenericReturnValue 874 check_profit_drain (struct WireTransferOutInfo *roi) 875 { 876 enum GNUNET_DB_QueryStatus qs; 877 uint64_t serial; 878 char *account_section; 879 struct TALER_FullPayto payto_uri; 880 struct GNUNET_TIME_Timestamp request_timestamp; 881 struct TALER_Amount amount; 882 struct TALER_MasterSignatureP master_sig; 883 884 qs = TALER_EXCHANGEDB_get_profit_drain ( 885 TALER_ARL_edb, 886 &roi->details.wtid, 887 &serial, 888 &account_section, 889 &payto_uri, 890 &request_timestamp, 891 &amount, 892 &master_sig); 893 switch (qs) 894 { 895 case GNUNET_DB_STATUS_HARD_ERROR: 896 GNUNET_break (0); 897 global_ret = EXIT_FAILURE; 898 GNUNET_SCHEDULER_shutdown (); 899 return GNUNET_SYSERR; 900 case GNUNET_DB_STATUS_SOFT_ERROR: 901 /* should fail on commit later ... */ 902 GNUNET_break (0); 903 return GNUNET_SYSERR; 904 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 905 /* not a profit drain */ 906 return GNUNET_NO; 907 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 908 break; 909 } 910 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 911 "Profit drain of %s to %s found!\n", 912 TALER_amount2s (&amount), 913 payto_uri.full_payto); 914 if (GNUNET_OK != 915 TALER_exchange_offline_profit_drain_verify ( 916 &roi->details.wtid, 917 request_timestamp, 918 &amount, 919 account_section, 920 payto_uri, 921 &TALER_ARL_master_pub, 922 &master_sig)) 923 { 924 struct TALER_AUDITORDB_RowInconsistency ri = { 925 .problem_row_id = roi->details.serial_id, 926 .row_table = (char *) "profit_drains", 927 .diagnostic = (char *) "invalid signature" 928 }; 929 930 GNUNET_break (0); 931 qs = TALER_AUDITORDB_insert_row_inconsistency ( 932 TALER_ARL_adb, 933 &ri); 934 GNUNET_free (payto_uri.full_payto); 935 GNUNET_free (account_section); 936 if (qs < 0) 937 { 938 global_qs = qs; 939 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 940 return GNUNET_SYSERR; 941 } 942 return GNUNET_NO; 943 } 944 GNUNET_free (account_section); 945 946 { 947 if (0 != 948 TALER_full_payto_normalize_and_cmp (payto_uri, 949 roi->details.credit_account_uri)) 950 { 951 struct TALER_AUDITORDB_WireOutInconsistency woi = { 952 .wire_out_row_id = serial, 953 .destination_account = roi->details.credit_account_uri, 954 .diagnostic = (char *) "profit drain wired to invalid account", 955 .expected = roi->details.amount, 956 .claimed = zero, 957 }; 958 959 qs = TALER_AUDITORDB_insert_wire_out_inconsistency ( 960 TALER_ARL_adb, 961 &woi); 962 if (qs < 0) 963 { 964 global_qs = qs; 965 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 966 GNUNET_free (payto_uri.full_payto); 967 return GNUNET_SYSERR; 968 } 969 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus), 970 &TALER_ARL_USE_AB (total_bad_amount_out_plus), 971 &amount); 972 GNUNET_free (payto_uri.full_payto); 973 return GNUNET_YES; /* justified, kind-of */ 974 } 975 } 976 GNUNET_free (payto_uri.full_payto); 977 if (0 != 978 TALER_amount_cmp (&amount, 979 &roi->details.amount)) 980 { 981 struct TALER_AUDITORDB_WireOutInconsistency woi = { 982 .wire_out_row_id = roi->details.serial_id, 983 .destination_account = roi->details.credit_account_uri, 984 .diagnostic = (char *) "incorrect amount drained to correct account", 985 .expected = roi->details.amount, 986 .claimed = amount, 987 }; 988 989 qs = TALER_AUDITORDB_insert_wire_out_inconsistency ( 990 TALER_ARL_adb, 991 &woi); 992 if (qs < 0) 993 { 994 global_qs = qs; 995 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 996 return GNUNET_SYSERR; 997 } 998 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus), 999 &TALER_ARL_USE_AB (total_bad_amount_out_minus), 1000 &roi->details.amount); 1001 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus), 1002 &TALER_ARL_USE_AB (total_bad_amount_out_plus), 1003 &amount); 1004 return GNUNET_YES; /* justified, kind-of */ 1005 } 1006 /* profit drain was correct */ 1007 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained), 1008 &TALER_ARL_USE_AB (total_drained), 1009 &amount); 1010 return GNUNET_YES; 1011 } 1012 1013 1014 /** 1015 * Check whether the given transfer was justified because it was 1016 * actually previously reported as missing. 1017 * 1018 * @param roi the reserve out operation to check 1019 * @return #GNUNET_OK on success, #GNUNET_NO if there was no 1020 * matching lag, #GNUNET_SYSERR on database trouble 1021 */ 1022 static enum GNUNET_GenericReturnValue 1023 check_closure_lag (const struct WireTransferOutInfo *roi) 1024 { 1025 enum GNUNET_DB_QueryStatus qs; 1026 1027 qs = TALER_AUDITORDB_delete_auditor_closure_lag ( 1028 TALER_ARL_adb, 1029 &roi->details.amount, 1030 &roi->details.wtid, 1031 roi->details.credit_account_uri); 1032 if (qs < 0) 1033 { 1034 global_qs = qs; 1035 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1036 return GNUNET_SYSERR; 1037 } 1038 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1039 return GNUNET_NO; 1040 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1041 "Cleared closure lag: found justification\n"); 1042 TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_closure_amount_lag), 1043 &TALER_ARL_USE_AB (total_closure_amount_lag), 1044 &roi->details.amount); 1045 return GNUNET_YES; /* found! */ 1046 } 1047 1048 1049 /** 1050 * Check whether the given transfer was justified by a reserve closure or 1051 * profit drain. If not, complain that we failed to match an entry from 1052 * #out_map. This means a wire transfer was made without proper 1053 * justification. 1054 * 1055 * @param cls a `struct WireAccount` 1056 * @param key unused key 1057 * @param value the `struct WireTransferOutInfo` to report 1058 * @return #GNUNET_OK on success 1059 */ 1060 static enum GNUNET_GenericReturnValue 1061 complain_out_not_found (void *cls, 1062 const struct GNUNET_HashCode *key, 1063 void *value) 1064 { 1065 // struct WireAccount *wa = cls; 1066 struct WireTransferOutInfo *roi = value; 1067 struct GNUNET_HashCode rkey; 1068 struct CheckMatchContext cmx = { 1069 .roi = roi, 1070 .found = false 1071 }; 1072 enum GNUNET_GenericReturnValue ret; 1073 1074 (void) cls; 1075 (void) key; 1076 hash_rc (roi->details.credit_account_uri, 1077 &roi->details.wtid, 1078 &rkey); 1079 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1080 "Checking for reserve closure %s benefiting %s\n", 1081 GNUNET_h2s (&rkey), 1082 roi->details.credit_account_uri.full_payto); 1083 GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures, 1084 &rkey, 1085 &check_rc_matches, 1086 &cmx); 1087 if (cmx.found) 1088 return GNUNET_OK; 1089 ret = check_reported_inconsistency (roi); 1090 if (GNUNET_NO != ret) 1091 return ret; 1092 ret = check_profit_drain (roi); 1093 if (GNUNET_NO != ret) 1094 return ret; 1095 ret = check_closure_lag (roi); 1096 if (GNUNET_NO != ret) 1097 return ret; 1098 1099 { 1100 struct TALER_AUDITORDB_WireOutInconsistency woi = { 1101 .destination_account = roi->details.credit_account_uri, 1102 .diagnostic = (char *) "missing justification for outgoing wire transfer", 1103 .wire_out_row_id = roi->details.serial_id, 1104 .expected = zero, 1105 .claimed = roi->details.amount 1106 }; 1107 enum GNUNET_DB_QueryStatus qs; 1108 1109 qs = TALER_AUDITORDB_insert_wire_out_inconsistency ( 1110 TALER_ARL_adb, 1111 &woi); 1112 if (qs < 0) 1113 { 1114 global_qs = qs; 1115 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1116 return GNUNET_SYSERR; 1117 } 1118 } 1119 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus), 1120 &TALER_ARL_USE_AB (total_bad_amount_out_plus), 1121 &roi->details.amount); 1122 return GNUNET_OK; 1123 } 1124 1125 1126 /** 1127 * Function called with details about outgoing wire transfers 1128 * as claimed by the exchange DB. 1129 * 1130 * @param wa a `struct WireAccount` 1131 * @param rowid unique serial ID in wire_out table 1132 * @param date timestamp of the transfer (roughly) 1133 * @param wtid wire transfer subject 1134 * @param payto_uri wire transfer details of the receiver 1135 * @param amount amount that was wired 1136 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1137 */ 1138 static enum GNUNET_GenericReturnValue 1139 wire_out_cb ( 1140 struct WireAccount *wa, 1141 uint64_t rowid, 1142 struct GNUNET_TIME_Timestamp date, 1143 const struct TALER_WireTransferIdentifierRawP *wtid, 1144 const struct TALER_FullPayto payto_uri, 1145 const struct TALER_Amount *amount) 1146 { 1147 struct GNUNET_HashCode key; 1148 struct WireTransferOutInfo *roi; 1149 1150 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1151 "Exchange wire OUT #%llu at %s of %s with WTID %s\n", 1152 (unsigned long long) rowid, 1153 GNUNET_TIME_timestamp2s (date), 1154 TALER_amount2s (amount), 1155 TALER_B2S (wtid)); 1156 wa->last_wire_out_serial_id = rowid + 1; 1157 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_out), 1158 &TALER_ARL_USE_AB (total_wire_out), 1159 amount); 1160 GNUNET_CRYPTO_hash (wtid, 1161 sizeof (*wtid), 1162 &key); 1163 roi = GNUNET_CONTAINER_multihashmap_get (out_map, 1164 &key); 1165 if (NULL == roi) 1166 { 1167 /* Wire transfer was not made (yet) at all (but would have been 1168 justified), so the entire amount is missing / still to be done. This 1169 is moderately harmless, it might just be that the 1170 taler-exchange-transfer tool or bank has not yet fully caught up with 1171 the transfers it should do. 1172 May be cleared later by check_reported_inconsistency() */ 1173 char diag[MAX_DIAG_LEN]; 1174 struct TALER_AUDITORDB_WireOutInconsistency woi = { 1175 .destination_account = payto_uri, 1176 .diagnostic = diag, 1177 .wire_out_row_id = rowid, 1178 .expected = *amount, 1179 .claimed = zero, 1180 }; 1181 enum GNUNET_DB_QueryStatus qs; 1182 1183 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1184 "Wire out for row %llu still missing\n", 1185 (unsigned long long) rowid); 1186 make_missing_diag (diag, 1187 wtid); 1188 qs = TALER_AUDITORDB_insert_wire_out_inconsistency ( 1189 TALER_ARL_adb, 1190 &woi); 1191 if (qs < 0) 1192 { 1193 global_qs = qs; 1194 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1195 return GNUNET_SYSERR; 1196 } 1197 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus), 1198 &TALER_ARL_USE_AB (total_bad_amount_out_minus), 1199 amount); 1200 return GNUNET_OK; 1201 } 1202 1203 if (0 != TALER_full_payto_normalize_and_cmp (payto_uri, 1204 roi->details.credit_account_uri)) 1205 { 1206 /* Destination bank account is wrong in actual wire transfer, so 1207 we should count the wire transfer as entirely spurious, and 1208 additionally consider the justified wire transfer as missing. */ 1209 struct TALER_AUDITORDB_WireOutInconsistency woi = { 1210 .wire_out_row_id = rowid, 1211 .destination_account = payto_uri, 1212 .diagnostic = (char *) "receiver account mismatch", 1213 .expected = *amount, 1214 .claimed = roi->details.amount, 1215 }; 1216 enum GNUNET_DB_QueryStatus qs; 1217 1218 qs = TALER_AUDITORDB_insert_wire_out_inconsistency ( 1219 TALER_ARL_adb, 1220 &woi); 1221 if (qs < 0) 1222 { 1223 global_qs = qs; 1224 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1225 return GNUNET_SYSERR; 1226 } 1227 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus), 1228 &TALER_ARL_USE_AB (total_bad_amount_out_plus), 1229 &roi->details.amount); 1230 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus), 1231 &TALER_ARL_USE_AB (total_bad_amount_out_minus), 1232 amount); 1233 GNUNET_assert (GNUNET_OK == 1234 free_roi (NULL, 1235 &key, 1236 roi)); 1237 return GNUNET_OK; 1238 } 1239 1240 if (0 != TALER_amount_cmp (&roi->details.amount, 1241 amount)) 1242 { 1243 struct TALER_AUDITORDB_WireOutInconsistency woi = { 1244 .destination_account = payto_uri, 1245 .diagnostic = (char *) "wire amount does not match", 1246 .wire_out_row_id = rowid, 1247 .expected = *amount, 1248 .claimed = roi->details.amount, 1249 }; 1250 enum GNUNET_DB_QueryStatus qs; 1251 1252 qs = TALER_AUDITORDB_insert_wire_out_inconsistency ( 1253 TALER_ARL_adb, 1254 &woi); 1255 if (qs < 0) 1256 { 1257 global_qs = qs; 1258 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1259 return GNUNET_SYSERR; 1260 } 1261 if (0 < TALER_amount_cmp (amount, 1262 &roi->details.amount)) 1263 { 1264 /* amount > roi->details.amount: wire transfer was smaller than it should have been */ 1265 struct TALER_Amount delta; 1266 1267 TALER_ARL_amount_subtract (&delta, 1268 amount, 1269 &roi->details.amount); 1270 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus), 1271 &TALER_ARL_USE_AB (total_bad_amount_out_minus), 1272 &delta); 1273 } 1274 else 1275 { 1276 /* roi->details.amount < amount: wire transfer was larger than it should have been */ 1277 struct TALER_Amount delta; 1278 1279 TALER_ARL_amount_subtract (&delta, 1280 &roi->details.amount, 1281 amount); 1282 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus), 1283 &TALER_ARL_USE_AB (total_bad_amount_out_plus), 1284 &delta); 1285 } 1286 GNUNET_assert (GNUNET_OK == 1287 free_roi (NULL, 1288 &key, 1289 roi)); 1290 return GNUNET_OK; 1291 } 1292 1293 { 1294 enum GNUNET_GenericReturnValue ret; 1295 1296 if (! check_time_difference ("wire_out", 1297 rowid, 1298 date, 1299 roi->details.execution_date)) 1300 { 1301 /* We had a database error, fail */ 1302 ret = GNUNET_SYSERR; 1303 } 1304 else 1305 { 1306 ret = GNUNET_OK; 1307 } 1308 GNUNET_assert (GNUNET_OK == 1309 free_roi (NULL, 1310 &key, 1311 roi)); 1312 return ret; 1313 } 1314 } 1315 1316 1317 /** 1318 * Main function for processing 'debit' data. We start by going over 1319 * the DEBIT transactions this time, and then verify that all of them are 1320 * justified by reserve closures, profit drains or regular outgoing 1321 * wire transfers from aggregated deposits. 1322 * 1323 * @param[in,out] wa wire account list to process 1324 */ 1325 static void 1326 process_debits (struct WireAccount *wa); 1327 1328 1329 /** 1330 * Go over the "wire_out" table of the exchange and 1331 * verify that all wire outs are in that table. 1332 * 1333 * @param[in,out] wa wire account we are processing 1334 */ 1335 static void 1336 check_exchange_wire_out (struct WireAccount *wa) 1337 { 1338 enum GNUNET_DB_QueryStatus qs; 1339 1340 GNUNET_assert (NULL == wa->dhh); 1341 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1342 "Analyzing exchange's wire OUT table for account `%s'\n", 1343 wa->ai->section_name); 1344 qs = TALER_EXCHANGEDB_iterate_wire_outs_above_serial_id_by_account ( 1345 TALER_ARL_edb, 1346 wa->ai->section_name, 1347 wa->last_wire_out_serial_id, 1348 &wire_out_cb, 1349 wa); 1350 if (0 > qs) 1351 { 1352 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1353 global_ret = EXIT_FAILURE; 1354 GNUNET_SCHEDULER_shutdown (); 1355 return; 1356 } 1357 GNUNET_CONTAINER_multihashmap_iterate (out_map, 1358 &complain_out_not_found, 1359 wa); 1360 /* clean up */ 1361 GNUNET_CONTAINER_multihashmap_iterate (out_map, 1362 &free_roi, 1363 NULL); 1364 process_debits (wa->next); 1365 } 1366 1367 1368 /** 1369 * This function is called for all transactions that 1370 * are debited from the exchange's account (outgoing 1371 * transactions). 1372 * 1373 * @param cls `struct WireAccount` with current wire account to process 1374 * @param dhr HTTP response details 1375 */ 1376 static void 1377 history_debit_cb ( 1378 void *cls, 1379 const struct TALER_BANK_DebitHistoryResponse *dhr); 1380 1381 1382 /** 1383 * Task scheduled to begin long-polling on the 1384 * bank transfer. 1385 * 1386 * @param cls a `struct WireAccount *` 1387 */ 1388 static void 1389 dh_long_poll (void *cls) 1390 { 1391 struct WireAccount *wa = cls; 1392 1393 wa->dhh_task = NULL; 1394 wa->dhh_next 1395 = GNUNET_TIME_relative_to_absolute (MIN_LONGPOLL_DELAY); 1396 GNUNET_assert (NULL == wa->dhh); 1397 wa->dhh = TALER_BANK_debit_history ( 1398 ctx, 1399 wa->ai->auth, 1400 wa->wire_off_out, 1401 MAX_PER_TRANSACTION, 1402 MAX_LONGPOLL_DELAY, 1403 &history_debit_cb, 1404 wa); 1405 if (NULL == wa->dhh) 1406 { 1407 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1408 "Failed to start long-polling for bank transaction history for `%s'\n", 1409 wa->ai->section_name); 1410 global_ret = EXIT_FAILURE; 1411 GNUNET_SCHEDULER_shutdown (); 1412 return; 1413 } 1414 } 1415 1416 1417 static void 1418 history_debit_cb ( 1419 void *cls, 1420 const struct TALER_BANK_DebitHistoryResponse *dhr) 1421 { 1422 struct WireAccount *wa = cls; 1423 struct WireTransferOutInfo *roi; 1424 size_t slen; 1425 1426 wa->dhh = NULL; 1427 if ( (MHD_HTTP_OK == dhr->http_status) && 1428 (0 != dhr->details.ok.details_length) ) 1429 { 1430 /* As we got results, we go again *immediately* */ 1431 wa->dhh_next = GNUNET_TIME_UNIT_ZERO_ABS; 1432 } 1433 GNUNET_assert (NULL == wa->dhh_task); 1434 wa->dhh_task 1435 = GNUNET_SCHEDULER_add_at (wa->dhh_next, 1436 &dh_long_poll, 1437 wa); 1438 switch (dhr->http_status) 1439 { 1440 case MHD_HTTP_OK: 1441 for (unsigned int i = 0; i < dhr->details.ok.details_length; i++) 1442 { 1443 const struct TALER_BANK_DebitDetails *dd 1444 = &dhr->details.ok.details[i]; 1445 1446 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1447 "Analyzing bank DEBIT #%llu at %s of %s with WTID %s\n", 1448 (unsigned long long) dd->serial_id, 1449 GNUNET_TIME_timestamp2s (dd->execution_date), 1450 TALER_amount2s (&dd->amount), 1451 TALER_B2S (&dd->wtid)); 1452 wa->wire_off_out = dd->serial_id; 1453 /* dd->amount is what was transferred, and what the exchange's 1454 justification (wire_out, reserve closure, profit drain) must 1455 match; the bank's fee was debited on top of it. */ 1456 TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_debit_fees), 1457 &TALER_ARL_USE_AB (total_wire_debit_fees), 1458 &dd->debit_fee); 1459 slen = strlen (dd->credit_account_uri.full_payto) + 1; 1460 roi = GNUNET_malloc (sizeof (struct WireTransferOutInfo) 1461 + slen); 1462 GNUNET_CRYPTO_hash (&dd->wtid, 1463 sizeof (dd->wtid), 1464 &roi->subject_hash); 1465 roi->details = *dd; 1466 roi->details.credit_account_uri.full_payto 1467 = (char *) &roi[1]; 1468 GNUNET_memcpy (&roi[1], 1469 dd->credit_account_uri.full_payto, 1470 slen); 1471 if (GNUNET_OK != 1472 GNUNET_CONTAINER_multihashmap_put (out_map, 1473 &roi->subject_hash, 1474 roi, 1475 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)) 1476 { 1477 struct TALER_AUDITORDB_WireFormatInconsistency wfi = { 1478 .amount = dd->amount, 1479 .wire_offset = dd->serial_id, 1480 .diagnostic = (char *) "duplicate outgoing wire transfer subject" 1481 }; 1482 enum GNUNET_DB_QueryStatus qs; 1483 1484 qs = TALER_AUDITORDB_insert_wire_format_inconsistency ( 1485 TALER_ARL_adb, 1486 &wfi); 1487 if (qs < 0) 1488 { 1489 global_qs = qs; 1490 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1491 commit (qs); 1492 return; 1493 } 1494 TALER_ARL_amount_add (&TALER_ARL_USE_AB ( 1495 wire_debit_duplicate_transfer_subject_total), 1496 &TALER_ARL_USE_AB ( 1497 wire_debit_duplicate_transfer_subject_total), 1498 &dd->amount); 1499 } 1500 } 1501 check_exchange_wire_out (wa); 1502 return; 1503 case MHD_HTTP_NO_CONTENT: 1504 check_exchange_wire_out (wa); 1505 return; 1506 case MHD_HTTP_NOT_FOUND: 1507 if (ignore_account_404) 1508 { 1509 check_exchange_wire_out (wa); 1510 return; 1511 } 1512 break; 1513 default: 1514 break; 1515 } 1516 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1517 "Error fetching debit history of account %s: %u/%u!\n", 1518 wa->ai->section_name, 1519 dhr->http_status, 1520 (unsigned int) dhr->ec); 1521 commit (GNUNET_DB_STATUS_HARD_ERROR); 1522 global_ret = EXIT_FAILURE; 1523 GNUNET_SCHEDULER_shutdown (); 1524 return; 1525 } 1526 1527 1528 static void 1529 process_debits (struct WireAccount *wa) 1530 { 1531 /* skip accounts where DEBIT is not enabled */ 1532 while ( (NULL != wa) && 1533 (! wa->ai->debit_enabled) ) 1534 wa = wa->next; 1535 if (NULL == wa) 1536 { 1537 /* end of iteration */ 1538 commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT); 1539 return; 1540 } 1541 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1542 "Checking bank DEBIT records of account `%s'\n", 1543 wa->ai->section_name); 1544 if ( (NULL == wa->dhh) && 1545 (NULL == wa->dhh_task) ) 1546 { 1547 wa->dhh = TALER_BANK_debit_history ( 1548 ctx, 1549 wa->ai->auth, 1550 wa->wire_off_out, 1551 MAX_PER_TRANSACTION, 1552 GNUNET_TIME_UNIT_ZERO, 1553 &history_debit_cb, 1554 wa); 1555 if (NULL == wa->dhh) 1556 { 1557 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1558 "Failed to obtain bank transaction history for `%s'\n", 1559 wa->ai->section_name); 1560 commit (GNUNET_DB_STATUS_HARD_ERROR); 1561 global_ret = EXIT_FAILURE; 1562 GNUNET_SCHEDULER_shutdown (); 1563 return; 1564 } 1565 } 1566 } 1567 1568 1569 /** 1570 * Function called about reserve closing operations the aggregator triggered. 1571 * 1572 * @param cls closure; NULL 1573 * @param rowid row identifier used to uniquely identify the reserve closing operation 1574 * @param execution_date when did we execute the close operation 1575 * @param amount_with_fee how much did we debit the reserve 1576 * @param closing_fee how much did we charge for closing the reserve 1577 * @param reserve_pub public key of the reserve 1578 * @param receiver_account where did we send the funds, in payto://-format 1579 * @param wtid identifier used for the wire transfer 1580 * @param close_request_row which close request triggered the operation? 1581 * 0 if it was a timeout (not used) 1582 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop 1583 */ 1584 static enum GNUNET_GenericReturnValue 1585 reserve_closed_cb ( 1586 void *cls, 1587 uint64_t rowid, 1588 struct GNUNET_TIME_Timestamp execution_date, 1589 const struct TALER_Amount *amount_with_fee, 1590 const struct TALER_Amount *closing_fee, 1591 const struct TALER_ReservePublicKeyP *reserve_pub, 1592 const struct TALER_FullPayto receiver_account, 1593 const struct TALER_WireTransferIdentifierRawP *wtid, 1594 uint64_t close_request_row) 1595 { 1596 struct ReserveClosure *rc; 1597 struct GNUNET_HashCode key; 1598 1599 (void) cls; 1600 (void) close_request_row; 1601 GNUNET_assert (TALER_ARL_USE_PP (wire_reserve_close_id) <= rowid); 1602 TALER_ARL_USE_PP (wire_reserve_close_id) = rowid + 1; 1603 rc = GNUNET_new (struct ReserveClosure); 1604 if (TALER_ARL_SR_INVALID_NEGATIVE == 1605 TALER_ARL_amount_subtract_neg (&rc->amount, 1606 amount_with_fee, 1607 closing_fee)) 1608 { 1609 struct TALER_AUDITORDB_RowInconsistency ri = { 1610 .problem_row_id = rowid, 1611 .row_table 1612 = (char *) "reserves_closures", 1613 .diagnostic 1614 = (char *) "closing fee above reserve balance (and closed anyway)" 1615 }; 1616 enum GNUNET_DB_QueryStatus qs; 1617 1618 qs = TALER_AUDITORDB_insert_row_inconsistency ( 1619 TALER_ARL_adb, 1620 &ri); 1621 if (qs < 0) 1622 { 1623 global_qs = qs; 1624 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1625 return GNUNET_OK; 1626 } 1627 GNUNET_free (rc); 1628 return GNUNET_OK; 1629 } 1630 rc->receiver_account.full_payto 1631 = GNUNET_strdup (receiver_account.full_payto); 1632 rc->wtid = *wtid; 1633 rc->execution_date = execution_date; 1634 rc->rowid = rowid; 1635 hash_rc (rc->receiver_account, 1636 wtid, 1637 &key); 1638 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1639 "Discovered reserve closure %llu (%s) over %s benefiting %s\n", 1640 (unsigned long long) rowid, 1641 GNUNET_h2s (&key), 1642 TALER_amount2s (amount_with_fee), 1643 receiver_account.full_payto); 1644 (void) GNUNET_CONTAINER_multihashmap_put ( 1645 reserve_closures, 1646 &key, 1647 rc, 1648 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE); 1649 return GNUNET_OK; 1650 } 1651 1652 1653 /** 1654 * Start the database transactions and begin the audit. 1655 * 1656 * @return transaction status code 1657 */ 1658 static enum GNUNET_DB_QueryStatus 1659 begin_transaction (void) 1660 { 1661 enum GNUNET_DB_QueryStatus qs; 1662 1663 if (GNUNET_SYSERR == 1664 TALER_EXCHANGEDB_preflight (TALER_ARL_edb)) 1665 { 1666 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1667 "Failed to initialize exchange database connection.\n"); 1668 return GNUNET_DB_STATUS_HARD_ERROR; 1669 } 1670 if (GNUNET_SYSERR == 1671 TALER_AUDITORDB_preflight (TALER_ARL_adb)) 1672 { 1673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1674 "Failed to initialize auditor database session.\n"); 1675 return GNUNET_DB_STATUS_HARD_ERROR; 1676 } 1677 global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1678 if (GNUNET_OK != 1679 TALER_AUDITORDB_start (TALER_ARL_adb, 1680 "auditor-wire-debit")) 1681 { 1682 GNUNET_break (0); 1683 return GNUNET_DB_STATUS_HARD_ERROR; 1684 } 1685 if (GNUNET_OK != 1686 TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb, 1687 "wire debit auditor")) 1688 { 1689 GNUNET_break (0); 1690 return GNUNET_DB_STATUS_HARD_ERROR; 1691 } 1692 qs = TALER_AUDITORDB_get_balance ( 1693 TALER_ARL_adb, 1694 TALER_ARL_GET_AB (total_drained), 1695 TALER_ARL_GET_AB (total_wire_out), 1696 TALER_ARL_GET_AB (total_bad_amount_out_plus), 1697 TALER_ARL_GET_AB (total_bad_amount_out_minus), 1698 TALER_ARL_GET_AB (total_closure_amount_lag), 1699 TALER_ARL_GET_AB (wire_debit_duplicate_transfer_subject_total), 1700 TALER_ARL_GET_AB (total_wire_out), 1701 TALER_ARL_GET_AB (total_wire_debit_fees), 1702 NULL); 1703 switch (qs) 1704 { 1705 case GNUNET_DB_STATUS_HARD_ERROR: 1706 GNUNET_break (0); 1707 return qs; 1708 case GNUNET_DB_STATUS_SOFT_ERROR: 1709 GNUNET_break (0); 1710 return qs; 1711 case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: 1712 case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: 1713 break; 1714 } 1715 for (struct WireAccount *wa = wa_head; 1716 NULL != wa; 1717 wa = wa->next) 1718 { 1719 /* we are called again after a serialization failure, so the labels 1720 may already have been computed */ 1721 GNUNET_free (wa->label_wire_out_serial_id); 1722 GNUNET_free (wa->label_wire_off_out); 1723 GNUNET_asprintf (&wa->label_wire_out_serial_id, 1724 "wire-%s-%s", 1725 wa->ai->section_name, 1726 "wire_out_serial_id"); 1727 GNUNET_asprintf (&wa->label_wire_off_out, 1728 "wire-%s-%s", 1729 wa->ai->section_name, 1730 "wire_off_out"); 1731 qs = TALER_AUDITORDB_get_auditor_progress ( 1732 TALER_ARL_adb, 1733 wa->label_wire_out_serial_id, 1734 &wa->last_wire_out_serial_id, 1735 wa->label_wire_off_out, 1736 &wa->wire_off_out, 1737 NULL); 1738 if (0 > qs) 1739 { 1740 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1741 return qs; 1742 } 1743 GNUNET_assert (2 == qs); 1744 wa->start_wire_out_serial_id = wa->last_wire_out_serial_id; 1745 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1746 "Resuming account %s debit audit at %llu/%llu\n", 1747 wa->ai->section_name, 1748 (unsigned long long) wa->last_wire_out_serial_id, 1749 (unsigned long long) wa->wire_off_out); 1750 } 1751 qs = TALER_AUDITORDB_get_auditor_progress ( 1752 TALER_ARL_adb, 1753 TALER_ARL_GET_PP (wire_reserve_close_id), 1754 NULL); 1755 if (0 > qs) 1756 { 1757 GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); 1758 return qs; 1759 } 1760 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) 1761 { 1762 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 1763 "First analysis of with wire auditor, starting audit from scratch\n"); 1764 } 1765 else 1766 { 1767 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1768 "Resuming wire debit audit at %llu\n", 1769 (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id)); 1770 } 1771 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1772 "Iterating over reserve closures from %llu\n", 1773 (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id)); 1774 qs = TALER_EXCHANGEDB_iterate_reserve_closed_above_serial_id ( 1775 TALER_ARL_edb, 1776 TALER_ARL_USE_PP (wire_reserve_close_id), 1777 &reserve_closed_cb, 1778 NULL); 1779 if (0 > qs) 1780 { 1781 GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); 1782 return GNUNET_DB_STATUS_HARD_ERROR; 1783 } 1784 process_debits (wa_head); 1785 return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; 1786 } 1787 1788 1789 /** 1790 * Function called with information about a wire account. Adds the 1791 * account to our list for processing (if it is enabled and we can 1792 * load the plugin). 1793 * 1794 * @param cls closure, NULL 1795 * @param ai account information 1796 */ 1797 static void 1798 process_account_cb (void *cls, 1799 const struct TALER_EXCHANGEDB_AccountInfo *ai) 1800 { 1801 struct WireAccount *wa; 1802 1803 (void) cls; 1804 if ( (! ai->debit_enabled) && 1805 (! ai->credit_enabled) ) 1806 return; /* not an active exchange account */ 1807 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1808 "Found exchange account `%s'\n", 1809 ai->section_name); 1810 wa = GNUNET_new (struct WireAccount); 1811 wa->ai = ai; 1812 GNUNET_CONTAINER_DLL_insert (wa_head, 1813 wa_tail, 1814 wa); 1815 } 1816 1817 1818 /** 1819 * Function called on events received from Postgres. 1820 * 1821 * @param cls closure, NULL 1822 * @param extra additional event data provided 1823 * @param extra_size number of bytes in @a extra 1824 */ 1825 static void 1826 db_notify (void *cls, 1827 const void *extra, 1828 size_t extra_size) 1829 { 1830 (void) cls; 1831 (void) extra; 1832 (void) extra_size; 1833 1834 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1835 "Received notification to wake wire helper\n"); 1836 /* If there are accounts we are still processing, abort 1837 the HTTP requests so we can start afresh. */ 1838 for (struct WireAccount *wa = wa_head; 1839 NULL != wa; 1840 wa = wa->next) 1841 { 1842 if (NULL != wa->dhh) 1843 { 1844 TALER_BANK_debit_history_cancel (wa->dhh); 1845 wa->dhh = NULL; 1846 } 1847 check_exchange_wire_out (wa); 1848 } 1849 1850 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 1851 begin_transaction ()) 1852 { 1853 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1854 "Audit failed\n"); 1855 GNUNET_break (0); 1856 global_ret = EXIT_FAILURE; 1857 GNUNET_SCHEDULER_shutdown (); 1858 return; 1859 } 1860 } 1861 1862 1863 /** 1864 * Main function that will be run. 1865 * 1866 * @param cls closure 1867 * @param args remaining command-line arguments 1868 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 1869 * @param c configuration 1870 */ 1871 static void 1872 run (void *cls, 1873 char *const *args, 1874 const char *cfgfile, 1875 const struct GNUNET_CONFIGURATION_Handle *c) 1876 { 1877 (void) cls; 1878 (void) args; 1879 (void) cfgfile; 1880 cfg = c; 1881 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 1882 "Launching wire debit auditor\n"); 1883 if (EXIT_SUCCESS != 1884 (global_ret = TALER_ARL_init (c))) 1885 { 1886 return; 1887 } 1888 1889 reserve_closures 1890 = GNUNET_CONTAINER_multihashmap_create (1024, 1891 GNUNET_NO); 1892 GNUNET_assert (GNUNET_OK == 1893 TALER_amount_set_zero (TALER_ARL_currency, 1894 &zero)); 1895 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 1896 NULL); 1897 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 1898 &rctx); 1899 rctx = GNUNET_CURL_gnunet_rc_create (ctx); 1900 if (NULL == ctx) 1901 { 1902 GNUNET_break (0); 1903 global_ret = EXIT_FAILURE; 1904 return; 1905 } 1906 reserve_closures = GNUNET_CONTAINER_multihashmap_create (1024, 1907 GNUNET_NO); 1908 out_map = GNUNET_CONTAINER_multihashmap_create (1024, 1909 true); 1910 if (GNUNET_OK != 1911 TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg, 1912 TALER_EXCHANGEDB_ALO_DEBIT 1913 | TALER_EXCHANGEDB_ALO_CREDIT 1914 | TALER_EXCHANGEDB_ALO_AUTHDATA)) 1915 { 1916 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1917 "No bank accounts configured\n"); 1918 global_ret = EXIT_NOTCONFIGURED; 1919 GNUNET_SCHEDULER_shutdown (); 1920 return; 1921 } 1922 TALER_EXCHANGEDB_find_accounts (&process_account_cb, 1923 NULL); 1924 1925 if (0 == test_mode) 1926 { 1927 struct GNUNET_DB_EventHeaderP es = { 1928 .size = htons (sizeof (es)), 1929 .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE) 1930 }; 1931 1932 eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb, 1933 &es, 1934 GNUNET_TIME_UNIT_FOREVER_REL, 1935 &db_notify, 1936 NULL); 1937 GNUNET_assert (NULL != eh); 1938 } 1939 if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 1940 begin_transaction ()) 1941 { 1942 GNUNET_break (0); 1943 global_ret = EXIT_FAILURE; 1944 GNUNET_SCHEDULER_shutdown (); 1945 return; 1946 } 1947 } 1948 1949 1950 /** 1951 * The main function of the wire auditing tool. Checks that 1952 * the exchange's records of wire transfers match that of 1953 * the wire gateway. 1954 * 1955 * @param argc number of arguments from the command line 1956 * @param argv command line arguments 1957 * @return 0 ok, 1 on error 1958 */ 1959 int 1960 main (int argc, 1961 char *const *argv) 1962 { 1963 const struct GNUNET_GETOPT_CommandLineOption options[] = { 1964 GNUNET_GETOPT_option_flag ('i', 1965 "internal", 1966 "perform checks only applicable for exchange-internal audits", 1967 &internal_checks), 1968 GNUNET_GETOPT_option_flag ('I', 1969 "ignore-not-found", 1970 "continue, even if the bank account of the exchange was not found", 1971 &ignore_account_404), 1972 GNUNET_GETOPT_option_flag ('t', 1973 "test", 1974 "run in test mode and exit when idle", 1975 &test_mode), 1976 GNUNET_GETOPT_option_timetravel ('T', 1977 "timetravel"), 1978 GNUNET_GETOPT_OPTION_END 1979 }; 1980 enum GNUNET_GenericReturnValue ret; 1981 1982 ret = GNUNET_PROGRAM_run ( 1983 TALER_AUDITOR_project_data (), 1984 argc, 1985 argv, 1986 "taler-helper-auditor-wire-debit", 1987 gettext_noop ( 1988 "Audit exchange database for consistency with the bank's outgoing wire transfers"), 1989 options, 1990 &run, 1991 NULL); 1992 if (GNUNET_SYSERR == ret) 1993 return EXIT_NOTCONFIGURED; 1994 if (GNUNET_NO == ret) 1995 return EXIT_SUCCESS; 1996 return global_ret; 1997 } 1998 1999 2000 /* end of taler-helper-auditor-wire-debit.c */