exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

test-kyc.sh (64388B)


      1 #!/bin/bash
      2 #
      3 #  This file is part of TALER
      4 #  Copyright (C) 2014-2026 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-merchant interaction on a KYC-enabled exchange and run
     24 # the auditor against it.
     25 #
     26 # The reference database (see generate-kyc-basedb.sh) contains two merchant
     27 # instances that were paid the same amount and both proved that they own
     28 # their bank account with a KYCAUTH wire transfer, but only one of them
     29 # then passed the KYC form.  The exchange therefore wired the funds of the
     30 # first one out and is still holding the funds of the second one.
     31 #
     32 # The auditor has almost no KYC-specific logic of its own -- see issues.txt
     33 # for what it could check and does not -- so this test has two jobs:
     34 #
     35 #  1. the KYC artefacts (KYCAUTH credits, legitimization measures and
     36 #     outcomes, an aggregation the exchange lawfully withheld) must not
     37 #     make the auditor invent findings, and the KYCAUTH totals it does
     38 #     track -- total_kycauth_in from the bank, total_kycauth_revenue from
     39 #     the exchange -- must be exactly right;
     40 #  2. fault detection must still work on a KYC-enabled exchange, so the
     41 #     later tests inject the same kinds of faults test-auditor.sh does and
     42 #     require the auditor to flag them.  Tests 2, 8 and 9 pull the two
     43 #     KYCAUTH totals apart in each of the ways they can disagree, which is
     44 #     what the reconciliation between them exists to catch.
     45 #
     46 # Requires 'jq' tool and Postgres superuser rights!
     47 set -eu
     48 #set -x
     49 
     50 # Set of numbers for all the testcases.
     51 # When adding new tests, increase the last number:
     52 ALL_TESTS=$(seq 0 16)
     53 
     54 # $TESTS determines which tests we should run.
     55 # This construction is used to make it easy to
     56 # only run a subset of the tests. To only run a subset,
     57 # pass the numbers of the tests to run as the FIRST
     58 # argument to test-kyc.sh, i.e.:
     59 #
     60 # $ test-kyc.sh "1 3"
     61 #
     62 # to run tests 1 and 3 only.  By default, all tests are run.
     63 #
     64 TESTS=${1:-$ALL_TESTS}
     65 
     66 export TALER_AUDITOR_TOKEN="secret-token:D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"
     67 export TALER_AUDITOR_SALT="64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE1H68SK8D9P6WW32CHK6GTKCDSR64S36D1N6RVKGC9J6CT3ADHQ70RK4CSM6MV3EE0"
     68 
     69 # Global variable to run the auditor processes under valgrind
     70 # VALGRIND=valgrind
     71 VALGRIND=""
     72 
     73 . setup.sh
     74 
     75 
     76 # Cleanup exchange and libeufin between runs.
     77 function cleanup()
     78 {
     79     if [ -n "${EPID:-}" ]
     80     then
     81         echo -n "Stopping exchange $EPID..."
     82         kill -TERM "$EPID"
     83         wait "$EPID" || true
     84         echo "DONE"
     85         unset EPID
     86     fi
     87     stop_libeufin &> /dev/null
     88 }
     89 
     90 # Cleanup to run whenever we exit
     91 function exit_cleanup()
     92 {
     93     jobs
     94     if [ -n "${POSTGRES_PATH:-}" ]
     95     then
     96         echo -n "Stopping Postgres at ${POSTGRES_PATH} ..."
     97         "${POSTGRES_PATH}/pg_ctl" \
     98                         -D "$TMPDIR" \
     99                         --log="${MY_TMP_DIR}/pg_ctl.log" \
    100                         stop \
    101             &> ${MY_TMP_DIR}/pg_ctl.out \
    102             || true
    103         echo "DONE"
    104     fi
    105     echo -n "Running exit-cleanup ..."
    106     cleanup
    107     for n in $(jobs -p)
    108     do
    109         kill "$n" 2> /dev/null || true
    110     done
    111     wait || true
    112     echo "DONE"
    113 }
    114 
    115 # Install cleanup handler (except for kill -9)
    116 trap exit_cleanup EXIT
    117 
    118 
    119 function await_bank () {
    120     for n in $(seq 1 80)
    121     do
    122         echo -n "."
    123         sleep 0.1
    124         OK=1
    125         wget http://localhost:8082/ \
    126              -o /dev/null \
    127              -O /dev/null \
    128              >/dev/null \
    129             && break
    130         OK=0
    131     done
    132     if [ 1 != "$OK" ]
    133     then
    134         exit_skip "Failed to launch libeufin-bank"
    135     fi
    136  }
    137 
    138 # Operations to run before the actual audit
    139 function pre_audit () {
    140     # Launch bank
    141     echo -n "Launching libeufin-bank"
    142     export CONF
    143     export MY_TMP_DIR
    144     launch_libeufin
    145     await_bank
    146     echo " DONE"
    147 
    148     if [ "${1:-no}" = "aggregator" ]
    149     then
    150         # NO '-y' here: that is --kyc-off, which makes the aggregator
    151         # wire funds out without consulting KYC at all and would defeat
    152         # the point of this test.
    153         echo -n "Running exchange aggregator ..."
    154         taler-exchange-aggregator \
    155             -L "INFO" \
    156             -t \
    157             -c "$CONF" \
    158             2> "${MY_TMP_DIR}/aggregator.log" \
    159             || exit_fail "FAIL"
    160         echo " DONE"
    161         echo -n "Running exchange closer ..."
    162         taler-exchange-closer \
    163             -L "INFO" \
    164             -t \
    165             -c "$CONF" \
    166             2> "${MY_TMP_DIR}/closer.log" \
    167             || exit_fail "FAIL"
    168         echo " DONE"
    169         echo -n "Running exchange transfer ..."
    170         taler-exchange-transfer \
    171             -L "INFO" \
    172             -t \
    173             -c "$CONF" \
    174             2> "${MY_TMP_DIR}/transfer.log" \
    175             || exit_fail "FAIL"
    176         echo " DONE"
    177     fi
    178 }
    179 
    180 # actual audit run
    181 function audit_only () {
    182     # Run the auditor!
    183     echo -n "Running audit(s) ..."
    184 
    185     # Restart so that first run is always fresh, and second one is incremental
    186     taler-auditor-dbinit \
    187         -r \
    188         -c "$CONF"
    189     $VALGRIND taler-helper-auditor-aml \
    190               -i \
    191               -L DEBUG \
    192               -c "$CONF" \
    193               -t \
    194               > "${MY_TMP_DIR}/test-audit-aml.out" \
    195               2> "${MY_TMP_DIR}/test-audit-aml.err" \
    196         || exit_fail "aml audit failed (see ${MY_TMP_DIR}/test-audit-aml.*)"
    197     echo -n "."
    198     $VALGRIND taler-helper-auditor-aml \
    199               -i \
    200               -L DEBUG \
    201               -c "$CONF" \
    202               -t \
    203               > "${MY_TMP_DIR}/test-audit-aml-inc.out" \
    204               2> "${MY_TMP_DIR}/test-audit-aml-inc.err" \
    205         || exit_fail "incremental aml audit failed (see ${MY_TMP_DIR}/test-audit-aml-inc.*)"
    206     echo -n "."
    207     $VALGRIND taler-helper-auditor-aggregation \
    208               -L DEBUG \
    209               -c "$CONF" \
    210               -t \
    211               > "${MY_TMP_DIR}/test-audit-aggregation.out" \
    212               2> "${MY_TMP_DIR}/test-audit-aggregation.err" \
    213         || exit_fail "aggregation audit failed (see ${MY_TMP_DIR}/test-audit-aggregation.*)"
    214     echo -n "."
    215     $VALGRIND taler-helper-auditor-aggregation \
    216               -L DEBUG \
    217               -c "$CONF" \
    218               -t \
    219               > "${MY_TMP_DIR}/test-audit-aggregation-inc.out" \
    220               2> "${MY_TMP_DIR}/test-audit-aggregation-inc.err" \
    221         || exit_fail "incremental aggregation audit failed (see ${MY_TMP_DIR}/test-audit-aggregation-inc.*)"
    222     echo -n "."
    223     $VALGRIND taler-helper-auditor-coins \
    224               -L DEBUG \
    225               -c "$CONF" \
    226               -t \
    227               > "${MY_TMP_DIR}/test-audit-coins.out" \
    228               2> "${MY_TMP_DIR}/test-audit-coins.err" \
    229         || exit_fail "coin audit failed (see ${MY_TMP_DIR}/test-audit-coins.*)"
    230     echo -n "."
    231     $VALGRIND taler-helper-auditor-coins \
    232               -L DEBUG  \
    233               -c "$CONF" \
    234               -t \
    235               > "${MY_TMP_DIR}/test-audit-coins-inc.out" \
    236               2> "${MY_TMP_DIR}/test-audit-coins-inc.err" \
    237         || exit_fail "incremental coin audit failed (see ${MY_TMP_DIR}/test-audit-coins-inc.*)"
    238     echo -n "."
    239     $VALGRIND taler-helper-auditor-deposits \
    240               -L DEBUG \
    241               -c "$CONF" \
    242               -t \
    243               > "${MY_TMP_DIR}/test-audit-deposits.out" \
    244               2> "${MY_TMP_DIR}/test-audit-deposits.err" \
    245         || exit_fail "deposits audit failed (see ${MY_TMP_DIR}/test-audit-deposits.*)"
    246     echo -n "."
    247     $VALGRIND taler-helper-auditor-deposits \
    248               -L DEBUG \
    249               -c "$CONF" \
    250               -t \
    251               > "${MY_TMP_DIR}/test-audit-deposits-inc.out" \
    252               2> "${MY_TMP_DIR}/test-audit-deposits-inc.err" \
    253         || exit_fail "incremental deposits audit failed (see ${MY_TMP_DIR}/test-audit-deposits-inc.*)"
    254     echo -n "."
    255     $VALGRIND taler-helper-auditor-reserves \
    256               -i \
    257               -L DEBUG \
    258               -c "$CONF" \
    259               -t \
    260               > "${MY_TMP_DIR}/test-audit-reserves.out" \
    261               2> "${MY_TMP_DIR}/test-audit-reserves.err" \
    262         || exit_fail "reserves audit failed (see ${MY_TMP_DIR}/test-audit-reserves.*)"
    263     echo -n "."
    264     $VALGRIND taler-helper-auditor-reserves \
    265               -i \
    266               -L DEBUG \
    267               -c "$CONF" \
    268               -t \
    269               > "${MY_TMP_DIR}/test-audit-reserves-inc.out" \
    270               2> "${MY_TMP_DIR}/test-audit-reserves-inc.err" \
    271         || exit_fail "incremental reserves audit failed (see ${MY_TMP_DIR}/test-audit-reserves-inc.*)"
    272     echo -n "."
    273     $VALGRIND taler-helper-auditor-wire-credit \
    274               -i \
    275               -L DEBUG \
    276               -c "$CONF" \
    277               -t \
    278               > "${MY_TMP_DIR}/test-audit-wire-credit.out" \
    279               2> "${MY_TMP_DIR}/test-audit-wire-credit.err" \
    280         || exit_fail "wire credit audit failed (see ${MY_TMP_DIR}/test-audit-wire-credit.*)"
    281     echo -n "."
    282     $VALGRIND taler-helper-auditor-wire-credit \
    283               -i \
    284               -L DEBUG \
    285               -c "$CONF" \
    286               -t \
    287               > "${MY_TMP_DIR}/test-audit-wire-credit-inc.out" \
    288               2> "${MY_TMP_DIR}/test-audit-wire-credit-inc.err" \
    289         || exit_fail "wire credit audit inc failed (see ${MY_TMP_DIR}/test-audit-wire-credit-inc.*)"
    290     echo -n "."
    291     $VALGRIND taler-helper-auditor-wire-debit \
    292               -i \
    293               -L DEBUG \
    294               -c "$CONF" \
    295               -t \
    296               > "${MY_TMP_DIR}/test-audit-wire-debit.out" \
    297               2> "${MY_TMP_DIR}/test-audit-wire-debit.err" \
    298         || exit_fail "wire debit audit failed (see ${MY_TMP_DIR}/test-audit-wire-debit.*)"
    299     echo -n "."
    300     $VALGRIND taler-helper-auditor-wire-debit \
    301               -i \
    302               -L DEBUG \
    303               -c "$CONF" \
    304               -t \
    305               > "${MY_TMP_DIR}/test-audit-wire-debit-inc.out" \
    306               2> "${MY_TMP_DIR}/test-audit-wire-debit-inc.err" \
    307         || exit_fail "wire debit audit inc failed (see ${MY_TMP_DIR}/test-audit-wire-debit-inc.*)"
    308     echo -n "."
    309     $VALGRIND taler-helper-auditor-purses \
    310              -i \
    311              -L DEBUG \
    312              -c "$CONF" \
    313              -t \
    314              > "${MY_TMP_DIR}/test-audit-purses.out" \
    315              2> "${MY_TMP_DIR}/test-audit-purses.err" \
    316        || exit_fail "audit purses failed"
    317     echo -n "."
    318     $VALGRIND taler-helper-auditor-purses \
    319               -i \
    320               -L DEBUG \
    321               -c "$CONF" \
    322               -t \
    323               > "${MY_TMP_DIR}/test-audit-purses-inc.out" \
    324               2> "${MY_TMP_DIR}/test-audit-purses-inc.err" \
    325         || exit_fail "audit purses inc failed"
    326     echo -n "."
    327     $VALGRIND taler-helper-auditor-transfer \
    328              -i \
    329              -L DEBUG \
    330              -c "$CONF" \
    331              -t \
    332              > "${MY_TMP_DIR}/test-audit-transfer.out" \
    333              2> "${MY_TMP_DIR}/test-audit-transfer.err" \
    334        || exit_fail "audit transfer failed"
    335     echo -n "."
    336     $VALGRIND taler-helper-auditor-transfer \
    337               -i \
    338               -L DEBUG \
    339               -c "$CONF" \
    340               -t \
    341               > "${MY_TMP_DIR}/test-audit-transfer-inc.out" \
    342               2> "${MY_TMP_DIR}/test-audit-transfer-inc.err" \
    343         || exit_fail "audit transfer inc failed"
    344     echo -n "."
    345 
    346     echo " DONE"
    347 }
    348 
    349 
    350 # Cleanup to run after the auditor
    351 function post_audit () {
    352     taler-exchange-dbinit \
    353         -c "$CONF" \
    354         -g \
    355         || exit_fail "exchange DB GC failed"
    356     cleanup
    357 }
    358 
    359 
    360 # Run audit process on current database, including report
    361 # generation.  Pass "aggregator" as $1 to run
    362 # $ taler-exchange-aggregator
    363 # before auditor (to trigger pending wire transfers).
    364 # Pass "drain" as $2 to run a drain operation as well.
    365 function run_audit () {
    366     pre_audit "${1:-no}"
    367     audit_only
    368     post_audit
    369 }
    370 
    371 
    372 function stop_auditor_httpd() {
    373   if [ -n "${APID:-}" ]
    374   then
    375       echo -n "Stopping auditor $APID..."
    376       kill -TERM "$APID"
    377       wait "$APID" || true
    378       echo "DONE"
    379       unset APID
    380   fi
    381 }
    382 
    383 
    384 # Do a full reload of the (original) database
    385 function full_reload()
    386 {
    387     echo -n "Doing full reload of the database (loading ${BASEDB}.sql into $DB at ${PGHOST:-})... "
    388     dropdb -f "$DB" &>> ${MY_TMP_DIR}/drop.log || true
    389     createdb -T template0 "$DB" \
    390         || exit_skip "could not create database $DB (at ${PGHOST:-})"
    391     # Import pre-generated database, -q(ietly) using single (-1) transaction
    392     psql -Aqt "$DB" \
    393          -q \
    394          -1 \
    395          -f "${BASEDB}.sql" \
    396          &>> ${MY_TMP_DIR}/postgresql-reload.log \
    397         || exit_skip "Failed to load database $DB from ${BASEDB}.sql"
    398     echo "DONE"
    399     # Technically, this call shouldn't be needed as libeufin should already be stopped here...
    400     stop_libeufin
    401     stop_auditor_httpd
    402 }
    403 
    404 function run_auditor_httpd() {
    405   echo -n "Starting auditor..."
    406   $VALGRIND taler-auditor-httpd \
    407       -c "${CONF}" \
    408       -L INFO \
    409       2> "${MY_TMP_DIR}/auditor-httpd.err" &
    410   APID=$!
    411 
    412   # Wait for auditor service to be available
    413   for n in $(seq 1 50)
    414   do
    415       echo -n "."
    416       sleep 0.2
    417       OK=0
    418       # auditor
    419       wget "http://localhost:8083/config" \
    420            -o /dev/null \
    421            -O /dev/null \
    422            >/dev/null \
    423           || continue
    424       OK=1
    425       break
    426   done
    427   echo "... DONE."
    428   wget "http://localhost:8083/webui/" \
    429        -o /dev/null \
    430        -O /dev/null \
    431        >/dev/null \
    432       || exit_fail "Auditor WebUI is not available under /webui/"
    433 }
    434 
    435 
    436 function check_auditor_running() {
    437   ARUNSTATUS=$(curl -Is http://localhost:8083/config | head -1)
    438   if [ -n "${ARUNSTATUS:-}" ]
    439     then
    440       echo "Auditor running"
    441     else
    442       echo "Auditor not running, starting it"
    443       run_auditor_httpd
    444   fi
    445   unset ARUNSTATUS
    446 }
    447 
    448 function call_endpoint() {
    449     if [ -n "${2+x}" ]
    450     then
    451         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}"
    452         echo "endpoint ${1} called (with balance_key)... "
    453     else
    454         curl -s -H "Accept: application/json" -H "Authorization: Bearer ${TALER_AUDITOR_TOKEN}" -o "${MY_TMP_DIR}/${1}.json" "localhost:8083/monitoring/${1}?limit=50"
    455         echo "endpoint ${1} called... "
    456     fi
    457 }
    458 
    459 
    460 function check_balance() {
    461     call_endpoint "balances" "$1"
    462     BAL=$(jq -r .records[0].balance_value < "${MY_TMP_DIR}/${1}.json")
    463     if [ "$BAL" != "$2" ]
    464     then
    465         exit_fail "$3 (got $BAL, wanted $2)"
    466     fi
    467     echo "PASS"
    468 }
    469 
    470 
    471 function check_not_balance() {
    472     call_endpoint "balances" "$1"
    473     BAL=$(jq -r .records[0].balance_value < "${MY_TMP_DIR}/${1}.json")
    474     if [ "$BAL" = "$2" ]
    475     then
    476         exit_fail "$3 (got $BAL, wanted NOT $2)"
    477     fi
    478     echo "PASS"
    479 }
    480 
    481 
    482 function check_report() {
    483     call_endpoint "$1"
    484     VAL=$(jq -r .records[0].\"$2\" < "${MY_TMP_DIR}/${1}.json")
    485     if [ "$VAL" != "$3" ]
    486     then
    487         exit_fail "$1::$2 (got $VAL, wanted $3)"
    488     fi
    489     echo "PASS"
    490 }
    491 
    492 # Check that at least one entry of report $1 has field $2 set to $3.
    493 # Unlike check_report, this does not depend on the order in which the
    494 # auditor inserted its findings: one fault can legitimately be reported
    495 # against several operations (a coin with a bad denomination signature is
    496 # flagged once per operation that used it).
    497 function check_report_any() {
    498     call_endpoint "$1"
    499     jq -e --arg want "$3" "any(.records[]; .\"$2\" == \$want)" \
    500        < "${MY_TMP_DIR}/${1}.json" \
    501        > /dev/null \
    502         || exit_fail "$1::$2 (no entry with value $3)"
    503     echo "PASS"
    504 }
    505 
    506 # Check that report $1 has at least one entry, without pinning down any of
    507 # its fields.  Useful when the injected fault is certain to be found but
    508 # the exact amounts depend on how much of a reserve happened to be left.
    509 function check_some_report() {
    510     call_endpoint "$1"
    511     jq -e .records[0] \
    512        < "${MY_TMP_DIR}/${1}.json" \
    513        > /dev/null \
    514         || exit_fail "Wanted a report for $1, but got none"
    515     echo "PASS"
    516 }
    517 
    518 function check_no_report() {
    519     call_endpoint "$1"
    520     jq -e .records[0] \
    521        < "${MY_TMP_DIR}/${1}.json" \
    522        > /dev/null \
    523        && exit_fail "Wanted empty report for $1, but got incidents"
    524     echo "PASS"
    525 }
    526 
    527 function check_report_neg() {
    528     call_endpoint "$1"
    529     VAL=$(jq -r .records[0].\"$2\" < "${MY_TMP_DIR}/${1}.json")
    530     if [ "$VAL" == "$3" ]
    531     then
    532         exit_fail "$1::$2 (got $VAL, wanted $3)"
    533     fi
    534     echo "PASS"
    535 }
    536 
    537 function check_row() {
    538     call_endpoint "$1"
    539     if [ -n "${3+x}" ]
    540     then
    541         RID="$2"
    542         WANT="$3"
    543     else
    544         RID="row_id"
    545         WANT="$2"
    546     fi
    547     ROW=$(jq -r .records[0].\"${RID}\" < "${MY_TMP_DIR}/${1}.json")
    548     if [ "$ROW" != "$WANT" ]
    549     then
    550         exit_fail "Row ${1} wrong (got ${ROW}, wanted ${WANT})"
    551     fi
    552     echo "PASS"
    553 }
    554 
    555 
    556 
    557 # Amount the exchange wired to the merchant that passed KYC.
    558 CLEARED_WIRED="TESTKUDOS:8.02"
    559 # Amount the exchange aggregated for the merchant that did not pass KYC
    560 # and then parked in aggregation_transient instead of wiring it out.
    561 WITHHELD="TESTKUDOS:8.03"
    562 # The account of that merchant.  The IBAN libeufin derives from the account
    563 # name is stable across regenerations of the reference database, so this can be
    564 # hardcoded -- but the wire transfer identifier cannot: the aggregator draws it
    565 # with GNUNET_CRYPTO_random_block(), so tests 14 and 15 read it out of the
    566 # exchange's own tables instead of naming it.
    567 BLOCKED_ACCOUNT="payto://iban/DE12500105170540732493?receiver-name=Blocked44"
    568 # Two KYCAUTH wire transfers of TESTKUDOS:0.1, one per merchant instance.
    569 KYCAUTH_IN="TESTKUDOS:0.2"
    570 # Everything the exchange's bank account was credited with: TESTKUDOS:56 of
    571 # reserve top-ups plus the two KYCAUTH transfers above.  The KYCAUTH part is
    572 # revenue rather than a liability, but it is money in the account all the
    573 # same, so it belongs in the balance the account is expected to have.
    574 WIRE_IN="TESTKUDOS:56.2"
    575 
    576 
    577 # Assert that all of the auditor's loss, delta and irregularity balances
    578 # are zero, i.e. that nothing about this database looks wrong to it.
    579 function check_all_clean() {
    580     echo -n "Test for emergencies... "
    581     check_no_report "emergencies"
    582     echo -n "Test for emergencies by count... "
    583     check_no_report "emergencies-by-count"
    584     echo -n "Test for deposit confirmation problems... "
    585     check_no_report "deposit-confirmations"
    586     echo -n "Test for denomination key validity problems... "
    587     check_no_report "denomination-key-validity-withdraw-inconsistencies"
    588     echo -n "Test for denominations without signatures... "
    589     check_no_report "denominations-without-sigs"
    590     echo -n "Test for wire out inconsistencies... "
    591     check_no_report "wire-out-inconsistencies"
    592     echo -n "Test for reserve in inconsistencies... "
    593     check_no_report "reserve-in-inconsistencies"
    594     echo -n "Test for KYCAUTH in inconsistencies... "
    595     check_no_report "kycauth-in-inconsistencies"
    596     echo -n "Test for misattribution inconsistencies... "
    597     check_no_report "misattribution-in-inconsistencies"
    598     echo -n "Test for row inconsistencies... "
    599     check_no_report "row-inconsistencies"
    600     echo -n "Test for minor row inconsistencies... "
    601     check_no_report "row-minor-inconsistencies"
    602     echo -n "Test for wire format inconsistencies... "
    603     check_no_report "wire-format-inconsistencies"
    604     echo -n "Test for arithmetic inconsistencies... "
    605     check_no_report "amount-arithmetic-inconsistencies"
    606     echo -n "Test for coin inconsistencies... "
    607     check_no_report "coin-inconsistencies"
    608     echo -n "Test for bad signature losses... "
    609     check_no_report "bad-sig-losses"
    610     echo -n "Test for fee time inconsistencies... "
    611     check_no_report "fee-time-inconsistencies"
    612     echo -n "Test for purses not closed... "
    613     check_no_report "purse-not-closed-inconsistencies"
    614     echo -n "Test for reserves not closed... "
    615     check_no_report "reserve-not-closed-inconsistencies"
    616     echo -n "Test for insufficient reserve balances... "
    617     check_no_report "reserve-balance-insufficient-inconsistencies"
    618     echo -n "Test for wrong reserve balance summaries... "
    619     check_no_report "reserve-balance-summary-wrong-inconsistencies"
    620     echo -n "Test for closure lags... "
    621     check_no_report "closure-lags"
    622 
    623     # Just to test the endpoint and for logging ...
    624     call_endpoint "balances"
    625 
    626     echo -n "Testing aggregation bad sig loss balance... "
    627     check_balance \
    628         "aggregation_total_bad_sig_loss" \
    629         "TESTKUDOS:0" \
    630         "Wrong total bad sig loss from aggregation"
    631     echo -n "Testing coin irregular loss balance... "
    632     check_balance \
    633         "coin_irregular_loss" \
    634         "TESTKUDOS:0" \
    635         "Wrong total irregular loss from coins"
    636     echo -n "Testing reserves bad sig loss balance... "
    637     check_balance \
    638         "reserves_total_bad_sig_loss" \
    639         "TESTKUDOS:0" \
    640         "Wrong total bad sig loss from reserves"
    641     echo -n "Testing purse bad sig loss balance... "
    642     check_balance \
    643         "purse_total_bad_sig_loss" \
    644         "TESTKUDOS:0" \
    645         "Wrong total bad sig loss from purses"
    646 
    647     echo -n "Test for bad incoming delta plus... "
    648     check_balance \
    649         "total_bad_amount_in_plus" \
    650         "TESTKUDOS:0" \
    651         "Expected total wire in delta plus wrong"
    652     echo -n "Test for bad incoming delta minus... "
    653     check_balance \
    654         "total_bad_amount_in_minus" \
    655         "TESTKUDOS:0" \
    656         "Expected total wire in delta minus wrong"
    657     echo -n "Test for bad outgoing delta plus... "
    658     check_balance \
    659         "total_bad_amount_out_plus" \
    660         "TESTKUDOS:0" \
    661         "Expected total wire out delta plus wrong"
    662     echo -n "Test for bad outgoing delta minus... "
    663     check_balance \
    664         "total_bad_amount_out_minus" \
    665         "TESTKUDOS:0" \
    666         "Expected total wire out delta minus wrong"
    667     echo -n "Test for misattribution amounts... "
    668     check_balance \
    669         "total_misattribution_in" \
    670         "TESTKUDOS:0" \
    671         "Expected total misattribution in wrong"
    672 
    673     echo -n "Test for aggregation wire out delta plus... "
    674     check_balance \
    675         "aggregation_total_wire_out_delta_plus" \
    676         "TESTKUDOS:0" \
    677         "Expected total wire out delta plus wrong"
    678     echo -n "Test for aggregation wire out delta minus... "
    679     check_balance \
    680         "aggregation_total_wire_out_delta_minus" \
    681         "TESTKUDOS:0" \
    682         "Expected total wire out delta minus wrong"
    683 
    684     echo -n "Checking for unexpected aggregation delta plus... "
    685     check_balance \
    686         "aggregation_total_arithmetic_delta_plus" \
    687         "TESTKUDOS:0" \
    688         "Wrong arithmetic delta plus from aggregations"
    689     echo -n "Checking for unexpected aggregation delta minus... "
    690     check_balance \
    691         "aggregation_total_arithmetic_delta_minus" \
    692         "TESTKUDOS:0" \
    693         "Wrong arithmetic delta minus from aggregations"
    694     echo -n "Checking for unexpected coin delta plus... "
    695     check_balance \
    696         "coins_total_arithmetic_delta_plus" \
    697         "TESTKUDOS:0" \
    698         "Wrong arithmetic delta plus from coins"
    699     echo -n "Checking for unexpected coin delta minus... "
    700     check_balance \
    701         "coins_total_arithmetic_delta_minus" \
    702         "TESTKUDOS:0" \
    703         "Wrong arithmetic delta minus from coins"
    704     echo -n "Checking for unexpected reserves delta plus... "
    705     check_balance \
    706         "reserves_total_arithmetic_delta_plus" \
    707         "TESTKUDOS:0" \
    708         "Wrong arithmetic delta plus from reserves"
    709     echo -n "Checking for unexpected reserves delta minus... "
    710     check_balance \
    711         "reserves_total_arithmetic_delta_minus" \
    712         "TESTKUDOS:0" \
    713         "Wrong arithmetic delta minus from reserves"
    714 }
    715 
    716 
    717 # The KYC-specific assertions: how much arrived by KYCAUTH, how much left
    718 # the exchange, and how much it is sitting on.
    719 function check_kyc_state() {
    720     echo -n "Checking the KYCAUTH credits were accounted for... "
    721     check_balance \
    722         "total_kycauth_in" \
    723         "$KYCAUTH_IN" \
    724         "Wrong total for KYCAUTH wire transfers"
    725 
    726     # KYCAUTH money is not credited to a reserve and never paid back, so
    727     # the auditor books it as revenue of the exchange operator and counts
    728     # it towards the balance the bank account is expected to have.
    729     echo -n "Checking the KYCAUTH credits were booked as revenue... "
    730     check_balance \
    731         "total_kycauth_revenue" \
    732         "$KYCAUTH_IN" \
    733         "Wrong KYCAUTH revenue"
    734     echo -n "Checking the KYCAUTH credits reached the balance sheet... "
    735     check_balance \
    736         "total_wire_in" \
    737         "$WIRE_IN" \
    738         "Wrong total credited to the exchange"
    739 
    740     echo -n "Checking that only the cleared merchant was paid... "
    741     check_balance \
    742         "total_wire_out" \
    743         "$CLEARED_WIRED" \
    744         "Wrong total wired out"
    745 
    746     # The exchange aggregated the blocked merchant's coins, wrote the
    747     # aggregation_tracking rows and then parked the payout in
    748     # aggregation_transient rather than executing it.  Confirm from the
    749     # exchange's own database that the money really is still there, so
    750     # that the auditor-side assertion below is about the auditor and not
    751     # about the reference database having drifted.
    752     echo -n "Checking that the exchange withheld the blocked payout... "
    753     TRANSIENT=$(psql -Aqt "$DB" \
    754                      -c "SELECT COUNT(*) FROM exchange.aggregation_transient;")
    755     if [ "$TRANSIENT" != "1" ]
    756     then
    757         exit_fail "Expected one withheld aggregation, got ${TRANSIENT}"
    758     fi
    759     WIRED=$(psql -Aqt "$DB" -c "SELECT COUNT(*) FROM exchange.wire_out;")
    760     if [ "$WIRED" != "1" ]
    761     then
    762         exit_fail "Expected exactly one wire transfer, got ${WIRED}"
    763     fi
    764     echo "PASS"
    765 
    766     # The exchange also said *why*, in the append-only table that replication
    767     # can carry.  aggregation_transient above is the same claim, but an
    768     # external auditor never gets to see it, so this is the row the auditor's
    769     # classification actually rests on.  Reason 2 is
    770     # TALER_EXCHANGEDB_DR_KYC.
    771     #
    772     # Restrict this to transfers that have not gone out.  Both merchants were
    773     # withheld on the generator's first aggregator run -- neither had passed
    774     # KYC yet -- so the cleared one has a deferral of its own, made obsolete by
    775     # the wire_out row that followed.  Append-only means those stay on file,
    776     # which is the point: the record of a hold outlives the hold.
    777     echo -n "Checking that the exchange recorded why it withheld it... "
    778     DEFERRED=$(psql -Aqt "$DB" \
    779                     -c "SELECT COUNT(*) FROM exchange.aggregation_deferrals d
    780                          WHERE d.deferral_reason=2
    781                            AND d.legitimization_requirement_serial_id != 0
    782                            AND NOT EXISTS (
    783                                  SELECT 1 FROM exchange.wire_out w
    784                                   WHERE w.wtid_raw=d.wtid_raw);")
    785     if [ "$DEFERRED" != "1" ]
    786     then
    787         exit_fail "Expected one open KYC deferral on record, got ${DEFERRED}"
    788     fi
    789     echo "PASS"
    790 
    791     # ... and the auditor sees it.  The withheld deposits do carry
    792     # aggregation_tracking rows, so they are not "lagging" in the sense of
    793     # total_amount_lag, which counts deposits the exchange has not aggregated
    794     # at all; that balance is legitimately zero here.  What the exchange has
    795     # not done is the wire transfer, and that is what total_aml_hold counts.
    796     echo -n "Checking that nothing is waiting to be aggregated... "
    797     check_balance \
    798         "total_amount_lag" \
    799         "TESTKUDOS:0" \
    800         "Wrong total for deposits that were never aggregated"
    801     echo -n "Checking that the withheld payout is booked as an AML hold... "
    802     check_balance \
    803         "total_aml_hold" \
    804         "$WITHHELD" \
    805         "Wrong total withheld pending legitimization"
    806     # The blocked merchant is the *only* transfer the exchange owes, and its
    807     # KYC measure is open, so nothing may land in the unexplained bucket.
    808     # Were the two ever to swap, the exchange would be stalling a payout it
    809     # has no legal reason to stall, which is the case this split exists for.
    810     echo -n "Checking that no payout is unexplained... "
    811     check_balance \
    812         "total_transfer_lag" \
    813         "TESTKUDOS:0" \
    814         "Exchange is withholding funds it gave no reason for"
    815     # Every deposit here is aggregated into a payout worth making, so the
    816     # third bucket -- money parked only because the aggregate would not
    817     # cover its wire fee -- must be empty.  It exists so that such money
    818     # does not read as either of the two above, both of which say something
    819     # is wrong.
    820     echo -n "Checking that nothing is parked as a small aggregate... "
    821     check_balance \
    822         "total_small_aggregate" \
    823         "TESTKUDOS:0" \
    824         "Exchange parked an aggregate as too small to pay out"
    825     echo -n "Checking that the hold names the blocked account... "
    826     check_report_any \
    827         "aml-holds" \
    828         "account" \
    829         "$BLOCKED_ACCOUNT"
    830     echo -n "Checking that the hold names the amount... "
    831     check_report "aml-holds" "amount" "$WITHHELD"
    832     echo -n "Checking that the hold is attributed to KYC... "
    833     check_report "aml-holds" "deferral_reason" "KYC"
    834     echo -n "Test for pending deposits... "
    835     check_no_report "pending-deposits"
    836     echo -n "Test for early aggregations... "
    837     check_no_report "early-aggregations"
    838 }
    839 
    840 
    841 # The KYC artefacts must not make the auditor invent findings.
    842 function test_0() {
    843 
    844     echo "===========0: normal run with aggregator==========="
    845     run_audit aggregator
    846     check_auditor_running
    847 
    848     echo "Checking output"
    849     check_all_clean
    850     check_kyc_state
    851 
    852     # cannot easily undo aggregator, hence full reload
    853     full_reload
    854     cleanup
    855 }
    856 
    857 
    858 # Same, but without letting the aggregator run first.  The aggregator
    859 # cannot lawfully do anything more than it already did when the reference
    860 # database was generated, so the auditor's view must be identical.
    861 function test_1() {
    862 
    863     echo "===========1: normal run==========="
    864     run_audit
    865     check_auditor_running
    866 
    867     echo "Checking output"
    868     check_all_clean
    869     check_kyc_state
    870 
    871     # Database was unmodified, no need to undo
    872     echo "OK"
    873 }
    874 
    875 
    876 # A KYCAUTH credit that the bank does not agree is a KYCAUTH credit.
    877 function test_2() {
    878 
    879     echo "===========2: KYCAUTH credit reclassified by the bank==========="
    880     # Modifying the bank's books needs libeufin to be restarted, it does
    881     # not re-read them.
    882     pre_audit
    883     stop_libeufin
    884     echo -n "Modifying database: "
    885     # libeufin records for every credit to the exchange whether it was a
    886     # reserve top-up or an account authorisation.  Turn one of the two
    887     # KYCAUTH credits into a reserve credit: the KYCAUTH total the auditor
    888     # tracks must drop by that amount, and because the 32 bytes of KYCAUTH
    889     # metadata are not a reserve public key the exchange knows, the credit
    890     # now also looks like an incoming transfer the exchange never booked.
    891     echo "UPDATE libeufin_bank.taler_exchange_incoming SET type='reserve' WHERE exchange_incoming_id=(SELECT MIN(exchange_incoming_id) FROM libeufin_bank.taler_exchange_incoming WHERE type='kyc')" \
    892         | psql -Aqt "$DB"
    893     echo "DONE"
    894     launch_libeufin
    895     await_bank
    896 
    897     audit_only
    898     check_auditor_running
    899 
    900     echo -n "Checking that the KYCAUTH total dropped... "
    901     check_balance \
    902         "total_kycauth_in" \
    903         "TESTKUDOS:0.1" \
    904         "Wrong total for KYCAUTH wire transfers"
    905     # Only the bank's books were touched, so the exchange still claims the
    906     # KYCAUTH revenue for a transfer the bank no longer reports as one.
    907     echo -n "Checking that the exchange still claims the revenue... "
    908     check_balance \
    909         "total_kycauth_revenue" \
    910         "$KYCAUTH_IN" \
    911         "Wrong KYCAUTH revenue"
    912     echo -n "Checking that the unbooked credit was flagged... "
    913     check_report_any \
    914         "reserve-in-inconsistencies" \
    915         "diagnostic" "unknown to exchange"
    916     echo -n "Checking that it was counted as an excess credit... "
    917     check_balance \
    918         "total_bad_amount_in_plus" \
    919         "TESTKUDOS:0.1" \
    920         "Wrong total_bad_amount_in_plus"
    921 
    922     # ... and the other side of the same disagreement: the exchange has a
    923     # kycauths_in row for a transfer the bank now says was something else,
    924     # so the auditor must not take the exchange's word for it.
    925     echo -n "Checking that the unconfirmed KYCAUTH was flagged... "
    926     check_report_any \
    927         "kycauth-in-inconsistencies" \
    928         "diagnostic" "KYCAUTH wire transfer claimed by exchange not found"
    929     echo -n "Checking the amount the exchange claims... "
    930     check_report_any \
    931         "kycauth-in-inconsistencies" \
    932         "amount_exchange_expected" "TESTKUDOS:0.1"
    933     echo -n "Checking that it was counted as a missing credit... "
    934     check_balance \
    935         "total_bad_amount_in_minus" \
    936         "TESTKUDOS:0.1" \
    937         "Wrong total_bad_amount_in_minus"
    938 
    939     post_audit
    940     full_reload
    941     cleanup
    942 }
    943 
    944 
    945 # The rest of the tests inject the same kinds of faults test-auditor.sh
    946 # injects, to show that KYC does not stop the auditor from finding them.
    947 
    948 
    949 # Change the amount of an incoming wire transfer as recorded by the
    950 # exchange.
    951 function test_3() {
    952 
    953     echo "===========3: reserves_in inconsistency==========="
    954     echo -n "Modifying database: "
    955     echo "UPDATE exchange.reserves_in SET credit.val=5 WHERE reserve_in_serial_id=1" \
    956         | psql -Aqt "$DB"
    957     echo "DONE"
    958 
    959     run_audit
    960     check_auditor_running
    961 
    962     echo -n "Testing inconsistency detection... "
    963     check_report \
    964         "reserve-in-inconsistencies" \
    965         "row_id" 1
    966     echo -n "Testing amount wired... "
    967     check_report \
    968         "reserve-in-inconsistencies" \
    969         "amount_wired" "TESTKUDOS:10"
    970     echo -n "Testing amount expected... "
    971     check_report \
    972         "reserve-in-inconsistencies" \
    973         "amount_exchange_expected" "TESTKUDOS:5"
    974     echo -n "Checking wire credit balance plus... "
    975     check_balance \
    976         "total_bad_amount_in_plus" \
    977         "TESTKUDOS:5" \
    978         "Expected total_bad_amount_in_plus wrong"
    979 
    980     full_reload
    981     cleanup
    982 }
    983 
    984 
    985 # Test where denom_sig in the known_coins table is wrong (=> bad signature)
    986 function test_4() {
    987 
    988     echo "===========4: known_coins signature wrong==========="
    989     OLD_ROW=$(echo "SELECT known_coin_id FROM exchange.known_coins LIMIT 1;" | psql "$DB" -Aqt)
    990     COIN_PUB=$(echo "SELECT coin_pub FROM exchange.known_coins WHERE known_coin_id=$OLD_ROW;" | psql "$DB" -Aqt)
    991 # shellcheck disable=SC2028
    992     echo "UPDATE exchange.known_coins SET denom_sig='\x0000000100000000287369672d76616c200a2028727361200a2020287320233542383731423743393036444643303442424430453039353246413642464132463537303139374131313437353746324632323332394644443146324643333445393939413336363430334233413133324444464239413833353833464536354442374335434445304441453035374438363336434541423834463843323843344446304144363030343430413038353435363039373833434431333239393736423642433437313041324632414132414435413833303432434346314139464635394244434346374436323238344143354544364131373739463430353032323241373838423837363535453434423145443831364244353638303232413123290a2020290a20290b' WHERE coin_pub='$COIN_PUB'" \
    993         | psql -Aqt "$DB"
    994 
    995     run_audit
    996     check_auditor_running
    997 
    998     echo -n "Checking bad-signature-loss detected... "
    999     check_report_neg \
   1000         "bad-sig-losses" \
   1001         "loss" "TESTKUDOS:0"
   1002     echo -n "Checking bad-signature-loss balance update... "
   1003     check_not_balance \
   1004         "coin_irregular_loss" \
   1005         "TESTKUDOS:0" \
   1006         "Wrong total bad sig loss"
   1007 
   1008     full_reload
   1009     cleanup
   1010 }
   1011 
   1012 
   1013 # Test where the amount the bank actually wired differs from what the
   1014 # exchange claims.
   1015 function test_5() {
   1016 
   1017     echo "===========5: incorrect wire_out amount==========="
   1018     pre_audit
   1019     stop_libeufin
   1020     echo -n "Modifying database: "
   1021     echo "UPDATE libeufin_bank.bank_account_transactions SET amount=(50,0) WHERE debtor_name='Exchange Company';" \
   1022         | psql -Aqt "$DB"
   1023     echo "DONE"
   1024     launch_libeufin
   1025     await_bank
   1026 
   1027     audit_only
   1028     check_auditor_running
   1029 
   1030     echo -n "Testing wire-out-inconsistency expected... "
   1031     check_report \
   1032         "wire-out-inconsistencies" \
   1033         "expected" \
   1034         "$CLEARED_WIRED"
   1035     echo -n "Testing wire-out-inconsistency claimed... "
   1036     check_report \
   1037         "wire-out-inconsistencies" \
   1038         "claimed" \
   1039         "TESTKUDOS:50"
   1040     echo -n "Testing bad_amount_out_plus balance reporting... "
   1041     check_not_balance \
   1042         "total_bad_amount_out_plus" \
   1043         "TESTKUDOS:0" \
   1044         "reported total_bad_amount_out_plus wrong"
   1045 
   1046     post_audit
   1047     full_reload
   1048     cleanup
   1049 }
   1050 
   1051 
   1052 # Test where the exchange claims to have received more than the bank sent,
   1053 # which also throws its reserve balance summary off.
   1054 function test_6() {
   1055 
   1056     echo "===========6: reserve balance summary wrong==========="
   1057     echo -n "Modifying database: "
   1058     echo "UPDATE exchange.reserves_in SET credit.val=15 WHERE reserve_in_serial_id=1" \
   1059         | psql -Aqt "$DB"
   1060     echo "DONE"
   1061 
   1062     run_audit
   1063     check_auditor_running
   1064 
   1065     echo -n "Checking reserve balance summary inconsistency detected... "
   1066     check_some_report \
   1067         "reserve-balance-summary-wrong-inconsistencies"
   1068     echo -n "Testing amount the exchange expected... "
   1069     check_report \
   1070         "reserve-in-inconsistencies" \
   1071         "amount_exchange_expected" "TESTKUDOS:15"
   1072     echo -n "Checking wire credit balance minus... "
   1073     check_balance \
   1074         "total_bad_amount_in_minus" \
   1075         "TESTKUDOS:5" \
   1076         "Wrong total_bad_amount_in_minus"
   1077 
   1078     full_reload
   1079     cleanup
   1080 }
   1081 
   1082 
   1083 # Test where the exchange forgot a deposit that a merchant can prove.
   1084 function test_7() {
   1085 
   1086     echo "===========7: deposit wire target malformed==========="
   1087     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)
   1088 # shellcheck disable=SC2028
   1089     echo "INSERT INTO exchange.wire_targets (payto_uri, wire_target_h_payto) VALUES ('payto://x-taler-bank/localhost/testuser-xxlargtp', '\x1e8f31936b3cee8f8afd3aac9e38b5db42d45b721ffc4eb1e5b9ddaf1565660b');" \
   1090         | psql "$DB" -Aqt
   1091 # shellcheck disable=SC2028
   1092     echo "UPDATE exchange.batch_deposits SET wire_target_h_payto='\x1e8f31936b3cee8f8afd3aac9e38b5db42d45b721ffc4eb1e5b9ddaf1565660b' WHERE batch_deposit_serial_id=${SERIAL};" \
   1093         | psql -Aqt "$DB"
   1094 
   1095     run_audit
   1096     check_auditor_running
   1097 
   1098     echo -n "Checking correct operation of loss reported... "
   1099     check_report \
   1100         "bad-sig-losses" \
   1101         "operation" "deposit"
   1102     echo -n "Checking correct problem row ID reported... "
   1103     check_report \
   1104         "bad-sig-losses" \
   1105         "problem_row_id" "$SERIAL"
   1106     echo -n "Checking irregular coin loss... "
   1107     check_not_balance \
   1108         "coin_irregular_loss" \
   1109         "TESTKUDOS:0" \
   1110         "Wrong total irregular coin loss"
   1111 
   1112     full_reload
   1113     cleanup
   1114 }
   1115 
   1116 
   1117 # The reverse of test 2: the bank reports a KYCAUTH transfer that the
   1118 # exchange has no record of.  An exchange that could make such a row
   1119 # disappear could also make the account authorisation it justified
   1120 # disappear, so this must be flagged even though the money involved is
   1121 # small.
   1122 function test_8() {
   1123 
   1124     echo "===========8: KYCAUTH credit missing from the exchange==========="
   1125     echo -n "Modifying database: "
   1126     echo "DELETE FROM exchange.kycauths_in WHERE kycauth_in_serial_id=1" \
   1127         | psql -Aqt "$DB"
   1128     echo "DONE"
   1129 
   1130     run_audit
   1131     check_auditor_running
   1132 
   1133     echo -n "Checking that the bank's view is unchanged... "
   1134     check_balance \
   1135         "total_kycauth_in" \
   1136         "$KYCAUTH_IN" \
   1137         "Wrong total for KYCAUTH wire transfers"
   1138     echo -n "Checking that the exchange claims less revenue... "
   1139     check_balance \
   1140         "total_kycauth_revenue" \
   1141         "TESTKUDOS:0.1" \
   1142         "Wrong KYCAUTH revenue"
   1143     echo -n "Checking that the unbooked KYCAUTH was flagged... "
   1144     check_report_any \
   1145         "kycauth-in-inconsistencies" \
   1146         "diagnostic" "unknown to exchange"
   1147     echo -n "Checking the amount the bank reports... "
   1148     check_report_any \
   1149         "kycauth-in-inconsistencies" \
   1150         "amount_wired" "TESTKUDOS:0.1"
   1151     echo -n "Checking that it was counted as an excess credit... "
   1152     check_balance \
   1153         "total_bad_amount_in_plus" \
   1154         "TESTKUDOS:0.1" \
   1155         "Wrong total_bad_amount_in_plus"
   1156 
   1157     full_reload
   1158     cleanup
   1159 }
   1160 
   1161 
   1162 # The exchange booked a KYCAUTH transfer, but for a different amount than
   1163 # the bank actually moved.
   1164 function test_9() {
   1165 
   1166     echo "===========9: KYCAUTH amount inconsistency==========="
   1167     echo -n "Modifying database: "
   1168     echo "UPDATE exchange.kycauths_in SET credit.val=5 WHERE kycauth_in_serial_id=1" \
   1169         | psql -Aqt "$DB"
   1170     echo "DONE"
   1171 
   1172     run_audit
   1173     check_auditor_running
   1174 
   1175     echo -n "Checking that the mismatch was flagged... "
   1176     check_report_any \
   1177         "kycauth-in-inconsistencies" \
   1178         "diagnostic" "wire amount does not match"
   1179     echo -n "Checking the amount the exchange claims... "
   1180     check_report_any \
   1181         "kycauth-in-inconsistencies" \
   1182         "amount_exchange_expected" "TESTKUDOS:5.1"
   1183     echo -n "Checking the amount the bank reports... "
   1184     check_report_any \
   1185         "kycauth-in-inconsistencies" \
   1186         "amount_wired" "TESTKUDOS:0.1"
   1187     echo -n "Checking that the shortfall was counted... "
   1188     check_balance \
   1189         "total_bad_amount_in_minus" \
   1190         "TESTKUDOS:5" \
   1191         "Wrong total_bad_amount_in_minus"
   1192     echo -n "Checking that the inflated revenue reached the balance sheet... "
   1193     check_balance \
   1194         "total_kycauth_revenue" \
   1195         "TESTKUDOS:5.2" \
   1196         "Wrong KYCAUTH revenue"
   1197 
   1198     full_reload
   1199     cleanup
   1200 }
   1201 
   1202 
   1203 # The auditor may well see a KYCAUTH transfer at the bank before the
   1204 # exchange has booked it.  It reports that, and must take the report back
   1205 # -- together with the excess credit it booked alongside -- once the
   1206 # exchange catches up.  Without the retraction of the balance, a purely
   1207 # transient disagreement would inflate total_bad_amount_in_plus forever.
   1208 function test_10() {
   1209 
   1210     echo "===========10: KYCAUTH finding retracted when the exchange catches up==========="
   1211     echo -n "Modifying database: "
   1212     # Hide the exchange's record of the *last* KYCAUTH transfer, keeping a
   1213     # copy so it can be restored byte for byte.  It has to be the last one:
   1214     # kycauth_in_cb() resumes from a progress point, so a row reinstated
   1215     # below that point would never be looked at again.
   1216     echo "CREATE TABLE stashed_kycauth AS
   1217             SELECT * FROM exchange.kycauths_in
   1218              WHERE kycauth_in_serial_id
   1219                    = (SELECT MAX(kycauth_in_serial_id) FROM exchange.kycauths_in);
   1220           DELETE FROM exchange.kycauths_in
   1221              WHERE kycauth_in_serial_id
   1222                    = (SELECT MAX(kycauth_in_serial_id) FROM exchange.kycauths_in);" \
   1223         | psql -Aqt "$DB"
   1224     echo "DONE"
   1225 
   1226     pre_audit
   1227     audit_only
   1228     check_auditor_running
   1229 
   1230     echo -n "Checking that the unbooked transfer was reported... "
   1231     check_report_any \
   1232         "kycauth-in-inconsistencies" \
   1233         "diagnostic" "unknown to exchange"
   1234     echo -n "Checking that it was counted as an excess credit... "
   1235     check_balance \
   1236         "total_bad_amount_in_plus" \
   1237         "TESTKUDOS:0.1" \
   1238         "Wrong total_bad_amount_in_plus"
   1239 
   1240     # The exchange catches up.  No progress points are touched: the credit
   1241     # audit is simply run once more, exactly as a resident helper would on
   1242     # its next wake-up.
   1243     echo -n "Letting the exchange catch up: "
   1244     echo "INSERT INTO exchange.kycauths_in SELECT * FROM stashed_kycauth;
   1245           DROP TABLE stashed_kycauth;" \
   1246         | psql -Aqt "$DB"
   1247     echo "DONE"
   1248 
   1249     echo -n "Re-running the credit audit ..."
   1250     $VALGRIND taler-helper-auditor-wire-credit \
   1251               -i \
   1252               -L DEBUG \
   1253               -c "$CONF" \
   1254               -t \
   1255               > "${MY_TMP_DIR}/test-audit-wire-credit-retract.out" \
   1256               2> "${MY_TMP_DIR}/test-audit-wire-credit-retract.err" \
   1257         || exit_fail "wire credit re-audit failed (see ${MY_TMP_DIR}/test-audit-wire-credit-retract.*)"
   1258     echo " DONE"
   1259 
   1260     echo -n "Checking that the report was retracted... "
   1261     check_no_report "kycauth-in-inconsistencies"
   1262     echo -n "Checking that the excess credit was retracted too... "
   1263     check_balance \
   1264         "total_bad_amount_in_plus" \
   1265         "TESTKUDOS:0" \
   1266         "Retracted finding left total_bad_amount_in_plus inflated"
   1267 
   1268     post_audit
   1269     full_reload
   1270     cleanup
   1271 }
   1272 
   1273 
   1274 # An AML staff appointment that the exchange's offline master key did not
   1275 # sign.  Without this check an exchange could appoint an AML officer, or
   1276 # quietly widen a read-only officer to read-write, with the offline key never
   1277 # having been involved.
   1278 function test_11() {
   1279 
   1280     echo "===========11: AML staff appointment with a bad master signature==========="
   1281     echo -n "Modifying database: "
   1282 # shellcheck disable=SC2028
   1283     echo "INSERT INTO exchange.aml_staff
   1284             (decider_pub, master_sig, decider_name, is_active, read_only, last_change)
   1285           VALUES
   1286             ('\x1111111111111111111111111111111111111111111111111111111111111111',
   1287              '\x22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222',
   1288              'Unappointed Officer', TRUE, FALSE, 1000000000000000);" \
   1289         | psql -Aqt "$DB"
   1290     echo "DONE"
   1291 
   1292     run_audit
   1293     check_auditor_running
   1294 
   1295     echo -n "Checking that the appointment was flagged... "
   1296     check_report_any \
   1297         "row-inconsistencies" \
   1298         "diagnostic" "invalid master key signature on staff status"
   1299     echo -n "Checking that it was blamed on the right table... "
   1300     check_report_any \
   1301         "row-inconsistencies" \
   1302         "row_table" "aml_staff"
   1303 
   1304     # aml_staff is append-only, so the helper can and must resume from where
   1305     # it stopped instead of re-reading the whole table on every wake-up.
   1306     # Both halves have to hold: the cursor has to be stored at all, and the
   1307     # second run has to pick it up.
   1308     echo -n "Checking that the aml_staff cursor was stored... "
   1309     AML_PP=$(psql -Aqt "$DB" \
   1310         -c "SELECT progress_offset FROM auditor.auditor_progress \
   1311             WHERE progress_key='aml_staff_uuid';")
   1312     if [ -z "$AML_PP" ] || [ "$AML_PP" = "0" ]
   1313     then
   1314         exit_fail "aml_staff_uuid progress point not recorded (got '$AML_PP')"
   1315     fi
   1316     echo "PASS ($AML_PP)"
   1317     echo -n "Checking that the second run resumed from it... "
   1318     if ! grep -q "Resuming AML audit at $AML_PP/" \
   1319          "${MY_TMP_DIR}/test-audit-aml-inc.err"
   1320     then
   1321         exit_fail "incremental AML audit did not resume at $AML_PP"
   1322     fi
   1323     echo "PASS"
   1324 
   1325     full_reload
   1326     cleanup
   1327 }
   1328 
   1329 
   1330 # An AML decision that no valid officer signature backs, by somebody the
   1331 # exchange never appointed at all.  Both halves have to be reported: the
   1332 # signature is what makes the decision accountable, and the appointment is
   1333 # what makes the signature mean anything.
   1334 function test_12() {
   1335 
   1336     echo "===========12: AML decision by an unappointed officer==========="
   1337     echo -n "Modifying database: "
   1338 # shellcheck disable=SC2028
   1339     echo "INSERT INTO exchange.aml_history
   1340             (h_payto, justification, decider_pub, decider_sig, outcome_serial_id)
   1341           SELECT h_payto,
   1342                  'injected by test 12',
   1343                  '\x3333333333333333333333333333333333333333333333333333333333333333',
   1344                  '\x44444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444',
   1345                  outcome_serial_id
   1346             FROM exchange.legitimization_outcomes
   1347            ORDER BY outcome_serial_id
   1348            LIMIT 1;" \
   1349         | psql -Aqt "$DB"
   1350     echo "DONE"
   1351 
   1352     run_audit
   1353     check_auditor_running
   1354 
   1355     echo -n "Checking that the bad signature was flagged... "
   1356     check_report_any \
   1357         "row-inconsistencies" \
   1358         "diagnostic" "invalid officer signature on decision"
   1359     echo -n "Checking that the missing appointment was flagged... "
   1360     check_report_any \
   1361         "row-inconsistencies" \
   1362         "diagnostic" "officer was not appointed when the decision was made"
   1363     echo -n "Checking that it was blamed on the right table... "
   1364     check_report_any \
   1365         "row-inconsistencies" \
   1366         "row_table" "aml_history"
   1367 
   1368     full_reload
   1369     cleanup
   1370 }
   1371 
   1372 
   1373 # `legitimization_outcomes' is the table that says which KYC rules apply to
   1374 # an account, so a row nothing produced is how an exchange would quietly
   1375 # exempt a customer from KYC.  The account here is fresh: it has no AML
   1376 # decision pointing at the outcome, no legitimization process, and no earlier
   1377 # outcome that expired, which is all three of the ways the exchange's own
   1378 # code creates one.
   1379 function test_13() {
   1380 
   1381     echo "===========13: legitimization outcome without a justification==========="
   1382     echo -n "Modifying database: "
   1383 # shellcheck disable=SC2028
   1384     echo "INSERT INTO exchange.kyc_targets
   1385             (h_normalized_payto, is_wallet)
   1386           VALUES
   1387             ('\x5555555555555555555555555555555555555555555555555555555555555555',
   1388              FALSE);
   1389           INSERT INTO exchange.legitimization_outcomes
   1390             (h_payto, decision_time, expiration_time, to_investigate)
   1391           VALUES
   1392             ('\x5555555555555555555555555555555555555555555555555555555555555555',
   1393              1000000000000000, 2000000000000000, FALSE);" \
   1394         | psql -Aqt "$DB"
   1395     echo "DONE"
   1396 
   1397     run_audit
   1398     check_auditor_running
   1399 
   1400     echo -n "Checking that the unjustified outcome was flagged... "
   1401     check_report_any \
   1402         "row-inconsistencies" \
   1403         "diagnostic" \
   1404         "KYC rules in force without an AML decision, a legitimization process or an expired predecessor"
   1405     echo -n "Checking that it was blamed on the right table... "
   1406     check_report_any \
   1407         "row-inconsistencies" \
   1408         "row_table" "legitimization_outcomes"
   1409 
   1410     # Only the injected row is bad.  The outcomes the reference database
   1411     # earned honestly are each accounted for by a legitimization process or
   1412     # an AML decision, and reporting those too would make the check useless.
   1413     echo -n "Checking that the honest outcomes were left alone... "
   1414     BAD=$(jq -r '[.records[]
   1415                   | select(.row_table == "legitimization_outcomes")]
   1416                  | length' \
   1417              < "${MY_TMP_DIR}/row-inconsistencies.json")
   1418     if [ "$BAD" != "1" ]
   1419     then
   1420         exit_fail "wanted exactly 1 bad legitimization outcome, got $BAD"
   1421     fi
   1422     echo "PASS"
   1423 
   1424     full_reload
   1425     cleanup
   1426 }
   1427 
   1428 
   1429 # Releasing the funds must retract the alert, not just stop adding to it.
   1430 # The exchange announces the release by writing the `wire_out' row, which is
   1431 # the only thing that distinguishes "paid" from "decided what to pay"; so
   1432 # that is what this test injects.  Nothing else changes: the deposits, the
   1433 # aggregation_tracking rows and the open KYC measure all stay exactly as they
   1434 # were, which is the point -- the hold must end because the money moved, and
   1435 # for no other reason.
   1436 function test_14() {
   1437 
   1438     echo "===========14: releasing a withheld payout==========="
   1439     echo -n "Modifying database: "
   1440     # Take the transfer straight from the transient the exchange parked it in,
   1441     # so that the test does not have to know the randomly drawn wtid.
   1442     echo "INSERT INTO exchange.wire_out
   1443             (execution_date, wtid_raw, wire_target_h_payto,
   1444              exchange_account_section, amount)
   1445           SELECT 1785869200000000
   1446                 ,t.wtid_raw
   1447                 ,t.wire_target_h_payto
   1448                 ,t.exchange_account_section
   1449                 ,t.amount
   1450             FROM exchange.aggregation_transient t;" \
   1451         | psql -Aqt "$DB"
   1452     echo "DONE"
   1453 
   1454     run_audit
   1455     check_auditor_running
   1456 
   1457     echo -n "Checking that the hold was retracted... "
   1458     check_no_report "aml-holds"
   1459     echo -n "Checking that the AML total went back to zero... "
   1460     check_balance \
   1461         "total_aml_hold" \
   1462         "TESTKUDOS:0" \
   1463         "Released funds are still counted as withheld"
   1464     echo -n "Checking that they did not turn into a lag instead... "
   1465     check_balance \
   1466         "total_transfer_lag" \
   1467         "TESTKUDOS:0" \
   1468         "Released funds were reclassified rather than dropped"
   1469     echo -n "Checking that they were not parked as a small aggregate... "
   1470     check_balance \
   1471         "total_small_aggregate" \
   1472         "TESTKUDOS:0" \
   1473         "Released funds were reclassified rather than dropped"
   1474 
   1475     full_reload
   1476     cleanup
   1477 }
   1478 
   1479 
   1480 # What the exchange *claims* decides which balance a hold lands in; whether the
   1481 # claim holds up is a separate question, and this is the test that they are
   1482 # separate.  Here the measure the exchange named is carried through to a
   1483 # decision while the exchange goes on withholding the money and goes on saying
   1484 # it is withholding it for that measure.
   1485 #
   1486 # The money must therefore stay in `total_aml_hold' -- the auditor does not get
   1487 # to overrule the exchange about its own reasons -- and the auditor must report
   1488 # a row_inconsistency against `aggregation_deferrals', because the one part of
   1489 # the claim it can check has stopped being true.  Deciding what to make of that
   1490 # is a job for the humans reading the finding.
   1491 function test_15() {
   1492 
   1493     echo "===========15: a hold whose KYC measure was satisfied==========="
   1494     echo -n "Modifying database: "
   1495     # Carry the measure that is blocking the payout through to a decision, the
   1496     # way the exchange would have if the merchant had filled in the form: a
   1497     # process referring to the measure, and an outcome for the account decided
   1498     # no earlier than that process started.  `decision_time' is rounded up to a
   1499     # whole second because the exchange stores it rounded and the AML helper
   1500     # reads it as a timestamp.
   1501     echo "INSERT INTO exchange.legitimization_processes
   1502             (h_payto, start_time, expiration_time, provider_name,
   1503              legitimization_measure_serial_id, measure_index)
   1504           SELECT kt.h_normalized_payto
   1505                 ,lm.start_time
   1506                 ,0
   1507                 ,'full_name_and_birthdate'
   1508                 ,lm.legitimization_measure_serial_id
   1509                 ,0
   1510             FROM exchange.aggregation_transient t
   1511             JOIN exchange.wire_targets wt
   1512               ON (wt.wire_target_h_payto=t.wire_target_h_payto)
   1513             JOIN exchange.kyc_targets kt
   1514               ON (kt.h_normalized_payto=wt.h_normalized_payto)
   1515             JOIN exchange.legitimization_measures lm
   1516               USING (access_token);
   1517           INSERT INTO exchange.legitimization_outcomes
   1518             (h_payto, decision_time, expiration_time, to_investigate)
   1519           SELECT kt.h_normalized_payto
   1520                 ,(lm.start_time/1000000+1)*1000000
   1521                 ,2000000000000000
   1522                 ,FALSE
   1523             FROM exchange.aggregation_transient t
   1524             JOIN exchange.wire_targets wt
   1525               ON (wt.wire_target_h_payto=t.wire_target_h_payto)
   1526             JOIN exchange.kyc_targets kt
   1527               ON (kt.h_normalized_payto=wt.h_normalized_payto)
   1528             JOIN exchange.legitimization_measures lm
   1529               USING (access_token);" \
   1530         | psql -Aqt "$DB"
   1531     echo "DONE"
   1532 
   1533     run_audit
   1534     check_auditor_running
   1535 
   1536     echo -n "Checking that the exchange is still taken at its word... "
   1537     check_balance \
   1538         "total_aml_hold" \
   1539         "$WITHHELD" \
   1540         "Auditor reclassified a hold the exchange still attributes to KYC"
   1541     echo -n "Checking that it did not become an unexplained lag... "
   1542     check_balance \
   1543         "total_transfer_lag" \
   1544         "TESTKUDOS:0" \
   1545         "Auditor overruled the exchange's stated reason"
   1546     echo -n "Checking that the hold still names the claimed reason... "
   1547     check_report "aml-holds" "deferral_reason" "KYC"
   1548     echo -n "Checking that it still names the blocked account... "
   1549     check_report_any \
   1550         "aml-holds" \
   1551         "account" \
   1552         "$BLOCKED_ACCOUNT"
   1553     echo -n "Checking that the stale claim was reported... "
   1554     check_report_any \
   1555         "row-inconsistencies" \
   1556         "row_table" "aggregation_deferrals"
   1557 
   1558     full_reload
   1559     cleanup
   1560 }
   1561 
   1562 
   1563 # The other thing an exchange can lawfully be sitting on: an aggregate that
   1564 # would not survive its own wire fee.  That is not an irregularity and must not
   1565 # read as one, so it gets a balance of its own rather than being lumped in with
   1566 # payouts nobody has explained.
   1567 #
   1568 # The reference database has no such aggregate -- every payout in it is worth
   1569 # making -- so the test restates the reason on the one deferral it does have.
   1570 # Reason 1 is TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL, 2 is ..._DR_KYC.  Note that
   1571 # this leaves the exchange's `aggregation_transient' still naming a
   1572 # legitimization requirement, so the -i cross-check must notice that the two
   1573 # no longer agree.
   1574 function test_16() {
   1575 
   1576     echo "===========16: an aggregate too small to pay out==========="
   1577     echo -n "Modifying database: "
   1578     # Only the transfer that is still outstanding; the cleared merchant's
   1579     # deferral is on file too, but it was overtaken by its wire_out row.
   1580     echo "UPDATE exchange.aggregation_deferrals d
   1581              SET deferral_reason=1
   1582                 ,legitimization_requirement_serial_id=0
   1583            WHERE NOT EXISTS (
   1584                  SELECT 1 FROM exchange.wire_out w
   1585                   WHERE w.wtid_raw=d.wtid_raw);" \
   1586         | psql -Aqt "$DB"
   1587     echo "DONE"
   1588 
   1589     run_audit
   1590     check_auditor_running
   1591 
   1592     echo -n "Checking that the funds are booked as a small aggregate... "
   1593     check_balance \
   1594         "total_small_aggregate" \
   1595         "$WITHHELD" \
   1596         "Aggregate below the wire fee was not booked as such"
   1597     echo -n "Checking that they are no longer an AML hold... "
   1598     check_balance \
   1599         "total_aml_hold" \
   1600         "TESTKUDOS:0" \
   1601         "Aggregate below the wire fee still counted as withheld for KYC"
   1602     echo -n "Checking that they are not an unexplained lag either... "
   1603     check_balance \
   1604         "total_transfer_lag" \
   1605         "TESTKUDOS:0" \
   1606         "Aggregate below the wire fee read as an unexplained delay"
   1607     echo -n "Checking that the hold names the new reason... "
   1608     check_report "aml-holds" "deferral_reason" "AMOUNT_TOO_SMALL"
   1609     echo -n "Checking that the contradiction with the transient was reported... "
   1610     check_report_any \
   1611         "row-inconsistencies" \
   1612         "row_table" "aggregation_transient"
   1613 
   1614     full_reload
   1615     cleanup
   1616 }
   1617 
   1618 
   1619 # *************** Main test loop starts here **************
   1620 
   1621 
   1622 # Run all the tests against the database given in $1.
   1623 # Sets $fail to 0 on success, non-zero on failure.
   1624 function check_with_database()
   1625 {
   1626     BASEDB="$1"
   1627     CONF="$1.conf"
   1628     export CONF
   1629     echo "Running test suite with database $BASEDB using configuration $CONF"
   1630     MASTER_PRIV_FILE="${BASEDB}.mpriv"
   1631     taler-exchange-config \
   1632         -f \
   1633         -c "${CONF}" \
   1634         -s exchange-offline \
   1635         -o MASTER_PRIV_FILE \
   1636         -V "${MASTER_PRIV_FILE}"
   1637 
   1638     # Load database
   1639     full_reload
   1640 
   1641     # Run test suite
   1642     fail=0
   1643     for i in $TESTS
   1644     do
   1645         "test_$i"
   1646         if test 0 != $fail
   1647         then
   1648             break
   1649         fi
   1650     done
   1651     echo "Cleanup (disabled, leaving database $DB behind)"
   1652     # dropdb $DB
   1653 }
   1654 
   1655 # When the script is not run as root, setup a temporary directory for the
   1656 # postgres database.
   1657 # Sets PGHOST accordingly to the freshly created socket.
   1658 function perform_initdb() {
   1659     # Available directly in path?
   1660     INITDB_BIN=$(command -v initdb) || true
   1661     if [[ -n "$INITDB_BIN" ]]; then
   1662       echo " FOUND (in path) at $INITDB_BIN"
   1663     else
   1664         HAVE_INITDB=$(find /usr -name "initdb" 2> /dev/null \
   1665                           | head -1 2> /dev/null \
   1666                           | grep postgres) \
   1667             || exit_skip " MISSING"
   1668       echo " FOUND at $(dirname "$HAVE_INITDB")"
   1669       INITDB_BIN=$(echo "$HAVE_INITDB" | grep bin/initdb | grep postgres | sort -n | tail -n1)
   1670     fi
   1671     POSTGRES_PATH=$(dirname "$INITDB_BIN")
   1672 
   1673     TMPDIR="$MY_TMP_DIR/postgres"
   1674     mkdir -p "$TMPDIR"
   1675     echo -n "Setting up Postgres DB at $TMPDIR ..."
   1676     $INITDB_BIN \
   1677         --no-sync \
   1678         --auth=trust \
   1679         -D "${TMPDIR}" \
   1680         > "${MY_TMP_DIR}/postgres-dbinit.log" \
   1681         2> "${MY_TMP_DIR}/postgres-dbinit.err" \
   1682         || {
   1683         echo "FAILED!"
   1684         echo "Last entries in ${MY_TMP_DIR}/postgres-dbinit.err:"
   1685         tail "${MY_TMP_DIR}/postgres-dbinit.err"
   1686         exit 1
   1687     }
   1688     echo "DONE"
   1689 
   1690     # Once we move to PG16, we can use:
   1691     #    --set listen_addresses='' \
   1692     #    --set fsync=off \
   1693     #    --set max_wal_senders=0 \
   1694     #    --set synchronous_commit=off \
   1695     #    --set wal_level=minimal \
   1696     #    --set unix_socket_directories="${TMPDIR}/sockets" \
   1697 
   1698 
   1699     SOCKETDIR="${TMPDIR}/sockets"
   1700     mkdir "${SOCKETDIR}"
   1701 
   1702     echo -n "Launching Postgres service"
   1703 
   1704     cat - >> "$TMPDIR/postgresql.conf" <<EOF
   1705 unix_socket_directories='${TMPDIR}/sockets'
   1706 fsync=off
   1707 max_wal_senders=0
   1708 synchronous_commit=off
   1709 wal_level=minimal
   1710 listen_addresses=''
   1711 EOF
   1712 
   1713     grep -v host \
   1714          < "$TMPDIR/pg_hba.conf" \
   1715          > "$TMPDIR/pg_hba.conf.new"
   1716     mv "$TMPDIR/pg_hba.conf.new" "$TMPDIR/pg_hba.conf"
   1717     "${POSTGRES_PATH}/pg_ctl" \
   1718         -D "$TMPDIR" \
   1719         -l "${MY_TMP_DIR}/postgres.log" \
   1720         start \
   1721         > "${MY_TMP_DIR}/postgres-start.log" \
   1722         2> "${MY_TMP_DIR}/postgres-start.err"
   1723     echo " DONE"
   1724     PGHOST="$TMPDIR/sockets"
   1725     export PGHOST
   1726 }
   1727 
   1728 
   1729 # *************** Main logic starts here **************
   1730 
   1731 # ####### Setup globals ######
   1732 # Postgres database to use (must match configuration file)
   1733 export DB="auditor-basedb"
   1734 
   1735 # test required commands exist
   1736 echo "Testing for jq"
   1737 jq -h > /dev/null || exit_skip "jq required"
   1738 echo "Testing for taler-merchant-config"
   1739 taler-merchant-config -h > /dev/null || exit_skip "taler-merchant-config required"
   1740 echo "Testing for taler-merchant-httpd"
   1741 taler-merchant-httpd -h > /dev/null || exit_skip "taler-merchant-httpd required"
   1742 echo "Testing for faketime"
   1743 faketime -h > /dev/null || exit_skip "faketime required"
   1744 # NOTE: really check for all three libeufin commands?
   1745 echo "Testing for libeufin"
   1746 libeufin-bank --help >/dev/null 2> /dev/null </dev/null || exit_skip "libeufin required"
   1747 echo "Testing for taler-wallet-cli"
   1748 taler-wallet-cli -h >/dev/null </dev/null 2>/dev/null || exit_skip "taler-wallet-cli required"
   1749 
   1750 
   1751 echo -n "Testing for Postgres"
   1752 
   1753 MY_TMP_DIR=$(mktemp -d /tmp/taler-auditor-basedbXXXXXX)
   1754 echo "Using $MY_TMP_DIR for logging and temporary data"
   1755 
   1756 # If run as root, simply use the running postgres instance.
   1757 # Otherwise create a temporary storage space for postgres.
   1758 [ $(id -u) == 0 ] || perform_initdb
   1759 
   1760 MYDIR="${MY_TMP_DIR}/basedb"
   1761 mkdir -p "${MYDIR}"
   1762 
   1763 if [ -z ${REUSE_BASEDB_DIR+x} ]
   1764 then
   1765     echo "Generating fresh database at $MYDIR"
   1766 
   1767     if faketime -f '-1 d' ./generate-kyc-basedb.sh -d "$MYDIR/$DB"
   1768     then
   1769         echo -n "Reset 'auditor-basedb' database at ${PGHOST:-} ..."
   1770         dropdb --if-exists "auditor-basedb" > /dev/null 2> /dev/null || true
   1771         createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB' at ${PGHOST:-}"
   1772         echo " DONE"
   1773     else
   1774         echo "Generation failed"
   1775         exit 1
   1776     fi
   1777     echo "To reuse this database in the future, use:"
   1778     echo "export REUSE_BASEDB_DIR=$MY_TMP_DIR"
   1779 else
   1780     echo "Reusing existing database from ${REUSE_BASEDB_DIR}"
   1781     cp -r "${REUSE_BASEDB_DIR}/basedb"/* "${MYDIR}/"
   1782 fi
   1783 
   1784 check_with_database "$MYDIR/$DB"
   1785 if [ "$fail" != "0" ]
   1786 then
   1787     exit "$fail"
   1788 fi
   1789 
   1790 if [ -z "${REUSE_BASEDB_DIR+x}" ]
   1791 then
   1792     echo "Run 'export REUSE_BASEDB_DIR=${MY_TMP_DIR}' to re-run tests against the same database"
   1793 fi
   1794 
   1795 exit 0