test-auditor.sh (78828B)
1 #!/bin/bash 2 # 3 # This file is part of TALER 4 # Copyright (C) 2014-2025 Taler Systems SA 5 # 6 # TALER is free software; you can redistribute it and/or modify it under the 7 # terms of the GNU General Public License as published by the Free Software 8 # Foundation; either version 3, or (at your option) any later version. 9 # 10 # TALER is distributed in the hope that it will be useful, but WITHOUT ANY 11 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 12 # A PARTICULAR PURPOSE. See the GNU General Public License for more details. 13 # 14 # You should have received a copy of the GNU General Public License along with 15 # TALER; see the file COPYING. If not, If not, see <http://www.gnu.org/license> 16 # 17 # 18 # shellcheck disable=SC2317 19 # shellcheck disable=SC1091 20 # 21 # 22 # Setup database which was generated from a perfectly normal 23 # exchange-wallet interaction and run the auditor against it. 24 # 25 # Check that the auditor report is as expected. 26 # 27 # Requires 'jq' tool and Postgres superuser rights! 28 set -eu 29 #set -x 30 31 # Set of numbers for all the testcases. 32 # When adding new tests, increase the last number: 33 ALL_TESTS=$(seq 0 31) 34 35 # $TESTS determines which tests we should run. 36 # This construction is used to make it easy to 37 # only run a subset of the tests. To only run a subset, 38 # pass the numbers of the tests to run as the FIRST 39 # argument to test-auditor.sh, i.e.: 40 # 41 # $ test-auditor.sh "1 3" 42 # 43 # to run tests 1 and 3 only. By default, all tests are run. 44 # 45 TESTS=${1:-$ALL_TESTS} 46 47 export TALER_AUDITOR_TOKEN="secret-token:D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0" 48 export TALER_AUDITOR_SALT="64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE1H68SK8D9P6WW32CHK6GTKCDSR64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE0" 49 50 # Global variable to run the auditor processes under valgrind 51 # VALGRIND=valgrind 52 VALGRIND="" 53 54 . setup.sh 55 56 57 # Cleanup exchange and libeufin between runs. 58 function cleanup() 59 { 60 if [ -n "${EPID:-}" ] 61 then 62 echo -n "Stopping exchange $EPID..." 63 kill -TERM "$EPID" 64 wait "$EPID" || true 65 echo "DONE" 66 unset EPID 67 fi 68 stop_libeufin &> /dev/null 69 } 70 71 # Cleanup to run whenever we exit 72 function exit_cleanup() 73 { 74 jobs 75 if [ -n "${POSTGRES_PATH:-}" ] 76 then 77 echo -n "Stopping Postgres at ${POSTGRES_PATH} ..." 78 "${POSTGRES_PATH}/pg_ctl" \ 79 -D "$TMPDIR" \ 80 --log="${MY_TMP_DIR}/pg_ctl.log" \ 81 stop \ 82 &> ${MY_TMP_DIR}/pg_ctl.out \ 83 || true 84 echo "DONE" 85 fi 86 echo -n "Running exit-cleanup ..." 87 cleanup 88 for n in $(jobs -p) 89 do 90 kill "$n" 2> /dev/null || true 91 done 92 wait || true 93 echo "DONE" 94 } 95 96 # Install cleanup handler (except for kill -9) 97 trap exit_cleanup EXIT 98 99 100 function await_bank () { 101 for n in $(seq 1 80) 102 do 103 echo -n "." 104 sleep 0.1 105 OK=1 106 wget http://localhost:8082/ \ 107 -o /dev/null \ 108 -O /dev/null \ 109 >/dev/null \ 110 && break 111 OK=0 112 done 113 if [ 1 != "$OK" ] 114 then 115 exit_skip "Failed to launch libeufin-bank" 116 fi 117 } 118 119 # Operations to run before the actual audit 120 function pre_audit () { 121 # Launch bank 122 echo -n "Launching libeufin-bank" 123 export CONF 124 export MY_TMP_DIR 125 launch_libeufin 126 await_bank 127 echo " DONE" 128 129 if [ "${1:-no}" = "aggregator" ] 130 then 131 echo -n "Running exchange aggregator ..." 132 taler-exchange-aggregator \ 133 -y \ 134 -L "INFO" \ 135 -t \ 136 -c "$CONF" \ 137 2> "${MY_TMP_DIR}/aggregator.log" \ 138 || exit_fail "FAIL" 139 echo " DONE" 140 echo -n "Running exchange closer ..." 141 taler-exchange-closer \ 142 -L "INFO" \ 143 -t \ 144 -c "$CONF" \ 145 2> "${MY_TMP_DIR}/closer.log" \ 146 || exit_fail "FAIL" 147 echo " DONE" 148 echo -n "Running exchange transfer ..." 149 taler-exchange-transfer \ 150 -L "INFO" \ 151 -t \ 152 -c "$CONF" \ 153 2> "${MY_TMP_DIR}/transfer.log" \ 154 || exit_fail "FAIL" 155 echo " DONE" 156 fi 157 } 158 159 # actual audit run 160 function audit_only () { 161 # Run the auditor! 162 echo -n "Running audit(s) ..." 163 164 # Restart so that first run is always fresh, and second one is incremental 165 taler-auditor-dbinit \ 166 -r \ 167 -c "$CONF" 168 $VALGRIND taler-helper-auditor-aggregation \ 169 -L DEBUG \ 170 -c "$CONF" \ 171 -t \ 172 > "${MY_TMP_DIR}/test-audit-aggregation.out" \ 173 2> "${MY_TMP_DIR}/test-audit-aggregation.err" \ 174 || exit_fail "aggregation audit failed (see ${MY_TMP_DIR}/test-audit-aggregation.*)" 175 echo -n "." 176 $VALGRIND taler-helper-auditor-aggregation \ 177 -L DEBUG \ 178 -c "$CONF" \ 179 -t \ 180 > "${MY_TMP_DIR}/test-audit-aggregation-inc.out" \ 181 2> "${MY_TMP_DIR}/test-audit-aggregation-inc.err" \ 182 || exit_fail "incremental aggregation audit failed (see ${MY_TMP_DIR}/test-audit-aggregation-inc.*)" 183 echo -n "." 184 $VALGRIND taler-helper-auditor-coins \ 185 -L DEBUG \ 186 -c "$CONF" \ 187 -t \ 188 > "${MY_TMP_DIR}/test-audit-coins.out" \ 189 2> "${MY_TMP_DIR}/test-audit-coins.err" \ 190 || exit_fail "coin audit failed (see ${MY_TMP_DIR}/test-audit-coins.*)" 191 echo -n "." 192 $VALGRIND taler-helper-auditor-coins \ 193 -L DEBUG \ 194 -c "$CONF" \ 195 -t \ 196 > "${MY_TMP_DIR}/test-audit-coins-inc.out" \ 197 2> "${MY_TMP_DIR}/test-audit-coins-inc.err" \ 198 || exit_fail "incremental coin audit failed (see ${MY_TMP_DIR}/test-audit-coins-inc.*)" 199 echo -n "." 200 $VALGRIND taler-helper-auditor-deposits \ 201 -L DEBUG \ 202 -c "$CONF" \ 203 -t \ 204 > "${MY_TMP_DIR}/test-audit-deposits.out" \ 205 2> "${MY_TMP_DIR}/test-audit-deposits.err" \ 206 || exit_fail "deposits audit failed (see ${MY_TMP_DIR}/test-audit-deposits.*)" 207 echo -n "." 208 $VALGRIND taler-helper-auditor-deposits \ 209 -L DEBUG \ 210 -c "$CONF" \ 211 -t \ 212 > "${MY_TMP_DIR}/test-audit-deposits-inc.out" \ 213 2> "${MY_TMP_DIR}/test-audit-deposits-inc.err" \ 214 || exit_fail "incremental deposits audit failed (see ${MY_TMP_DIR}/test-audit-deposits-inc.*)" 215 echo -n "." 216 $VALGRIND taler-helper-auditor-reserves \ 217 -i \ 218 -L DEBUG \ 219 -c "$CONF" \ 220 -t \ 221 > "${MY_TMP_DIR}/test-audit-reserves.out" \ 222 2> "${MY_TMP_DIR}/test-audit-reserves.err" \ 223 || exit_fail "reserves audit failed (see ${MY_TMP_DIR}/test-audit-reserves.*)" 224 echo -n "." 225 $VALGRIND taler-helper-auditor-reserves \ 226 -i \ 227 -L DEBUG \ 228 -c "$CONF" \ 229 -t \ 230 > "${MY_TMP_DIR}/test-audit-reserves-inc.out" \ 231 2> "${MY_TMP_DIR}/test-audit-reserves-inc.err" \ 232 || exit_fail "incremental reserves audit failed (see ${MY_TMP_DIR}/test-audit-reserves-inc.*)" 233 echo -n "." 234 $VALGRIND taler-helper-auditor-wire-credit \ 235 -i \ 236 -L DEBUG \ 237 -c "$CONF" \ 238 -t \ 239 > "${MY_TMP_DIR}/test-audit-wire-credit.out" \ 240 2> "${MY_TMP_DIR}/test-audit-wire-credit.err" \ 241 || exit_fail "wire credit audit failed (see ${MY_TMP_DIR}/test-audit-wire-credit.*)" 242 echo -n "." 243 $VALGRIND taler-helper-auditor-wire-credit \ 244 -i \ 245 -L DEBUG \ 246 -c "$CONF" \ 247 -t \ 248 > "${MY_TMP_DIR}/test-audit-wire-credit-inc.out" \ 249 2> "${MY_TMP_DIR}/test-audit-wire-credit-inc.err" \ 250 || exit_fail "wire credit audit inc failed (see ${MY_TMP_DIR}/test-audit-wire-credit-inc.*)" 251 echo -n "." 252 $VALGRIND taler-helper-auditor-wire-debit \ 253 -i \ 254 -L DEBUG \ 255 -c "$CONF" \ 256 -t \ 257 > "${MY_TMP_DIR}/test-audit-wire-debit.out" \ 258 2> "${MY_TMP_DIR}/test-audit-wire-debit.err" \ 259 || exit_fail "wire debit audit failed (see ${MY_TMP_DIR}/test-audit-wire-debit.*)" 260 echo -n "." 261 $VALGRIND taler-helper-auditor-wire-debit \ 262 -i \ 263 -L DEBUG \ 264 -c "$CONF" \ 265 -t \ 266 > "${MY_TMP_DIR}/test-audit-wire-debit-inc.out" \ 267 2> "${MY_TMP_DIR}/test-audit-wire-debit-inc.err" \ 268 || exit_fail "wire debit audit inc failed (see ${MY_TMP_DIR}/test-audit-wire-debit-inc.*)" 269 echo -n "." 270 $VALGRIND taler-helper-auditor-purses \ 271 -i \ 272 -L DEBUG \ 273 -c "$CONF" \ 274 -t \ 275 > "${MY_TMP_DIR}/test-audit-purses.out" \ 276 2> "${MY_TMP_DIR}/test-audit-purses.err" \ 277 || exit_fail "audit purses failed" 278 echo -n "." 279 $VALGRIND taler-helper-auditor-purses \ 280 -i \ 281 -L DEBUG \ 282 -c "$CONF" \ 283 -t \ 284 > "${MY_TMP_DIR}/test-audit-purses-inc.out" \ 285 2> "${MY_TMP_DIR}/test-audit-purses-inc.err" \ 286 || exit_fail "audit purses inc failed" 287 echo -n "." 288 $VALGRIND taler-helper-auditor-transfer \ 289 -i \ 290 -L DEBUG \ 291 -c "$CONF" \ 292 -t \ 293 > "${MY_TMP_DIR}/test-audit-transfer.out" \ 294 2> "${MY_TMP_DIR}/test-audit-transfer.err" \ 295 || exit_fail "audit transfer failed" 296 echo -n "." 297 $VALGRIND taler-helper-auditor-transfer \ 298 -i \ 299 -L DEBUG \ 300 -c "$CONF" \ 301 -t \ 302 > "${MY_TMP_DIR}/test-audit-transfer-inc.out" \ 303 2> "${MY_TMP_DIR}/test-audit-transfer-inc.err" \ 304 || exit_fail "audit transfer inc failed" 305 echo -n "." 306 307 echo " DONE" 308 } 309 310 311 # Cleanup to run after the auditor 312 function post_audit () { 313 taler-exchange-dbinit \ 314 -c "$CONF" \ 315 -g \ 316 || exit_fail "exchange DB GC failed" 317 cleanup 318 } 319 320 321 # Run audit process on current database, including report 322 # generation. Pass "aggregator" as $1 to run 323 # $ taler-exchange-aggregator 324 # before auditor (to trigger pending wire transfers). 325 # Pass "drain" as $2 to run a drain operation as well. 326 function run_audit () { 327 pre_audit "${1:-no}" 328 if [ "${2:-no}" = "drain" ] 329 then 330 echo -n "Starting exchange..." 331 taler-exchange-httpd \ 332 -c "${CONF}" \ 333 -L INFO \ 334 2> "${MY_TMP_DIR}/exchange-httpd-drain.err" & 335 EPID=$! 336 337 # Wait for exchange service to be available 338 for n in $(seq 1 50) 339 do 340 echo -n "." 341 sleep 0.1 342 OK=0 343 # exchange 344 wget "http://localhost:8081/config" \ 345 -o /dev/null \ 346 -O /dev/null \ 347 >/dev/null \ 348 || continue 349 OK=1 350 break 351 done 352 echo "... DONE." 353 export CONF 354 355 echo -n "Running taler-exchange-offline drain " 356 357 taler-exchange-offline \ 358 -L DEBUG \ 359 -c "${CONF}" \ 360 drain TESTKUDOS:0.1 \ 361 exchange-account-1 payto://iban/DE474361?receiver-name=Merchant43 \ 362 upload \ 363 2> "${MY_TMP_DIR}/taler-exchange-offline-drain.log" \ 364 || exit_fail "offline draining failed" 365 kill -TERM "$EPID" 366 wait "$EPID" || true 367 unset EPID 368 echo -n "Running taler-exchange-drain ..." 369 printf "\n" | taler-exchange-drain \ 370 -L DEBUG \ 371 -c "$CONF" \ 372 2> "${MY_TMP_DIR}/taler-exchange-drain.log" \ 373 || exit_fail "FAIL" 374 echo " DONE" 375 376 echo -n "Running taler-exchange-transfer ..." 377 taler-exchange-transfer \ 378 -L INFO \ 379 -t \ 380 -c "$CONF" \ 381 2> "${MY_TMP_DIR}/drain-transfer.log" \ 382 || exit_fail "FAIL" 383 echo " DONE" 384 fi 385 audit_only 386 post_audit 387 } 388 389 390 function stop_auditor_httpd() { 391 if [ -n "${APID:-}" ] 392 then 393 echo -n "Stopping auditor $APID..." 394 kill -TERM "$APID" 395 wait "$APID" || true 396 echo "DONE" 397 unset APID 398 fi 399 } 400 401 402 # Do a full reload of the (original) database 403 function full_reload() 404 { 405 echo -n "Doing full reload of the database (loading ${BASEDB}.sql into $DB at ${PGHOST:-})... " 406 dropdb -f "$DB" &>> ${MY_TMP_DIR}/drop.log || true 407 createdb -T template0 "$DB" \ 408 || exit_skip "could not create database $DB (at ${PGHOST:-})" 409 # Import pre-generated database, -q(ietly) using single (-1) transaction 410 psql -Aqt "$DB" \ 411 -q \ 412 -1 \ 413 -f "${BASEDB}.sql" \ 414 &>> ${MY_TMP_DIR}/postgresql-reload.log \ 415 || exit_skip "Failed to load database $DB from ${BASEDB}.sql" 416 echo "DONE" 417 # Technically, this call shouldn't be needed as libeufin should already be stopped here... 418 stop_libeufin 419 stop_auditor_httpd 420 } 421 422 function run_auditor_httpd() { 423 echo -n "Starting auditor..." 424 $VALGRIND taler-auditor-httpd \ 425 -c "${CONF}" \ 426 -L INFO \ 427 2> "${MY_TMP_DIR}/auditor-httpd.err" & 428 APID=$! 429 430 # Wait for auditor service to be available 431 for n in $(seq 1 50) 432 do 433 echo -n "." 434 sleep 0.2 435 OK=0 436 # auditor 437 wget "http://localhost:8083/config" \ 438 -o /dev/null \ 439 -O /dev/null \ 440 >/dev/null \ 441 || continue 442 OK=1 443 break 444 done 445 echo "... DONE." 446 } 447 448 449 function check_auditor_running() { 450 ARUNSTATUS=$(curl -Is http://localhost:8083/config | head -1) 451 if [ -n "${ARUNSTATUS:-}" ] 452 then 453 echo "Auditor running" 454 else 455 echo "Auditor not running, starting it" 456 run_auditor_httpd 457 fi 458 unset ARUNSTATUS 459 } 460 461 function call_endpoint() { 462 if [ -n "${2+x}" ] 463 then 464 curl -s -H "Accept: application/json" -H "Authorization: Bearer ${TALER_AUDITOR_TOKEN}" -o "${MY_TMP_DIR}/${2}.json" "localhost:8083/monitoring/${1}?limit=50&balance_key=${2}" 465 echo "endpoint ${1} called (with balance_key)... " 466 else 467 curl -s -H "Accept: application/json" -H "Authorization: Bearer ${TALER_AUDITOR_TOKEN}" -o "${MY_TMP_DIR}/${1}.json" "localhost:8083/monitoring/${1}?limit=50" 468 echo "endpoint ${1} called... " 469 fi 470 } 471 472 473 function check_balance() { 474 call_endpoint "balances" "$1" 475 BAL=$(jq -r .balances[0].balance_value < "${MY_TMP_DIR}/${1}.json") 476 if [ "$BAL" != "$2" ] 477 then 478 exit_fail "$3 (got $BAL, wanted $2)" 479 fi 480 echo "PASS" 481 } 482 483 484 function check_not_balance() { 485 call_endpoint "balances" "$1" 486 BAL=$(jq -r .balances[0].balance_value < "${MY_TMP_DIR}/${1}.json") 487 if [ "$BAL" = "$2" ] 488 then 489 exit_fail "$3 (got $BAL, wanted NOT $2)" 490 fi 491 echo "PASS" 492 } 493 494 495 function check_report() { 496 call_endpoint "$1" 497 NAME=$(echo "$1" | tr '-' '_') 498 # shellcheck disable=SC2086 499 VAL=$(jq -r .\"${NAME}\"[0].\"$2\" < "${MY_TMP_DIR}/${1}.json") 500 if [ "$VAL" != "$3" ] 501 then 502 exit_fail "$1::$2 (got $VAL, wanted $3)" 503 fi 504 echo "PASS" 505 } 506 507 function check_no_report() { 508 call_endpoint "$1" 509 NAME=$(echo "$1" | tr '-' '_') 510 # shellcheck disable=SC2086 511 jq -e .\"${NAME}\"[0] \ 512 < "${MY_TMP_DIR}/${1}.json" \ 513 > /dev/null \ 514 && exit_fail "Wanted empty report for $1, but got incidents" 515 echo "PASS" 516 } 517 518 function check_report_neg() { 519 call_endpoint "$1" 520 NAME=$(echo "$1" | tr '-' '_') 521 # shellcheck disable=SC2086 522 VAL=$(jq -r .\"${NAME}\"[0].\"$2\" < "${MY_TMP_DIR}/${1}.json") 523 if [ "$VAL" == "$3" ] 524 then 525 exit_fail "$1::$2 (got $VAL, wanted $3)" 526 fi 527 echo "PASS" 528 } 529 530 function check_row() { 531 call_endpoint "$1" 532 NAME=$(echo "$1" | tr '-' '_') 533 if [ -n "${3+x}" ] 534 then 535 RID="$2" 536 WANT="$3" 537 else 538 RID="row_id" 539 WANT="$2" 540 fi 541 # shellcheck disable=SC2086 542 ROW=$(jq -r .\"${NAME}\"[0].\"${RID}\" < "${MY_TMP_DIR}/${1}.json") 543 if [ "$ROW" != "$WANT" ] 544 then 545 exit_fail "Row ${1} wrong (got ${ROW}, wanted ${WANT})" 546 fi 547 echo "PASS" 548 } 549 550 551 function test_0() { 552 553 echo "===========0: normal run with aggregator===========" 554 run_audit aggregator 555 check_auditor_running 556 557 echo "Checking output" 558 559 # if an emergency was detected, that is a bug and we should fail 560 echo -n "Test for emergencies... " 561 check_no_report "emergency" 562 echo -n "Test for emergencies by count... " 563 check_no_report "emergency-by-count" 564 echo -n "Test for wire inconsistencies... " 565 check_no_report "denomination-key-validity-withdraw-inconsistency" 566 echo -n "Test for deposit confirmation problems... " 567 check_no_report "deposit-confirmation" 568 569 # Just to test the endpoint and for logging ... 570 call_endpoint "balances" 571 572 echo -n "Testing bad sig loss balance... " 573 check_balance \ 574 "aggregation_total_bad_sig_loss" \ 575 "TESTKUDOS:0" \ 576 "Wrong total bad sig loss from aggregation, got unexpected loss" 577 578 echo -n "Testing coin irregular loss balances... " 579 check_balance \ 580 "coin_irregular_loss" \ 581 "TESTKUDOS:0" \ 582 "Wrong total bad sig loss from coins" 583 584 echo -n "Testing reserves bad sig loss balances... " 585 check_balance \ 586 "reserves_total_bad_sig_loss" \ 587 "TESTKUDOS:0" \ 588 "Wrong total bad sig loss from reserves" 589 590 echo -n "Test for aggregation wire out delta plus... " 591 check_balance \ 592 "aggregation_total_wire_out_delta_plus" \ 593 "TESTKUDOS:0" \ 594 "Expected total wire out delta plus wrong" 595 596 echo -n "Test for aggregation wire out delta minus... " 597 check_balance \ 598 "aggregation_total_wire_out_delta_minus" \ 599 "TESTKUDOS:0" \ 600 "Expected total wire out delta minus wrong" 601 602 echo -n "Test for bad incoming delta plus... " 603 check_balance \ 604 "total_bad_amount_in_plus" \ 605 "TESTKUDOS:0" \ 606 "Expected total wire in delta plus wrong" 607 608 echo -n "Test for bad incoming delta minus... " 609 check_balance \ 610 "total_bad_amount_in_minus" \ 611 "TESTKUDOS:0" \ 612 "Expected total wire in delta minus wrong" 613 614 echo -n "Test for misattribution amounts... " 615 check_balance \ 616 "total_misattribution_in" \ 617 "TESTKUDOS:0" \ 618 "Expected total misattribution in wrong" 619 620 echo -n "Checking for unexpected aggregation delta plus differences... " 621 check_balance \ 622 "aggregation_total_arithmetic_delta_plus" \ 623 "TESTKUDOS:0" \ 624 "Wrong arithmetic delta plus from aggregations" 625 626 echo -n "Checking for unexpected aggregation delta minus differences... " 627 check_balance \ 628 "aggregation_total_arithmetic_delta_minus" \ 629 "TESTKUDOS:0" \ 630 "Wrong arithmetic delta minus from aggregations" 631 632 echo -n "Checking for unexpected coin delta plus differences... " 633 check_balance \ 634 "coins_total_arithmetic_delta_plus" \ 635 "TESTKUDOS:0" \ 636 "Wrong arithmetic delta plus from coins" 637 638 echo -n "Checking for unexpected coin delta minus differences... " 639 check_balance \ 640 "coins_total_arithmetic_delta_minus" \ 641 "TESTKUDOS:0" \ 642 "Wrong arithmetic delta minus from coins" 643 644 echo -n "Checking for unexpected reserves delta plus... " 645 check_balance \ 646 "reserves_total_arithmetic_delta_plus" \ 647 "TESTKUDOS:0" \ 648 "Wrong arithmetic delta plus from reserves" 649 650 echo -n "Checking for unexpected reserves delta minus... " 651 check_balance \ 652 "reserves_total_arithmetic_delta_minus" \ 653 "TESTKUDOS:0" \ 654 "Wrong arithmetic delta minus from reserves" 655 656 echo -n "Checking for unexpected wire out differences " 657 check_no_report "wire-out-inconsistency" 658 659 # cannot easily undo aggregator, hence full reload 660 full_reload 661 cleanup 662 } 663 664 665 # Run without aggregator, hence auditor should detect wire 666 # transfer lag! 667 function test_1() { 668 669 echo "===========1: normal run===========" 670 run_audit 671 check_auditor_running 672 673 echo "Checking output" 674 # if an emergency was detected, that is a bug and we should fail 675 676 call_endpoint "balances" 677 678 echo -n "Test for emergencies... " 679 check_no_report "emergency" 680 echo -n "Test for emergencies by count... " 681 check_no_report "emergency-by-count" 682 echo -n "Test for wire inconsistencies... " 683 check_no_report "denomination-key-validity-withdraw-inconsistency" 684 685 # TODO: check operation balances are correct (once we have all transaction types and wallet is deterministic) 686 # TODO: check revenue summaries are correct (once we have all transaction types and wallet is deterministic) 687 688 echo -n "Check for lag detection... " 689 # Check wire transfer lag reported (no aggregator!) 690 check_not_balance \ 691 "total_amount_lag" \ 692 "TESTKUDOS:0" \ 693 "Failed to detect lag" 694 695 echo -n "Test for bad incoming delta plus... " 696 check_balance \ 697 "total_bad_amount_in_plus" \ 698 "TESTKUDOS:0" \ 699 "Expected total wire in delta plus wrong" 700 701 echo -n "Test for bad incoming delta minus... " 702 check_balance \ 703 "total_bad_amount_in_minus" \ 704 "TESTKUDOS:0" \ 705 "Expected total wire in delta minus wrong" 706 707 echo -n "Test for misattribution amounts... " 708 check_balance \ 709 "total_misattribution_in" \ 710 "TESTKUDOS:0" \ 711 "Expected total misattribution in wrong" 712 # Database was unmodified, no need to undo 713 } 714 715 716 # Change amount of wire transfer reported by exchange 717 function test_2() { 718 719 echo "===========2: reserves_in inconsistency ===========" 720 echo -n "Modifying database: " 721 echo "UPDATE exchange.reserves_in SET credit.val=5 WHERE reserve_in_serial_id=1" \ 722 | psql -At "$DB" 723 724 run_audit 725 check_auditor_running 726 727 echo -n "Testing inconsistency detection ... " 728 check_report \ 729 "reserve-in-inconsistency" \ 730 "row_id" 1 731 echo -n "Testing inconsistency detection amount wired ... " 732 check_report \ 733 "reserve-in-inconsistency" \ 734 "amount_wired" "TESTKUDOS:10" 735 echo -n "Testing inconsistency detection amount expected ... " 736 check_report \ 737 "reserve-in-inconsistency" \ 738 "amount_exchange_expected" "TESTKUDOS:5" 739 740 call_endpoint "balances" 741 echo -n "Checking wire credit balance minus ... " 742 check_balance \ 743 "total_bad_amount_in_minus" \ 744 "TESTKUDOS:0" \ 745 "Wrong total_bad_amount_in_minus" 746 echo -n "Checking wire credit balance plus ... " 747 check_balance \ 748 "total_bad_amount_in_plus" \ 749 "TESTKUDOS:5" \ 750 "Expected total_bad_amount_in_plus wrong" 751 752 echo -n "Undoing database modification " 753 echo "UPDATE exchange.reserves_in SET credit.val=10 WHERE reserve_in_serial_id=1" \ 754 | psql -Aqt "$DB" 755 full_reload 756 cleanup 757 } 758 759 760 # Check for incoming wire transfer amount given being 761 # lower than what exchange claims to have received. 762 function test_3() { 763 764 echo "===========3: reserves_in inconsistency===========" 765 echo "UPDATE exchange.reserves_in SET credit.val=15 WHERE reserve_in_serial_id=1" \ 766 | psql -Aqt "$DB" 767 768 run_audit 769 check_auditor_running 770 771 echo "Checking reserve balance summary inconsistency detection ..." 772 check_report \ 773 "reserve-balance-summary-wrong-inconsistency" \ 774 "auditor_amount" "TESTKUDOS:5.01" 775 check_report \ 776 "reserve-balance-summary-wrong-inconsistency" \ 777 "exchange_amount" "TESTKUDOS:0.01" 778 779 call_endpoint "balances" 780 check_balance \ 781 "reserves_reserve_loss" \ 782 "TESTKUDOS:0" \ 783 "Wrong total loss from insufficient balance" 784 785 echo -n "Testing inconsistency detection ... " 786 check_report \ 787 "reserve-in-inconsistency" \ 788 "row_id" 1 789 echo -n "Testing inconsistency detection amount wired ... " 790 check_report \ 791 "reserve-in-inconsistency" \ 792 "amount_wired" "TESTKUDOS:10" 793 echo -n "Testing inconsistency detection amount expected ... " 794 check_report \ 795 "reserve-in-inconsistency" \ 796 "amount_exchange_expected" "TESTKUDOS:15" 797 798 echo -n "Checking wire credit balance minus ... " 799 check_balance \ 800 "total_bad_amount_in_minus" \ 801 "TESTKUDOS:5" \ 802 "Wrong total_bad_amount_in_minus" 803 echo -n "Checking wire credit balance plus ... " 804 check_balance \ 805 "total_bad_amount_in_plus" \ 806 "TESTKUDOS:0" \ 807 "Wrong total_bad_amount_in_plus" 808 809 # Undo database modification 810 echo "UPDATE exchange.reserves_in SET credit.val=10 WHERE reserve_in_serial_id=1" | psql -Aqt "$DB" 811 full_reload 812 cleanup 813 } 814 815 816 # Check for incoming wire transfer amount given being 817 # lower than what exchange claims to have received. 818 function test_4() { 819 echo "===========4: deposit wire target wrong=================" 820 821 SERIALE=$(echo "SELECT coin_deposit_serial_id FROM exchange.coin_deposits WHERE (amount_with_fee).val=3 ORDER BY coin_deposit_serial_id LIMIT 1;" | psql "$DB" -Aqt) 822 OLD_COIN_SIG=$(echo "SELECT coin_sig FROM exchange.coin_deposits WHERE coin_deposit_serial_id=${SERIALE};" | psql "$DB" -Aqt) 823 echo -n "Manipulating row ${SERIALE} ..." 824 # shellcheck disable=SC2028 825 echo "INSERT INTO exchange.wire_targets (payto_uri, wire_target_h_payto) VALUES ('payto://x-taler-bank/localhost/testuser-xxlargtp', '\x1e8f31936b3cee8f8afd3aac9e38b5db42d45b721ffc4eb1e5b9ddaf1565660b');" \ 826 | psql -Aqt "$DB" 827 # shellcheck disable=SC2028 828 echo "UPDATE exchange.coin_deposits SET coin_sig='\x0f29b2ebf3cd1ecbb3e1f2a7888872058fc870c28c0065d4a7d457f2fee9eb5ec376958fc52460c8c540e583be10cf67491a6651a62c1bda68051c62dbe9130c' WHERE coin_deposit_serial_id=${SERIALE}" \ 829 | psql -Aqt "$DB" 830 echo " DONE" 831 832 run_audit 833 check_auditor_running 834 835 echo -n "Testing inconsistency detection... " 836 check_report \ 837 "bad-sig-losses" \ 838 "problem_row_id" "${SERIALE}" 839 echo -n "Testing loss report... " 840 check_report \ 841 "bad-sig-losses" \ 842 "loss" "TESTKUDOS:3.02" 843 echo -n "Testing loss operation attribution... " 844 check_report \ 845 "bad-sig-losses" \ 846 "operation" "deposit" 847 echo -n "Testing total coin_irregular_loss balance update... " 848 check_balance \ 849 "coin_irregular_loss" \ 850 "TESTKUDOS:3.02" \ 851 "wrong total coin_irregular_loss" 852 # Undo: 853 echo "UPDATE exchange.coin_deposits SET coin_sig='$OLD_COIN_SIG' WHERE coin_deposit_serial_id=${SERIALE}" | psql -Aqt "$DB" 854 855 full_reload 856 cleanup 857 } 858 859 860 # Test where h_contract_terms in the deposit table is wrong 861 # (=> bad signature) 862 function test_5() { 863 echo "===========5: deposit contract hash wrong=================" 864 # Modify h_wire hash, so it is inconsistent with 'wire' 865 CSERIAL=$(echo "SELECT coin_deposit_serial_id FROM exchange.coin_deposits WHERE (amount_with_fee).val=3 ORDER BY coin_deposit_serial_id LIMIT 1;" | psql "$DB" -Aqt) 866 SERIAL=$(echo "SELECT batch_deposit_serial_id FROM exchange.coin_deposits WHERE (amount_with_fee).val=3 ORDER BY coin_deposit_serial_id LIMIT 1;" | psql "$DB" -Aqt) 867 OLD_H=$(echo "SELECT h_contract_terms FROM exchange.batch_deposits WHERE batch_deposit_serial_id=$SERIAL;" | psql "$DB" -Aqt) 868 echo -n "Manipulating row ${SERIAL} ..." 869 # shellcheck disable=SC2028 870 echo "UPDATE exchange.batch_deposits SET h_contract_terms='\x12bb676444955c98789f219148aa31899d8c354a63330624d3d143222cf3bb8b8e16f69accd5a8773127059b804c1955696bf551dd7be62719870613332aa8d5' WHERE batch_deposit_serial_id=${SERIAL}" \ 871 | psql -At "$DB" 872 # 873 run_audit 874 check_auditor_running 875 876 echo -n "Checking bad signature detection... " 877 check_report \ 878 "bad-sig-losses" \ 879 "problem_row_id" "$CSERIAL" 880 echo -n "Testing loss report... " 881 check_report \ 882 "bad-sig-losses" \ 883 "loss" "TESTKUDOS:3.02" 884 echo -n "Testing loss operation attribution... " 885 check_report \ 886 "bad-sig-losses" \ 887 "operation" "deposit" 888 echo -n "Testing total coin_irregular_loss balance update... " 889 check_balance \ 890 "coin_irregular_loss" \ 891 "TESTKUDOS:3.02" \ 892 "wrong total coin_irregular_loss" 893 894 # Undo: 895 echo "UPDATE exchange.batch_deposits SET h_contract_terms='${OLD_H}' WHERE batch_deposit_serial_id=$SERIAL" \ 896 | psql -Aqt "$DB" 897 898 } 899 900 901 # Test where denom_sig in known_coins table is wrong 902 # (=> bad signature) 903 function test_6() { 904 echo "===========6: known_coins signature wrong=================" 905 # Modify denom_sig, so it is wrong 906 OLD_ROW=$(echo "SELECT known_coin_id FROM exchange.known_coins LIMIT 1;" | psql "$DB" -Aqt) 907 OLD_SIG=$(echo "SELECT denom_sig FROM exchange.known_coins WHERE known_coin_id=$OLD_ROW;" | psql "$DB" -Aqt) 908 COIN_PUB=$(echo "SELECT coin_pub FROM exchange.known_coins WHERE denom_sig='$OLD_SIG';" | psql "$DB" -Aqt) 909 # shellcheck disable=SC2028 910 echo "UPDATE exchange.known_coins SET denom_sig='\x0000000100000000287369672d76616c200a2028727361200a2020287320233542383731423743393036444643303442424430453039353246413642464132463537303139374131313437353746324632323332394644443146324643333445393939413336363430334233413133324444464239413833353833464536354442374335434445304441453035374438363336434541423834463843323843344446304144363030343430413038353435363039373833434431333239393736423642433437313041324632414132414435413833303432434346314139464635394244434346374436323238344143354544364131373739463430353032323241373838423837363535453434423145443831364244353638303232413123290a2020290a20290b' WHERE coin_pub='$COIN_PUB'" \ 911 | psql -Aqt "$DB" 912 913 run_audit 914 check_auditor_running 915 916 echo -n "Checking bad-signature-loss detected ..." 917 check_row \ 918 "bad-sig-losses" \ 919 "problem_row_id" "1" # Row reported is that of deposits or melt table, not known_coins 920 echo -n "Checking bad-signature-loss amount detected ..." 921 check_report_neg \ 922 "bad-sig-losses" \ 923 "loss" "TESTKUDOS:0" 924 echo -n "Checking bad-signature-loss operation detected ..." 925 check_report \ 926 "bad-sig-losses" \ 927 "operation" "deposit" 928 echo -n "Checking bad-signature-loss balance update ..." 929 check_not_balance \ 930 "coin_irregular_loss" \ 931 "TESTKUDOS:0" \ 932 "Wrong total bad sig loss" 933 934 echo -n "Undo database change ... " 935 echo "UPDATE exchange.known_coins SET denom_sig='$OLD_SIG' WHERE coin_pub='$COIN_PUB'" | psql -Aqt "$DB" 936 full_reload 937 cleanup 938 } 939 940 941 # Test where signature in the withdraw table is wrong 942 function test_7() { 943 echo "===========7: withdraw signature wrong=================" 944 # Modify reserve_sig, so it is bogus 945 HBE=$(echo 'SELECT withdraw_id FROM exchange.withdraw LIMIT 1;' | psql "$DB" -Aqt) 946 OLD_SIG=$(echo "SELECT reserve_sig FROM exchange.withdraw WHERE withdraw_id='$HBE';" | psql "$DB" -Aqt) 947 A_VAL=$(echo "SELECT (amount_with_fee).val FROM exchange.withdraw WHERE withdraw_id='$HBE';" | psql "$DB" -Aqt) 948 A_FRAC=$(echo "SELECT (amount_with_fee).frac FROM exchange.withdraw WHERE withdraw_id='$HBE';" | psql "$DB" -Aqt) 949 # Normalize, we only deal with cents in this test-case 950 A_FRAC=$(( A_FRAC / 1000000)) 951 # shellcheck disable=SC2028 952 echo "UPDATE exchange.withdraw SET reserve_sig='\x9ef381a84aff252646a157d88eded50f708b2c52b7120d5a232a5b628f9ced6d497e6652d986b581188fb014ca857fd5e765a8ccc4eb7e2ce9edcde39accaa4b' WHERE withdraw_id='$HBE'" \ 953 | psql -Aqt "$DB" 954 run_audit 955 check_auditor_running 956 957 echo -n "Checking bad signature was detected ..." 958 check_report \ 959 "bad-sig-losses" \ 960 "operation" "withdraw" 961 echo -n "Checking loss was reported ..." 962 if [ "$A_FRAC" != 0 ] 963 then 964 if [ "$A_FRAC" -lt 10 ] 965 then 966 A_PREV="0" 967 else 968 A_PREV="" 969 fi 970 EXPECTED_LOSS="TESTKUDOS:$A_VAL.$A_PREV$A_FRAC" 971 else 972 EXPECTED_LOSS="TESTKUDOS:$A_VAL" 973 fi 974 check_report \ 975 "bad-sig-losses" \ 976 "loss" "$EXPECTED_LOSS" 977 echo "Checking loss was totaled up ..." 978 check_balance \ 979 "reserves_total_bad_sig_loss" \ 980 "$EXPECTED_LOSS" \ 981 "wrong total bad sig loss" 982 983 # Undo: 984 echo "UPDATE exchange.withdraw SET reserve_sig='$OLD_SIG' WHERE withdraw_id='$HBE'" | psql -Aqt "$DB" 985 full_reload 986 cleanup 987 } 988 989 990 # Test wire transfer subject disagreement! 991 function test_8() { 992 993 echo "===========8: wire-transfer-subject disagreement===========" 994 # Technically, this call shouldn't be needed, as libeufin should already be stopped here. 995 stop_libeufin 996 OLD_ID=$(echo "SELECT exchange_incoming_id FROM libeufin_bank.taler_exchange_incoming JOIN libeufin_bank.bank_account_transactions ON (bank_transaction=bank_transaction_id) WHERE (amount).val=10 ORDER BY exchange_incoming_id LIMIT 1;" | psql "${DB}" -Aqt) \ 997 || exit_fail "Failed to SELECT FROM libeufin_bank.bank_account_transactions!" 998 OLD_WTID=$(echo "SELECT metadata FROM libeufin_bank.taler_exchange_incoming WHERE exchange_incoming_id='$OLD_ID';" \ 999 | psql "${DB}" -Aqt) 1000 NEW_WTID="\x77b4e23a41a0158299cdbe4d3247b42f907836d76dbc45c585c6a9beb196e6ca" 1001 echo -n "Modifying $OLD_ID ..." 1002 echo "UPDATE libeufin_bank.taler_exchange_incoming SET metadata='$NEW_WTID' WHERE exchange_incoming_id='$OLD_ID';" \ 1003 | psql "${DB}" -At \ 1004 || exit_fail "Failed to update taler_exchange_incoming" 1005 echo "DONE" 1006 1007 run_audit 1008 check_auditor_running 1009 1010 echo -n "Checking inconsistency diagnostic ..." 1011 check_report \ 1012 "reserve-in-inconsistency" \ 1013 "diagnostic" "wire subject does not match" 1014 echo -n "Checking expected balance report ..." 1015 check_report \ 1016 "reserve-in-inconsistency" \ 1017 "amount_exchange_expected" "TESTKUDOS:10" 1018 echo -n "Checking actual incoming balance report ..." 1019 check_report \ 1020 "reserve-in-inconsistency" \ 1021 "amount_wired" "TESTKUDOS:0" 1022 echo -n "Checking balance update (bad plus)..." 1023 check_balance \ 1024 "total_bad_amount_in_plus" \ 1025 "TESTKUDOS:10" \ 1026 "Wrong total_bad_amount_in_plus" 1027 echo -n "Checking balance update (bad minus)..." 1028 check_balance \ 1029 "total_bad_amount_in_minus" \ 1030 "TESTKUDOS:10" \ 1031 "Wrong total_bad_amount_in_plus" 1032 1033 # Undo database modification 1034 echo "UPDATE libeufin_bank.taler_exchange_incoming SET metadata='$OLD_WTID' WHERE exchange_incoming_id='$OLD_ID';" \ 1035 | psql "${DB}" -q 1036 full_reload 1037 cleanup 1038 } 1039 1040 1041 # Test wire origin disagreement! 1042 function test_9() { 1043 1044 echo "===========9: wire-origin disagreement===========" 1045 # Technically, this call shouldn't be needed, as libeufin should already be stopped here. 1046 stop_libeufin 1047 OLD_ID=$(echo "SELECT bank_transaction FROM libeufin_bank.taler_exchange_incoming JOIN libeufin_bank.bank_account_transactions ON (bank_transaction=bank_transaction_id) WHERE (amount).val=10 ORDER BY bank_transaction LIMIT 1;" | psql "${DB}" -Aqt) \ 1048 || exit_fail "Failed to SELECT FROM libeufin_bank.bank_account_transactions!" 1049 OLD_ACC=$(echo "SELECT debtor_payto FROM libeufin_bank.bank_account_transactions WHERE bank_transaction_id='$OLD_ID';" | psql "${DB}" -Aqt) 1050 1051 echo -n "Modifying $OLD_ID ..." 1052 echo "UPDATE libeufin_bank.bank_account_transactions SET debtor_payto='payto://iban/DE144373' WHERE bank_transaction_id='$OLD_ID';" \ 1053 | psql "${DB}" -At 1054 1055 run_audit 1056 check_auditor_running 1057 1058 echo -n "Testing inconsistency detection... " 1059 check_report \ 1060 misattribution-in-inconsistency \ 1061 "amount" "TESTKUDOS:10" 1062 echo -n "Testing balance update... " 1063 check_balance \ 1064 "total_misattribution_in" \ 1065 "TESTKUDOS:10" \ 1066 "Reported total_misattribution_in wrong" 1067 # Undo database modification 1068 echo "UPDATE libeufin_bank.bank_account_transactions SET debtor_payto='$OLD_ACC' WHERE bank_transaction_id='$OLD_ID';" \ 1069 | psql "${DB}" -Atq 1070 full_reload 1071 cleanup 1072 } 1073 1074 1075 # Test wire_in timestamp disagreement! 1076 function test_10() { 1077 NOW_MS=$(date +%s)000 1078 echo "===========10: wire-timestamp disagreement===========" 1079 # Technically, this call shouldn't be needed, as libeufin should already be stopped here. 1080 stop_libeufin 1081 OLD_ID=$(echo "SELECT bank_transaction FROM libeufin_bank.taler_exchange_incoming JOIN libeufin_bank.bank_account_transactions ON (bank_transaction=bank_transaction_id) WHERE (amount).val=10 ORDER BY exchange_incoming_id LIMIT 1;" | psql "${DB}" -Aqt) \ 1082 || exit_fail "Failed to SELECT FROM libeufin_bank.bank_account_transactions!" 1083 OLD_DATE=$(echo "SELECT transaction_date FROM libeufin_bank.bank_account_transactions WHERE bank_transaction_id='$OLD_ID';" | psql "${DB}" -Aqt) 1084 echo -n "Modifying $OLD_ID ..." 1085 echo "UPDATE libeufin_bank.bank_account_transactions SET transaction_date=$NOW_MS WHERE bank_transaction_id=$OLD_ID;" \ 1086 | psql "${DB}" -At 1087 1088 run_audit 1089 check_auditor_running 1090 1091 echo -n "Testing inconsistency detection diagnostic... " 1092 check_report \ 1093 row-minor-inconsistencies \ 1094 "diagnostic" "execution date mismatch" 1095 echo -n "Testing inconsistency detection table... " 1096 check_report \ 1097 row-minor-inconsistencies \ 1098 "row_table" "reserves_in" 1099 # Undo database modification 1100 echo "UPDATE libeufin_bank.bank_account_transactions SET transaction_date=$OLD_DATE WHERE bank_transaction_id=$OLD_ID;" \ 1101 | psql "${DB}" -Aqt 1102 full_reload 1103 cleanup 1104 } 1105 1106 1107 # Test for extra outgoing wire transfer. 1108 function test_11() { 1109 echo "===========11: spurious outgoing transfer ===========" 1110 # Technically, this call shouldn't be needed, as libeufin should already be stopped here. 1111 stop_libeufin 1112 launch_libeufin 1113 OTHER_IBAN=$(echo "SELECT internal_payto FROM libeufin_bank.bank_accounts ba JOIN libeufin_bank.customers bc ON (ba.owning_customer_id = bc.customer_id) WHERE username='fortytwo'" | psql "${DB}" -Aqt) 1114 1115 await_bank 1116 echo -n "Creating bogus transfer... " 1117 STATUS=$(curl -H "Content-Type: application/json" -X POST \ 1118 -u 'exchange:password' \ 1119 http://localhost:8082/accounts/exchange/taler-wire-gateway/transfer \ 1120 -d '{"credit_account":"'"$OTHER_IBAN"'","exchange_base_url":"http://exchange.example.com/","amount":"TESTKUDOS:10","wtid":"7X93HVKPHE0KAQ6KHSB3921KJGSVDMQFHMQV17885YJDMZ20XS9G","request_uid":"7X93HKPHE0KAQ6KHSB3921KJGSVDMQFHMQV17885YJDMZ20XS9G7X93HVKPHE0KAQ6KHSB3921KJGSVDMQFHMQV17885YJDMZ20XS9G"}' \ 1121 -w "%{http_code}" -s -o /dev/null) 1122 1123 if [ "$STATUS" != "200" ] 1124 then 1125 exit_fail "Expected 200 OK. Got: $STATUS" 1126 fi 1127 echo "DONE" 1128 stop_libeufin 1129 1130 run_audit 1131 check_auditor_running 1132 1133 echo -n "Testing inconsistency detection... " 1134 check_report \ 1135 "wire-out-inconsistency" \ 1136 "claimed" \ 1137 "TESTKUDOS:10" 1138 echo -n "Testing bad_amount_plus balance reporting... " 1139 check_balance \ 1140 "total_bad_amount_out_plus" \ 1141 "TESTKUDOS:10" \ 1142 "reported total_bad_amount_plus wrong" 1143 echo -n "Testing bad_amount_minus balance reporting... " 1144 check_balance \ 1145 "total_bad_amount_out_minus" \ 1146 "TESTKUDOS:0" \ 1147 "reported total_bad_amount_minus wrong" 1148 echo -n "Testing expected amount is correct... " 1149 check_report \ 1150 "wire-out-inconsistency" \ 1151 "expected" \ 1152 "TESTKUDOS:0" 1153 echo -n "Testing diagnostic message is correct... " 1154 check_report \ 1155 "wire-out-inconsistency" \ 1156 "diagnostic" \ 1157 "missing justification for outgoing wire transfer" 1158 full_reload 1159 } 1160 1161 1162 function test_12() { 1163 1164 echo "===========12: normal run with aggregator and profit drain===========" 1165 run_audit aggregator drain 1166 check_auditor_running 1167 1168 echo "Checking output" 1169 # if an emergency was detected, that is a bug and we should fail 1170 echo -n "Test for emergencies... " 1171 check_no_report "emergency" 1172 echo -n "Test for deposit confirmation detection... " 1173 check_no_report "deposit-confirmation" 1174 echo -n "Test for emergencies by count... " 1175 check_no_report "emergency-by-count" 1176 1177 echo -n "Testing bad sig loss balance... " 1178 check_balance \ 1179 "aggregation_total_bad_sig_loss" \ 1180 "TESTKUDOS:0" \ 1181 "Wrong total bad sig loss from aggregation, got unexpected loss" 1182 1183 echo -n "Testing coin irregular loss balances... " 1184 check_balance \ 1185 "coin_irregular_loss" \ 1186 "TESTKUDOS:0" \ 1187 "Wrong total bad sig loss from coins" 1188 1189 echo -n "Testing reserves bad sig loss balances... " 1190 check_balance \ 1191 "reserves_total_bad_sig_loss" \ 1192 "TESTKUDOS:0" \ 1193 "Wrong total bad sig loss from reserves" 1194 1195 echo -n "Test for aggregation wire out delta plus... " 1196 check_balance \ 1197 "aggregation_total_wire_out_delta_plus" \ 1198 "TESTKUDOS:0" \ 1199 "Expected total wire out delta plus wrong" 1200 1201 echo -n "Test for aggregation wire out delta minus... " 1202 check_balance \ 1203 "aggregation_total_wire_out_delta_minus" \ 1204 "TESTKUDOS:0" \ 1205 "Expected total wire out delta minus wrong" 1206 1207 echo -n "Test for bad incoming delta plus... " 1208 check_balance \ 1209 "total_bad_amount_in_plus" \ 1210 "TESTKUDOS:0" \ 1211 "Expected total wire in delta plus wrong" 1212 1213 echo -n "Test for total misattribution in ... " 1214 check_balance \ 1215 "total_misattribution_in" \ 1216 "TESTKUDOS:0" \ 1217 "Expected total wire in delta plus wrong" 1218 1219 echo -n "Test for bad incoming delta minus... " 1220 check_balance \ 1221 "total_bad_amount_in_minus" \ 1222 "TESTKUDOS:0" \ 1223 "Expected total wire in delta minus wrong" 1224 1225 echo -n "Test for bad outgoing delta plus... " 1226 check_balance \ 1227 "total_bad_amount_out_plus" \ 1228 "TESTKUDOS:0" \ 1229 "Expected total wire out delta plus wrong" 1230 1231 echo -n "Test for bad outgoing delta minus... " 1232 check_balance \ 1233 "total_bad_amount_out_minus" \ 1234 "TESTKUDOS:0" \ 1235 "Expected total wire in delta minus wrong" 1236 1237 echo -n "Test for misattribution amounts... " 1238 check_balance \ 1239 "total_misattribution_in" \ 1240 "TESTKUDOS:0" \ 1241 "Expected total misattribution in wrong" 1242 1243 echo -n "Checking for unexpected aggregation delta plus differences... " 1244 check_balance \ 1245 "aggregation_total_arithmetic_delta_plus" \ 1246 "TESTKUDOS:0" \ 1247 "Wrong arithmetic delta plus from aggregations" 1248 1249 echo -n "Checking for unexpected aggregation delta minus differences... " 1250 check_balance \ 1251 "aggregation_total_arithmetic_delta_minus" \ 1252 "TESTKUDOS:0" \ 1253 "Wrong arithmetic delta minus from aggregations" 1254 1255 echo -n "Checking for unexpected coin delta plus differences... " 1256 check_balance \ 1257 "coins_total_arithmetic_delta_plus" \ 1258 "TESTKUDOS:0" \ 1259 "Wrong arithmetic delta plus from coins" 1260 1261 echo -n "Checking for unexpected coin delta minus differences... " 1262 check_balance \ 1263 "coins_total_arithmetic_delta_minus" \ 1264 "TESTKUDOS:0" \ 1265 "Wrong arithmetic delta minus from coins" 1266 1267 echo -n "Checking for unexpected reserves delta plus... " 1268 check_balance \ 1269 "reserves_total_arithmetic_delta_plus" \ 1270 "TESTKUDOS:0" \ 1271 "Wrong arithmetic delta plus from reserves" 1272 1273 echo -n "Checking for unexpected reserves delta minus... " 1274 check_balance \ 1275 "reserves_total_arithmetic_delta_minus" \ 1276 "TESTKUDOS:0" \ 1277 "Wrong arithmetic delta minus from reserves" 1278 1279 echo -n "Checking for unexpected wire out differences... " 1280 check_no_report "wire-out-inconsistency" 1281 1282 # Just to test the endpoint and for logging ... 1283 call_endpoint "balances" 1284 1285 echo -n "Testing for aggregation bad sig loss... " 1286 check_balance \ 1287 "aggregation_total_bad_sig_loss" \ 1288 "TESTKUDOS:0" \ 1289 "Wrong total bad sig loss from aggregation, got unexpected loss" 1290 1291 echo -n "Testing for coin bad sig loss... " 1292 check_balance \ 1293 "coin_irregular_loss" \ 1294 "TESTKUDOS:0" \ 1295 "Wrong total bad sig loss from coins, got unexpected loss" 1296 1297 echo -n "Testing for reserves bad sig loss... " 1298 check_balance \ 1299 "reserves_total_bad_sig_loss" \ 1300 "TESTKUDOS:0" \ 1301 "Wrong total bad sig loss from reserves, got unexpected loss" 1302 1303 echo -n "Checking for unexpected aggregation delta plus differences... " 1304 check_balance \ 1305 "aggregation_total_arithmetic_delta_plus" \ 1306 "TESTKUDOS:0" \ 1307 "Wrong arithmetic delta plus from aggregations" 1308 1309 echo -n "Checking for unexpected aggregation delta minus differences... " 1310 check_balance \ 1311 "aggregation_total_arithmetic_delta_minus" \ 1312 "TESTKUDOS:0" \ 1313 "Wrong arithmetic delta minus from aggregations" 1314 1315 echo -n "Checking for unexpected coin delta plus differences... " 1316 check_balance \ 1317 "coins_total_arithmetic_delta_plus" \ 1318 "TESTKUDOS:0" \ 1319 "Wrong arithmetic delta plus from coins" 1320 1321 echo -n "Checking for unexpected coin delta minus differences... " 1322 check_balance \ 1323 "coins_total_arithmetic_delta_minus" \ 1324 "TESTKUDOS:0" \ 1325 "Wrong arithmetic delta minus from coins" 1326 1327 echo -n "Checking for unexpected reserves delta plus... " 1328 check_balance \ 1329 "reserves_total_arithmetic_delta_plus" \ 1330 "TESTKUDOS:0" \ 1331 "Wrong arithmetic delta plus from reserves" 1332 1333 echo -n "Checking for unexpected reserves delta minus... " 1334 check_balance \ 1335 "reserves_total_arithmetic_delta_minus" \ 1336 "TESTKUDOS:0" \ 1337 "Wrong arithmetic delta minus from reserves" 1338 1339 echo -n "Checking amount arithmetic inconsistency" 1340 check_no_report "amount-arithmetic-inconsistency" 1341 1342 echo -n "Checking for unexpected wire out differences " 1343 check_no_report "wire-out-inconsistency" 1344 1345 echo -n "Checking total drained... " 1346 check_balance \ 1347 "total_drained" \ 1348 "TESTKUDOS:0.1" \ 1349 "Wrong total drained amount reported" 1350 # cannot easily undo aggregator, hence full reload 1351 full_reload 1352 } 1353 1354 1355 # Test for wrong signature on refresh. 1356 function test_13() { 1357 1358 echo "===========13: wrong melt signature ===========" 1359 # Modify denom_sig, so it is wrong 1360 COIN_PUB=$(echo "SELECT old_coin_pub FROM exchange.refresh LIMIT 1;" | psql "$DB" -Aqt) 1361 OLD_SIG=$(echo "SELECT old_coin_sig FROM exchange.refresh WHERE old_coin_pub='$COIN_PUB';" | psql "$DB" -Aqt) 1362 NEW_SIG="\xba588af7c13c477dca1ac458f65cc484db8fba53b969b873f4353ecbd815e6b4c03f42c0cb63a2b609c2d726e612fd8e0c084906a41f409b6a23a08a83c89a02" 1363 echo "UPDATE exchange.refresh SET old_coin_sig='$NEW_SIG' WHERE old_coin_pub='$COIN_PUB'" \ 1364 | psql -Aqt "$DB" 1365 1366 run_audit 1367 check_auditor_running 1368 1369 echo -n "Testing inconsistency detection... " 1370 1371 check_report \ 1372 "bad-sig-losses" \ 1373 "operation" "melt" 1374 echo -n "Checking loss amount reported ..." 1375 check_report \ 1376 "bad-sig-losses" \ 1377 "loss" "TESTKUDOS:3.96" 1378 echo -n "Checking loss amount totaled ..." 1379 check_balance \ 1380 "coin_irregular_loss" \ 1381 "TESTKUDOS:3.96" \ 1382 "Loss inconsistent" 1383 1384 # cannot easily undo DELETE, hence full reload 1385 full_reload 1386 } 1387 1388 1389 # Test for wire fee disagreement 1390 function test_14() { 1391 1392 echo "===========14: wire-fee disagreement===========" 1393 1394 # Wire fees are only checked/generated once there are 1395 # actual outgoing wire transfers, so we need to run the 1396 # aggregator here. 1397 pre_audit aggregator 1398 echo "UPDATE exchange.wire_fee SET wire_fee.frac=100 WHERE wire_fee_serial=1;" \ 1399 | psql -Aqt "$DB" 1400 audit_only 1401 post_audit 1402 check_auditor_running 1403 1404 echo -n "Checking wire-fee inconsistency was detected ..." 1405 check_report \ 1406 "row-inconsistency" \ 1407 "row_table" "wire-fee" 1408 echo -n "Checking diagnostic was set correctly ..." 1409 check_report \ 1410 "row-inconsistency" \ 1411 "diagnostic" "wire fee signature invalid at given time" 1412 1413 # cannot easily undo aggregator, hence full reload 1414 full_reload 1415 } 1416 1417 1418 # Test where salt in the deposit table is wrong 1419 function test_15() { 1420 echo "===========15: deposit wire salt wrong=================" 1421 1422 # Modify wire_salt hash, so it is inconsistent 1423 ##SALT=$(echo "SELECT wire_salt FROM exchange.deposits WHERE deposit_serial_id=1;" | psql -Aqt "$DB") 1424 SALT=$(echo "SELECT wire_salt FROM exchange.batch_deposits WHERE batch_deposit_serial_id=1;" | psql -Aqt "$DB") 1425 # shellcheck disable=SC2028 1426 echo "UPDATE exchange.batch_deposits SET wire_salt='\x1197cd7f7b0e13ab1905fedb36c536a2' WHERE batch_deposit_serial_id=1;" \ 1427 | psql -Aqt "$DB" 1428 1429 run_audit 1430 check_auditor_running 1431 1432 echo -n "Checking broken deposit signature detected ..." 1433 check_report \ 1434 "bad-sig-losses" \ 1435 "operation" "deposit" 1436 1437 # Restore DB 1438 echo "UPDATE exchange.batch_deposits SET wire_salt='$SALT' WHERE batch_deposit_serial_id=1;" \ 1439 | psql -Aqt "$DB" 1440 stop_auditor_httpd 1441 1442 } 1443 1444 1445 # Test where wired amount (wire out) is wrong 1446 function test_16() { 1447 echo "===========16: incorrect wire_out amount=================" 1448 1449 # First, we need to run the aggregator so we even 1450 # have a wire_out to modify. 1451 pre_audit aggregator 1452 check_auditor_running 1453 stop_libeufin 1454 OLD_AMOUNT_VAL=$(echo "SELECT (amount).val FROM libeufin_bank.bank_account_transactions WHERE debtor_name='Exchange Company' AND direction='debit';" | psql "${DB}" -Aqt) 1455 OLD_AMOUNT_FRAC=$(echo "SELECT (amount).frac FROM libeufin_bank.bank_account_transactions WHERE debtor_name='Exchange Company' AND direction='debit';" | psql "${DB}" -Aqt) 1456 if [[ 0 = "$OLD_AMOUNT_FRAC" ]] 1457 then 1458 OLD_AMOUNT="TESTKUDOS:${OLD_AMOUNT_VAL}" 1459 else 1460 OLD_AMOUNT_CENTS=$(($OLD_AMOUNT_FRAC / 1000000)) 1461 if [[ 10 -gt "$OLD_AMOUNT_CENTS" ]] 1462 then 1463 OLD_AMOUNT="TESTKUDOS:${OLD_AMOUNT_VAL}.0${OLD_AMOUNT_CENTS}" 1464 else 1465 OLD_AMOUNT="TESTKUDOS:${OLD_AMOUNT_VAL}.${OLD_AMOUNT_CENTS}" 1466 fi 1467 fi 1468 NEW_AMOUNT="TESTKUDOS:50" 1469 echo "UPDATE libeufin_bank.bank_account_transactions SET amount=(50,0) WHERE debtor_name='Exchange Company';" \ 1470 | psql "${DB}" -q 1471 launch_libeufin 1472 await_bank 1473 1474 audit_only 1475 check_auditor_running 1476 1477 echo -n "Testing wire-out-inconsistency-expected... " 1478 check_report \ 1479 "wire-out-inconsistency" \ 1480 "expected" \ 1481 "$OLD_AMOUNT" 1482 echo -n "Testing wire-out-inconsistency-claimed... " 1483 check_report \ 1484 "wire-out-inconsistency" \ 1485 "claimed" \ 1486 "$NEW_AMOUNT" 1487 echo -n "Testing bad_amount_minus balance reporting... " 1488 check_balance \ 1489 "total_bad_amount_out_minus" \ 1490 "TESTKUDOS:0" \ 1491 "reported total_bad_amount_minus wrong" 1492 echo -n "Testing bad_amount_plus balance reporting... " 1493 check_not_balance \ 1494 "total_bad_amount_out_plus" \ 1495 "TESTKUDOS:0" \ 1496 "reported total_bad_amount_plus wrong" 1497 1498 stop_libeufin 1499 echo "Second modification: wire nothing" 1500 NEW_AMOUNT="TESTKUDOS:0" 1501 echo "UPDATE libeufin_bank.bank_account_transactions SET amount=(0,0) WHERE debtor_name='Exchange Company';" \ 1502 | psql "${DB}" -q 1503 launch_libeufin 1504 audit_only 1505 stop_libeufin 1506 1507 echo -n "Testing wire-out-inconsistency-expected... " 1508 check_report \ 1509 "wire-out-inconsistency" \ 1510 "expected" \ 1511 "$OLD_AMOUNT" 1512 echo -n "Testing wire-out-inconsistency-claimed... " 1513 check_report \ 1514 "wire-out-inconsistency" \ 1515 "claimed" \ 1516 "$NEW_AMOUNT" 1517 echo -n "Testing bad_amount_minus balance reporting... " 1518 check_balance \ 1519 "total_bad_amount_out_minus" \ 1520 "$OLD_AMOUNT" \ 1521 "reported total_bad_amount_minus wrong" 1522 echo -n "Testing bad_amount_plus balance reporting... " 1523 check_balance \ 1524 "total_bad_amount_out_plus" \ 1525 "TESTKUDOS:0" \ 1526 "reported total_bad_amount_plus wrong" 1527 1528 post_audit 1529 1530 # cannot easily undo aggregator, hence full reload 1531 full_reload 1532 } 1533 1534 1535 # Test where wire-out timestamp is wrong 1536 function test_17() { 1537 echo "===========17: incorrect wire_out timestamp=================" 1538 1539 # First, we need to run the aggregator so we even 1540 # have a wire_out to modify. 1541 pre_audit aggregator 1542 stop_libeufin 1543 1544 echo -n "Modifying timestamp of existing wire_out transaction... " 1545 OLD_DATE=$(echo "SELECT transaction_date FROM libeufin_bank.bank_account_transactions WHERE debtor_name='Exchange Company' AND direction='debit';" | psql "${DB}" -Aqt) 1546 # Note: need - interval '1h' as "NOW()" may otherwise be exactly what is already in the DB 1547 # (due to rounding, if this machine is fast...) 1548 NOW_1HR=$(( $(date +%s) - 3600)) 1549 1550 echo "UPDATE libeufin_bank.bank_account_transactions SET transaction_date='${NOW_1HR}000000' WHERE debtor_name='Exchange Company';" \ 1551 | psql "${DB}" -q 1552 echo "DONE" 1553 1554 launch_libeufin 1555 await_bank 1556 audit_only 1557 post_audit 1558 check_auditor_running 1559 1560 echo -n "Testing inconsistency detection... " 1561 check_report \ 1562 row-minor-inconsistencies \ 1563 "row_table" "wire_out" 1564 1565 echo -n "Testing inconsistency diagnostic... " 1566 call_endpoint "row-minor-inconsistencies" 1567 DIAG=$(jq -r .row_minor_inconsistencies[0].diagnostic < "${MY_TMP_DIR}/row-minor-inconsistencies.json" | awk '{print $1 " " $2 " " $3}') 1568 if [ "$DIAG" != "execution date mismatch" ] 1569 then 1570 exit_fail "Reported diagnostic wrong: $DIAG" 1571 fi 1572 echo "PASS" 1573 1574 # cannot easily undo aggregator, hence full reload 1575 full_reload 1576 } 1577 1578 1579 # Test where we trigger an emergency. 1580 function test_18() { 1581 echo "===========18: emergency=================" 1582 1583 echo "DELETE FROM exchange.withdraw;" \ 1584 | psql -Aqt "$DB" -q 1585 1586 run_audit 1587 check_auditor_running 1588 1589 echo -n "Testing bad reserve balance summary reporting ... " 1590 # note: we check "suppressed" to only check the *existence* here. 1591 check_report \ 1592 "reserve-balance-summary-wrong-inconsistency" \ 1593 "suppressed" "false" 1594 echo -n "Testing emergency detection... " 1595 check_report \ 1596 "emergency" \ 1597 "suppressed" "false" 1598 echo -n "Testing emergency detection by count... " 1599 check_report \ 1600 "emergency-by-count" \ 1601 "suppressed" "false" 1602 echo -n "Testing escrow balance calculation impossibility... " 1603 check_report \ 1604 "amount-arithmetic-inconsistency" \ 1605 "suppressed" "false" 1606 echo -n "Testing loss calculation by count... " 1607 check_not_balance \ 1608 "coins_emergencies_loss_by_count" \ 1609 "TESTKUDOS:0" \ 1610 "Emergency by count loss not reported" 1611 echo -n "Testing loss calculation... " 1612 check_not_balance \ 1613 "coins_emergencies_loss" \ 1614 "TESTKUDOS:0" \ 1615 "Emergency loss not reported" 1616 # cannot easily undo broad DELETE operation, hence full reload 1617 full_reload 1618 } 1619 1620 1621 # Test where reserve closure was done properly 1622 function test_19() { 1623 echo "===========19: reserve closure done properly =================" 1624 1625 OLD_TIME=$(echo "SELECT execution_date FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1626 OLD_VAL=$(echo "SELECT (credit).val FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1627 RES_PUB=$(echo "SELECT reserve_pub FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1628 OLD_EXP=$(echo "SELECT expiration_date FROM exchange.reserves WHERE reserve_pub='${RES_PUB}';" | psql "$DB" -Aqt) 1629 VAL_DELTA=1 1630 NEW_TIME=$(( OLD_TIME - 3024000000000)) # 5 weeks 1631 NEW_EXP=$(( OLD_EXP - 3024000000000)) # 5 weeks 1632 NEW_CREDIT=$(( OLD_VAL + VAL_DELTA)) 1633 echo "UPDATE exchange.reserves_in SET execution_date='${NEW_TIME}',credit.val=${NEW_CREDIT} WHERE reserve_in_serial_id=1;" \ 1634 | psql -Aqt "$DB" 1635 echo "UPDATE exchange.reserves SET current_balance.val=${VAL_DELTA}+(current_balance).val,expiration_date='${NEW_EXP}' WHERE reserve_pub='${RES_PUB}';" \ 1636 | psql -Aqt "$DB" 1637 # Need to run with the aggregator so the reserve closure happens 1638 run_audit aggregator 1639 check_auditor_running 1640 1641 echo -n "Testing reserve closure was done correctly... " 1642 check_no_report "reserve-not-closed-inconsistency" 1643 echo -n "Testing no bogus transfers detected... " 1644 check_no_report "wire-out-inconsistency" 1645 1646 # cannot easily undo aggregator, hence full reload 1647 full_reload 1648 } 1649 1650 1651 # Test where reserve closure was not done properly 1652 function test_20() { 1653 echo "===========20: reserve closure missing =================" 1654 1655 OLD_TIME=$(echo "SELECT execution_date FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1656 OLD_VAL=$(echo "SELECT (credit).val FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1657 RES_PUB=$(echo "SELECT reserve_pub FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1658 NEW_TIME=$(( OLD_TIME - 3024000000000 )) # 5 weeks 1659 NEW_CREDIT=$(( OLD_VAL + 100 )) 1660 echo "UPDATE exchange.reserves_in SET execution_date='${NEW_TIME}',credit.val=${NEW_CREDIT} WHERE reserve_in_serial_id=1;" \ 1661 | psql -Aqt "$DB" 1662 echo "UPDATE exchange.reserves SET current_balance.val=100+(current_balance).val WHERE reserve_pub='${RES_PUB}';" \ 1663 | psql -Aqt "$DB" 1664 1665 # This time, run without the aggregator so the reserve closure is skipped! 1666 run_audit 1667 check_auditor_running 1668 1669 echo -n "Testing reserve closure missing detected... " 1670 check_report \ 1671 "reserve-not-closed-inconsistency" \ 1672 "suppressed" "false" 1673 echo -n "Testing balance updated correctly... " 1674 check_not_balance \ 1675 "total_balance_reserve_not_closed" \ 1676 "TESTKUDOS:0" \ 1677 "Reported total amount wrong" 1678 1679 # Undo 1680 echo "UPDATE exchange.reserves_in SET execution_date='${OLD_TIME}',credit.val=${OLD_VAL} WHERE reserve_in_serial_id=1;" \ 1681 | psql -Aqt "$DB" 1682 echo "UPDATE exchange.reserves SET current_balance.val=(current_balance).val-100 WHERE reserve_pub='${RES_PUB}';" \ 1683 | psql -Aqt "$DB" 1684 1685 full_reload 1686 } 1687 1688 1689 # Test reserve closure reported but wire transfer missing detection 1690 function test_21() { 1691 echo "===========21: reserve closure missreported =================" 1692 1693 OLD_TIME=$(echo "SELECT execution_date FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1694 OLD_VAL=$(echo "SELECT (credit).val FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1695 RES_PUB=$(echo "SELECT reserve_pub FROM exchange.reserves_in WHERE reserve_in_serial_id=1;" | psql "$DB" -Aqt) 1696 OLD_EXP=$(echo "SELECT expiration_date FROM exchange.reserves WHERE reserve_pub='${RES_PUB}';" | psql "$DB" -Aqt) 1697 VAL_DELTA=1 1698 NEW_TIME=$(( OLD_TIME - 3024000000000 )) # 5 weeks 1699 NEW_EXP=$(( OLD_EXP - 3024000000000 )) # 5 weeks 1700 NEW_CREDIT=$(( OLD_VAL + VAL_DELTA )) 1701 echo "UPDATE exchange.reserves_in SET execution_date='${NEW_TIME}',credit.val=${NEW_CREDIT} WHERE reserve_in_serial_id=1;" \ 1702 | psql -Aqt "$DB" 1703 echo "UPDATE exchange.reserves SET current_balance.val=${VAL_DELTA}+(current_balance).val,expiration_date='${NEW_EXP}' WHERE reserve_pub='${RES_PUB}';" \ 1704 | psql -Aqt "$DB" 1705 1706 # Need to first run the aggregator so the transfer is marked as done 1707 pre_audit aggregator 1708 stop_libeufin 1709 1710 # remove wire transfer from bank DB 1711 echo "DELETE FROM libeufin_bank.bank_account_transactions WHERE debtor_name='Exchange Company';" \ 1712 | psql "${DB}" -q 1713 1714 launch_libeufin 1715 audit_only 1716 post_audit 1717 check_auditor_running 1718 1719 echo -n "Testing reserve_in inconsistency detection... " 1720 check_report \ 1721 row-minor-inconsistencies \ 1722 "row_table" "reserves_in" 1723 1724 echo -n "Testing lack of reserve closure transaction detected... " 1725 check_report \ 1726 "closure-lags" \ 1727 "suppressed" "false" 1728 echo -n "Checking closure lag amount ..." 1729 check_report \ 1730 "closure-lags" \ 1731 "amount" "TESTKUDOS:${VAL_DELTA}" 1732 echo -n "Checking closure lag total balance ..." 1733 check_balance \ 1734 "total_closure_amount_lag" \ 1735 "TESTKUDOS:${VAL_DELTA}" \ 1736 "Reported total_closure_amount_lag wrong" 1737 # cannot easily undo aggregator, hence full reload 1738 full_reload 1739 } 1740 1741 1742 # Test use of withdraw-expired denomination key 1743 function test_22() { 1744 echo "===========22: denomination key expired =================" 1745 1746 S_DENOM=$(echo 'SELECT denom_serials[1] FROM exchange.withdraw LIMIT 1;' | psql "$DB" -Aqt) 1747 1748 OLD_START=$(echo "SELECT valid_from FROM exchange.denominations WHERE denominations_serial='${S_DENOM}';" | psql "$DB" -Aqt) 1749 OLD_WEXP=$(echo "SELECT expire_withdraw FROM exchange.denominations WHERE denominations_serial='${S_DENOM}';" | psql "$DB" -Aqt) 1750 # Basically expires 'immediately', so that the withdraw must have been 'invalid' 1751 NEW_WEXP=$OLD_START 1752 1753 echo "UPDATE exchange.denominations SET expire_withdraw=${NEW_WEXP} WHERE denominations_serial='${S_DENOM}';" | psql -Aqt "$DB" 1754 1755 1756 run_audit 1757 check_auditor_running 1758 1759 echo -n "Testing inconsistency detection... " 1760 check_report \ 1761 "denomination-key-validity-withdraw-inconsistency" \ 1762 "suppressed" "false" 1763 call_endpoint "denomination-key-validity-withdraw-inconsistency" 1764 1765 # Undo modification 1766 echo "UPDATE exchange.denominations SET expire_withdraw=${OLD_WEXP} WHERE denominations_serial='${S_DENOM}';" | psql -Aqt "$DB" 1767 1768 full_reload 1769 } 1770 1771 1772 # Test calculation of wire-out amounts 1773 function test_23() { 1774 echo "===========23: wire out calculations =================" 1775 1776 # Need to first run the aggregator so the transfer is marked as done exists 1777 pre_audit aggregator 1778 1779 OLD_AMOUNT=$(echo "SELECT (amount).frac FROM exchange.wire_out WHERE wireout_uuid=1;" | psql "$DB" -Aqt) 1780 NEW_AMOUNT=$(( OLD_AMOUNT - 1000000 )) 1781 echo "UPDATE exchange.wire_out SET amount.frac=${NEW_AMOUNT} WHERE wireout_uuid=1;" \ 1782 | psql -Aqt "$DB" 1783 1784 audit_only 1785 post_audit 1786 check_auditor_running 1787 1788 echo -n "Testing inconsistency detection... " 1789 check_report \ 1790 "wire-out-inconsistency" \ 1791 "suppressed" "false" 1792 echo -n "Testing inconsistency row report... " 1793 check_report \ 1794 "wire-out-inconsistency" \ 1795 "wire_out_row_id" "1" 1796 echo -n "Testing inconsistency balance... " 1797 check_balance \ 1798 "aggregation_total_wire_out_delta_plus" \ 1799 "TESTKUDOS:0" \ 1800 "Reported aggregation_total_wire_out_delta_plus wrong" 1801 echo -n "Testing inconsistency balance change ... " 1802 check_balance \ 1803 "aggregation_total_wire_out_delta_minus" \ 1804 "TESTKUDOS:0.01" \ 1805 "Reported aggregation_total_wire_out_delta_minus wrong" 1806 1807 echo "Second pass: changing how amount is wrong to other direction" 1808 NEW_AMOUNT=$(( OLD_AMOUNT + 1000000 )) 1809 echo "UPDATE exchange.wire_out SET amount.frac=${NEW_AMOUNT} WHERE wireout_uuid=1;" | psql -Aqt "$DB" 1810 1811 pre_audit 1812 audit_only 1813 post_audit 1814 1815 echo -n "Testing inconsistency detection... " 1816 1817 echo -n "Testing inconsistency detection... " 1818 check_report \ 1819 "wire-out-inconsistency" \ 1820 "suppressed" "false" 1821 echo -n "Testing inconsistency row report... " 1822 check_report \ 1823 "wire-out-inconsistency" \ 1824 "wire_out_row_id" "1" 1825 echo -n "Testing inconsistency balance... " 1826 check_balance \ 1827 "aggregation_total_wire_out_delta_plus" \ 1828 "TESTKUDOS:0.01" \ 1829 "Reported aggregation_total_wire_out_delta_plus wrong" 1830 echo -n "Testing inconsistency balance change ... " 1831 check_balance \ 1832 "aggregation_total_wire_out_delta_minus" \ 1833 "TESTKUDOS:0" \ 1834 "Reported aggregation_total_wire_out_delta_minus wrong" 1835 1836 # cannot easily undo aggregator, hence full reload 1837 full_reload 1838 } 1839 1840 1841 # Test for missing deposits in exchange database. 1842 function test_24() { 1843 echo "===========24: deposits missing ===========" 1844 # Modify denom_sig, so it is wrong 1845 CNT=$(echo "SELECT COUNT(*) FROM auditor.auditor_deposit_confirmations;" | psql -Aqt "$DB") 1846 if [ "$CNT" = "0" ] 1847 then 1848 echo "Skipping deposits missing test: no deposit confirmations in database!" 1849 else 1850 echo "DELETE FROM exchange.batch_deposits;" | psql -Aqt "$DB" 1851 echo "DELETE FROM exchange.batch_deposits WHERE batch_deposit_serial_id=1;" \ 1852 | psql -Aqt "$DB" 1853 1854 run_audit 1855 check_auditor_running 1856 1857 echo -n "Testing inconsistency detection... " 1858 call_endpoint "balances" 1859 check_report \ 1860 "deposit-confirmation" \ 1861 "suppressed" "false" 1862 echo -n "Testing inconsistency detection balance change ... " 1863 check_not_balance \ 1864 "total_missed_deposit_confirmations" \ 1865 "TESTKUDOS:0" \ 1866 "Expected non-zero total missing deposit confirmation amount" 1867 # cannot easily undo DELETE, hence full reload 1868 full_reload 1869 fi 1870 } 1871 1872 1873 # Test for inconsistent coin history. 1874 function test_25() { 1875 1876 echo "=========25: inconsistent coin history=========" 1877 1878 # Drop refund, so coin history is bogus. 1879 echo -n "Dropping refund from DB... " 1880 echo "DELETE FROM exchange.refunds WHERE refund_serial_id=1;" \ 1881 | psql -At "$DB" 1882 1883 run_audit aggregator 1884 check_auditor_running 1885 1886 echo -n "Testing inconsistency detection... " 1887 check_report \ 1888 "coin-inconsistency" \ 1889 "profitable" "true" 1890 echo -n "Testing emergency risk reporting... " 1891 check_report \ 1892 "emergency" \ 1893 "denom_risk" "TESTKUDOS:10" 1894 echo -n "Testing emergency loss reporting... " 1895 check_report \ 1896 "emergency" \ 1897 "denom_loss" "TESTKUDOS:5.98" 1898 echo -n "Testing double-spending reporting... " 1899 check_balance \ 1900 "coins_reported_emergency_risk_by_amount" \ 1901 "TESTKUDOS:10" \ 1902 "double-spending not detected" 1903 echo -n "Testing balance loss update... " 1904 check_balance \ 1905 "aggregation_total_coin_delta_minus" \ 1906 "TESTKUDOS:5.98" \ 1907 "aggregation total coin delta minus not reported" 1908 # cannot easily undo DELETE, hence full reload 1909 full_reload 1910 } 1911 1912 1913 # Test for deposit wire target malformed 1914 function test_26() { 1915 echo "===========26: deposit wire target malformed =================" 1916 1917 # Expects 'payto_uri', not 'url' (also breaks signature, but we cannot even check that). 1918 SERIAL=$(echo "SELECT batch_deposit_serial_id FROM exchange.coin_deposits WHERE (amount_with_fee).val=3 ORDER BY batch_deposit_serial_id LIMIT 1" | psql "$DB" -Aqt) 1919 OLD_WIRE_ID=$(echo "SELECT wire_target_h_payto FROM exchange.batch_deposits WHERE batch_deposit_serial_id=${SERIAL};" | psql "$DB" -Aqt) 1920 # shellcheck disable=SC2028 1921 echo "INSERT INTO exchange.wire_targets (payto_uri, wire_target_h_payto) VALUES ('payto://x-taler-bank/localhost/testuser-xxlargtp', '\x1e8f31936b3cee8f8afd3aac9e38b5db42d45b721ffc4eb1e5b9ddaf1565660b');" \ 1922 | psql "$DB" -Aqt 1923 # shellcheck disable=SC2028 1924 echo "UPDATE exchange.batch_deposits SET wire_target_h_payto='\x1e8f31936b3cee8f8afd3aac9e38b5db42d45b721ffc4eb1e5b9ddaf1565660b' WHERE batch_deposit_serial_id=${SERIAL};" \ 1925 | psql -Aqt "$DB" 1926 1927 run_audit 1928 check_auditor_running 1929 1930 check_balance \ 1931 "coin_irregular_loss" \ 1932 "TESTKUDOS:3.02" \ 1933 "wrong total irregular coin loss" 1934 call_endpoint "bad_sig_losses" 1935 echo -n "Checking correct operation of loss reported... " 1936 check_report \ 1937 "bad-sig-losses" \ 1938 "operation" "deposit" 1939 echo -n "Checking correct loss reported... " 1940 check_report \ 1941 "bad-sig-losses" \ 1942 "loss" "TESTKUDOS:3.02" 1943 echo -n "Checking correct problem row ID reported... " 1944 check_report \ 1945 "bad-sig-losses" \ 1946 "problem_row_id" "$SERIAL" 1947 1948 # Undo: 1949 echo "UPDATE exchange.batch_deposits SET wire_target_h_payto='$OLD_WIRE_ID' WHERE batch_deposit_serial_id=${SERIAL}" \ 1950 | psql -Aqt "$DB" 1951 } 1952 1953 1954 # Test where denom_sig in known_coins table is wrong 1955 # (=> bad signature) AND the coin is used in aggregation 1956 function test_27() { 1957 1958 echo "===========27: known_coins signature wrong=================" 1959 # Modify denom_sig, so it is wrong 1960 OLD_SIG=$(echo 'SELECT denom_sig FROM exchange.known_coins LIMIT 1;' | psql "$DB" -Aqt) 1961 COIN_PUB=$(echo "SELECT coin_pub FROM exchange.known_coins WHERE denom_sig='$OLD_SIG';" | psql "$DB" -Aqt) 1962 # shellcheck disable=SC2028 1963 echo "UPDATE exchange.known_coins SET denom_sig='\x0000000100000000287369672d76616c200a2028727361200a2020287320233542383731423743393036444643303442424430453039353246413642464132463537303139374131313437353746324632323332394644443146324643333445393939413336363430334233413133324444464239413833353833464536354442374335434445304441453035374438363336434541423834463843323843344446304144363030343430413038353435363039373833434431333239393736423642433437313041324632414132414435413833303432434346314139464635394244434346374436323238344143354544364131373739463430353032323241373838423837363535453434423145443831364244353638303232413123290a2020290a20290b' WHERE coin_pub='$COIN_PUB'" \ 1964 | psql -Aqt "$DB" 1965 1966 run_audit aggregator 1967 check_auditor_running 1968 1969 echo -n "Testing inconsistency detection... " 1970 check_report_neg \ 1971 "bad-sig-losses" \ 1972 "loss" "TESTKUDOS:0" 1973 echo -n "Testing inconsistency detection operation attribution... " 1974 check_report \ 1975 "bad-sig-losses" \ 1976 "operation" "wire" 1977 echo -n "Testing table attribution for inconsistency... " 1978 check_report \ 1979 "row-inconsistency" \ 1980 "row_table" "deposit" 1981 echo -n "Check signature loss was accumulated ..." 1982 check_not_balance \ 1983 "aggregation_total_bad_sig_loss" \ 1984 "TESTKUDOS:0" \ 1985 "Wrong aggregation_total_bad_sig_loss" 1986 1987 # cannot easily undo aggregator, hence full reload 1988 full_reload 1989 } 1990 1991 1992 1993 # Test where fees known to the auditor differ from those 1994 # accounted for by the exchange 1995 function test_28() { 1996 echo "===========28: withdraw fee inconsistency =================" 1997 1998 echo "UPDATE exchange.withdraw SET amount_with_fee.val=(amount_with_fee).val+1 WHERE withdraw_id=1;" | psql -Aqt "$DB" 1999 2000 run_audit 2001 check_auditor_running 2002 2003 echo -n "Testing inconsistency detection... " 2004 check_report \ 2005 "row-inconsistency" \ 2006 "row_table" "withdraw" 2007 # Undo 2008 full_reload 2009 } 2010 2011 2012 # Test where fees known to the auditor differ from those 2013 # accounted for by the exchange 2014 function test_29() { 2015 echo "===========29: melt fee inconsistency =================" 2016 2017 echo "UPDATE exchange.denominations SET fee_refresh.frac=5000000 WHERE (coin).val=10;" | psql -Aqt "$DB" 2018 2019 run_audit 2020 check_auditor_running 2021 2022 echo -n "Testing inconsistency detection... " 2023 check_report_neg \ 2024 "bad-sig-losses" \ 2025 "loss" "TESTKUDOS:0" 2026 echo -n "Testing inconsistency was reported as profitable... " 2027 check_report \ 2028 "amount-arithmetic-inconsistency" \ 2029 "profitable" "true" 2030 echo -n "Testing no emergency was raised... " 2031 check_no_report "emergency" 2032 2033 # Undo 2034 echo "UPDATE exchange.denominations SET fee_refresh.frac=3000000 WHERE (coin).val=10;" | psql -Aqt "$DB" 2035 2036 full_reload 2037 } 2038 2039 2040 # Test where fees known to the auditor differ from those 2041 # accounted for by the exchange 2042 function test_30() { 2043 echo "===========30: deposit fee inconsistency =================" 2044 2045 echo "UPDATE exchange.denominations SET fee_deposit.frac=5000000 WHERE (coin).val=8;" | psql -Aqt "$DB" 2046 2047 run_audit aggregator 2048 check_auditor_running 2049 2050 echo -n "Testing inconsistency detection... " 2051 2052 check_not_balance \ 2053 "coin_irregular_loss" \ 2054 "TESTKUDOS:0" \ 2055 "Reported total coin_irregular_loss wrong" 2056 check_report \ 2057 "bad-sig-losses" \ 2058 "operation" "deposit" 2059 # Undo 2060 echo "UPDATE exchange.denominations SET fee_deposit.frac=2000000 WHERE (coin).val=8;" | psql -Aqt "$DB" 2061 full_reload 2062 } 2063 2064 2065 2066 2067 # Test where denom_sig in known_coins table is wrong 2068 # (=> bad signature) 2069 function test_31() { 2070 echo "===========31: known_coins signature wrong w. aggregation=================" 2071 # Modify denom_sig, so it is wrong 2072 OLD_SIG=$(echo 'SELECT denom_sig FROM exchange.known_coins LIMIT 1;' | psql "$DB" -Aqt) 2073 COIN_PUB=$(echo "SELECT coin_pub FROM exchange.known_coins WHERE denom_sig='$OLD_SIG';" | psql "$DB" -Aqt) 2074 # shellcheck disable=SC2028 2075 echo "UPDATE exchange.known_coins SET denom_sig='\x0000000100000000287369672d76616c200a2028727361200a2020287320233542383731423743393036444643303442424430453039353246413642464132463537303139374131313437353746324632323332394644443146324643333445393939413336363430334233413133324444464239413833353833464536354442374335434445304441453035374438363336434541423834463843323843344446304144363030343430413038353435363039373833434431333239393736423642433437313041324632414132414435413833303432434346314139464635394244434346374436323238344143354544364131373739463430353032323241373838423837363535453434423145443831364244353638303232413123290a2020290a20290b' WHERE coin_pub='$COIN_PUB'" \ 2076 | psql -Aqt "$DB" 2077 2078 run_audit aggregator 2079 check_auditor_running 2080 2081 echo -n "Testing inconsistency detection... " 2082 check_report \ 2083 "bad-sig-losses" \ 2084 "operation" "wire" 2085 echo -n "Testing inconsistency balance update... " 2086 check_not_balance \ 2087 "aggregation_total_bad_sig_loss" \ 2088 "TESTKUDOS:0" \ 2089 "Missed updating aggregation_total_bad_sig_loss" 2090 2091 # Cannot undo aggregation, do full reload 2092 full_reload 2093 cleanup 2094 } 2095 2096 2097 # *************** Main test loop starts here ************** 2098 2099 2100 # Run all the tests against the database given in $1. 2101 # Sets $fail to 0 on success, non-zero on failure. 2102 function check_with_database() 2103 { 2104 BASEDB="$1" 2105 CONF="$1.conf" 2106 export CONF 2107 echo "Running test suite with database $BASEDB using configuration $CONF" 2108 MASTER_PRIV_FILE="${BASEDB}.mpriv" 2109 taler-exchange-config \ 2110 -f \ 2111 -c "${CONF}" \ 2112 -s exchange-offline \ 2113 -o MASTER_PRIV_FILE \ 2114 -V "${MASTER_PRIV_FILE}" 2115 2116 # Load database 2117 full_reload 2118 2119 # Run test suite 2120 fail=0 2121 for i in $TESTS 2122 do 2123 "test_$i" 2124 if test 0 != $fail 2125 then 2126 break 2127 fi 2128 done 2129 echo "Cleanup (disabled, leaving database $DB behind)" 2130 # dropdb $DB 2131 } 2132 2133 # When the script is not run as root, setup a temporary directory for the 2134 # postgres database. 2135 # Sets PGHOST accordingly to the freshly created socket. 2136 function perform_initdb() { 2137 # Available directly in path? 2138 INITDB_BIN=$(command -v initdb) || true 2139 if [[ -n "$INITDB_BIN" ]]; then 2140 echo " FOUND (in path) at $INITDB_BIN" 2141 else 2142 HAVE_INITDB=$(find /usr -name "initdb" 2> /dev/null \ 2143 | head -1 2> /dev/null \ 2144 | grep postgres) \ 2145 || exit_skip " MISSING" 2146 echo " FOUND at $(dirname "$HAVE_INITDB")" 2147 INITDB_BIN=$(echo "$HAVE_INITDB" | grep bin/initdb | grep postgres | sort -n | tail -n1) 2148 fi 2149 POSTGRES_PATH=$(dirname "$INITDB_BIN") 2150 2151 TMPDIR="$MY_TMP_DIR/postgres" 2152 mkdir -p "$TMPDIR" 2153 echo -n "Setting up Postgres DB at $TMPDIR ..." 2154 $INITDB_BIN \ 2155 --no-sync \ 2156 --auth=trust \ 2157 -D "${TMPDIR}" \ 2158 > "${MY_TMP_DIR}/postgres-dbinit.log" \ 2159 2> "${MY_TMP_DIR}/postgres-dbinit.err" \ 2160 || { 2161 echo "FAILED!" 2162 echo "Last entries in ${MY_TMP_DIR}/postgres-dbinit.err:" 2163 tail "${MY_TMP_DIR}/postgres-dbinit.err" 2164 exit 1 2165 } 2166 echo "DONE" 2167 2168 # Once we move to PG16, we can use: 2169 # --set listen_addresses='' \ 2170 # --set fsync=off \ 2171 # --set max_wal_senders=0 \ 2172 # --set synchronous_commit=off \ 2173 # --set wal_level=minimal \ 2174 # --set unix_socket_directories="${TMPDIR}/sockets" \ 2175 2176 2177 SOCKETDIR="${TMPDIR}/sockets" 2178 mkdir "${SOCKETDIR}" 2179 2180 echo -n "Launching Postgres service" 2181 2182 cat - >> "$TMPDIR/postgresql.conf" <<EOF 2183 unix_socket_directories='${TMPDIR}/sockets' 2184 fsync=off 2185 max_wal_senders=0 2186 synchronous_commit=off 2187 wal_level=minimal 2188 listen_addresses='' 2189 EOF 2190 2191 grep -v host \ 2192 < "$TMPDIR/pg_hba.conf" \ 2193 > "$TMPDIR/pg_hba.conf.new" 2194 mv "$TMPDIR/pg_hba.conf.new" "$TMPDIR/pg_hba.conf" 2195 "${POSTGRES_PATH}/pg_ctl" \ 2196 -D "$TMPDIR" \ 2197 -l "${MY_TMP_DIR}/postgres.log" \ 2198 start \ 2199 > "${MY_TMP_DIR}/postgres-start.log" \ 2200 2> "${MY_TMP_DIR}/postgres-start.err" 2201 echo " DONE" 2202 PGHOST="$TMPDIR/sockets" 2203 export PGHOST 2204 } 2205 2206 2207 # *************** Main logic starts here ************** 2208 2209 # ####### Setup globals ###### 2210 # Postgres database to use (must match configuration file) 2211 export DB="auditor-basedb" 2212 2213 # test required commands exist 2214 echo "Testing for jq" 2215 jq -h > /dev/null || exit_skip "jq required" 2216 echo "Testing for faketime" 2217 faketime -h > /dev/null || exit_skip "faketime required" 2218 # NOTE: really check for all three libeufin commands? 2219 echo "Testing for libeufin" 2220 libeufin-bank --help >/dev/null 2> /dev/null </dev/null || exit_skip "libeufin required" 2221 echo "Testing for taler-wallet-cli" 2222 taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || exit_skip "taler-wallet-cli required" 2223 2224 2225 echo -n "Testing for Postgres" 2226 2227 MY_TMP_DIR=$(mktemp -d /tmp/taler-auditor-basedbXXXXXX) 2228 echo "Using $MY_TMP_DIR for logging and temporary data" 2229 2230 # If run as root, simply use the running postgres instance. 2231 # Otherwise create a temporary storage space for postgres. 2232 [ $(id -u) == 0 ] || perform_initdb 2233 2234 MYDIR="${MY_TMP_DIR}/basedb" 2235 mkdir -p "${MYDIR}" 2236 2237 if [ -z ${REUSE_BASEDB_DIR+x} ] 2238 then 2239 echo "Generating fresh database at $MYDIR" 2240 2241 if faketime -f '-1 d' ./generate-auditor-basedb.sh -d "$MYDIR/$DB" 2242 then 2243 echo -n "Reset 'auditor-basedb' database at ${PGHOST:-} ..." 2244 dropdb --if-exists "auditor-basedb" > /dev/null 2> /dev/null || true 2245 createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB' at ${PGHOST:-}" 2246 echo " DONE" 2247 else 2248 echo "Generation failed" 2249 exit 1 2250 fi 2251 echo "To reuse this database in the future, use:" 2252 echo "export REUSE_BASEDB_DIR=$MY_TMP_DIR" 2253 else 2254 echo "Reusing existing database from ${REUSE_BASEDB_DIR}" 2255 cp -r "${REUSE_BASEDB_DIR}/basedb"/* "${MYDIR}/" 2256 fi 2257 2258 check_with_database "$MYDIR/$DB" 2259 if [ "$fail" != "0" ] 2260 then 2261 exit "$fail" 2262 fi 2263 2264 if [ -z "${REUSE_BASEDB_DIR+x}" ] 2265 then 2266 echo "Run 'export REUSE_BASEDB_DIR=${MY_TMP_DIR}' to re-run tests against the same database" 2267 fi 2268 2269 exit 0