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