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