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