exchange

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

generate-revoke-basedb.sh (12464B)


      1 #!/bin/bash
      2 # Script to test revocation.
      3 #
      4 # Requires the wallet CLI to be installed and in the path.  Furthermore, the
      5 # user running this script must be Postgres superuser and be allowed to
      6 # create/drop databases.
      7 #
      8 set -eu
      9 # set -x
     10 
     11 # The revocation test depends on the wallet picking specific coins (it
     12 # revokes the denomination of a particular coin), so coin selection has to
     13 # be deterministic.  See generate-auditor-basedb.sh and
     14 # https://bugs.gnunet.org/view.php?id=11272.
     15 export TALER_WALLET_COINSEL="legacy-2024"
     16 
     17 . setup.sh
     18 
     19 echo -n "Testing for curl ..."
     20 curl --help >/dev/null </dev/null || exit_skip " MISSING"
     21 echo " FOUND"
     22 
     23 # reset database
     24 echo -n "Reset 'auditor-basedb' database ..."
     25 dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
     26 createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB'"
     27 echo " DONE"
     28 
     29 # Launch exchange, merchant and bank.
     30 setup -c generate-auditor-basedb.conf \
     31       -abemw \
     32       -d "iban"
     33 CONF="generate-auditor-basedb.conf.edited"
     34 
     35 # obtain key configuration data
     36 EXCHANGE_URL=$(taler-exchange-config -c "$CONF" -s EXCHANGE -o BASE_URL)
     37 MERCHANT_PORT=$(taler-merchant-config -c "$CONF" -s MERCHANT -o PORT)
     38 MERCHANT_URL="http://localhost:${MERCHANT_PORT}/"
     39 BANK_PORT=$(taler-exchange-config -c "$CONF" -s BANK -o HTTP_PORT)
     40 BANK_URL="http://localhost:${BANK_PORT}/"
     41 
     42 
     43 # Setup merchant
     44 export MERCHANT_URL
     45 echo -n "Setting up merchant ..."
     46 curl -H "Content-Type: application/json" -X POST -d '{"auth": {"method": "external"},"id":"admin","name":"admin","address":{},"jurisdiction":{},"default_max_wire_fee":"TESTKUDOS:1", "default_max_deposit_fee":"TESTKUDOS:1","default_wire_fee_amortization":1,"default_wire_transfer_delay":{"d_us" : 3600000000},"default_pay_delay":{"d_us": 3600000000},"use_stefan":true}' "${MERCHANT_URL}management/instances"
     47 echo " DONE"
     48 
     49 echo -n "Setting up merchant account ..."
     50 FORTYTHREE="payto://iban/DE474361?receiver-name=Merchant43"
     51 STATUS=$(curl -H "Content-Type: application/json" -X POST \
     52     "${MERCHANT_URL}private/accounts" \
     53     -d '{"payto_uri":"'"$FORTYTHREE"'"}' \
     54     -w "%{http_code}" -s -o /dev/null)
     55 if [ "$STATUS" != "200" ]
     56 then
     57     exit_fail "Expected 200 OK. Got: $STATUS"
     58 fi
     59 echo " DONE"
     60 
     61 echo -n "Setting up libeufin merchant account ..."
     62 libeufin-bank create-account \
     63               --config="${CONF}" \
     64               --name="Merchant43" \
     65               --username="Merchant43" \
     66               --password="password" \
     67               --payto_uri="payto://iban/DE474361?receiver-name=Merchant43"
     68 echo " DONE"
     69 
     70 
     71 # run wallet CLI
     72 echo "Running wallet"
     73 
     74 export WALLET_DB="wallet.wdb"
     75 rm -f "$WALLET_DB"
     76 
     77 wlog="taler-wallet-cli-withdraw.log"
     78 taler-wallet-cli \
     79     --no-throttle \
     80     --wallet-db="$WALLET_DB" \
     81     api \
     82     --expect-success 'withdrawTestBalance' \
     83   "$(jq -n '
     84     {
     85       amount: "TESTKUDOS:8",
     86       corebankApiBaseUrl: $BANK_URL,
     87       exchangeBaseUrl: $EXCHANGE_URL,
     88     }' \
     89     --arg BANK_URL "$BANK_URL" \
     90     --arg EXCHANGE_URL "$EXCHANGE_URL"
     91   )" &> $wlog || {
     92     echo " FAILED(withdraw)!  Last entries in $wlog:"
     93     tail $wlog
     94     exit 2
     95 }
     96 
     97 taler-wallet-cli \
     98     --no-throttle \
     99     --wallet-db="$WALLET_DB" \
    100     run-until-done \
    101     &> taler-wallet-cli-withdraw-finish.log
    102 
    103 export COINS=$(taler-wallet-cli --wallet-db="$WALLET_DB" advanced dump-coins)
    104 
    105 echo -n "COINS are:"
    106 echo "$COINS"
    107 
    108 export COINS
    109 # Find coin we want to revoke
    110 export rc=$(echo "$COINS" | jq -r '[.coins[] | select((.denomValue == "TESTKUDOS:2"))][0] | .coinPub')
    111 # Find the denom
    112 export rd=$(echo "$COINS" | jq -r '[.coins[] | select((.denomValue == "TESTKUDOS:2"))][0] | .denomPubHash')
    113 
    114 # This database is all about recoup, which the exchange currently does not
    115 # implement: the recoup handlers are compiled out behind FIXME_9828 (see
    116 # src/exchange/taler-exchange-httpd.c and
    117 # https://bugs.gnunet.org/view.php?id=9828).  Without them the wallet can
    118 # never recoup the coins of the revoked denomination and we would spin until
    119 # the test times out, so detect that up front and skip.
    120 echo -n "Checking that the exchange implements recoup ..."
    121 RECOUP_CODE=$(curl -s -X POST \
    122                    -o "${MY_TMP_DIR}/recoup-probe.json" \
    123                    -w "%{http_code}" \
    124                    -H "Content-Type: application/json" \
    125                    -d '{}' \
    126                    "${EXCHANGE_URL}coins/${rc}/recoup")
    127 if [ "$RECOUP_CODE" = "404" ] &&
    128    [ "$(jq -r '.code' < "${MY_TMP_DIR}/recoup-probe.json")" = "1001" ]
    129 then
    130     echo " NO"
    131     exit_skip "exchange has no /coins/\$COIN_PUB/recoup endpoint (recoup is disabled, see FIXME_9828)"
    132 fi
    133 echo " YES"
    134 
    135 echo -n "Revoking denomination ${rd} (to affect coin ${rc}) ..."
    136 # Find all other coins, which will be suspended
    137 export susp=$(echo "$COINS" | jq --arg rc "$rc" '[.coins[] | select(.coinPub != $rc) | .coinPub]')
    138 
    139 # Do the revocation
    140 taler-exchange-offline \
    141     -c "$CONF" \
    142     revoke-denomination "${rd}" \
    143     upload \
    144     &> taler-exchange-offline-revoke.log
    145 echo "DONE"
    146 
    147 echo -n "Signing replacement keys ..."
    148 sleep 1 # Give exchange time to create replacmenent key
    149 
    150 # Re-sign replacement keys
    151 taler-auditor-offline \
    152     -c "$CONF" \
    153     download \
    154     sign \
    155     upload \
    156     &> taler-auditor-offline-reinit.log
    157 echo " DONE"
    158 
    159 # Now we suspend the other coins, so later we will pay with the recouped coin
    160 taler-wallet-cli \
    161     --wallet-db="$WALLET_DB" \
    162     advanced \
    163     suspend-coins "$susp"
    164 
    165 # Update exchange /keys so recoup gets scheduled
    166 taler-wallet-cli \
    167     --wallet-db="$WALLET_DB" \
    168     exchanges \
    169     update \
    170     -f "$EXCHANGE_URL"
    171 
    172 # Block until scheduled operations are done
    173 taler-wallet-cli \
    174     --wallet-db="$WALLET_DB"\
    175     run-until-done
    176 
    177 # Now we buy something, only the coins resulting from recoup will be
    178 # used, as other ones are suspended
    179 taler-wallet-cli \
    180     --no-throttle \
    181     --wallet-db="$WALLET_DB" \
    182     api \
    183     'testPay' \
    184   "$(jq -n '
    185     {
    186       amount: "TESTKUDOS:1",
    187       merchantBaseUrl: $MERCHANT_URL,
    188       summary: "foo",
    189     }' \
    190     --arg MERCHANT_URL "$MERCHANT_URL"
    191   )"
    192 
    193 taler-wallet-cli \
    194     --wallet-db="$WALLET_DB" \
    195     run-until-done
    196 
    197 echo "Purchase with recoup'ed coin (via reserve) done"
    198 
    199 # Re-read the coins: the recoup and the purchase above changed the wallet's
    200 # coin set, so the dump taken before the first revocation is stale.
    201 COINS=$(taler-wallet-cli --wallet-db="$WALLET_DB" advanced dump-coins)
    202 export COINS
    203 
    204 # Find coin we want to refresh, then revoke
    205 export rrc=$(echo "$COINS" | jq -r '[.coins[] | select((.denomValue == "TESTKUDOS:5"))][0] | .coinPub')
    206 # Find the denom
    207 export zombie_denom=$(echo "$COINS" | jq -r '[.coins[] | select((.denomValue == "TESTKUDOS:5"))][0] | .denomPubHash')
    208 
    209 echo "Will refresh coin ${rrc} of denomination ${zombie_denom}"
    210 # Find all other coins, which will be suspended
    211 export susp=$(echo "$COINS" | jq --arg rrc "$rrc" '[.coins[] | select(.coinPub != $rrc) | .coinPub]')
    212 
    213 # Travel into the future! (must match DURATION_WITHDRAW option)
    214 export TIMETRAVEL="--timetravel=604800000000"
    215 
    216 echo "Launching exchange 1 week in the future"
    217 # The exchange and its security modules were started by
    218 # taler-unified-setup.sh, so we do not have their PIDs; stop them by name
    219 # and bring them back up with the time offset applied.
    220 for proc in taler-exchange-httpd \
    221             taler-exchange-secmod-rsa \
    222             taler-exchange-secmod-cs \
    223             taler-exchange-secmod-eddsa
    224 do
    225     pkill -x -u "$(id -u)" -TERM "$proc" || true
    226 done
    227 # Give them a moment to release their sockets
    228 sleep 1
    229 taler-exchange-secmod-eddsa $TIMETRAVEL -c "$CONF" 2> "${MY_TMP_DIR}/taler-exchange-secmod-eddsa.log" &
    230 SIGNKEY_HELPER_PID=$!
    231 taler-exchange-secmod-rsa $TIMETRAVEL -c "$CONF" 2> "${MY_TMP_DIR}/taler-exchange-secmod-rsa.log" &
    232 RSA_DENOM_HELPER_PID=$!
    233 taler-exchange-secmod-cs $TIMETRAVEL -c "$CONF" 2> "${MY_TMP_DIR}/taler-exchange-secmod-cs.log" &
    234 CS_DENOM_HELPER_PID=$!
    235 export SIGNKEY_HELPER_PID RSA_DENOM_HELPER_PID CS_DENOM_HELPER_PID
    236 taler-exchange-httpd $TIMETRAVEL -c "$CONF" 2> "${MY_TMP_DIR}/taler-exchange-httpd.log" &
    237 export EXCHANGE_PID=$!
    238 
    239 # Wait for exchange to be available
    240 OK=0
    241 for n in `seq 1 100`
    242 do
    243     echo -n "."
    244     sleep 0.2
    245     # exchange
    246     wget "${EXCHANGE_URL}config" -o /dev/null -O /dev/null >/dev/null || continue
    247     OK=1
    248     break
    249 done
    250 if [ 1 != "$OK" ]
    251 then
    252     exit_fail "Failed to restart exchange in the future"
    253 fi
    254 echo " DONE"
    255 
    256 echo "Refreshing coin $rrc"
    257 taler-wallet-cli \
    258     "$TIMETRAVEL" \
    259     --wallet-db="$WALLET_DB" \
    260     advanced force-refresh \
    261     "$rrc"
    262 taler-wallet-cli \
    263     "$TIMETRAVEL" \
    264     --wallet-db="$WALLET_DB" \
    265     run-until-done
    266 
    267 # Update our list of the coins
    268 export coins=$(taler-wallet-cli "$TIMETRAVEL" --wallet-db="$WALLET_DB" advanced dump-coins)
    269 
    270 # Find resulting refreshed coin
    271 export freshc=$(echo "$coins" | jq -r --arg rrc "$rrc" \
    272   '[.coins[] | select((.refreshParentCoinPub == $rrc) and .denomValue == "TESTKUDOS:0.1")][0] | .coinPub'
    273 )
    274 
    275 # Find the denom of freshc
    276 export fresh_denom=$(echo "$coins" | jq -r --arg rrc "$rrc" \
    277   '[.coins[] | select((.refreshParentCoinPub == $rrc) and .denomValue == "TESTKUDOS:0.1")][0] | .denomPubHash'
    278 )
    279 
    280 echo "Coin ${freshc} of denomination ${fresh_denom} is the result of the refresh"
    281 
    282 # Find all other coins, which will be suspended
    283 export susp=$(echo "$coins" | jq --arg freshc "$freshc" '[.coins[] | select(.coinPub != $freshc) | .coinPub]')
    284 
    285 
    286 # Do the revocation of freshc
    287 echo "Revoking ${fresh_denom} (to affect coin ${freshc})"
    288 taler-exchange-offline \
    289     -c "$CONF" \
    290     revoke-denomination \
    291     "${fresh_denom}" \
    292     upload &> taler-exchange-offline-revoke-2.log
    293 
    294 sleep 1 # Give exchange time to create replacmenent key
    295 
    296 # Re-sign replacement keys
    297 taler-auditor-offline \
    298     -c "$CONF" \
    299     download \
    300     sign \
    301     upload &> taler-auditor-offline.log
    302 
    303 # Now we suspend the other coins, so later we will pay with the recouped coin
    304 taler-wallet-cli \
    305     "$TIMETRAVEL" \
    306     --wallet-db="$WALLET_DB" \
    307     advanced \
    308     suspend-coins "$susp"
    309 
    310 # Update exchange /keys so recoup gets scheduled
    311 taler-wallet-cli \
    312     "$TIMETRAVEL"\
    313     --wallet-db="$WALLET_DB" \
    314     exchanges update \
    315     -f "$EXCHANGE_URL"
    316 
    317 # Block until scheduled operations are done
    318 taler-wallet-cli \
    319     "$TIMETRAVEL" \
    320     --wallet-db="$WALLET_DB" \
    321     run-until-done
    322 
    323 echo "Restarting merchant (so new keys are known)"
    324 pkill -x -u "$(id -u)" -TERM taler-merchant-httpd || true
    325 sleep 1
    326 taler-merchant-httpd \
    327     -c "$CONF" \
    328     -L INFO \
    329     2> ${MY_TMP_DIR}/taler-merchant-httpd.log &
    330 MERCHANT_PID=$!
    331 export MERCHANT_PID
    332 
    333 # Wait for merchant to be again available
    334 OK=0
    335 for n in `seq 1 100`
    336 do
    337     echo -n "."
    338     sleep 0.2
    339     # merchant
    340     wget "${MERCHANT_URL}config" -o /dev/null -O /dev/null >/dev/null || continue
    341     OK=1
    342     break
    343 done
    344 if [ 1 != "$OK" ]
    345 then
    346     exit_fail "Failed to restart merchant"
    347 fi
    348 echo " DONE"
    349 
    350 # Now we buy something, only the coins resulting from recoup+refresh will be
    351 # used, as other ones are suspended
    352 taler-wallet-cli $TIMETRAVEL --no-throttle --wallet-db=$WALLET_DB api 'testPay' \
    353   "$(jq -n '
    354     {
    355       amount: "TESTKUDOS:0.02",
    356       merchantBaseUrl: $MERCHANT_URL,
    357       summary: "bar",
    358     }' \
    359     --arg MERCHANT_URL $MERCHANT_URL
    360   )"
    361 taler-wallet-cli \
    362     "$TIMETRAVEL" \
    363     --wallet-db="$WALLET_DB" \
    364     run-until-done
    365 
    366 echo "Bought something with refresh-recouped coin"
    367 
    368 echo "Shutting down services"
    369 # Stop the exchange/merchant processes we restarted ourselves first: they
    370 # are not children of taler-unified-setup.sh, so its teardown misses them
    371 # and a bare 'wait' below would block forever.
    372 for pid in "${EXCHANGE_PID:-}" \
    373            "${MERCHANT_PID:-}" \
    374            "${RSA_DENOM_HELPER_PID:-}" \
    375            "${CS_DENOM_HELPER_PID:-}" \
    376            "${SIGNKEY_HELPER_PID:-}"
    377 do
    378     if [ -n "$pid" ]
    379     then
    380         kill -TERM "$pid" 2> /dev/null || true
    381         wait "$pid" 2> /dev/null || true
    382     fi
    383 done
    384 exit_cleanup
    385 unset SETUP_PID
    386 
    387 
    388 # Where do we write the result?
    389 export BASEDB=${1:-"revoke-basedb"}
    390 
    391 
    392 # Dump database
    393 mkdir -p "$(dirname "$BASEDB")"
    394 echo "Dumping database ${BASEDB}.sql"
    395 pg_dump -O "auditor-basedb" | sed -e '/AS integer/d' > "${BASEDB}.sql"
    396 cp "${CONF}" "${BASEDB}.conf"
    397 cp "$(taler-exchange-config -c "${CONF}" -s exchange-offline -o MASTER_PRIV_FILE -f)" "${BASEDB}.mpriv"
    398 
    399 # clean up
    400 echo -n "Final clean up ..."
    401 dropdb "auditor-basedb"
    402 echo " DONE"
    403 
    404 echo "====================================="
    405 echo "Finished generation of ${BASEDB}.sql"
    406 echo "====================================="
    407 
    408 exit 0