diff --git a/proxy_docker/Dockerfile b/proxy_docker/Dockerfile index 9dd7574..d578acf 100644 --- a/proxy_docker/Dockerfile +++ b/proxy_docker/Dockerfile @@ -12,13 +12,14 @@ WORKDIR ${HOME} COPY app/data/* ./ COPY app/script/* ./ +COPY app/tests/* ./tests/ COPY --from=cyphernode/clightning:v0.10.0 /usr/local/bin/lightning-cli ./ COPY --from=eclipse-mosquitto:1.6 /usr/bin/mosquitto_rr /usr/bin/mosquitto_sub /usr/bin/mosquitto_pub /usr/bin/ COPY --from=eclipse-mosquitto:1.6 /usr/lib/libmosquitto* /usr/lib/ COPY --from=eclipse-mosquitto:1.6 /usr/lib/libcrypto* /usr/lib/ COPY --from=eclipse-mosquitto:1.6 /usr/lib/libssl* /usr/lib/ -RUN chmod +x startproxy.sh requesthandler.sh lightning-cli sqlmigrate*.sh waitanyinvoice.sh tests* \ +RUN chmod +x startproxy.sh requesthandler.sh lightning-cli sqlmigrate*.sh waitanyinvoice.sh tests/* \ && chmod o+w . \ && mkdir db diff --git a/proxy_docker/app/data/cyphernode.sql b/proxy_docker/app/data/cyphernode.sql index b3ba8c1..31e3693 100644 --- a/proxy_docker/app/data/cyphernode.sql +++ b/proxy_docker/app/data/cyphernode.sql @@ -15,6 +15,7 @@ CREATE TABLE watching_by_pub32 ( CREATE TABLE watching ( id INTEGER PRIMARY KEY AUTOINCREMENT, address TEXT, + label TEXT, watching INTEGER DEFAULT FALSE, callback0conf TEXT, calledback0conf INTEGER DEFAULT FALSE, diff --git a/proxy_docker/app/script/callbacks_job.sh b/proxy_docker/app/script/callbacks_job.sh index 53c108f..39337db 100644 --- a/proxy_docker/app/script/callbacks_job.sh +++ b/proxy_docker/app/script/callbacks_job.sh @@ -11,7 +11,7 @@ do_callbacks() { trace "Entering do_callbacks()..." # Let's fetch all the watching addresses still being watched but not called back - local callbacks=$(sql 'SELECT DISTINCT w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, label, derivation_path, event_message, hash FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE NOT calledback0conf AND watching_id NOT NULL AND w.callback0conf NOT NULL AND w.watching') + local callbacks=$(sql 'SELECT DISTINCT w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, w32.label, derivation_path, event_message, hash FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE NOT calledback0conf AND watching_id NOT NULL AND w.callback0conf NOT NULL AND w.watching') trace "[do_callbacks] callbacks0conf=${callbacks}" local returncode @@ -30,7 +30,7 @@ do_callbacks() { fi done - callbacks=$(sql 'SELECT DISTINCT w.callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, label, derivation_path, event_message, hash FROM watching w, watching_tx wt, tx t LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE w.id = watching_id AND tx_id = t.id AND NOT calledback1conf AND confirmations>0 AND w.callback1conf NOT NULL AND w.watching') + callbacks=$(sql 'SELECT DISTINCT w.callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, w32.label, derivation_path, event_message, hash FROM watching w, watching_tx wt, tx t LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE w.id = watching_id AND tx_id = t.id AND NOT calledback1conf AND confirmations>0 AND w.callback1conf NOT NULL AND w.watching') trace "[do_callbacks] callbacks1conf=${callbacks}" for row in ${callbacks} diff --git a/proxy_docker/app/script/confirmation.sh b/proxy_docker/app/script/confirmation.sh index 4218d15..b03d36f 100644 --- a/proxy_docker/app/script/confirmation.sh +++ b/proxy_docker/app/script/confirmation.sh @@ -29,6 +29,8 @@ confirmation() { local returncode local txid=${1} + local bypass_callbacks=${2} + trace "[confirmation] bypass_callbacks=${bypass_callbacks}" local tx_details tx_details="$(get_transaction ${txid})" returncode=$? @@ -196,7 +198,13 @@ confirmation() { ) 201>./.confirmation.lock # There's a lock in callbacks, let's get out of the confirmation lock before entering another one - do_callbacks + # If this was called by missed_conf algo, we don't want to process all the callbacks now. We wait + # for next cron. + if [ -z "${bypass_callbacks}" ]; then + trace "[confirmation] Let's do the callbacks!" + do_callbacks + fi + echo '{"result":"confirmed"}' return 0 diff --git a/proxy_docker/app/script/manage_missed_conf.sh b/proxy_docker/app/script/manage_missed_conf.sh index ec202b8..e631f5e 100644 --- a/proxy_docker/app/script/manage_missed_conf.sh +++ b/proxy_docker/app/script/manage_missed_conf.sh @@ -38,46 +38,63 @@ manage_missed_conf() { trace "[Entering manage_missed_conf()]" - local watches=$(sql 'SELECT DISTINCT address FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx t ON t.id = tx_id WHERE watching AND imported AND (tx_id IS NULL OR t.confirmations=0)') + local watches=$(sql 'SELECT DISTINCT address, w.inserted_ts FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx t ON t.id = tx_id WHERE watching AND imported AND (tx_id IS NULL OR t.confirmations=0)') trace "[manage_missed_conf] watches=${watches}" if [ ${#watches} -eq 0 ]; then trace "[manage_missed_conf] Nothing missed!" return 0 fi - local addresses + local received + local latesttxid + local tx + local blocktime local data local result local returncode + local row + local address + local inserted_ts + local txid + local txids local IFS=$'\n' - for address in ${watches} + for row in ${watches} do - if [ -z ${addresses} ]; then - addresses="[\"${address}\"" + # Let's get confirmed received txs for the address + address=$(echo "${row}" | cut -d '|' -f1) + inserted_ts=$(date -d "$(echo "${row}" | cut -d '|' -f2)" +"%s") + trace "[manage_missed_conf] address=${address}" + + data='{"method":"listreceivedbyaddress","params":[0, false, true, "'${address}'"]}' + received=$(send_to_watcher_node ${data} | jq '.result[0]') + if [ "${received}" = "null" ]; then + # Not confirmed while we were away... + trace "[manage_missed_conf] Nothing missed here" else - addresses="${addresses},\"${address}\"" + # We got something confirmed + # Let's find out if it was confirmed after being watched + trace "[manage_missed_conf] We got something confirmed" + latesttxid=$(echo "${received}" | jq -r ".txids | last") + data='{"method":"gettransaction","params":["'${latesttxid}'"]}' + tx=$(send_to_watcher_node ${data}) + blocktime=$(echo "${tx}" | jq '.result.blocktime') + txtime=$(echo "${tx}" | jq '.result.time') + confirmations=$(echo "${tx}" | jq '.result.confirmations') + + trace "[manage_missed_conf] blocktime=${blocktime}" + trace "[manage_missed_conf] txtime=${txtime}" + trace "[manage_missed_conf] inserted_ts=${inserted_ts}" + trace "[manage_missed_conf] confirmations=${confirmations}" + + if [ "${txtime}" -gt "${inserted_ts}" ]; then + # Mined after watch, we missed it! + trace "[manage_missed_conf] Mined after watch, we missed it!" + confirmation "${latesttxid}" "true" + fi fi done - addresses="${addresses}]" - - # Watching addresses with UTXO are transactions being watched that went through without us knowing it, we missed the conf - data="{\"method\":\"listunspent\",\"params\":[0, 9999999, ${addresses}]}" - local unspents - unspents=$(send_to_watcher_node ${data}) - returncode=$? - trace_rc ${returncode} - if [ "${returncode}" -ne 0 ]; then - return ${returncode} - fi - - local txids=$(echo "${unspents}" | jq -r ".result[].txid") - for txid in ${txids} - do - confirmation "${txid}" - done return 0 - } case "${0}" in *manage_missed_conf.sh) manage_not_imported $@; manage_missed_conf $@;; esac diff --git a/proxy_docker/app/script/tests.sh b/proxy_docker/app/script/tests.sh deleted file mode 100644 index 4cdad64..0000000 --- a/proxy_docker/app/script/tests.sh +++ /dev/null @@ -1,322 +0,0 @@ -#!/bin/sh - -tests() -{ - local address - local address1 - local address2 - local address3 - local response - - # getbestblockhash - # (GET) http://proxy:8888/getbestblockhash - - echo "Testing getbestblockhash..." - response=$(curl -s proxy:8888/getbestblockhash) - echo "response=${response}" - local blockhash=$(echo ${response} | jq ".result" | tr -d '\"') - echo "blockhash=${blockhash}" - if [ -z "${blockhash}" ]; then - exit 2 - fi - echo "Tested getbestblockhash." - - # getbestblockinfo - # curl (GET) http://proxy:8888/getbestblockinfo - - echo "Testing getbestblockinfo..." - response=$(curl -s proxy:8888/getbestblockinfo) - echo "response=${response}" - local blockhash2=$(echo ${response} | jq ".result.hash" | tr -d '\"') - echo "blockhash2=${blockhash2}" - if [ "${blockhash2}" != "${blockhash}" ]; then - exit 4 - fi - echo "Tested getbestblockinfo." - - # getblockinfo - # (GET) http://proxy:8888/getblockinfo/000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea - - echo "Testing getblockinfo..." - response=$(curl -s proxy:8888/getblockinfo/${blockhash}) - echo "response=${response}" - blockhash2=$(echo ${response} | jq ".result.hash" | tr -d '\"') - echo "blockhash2=${blockhash2}" - if [ "${blockhash2}" != "${blockhash}" ]; then - exit 6 - fi - echo "Tested getblockinfo." - - # gettransaction - # (GET) http://proxy:8888/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648 - - echo "Testing gettransaction..." - response=$(curl -s proxy:8888/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648) - echo "response=${response}" - local txid=$(echo ${response} | jq ".result.txid" | tr -d '\"') - echo "txid=${txid}" - if [ "${txid}" != "af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648" ]; then - exit 8 - fi - echo "Tested gettransaction." - - # getnewaddress - # (GET) http://proxy:8888/getnewaddress - # returns {"address":"2MuiUu8AyuByAGYRDAqqhdYxt8gXcsQ1Ymw"} - - echo "Testing getnewaddress..." - response=$(curl -s proxy:8888/getnewaddress) - echo "response=${response}" - address1=$(echo ${response} | jq ".address" | tr -d '\"') - echo "address1=${address1}" - if [ -z "${address1}" ]; then - exit 10 - fi - address2=$(curl -s proxy:8888/getnewaddress | jq ".address" | tr -d '\"') - echo "address2=${address2}" - echo "Tested getnewaddress." - - # getbalance - # (GET) http://proxy:8888/getbalance - - echo "Testing getbalance..." - response=$(curl -s proxy:8888/getbalance) - echo "response=${response}" - local balance=$(echo ${response} | jq ".balance") - echo "balance=${balance}" - if [ -z "${balance}" ]; then - exit 12 - fi - echo "Tested getbalance." - - # watch - # POST http://proxy:8888/watch - # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.122.233:1111/callback0conf","confirmedCallbackURL":"192.168.122.233:1111/callback1conf"} - - echo "Testing watch..." - local url1="$(hostname):1111/callback0conf" - local url2="$(hostname):1111/callback1conf" - echo "url1=${url1}" - echo "url2=${url2}" - response=$(curl -s -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"unconfirmedCallbackURL\":\"${url1}\",\"confirmedCallbackURL\":\"${url2}\"}" proxy:8888/watch) - echo "response=${response}" - - local id=$(echo "${response}" | jq ".id" | tr -d '\"') - echo "id=${id}" - local event=$(echo "${response}" | jq ".event" | tr -d '\"') - echo "event=${event}" - if [ "${event}" != "watch" ]; then - exit 15 - fi - address=$(echo "${response}" | jq ".address" | tr -d '\"') - echo "address=${address}" - if [ "${address}" != "${address1}" ]; then - exit 20 - fi - local imported=$(echo "${response}" | jq ".imported" | tr -d '\"') - echo "imported=${imported}" - if [ "${imported}" != "1" ]; then - exit 30 - fi - local inserted=$(echo "${response}" | jq ".inserted" | tr -d '\"') - echo "inserted=${inserted}" - if [ "${inserted}" != "1" ]; then - exit 40 - fi - local unconfirmedCallbackURL=$(echo "${response}" | jq ".unconfirmedCallbackURL" | tr -d '\"') - echo "unconfirmedCallbackURL=${unconfirmedCallbackURL}" - if [ "${unconfirmedCallbackURL}" != "${url1}" ]; then - exit 60 - fi - local confirmedCallbackURL=$(echo "${response}" | jq ".confirmedCallbackURL" | tr -d '\"') - echo "confirmedCallbackURL=${confirmedCallbackURL}" - if [ "${confirmedCallbackURL}" != "${url2}" ]; then - exit 70 - fi - - # Let's watch another address just to be able to test unwatch later and test if found in getactivewatches - response=$(curl -s -H "Content-Type: application/json" -d "{\"address\":\"${address2}\",\"unconfirmedCallbackURL\":\"${url1}2\",\"confirmedCallbackURL\":\"${url2}2\"}" proxy:8888/watch) - echo "response=${response}" - echo "Tested watch." - - # getactivewatches - # (GET) http://proxy:8888/getactivewatches - - echo "Testing getactivewatches..." - response=$(curl -s proxy:8888/getactivewatches) - echo "response=${response}" - response=$(echo ${response} | jq ".watches[]") - echo "response=${response}" - local id2=$(echo ${response} | jq "select(.address == \"${address1}\") | .id" | tr -d '\"') - echo "id2=${id2}" - if [ "${id2}" != "${id}" ]; then - exit 80 - fi - id2=$(echo ${response} | jq "select(.address == \"${address2}\") | .id" | tr -d '\"') - echo "id2=${id2}" - if [ -z "${id2}" ]; then - exit 90 - fi - echo "Tested getactivewatches." - - # unwatch - # (GET) http://proxy:8888/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp - - echo "Testing unwatch..." - response=$(curl -s proxy:8888/unwatch/${address2}) - echo "response=${response}" - event=$(echo "${response}" | jq ".event" | tr -d '\"') - echo "event=${event}" - if [ "${event}" != "unwatch" ]; then - exit 100 - fi - address=$(echo "${response}" | jq ".address" | tr -d '\"') - echo "address=${address}" - if [ "${address}" != "${address2}" ]; then - exit 110 - fi - response=$(curl -s proxy:8888/getactivewatches) - echo "response=${response}" - response=$(echo "${response}" | jq ".watches[]") - echo "response=${response}" - id2=$(echo ${response} | jq "select(.address == \"${address2}\") | .id" | tr -d '\"') - echo "id2=${id2}" - if [ -n "${id2}" ]; then - exit 120 - fi - echo "Tested unwatch." - - # deriveindex - # (GET) http://proxy:8888/deriveindex/25-30 - # {"addresses":[{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}]} - - echo "Testing deriveindex..." - response=$(curl -v proxy:8888/deriveindex/25-30) - echo "response=${response}" - local nbaddr=$(echo "${response}" | jq ".addresses | length") - if [ "${nbaddr}" -ne "6" ]; then - exit 130 - fi - address=$(echo "${response}" | jq ".addresses[2].address" | tr -d '\"') - if [ "${address}" != "2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP" ]; then - exit 140 - fi - echo "Tested deriveindex." - - # derivepubpath - # (GET) http://proxy:8888/derivepubpath - # BODY {"pub32":"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb","path":"0/25-30"} - # {"addresses":[{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}]} - - echo "Testing derivepubpath..." - response=$(curl -v -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" proxy:8888/derivepubpath) - echo "response=${response}" - local nbaddr=$(echo "${response}" | jq ".addresses | length") - if [ "${nbaddr}" -ne "6" ]; then - exit 150 - fi - address=$(echo "${response}" | jq ".addresses[2].address" | tr -d '\"') - if [ "${address}" != "2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP" ]; then - exit 160 - fi - echo "Tested derivepubpath." - - # spend - # POST http://proxy:8888/spend - # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233} - - # By spending to a watched address, we will test the spending feature and trigger the confirmation to test - # confirmations of watched addresses... Cleva!!! - - echo "Testing spend, conf and callbacks..." - response=$(curl -v -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"amount\":0.00001}" proxy:8888/spend) - echo "response=${response}" - wait_for_callbacks - echo "Tested spend, conf and callbacks." - - # addtobatch - # POST http://proxy:8888/addtobatch - # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233} - - # By spending to a watched address, we will test the spending feature and trigger the confirmation to test - # confirmations of watched addresses... Cleva!!! - -# echo "Testing addtobatch..." -# response=$(curl -v -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"amount\":0.00001}" proxy:8888/spend) -# echo "response=${response}" -# wait_for_callbacks -# echo "Tested addtobatch ." - - - - - # conf - # (GET) http://proxy:8888/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387 - - # Let's trigger tx confirmation even if not confirmed. Will be funny. Should take care of - # multiple confirmations of the same state. - - - - # executecallbacks - # (GET) http://cyphernode::8080/executecallbacks - - #echo "GET /getbestblockinfo" | nc proxy:8888 - | sed -En "s/^(\{.*)/\1/p" | jq - - - - - # spend - # POST http://proxy:8888/spend - # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233} - - #curl -v -H "Content-Type: application/json" -d '{"address":"2MsWyaQ8APbnqasFpWopqUKqsdpiVY3EwLE","amount":0.0001}' proxy:8888/spend - - # ln_getinfo - # (GET) http://proxy:8888/ln_getinfo - - echo "Testing ln_getinfo..." - response=$(curl -s proxy:8888/ln_getinfo) - echo "response=${response}" - local port=$(echo ${response} | jq ".binding[] | select(.type == \"ipv4\") | .port") - echo "port=${port}" - if [ "${port}" != "9735" ]; then - exit 170 - fi - echo "Tested ln_getinfo." - - # ln_newaddr - # (GET) http://proxy:8888/ln_newaddr - - echo "Testing ln_newaddr..." - response=$(curl -s proxy:8888/ln_newaddr) - echo "response=${response}" - address=$(echo ${response} | jq ".address") - echo "address=${address}" - if [ -z "${address}" ]; then - exit 180 - fi - echo "Tested ln_newaddr." - - # ln_create_invoice - # POST http://proxy:8888/ln_create_invoice - # BODY {"msatoshi":"10000","label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":"10"} - - #echo "Testing ln_create_invoice..." - #response=$(curl -v -H "Content-Type: application/json" -d "{\"msatoshi\":10000,\"label\":\"koNCcrSvhX3dmyFhW\",\"description\":\"Bylls order #10649\",\"expiry\":10}" proxy:8888/ln_create_invoice) - #echo "response=${response}" - - #echo "Tested ln_create_invoice." - - # ln_pay - - -} - -wait_for_callbacks() -{ - nc -vlp1111 -e ./tests-cb.sh - nc -vlp1111 -e ./tests-cb.sh -} - -tests diff --git a/proxy_docker/app/tests/colors.sh b/proxy_docker/app/tests/colors.sh new file mode 100755 index 0000000..fd78e7d --- /dev/null +++ b/proxy_docker/app/tests/colors.sh @@ -0,0 +1,74 @@ +#!/bin/sh + +# Reset +Color_Off='\033[0m' # Text Reset + +# Regular Colors +Black='\033[0;30m' # Black +Red='\033[0;31m' # Red +Green='\033[0;32m' # Green +Yellow='\033[0;33m' # Yellow +Blue='\033[0;34m' # Blue +Purple='\033[0;35m' # Purple +Cyan='\033[0;36m' # Cyan +White='\033[0;37m' # White + +# Bold +BBlack='\033[1;30m' # Black +BRed='\033[1;31m' # Red +BGreen='\033[1;32m' # Green +BYellow='\033[1;33m' # Yellow +BBlue='\033[1;34m' # Blue +BPurple='\033[1;35m' # Purple +BCyan='\033[1;36m' # Cyan +BWhite='\033[1;37m' # White + +# Underline +UBlack='\033[4;30m' # Black +URed='\033[4;31m' # Red +UGreen='\033[4;32m' # Green +UYellow='\033[4;33m' # Yellow +UBlue='\033[4;34m' # Blue +UPurple='\033[4;35m' # Purple +UCyan='\033[4;36m' # Cyan +UWhite='\033[4;37m' # White + +# Background +On_Black='\033[40m' # Black +On_Red='\033[41m' # Red +On_Green='\033[42m' # Green +On_Yellow='\033[43m' # Yellow +On_Blue='\033[44m' # Blue +On_Purple='\033[45m' # Purple +On_Cyan='\033[46m' # Cyan +On_White='\033[47m' # White + +# High Intensity +IBlack='\033[0;90m' # Black +IRed='\033[0;91m' # Red +IGreen='\033[0;92m' # Green +IYellow='\033[0;93m' # Yellow +IBlue='\033[0;94m' # Blue +IPurple='\033[0;95m' # Purple +ICyan='\033[0;96m' # Cyan +IWhite='\033[0;97m' # White + +# Bold High Intensity +BIBlack='\033[1;90m' # Black +BIRed='\033[1;91m' # Red +BIGreen='\033[1;92m' # Green +BIYellow='\033[1;93m' # Yellow +BIBlue='\033[1;94m' # Blue +BIPurple='\033[1;95m' # Purple +BICyan='\033[1;96m' # Cyan +BIWhite='\033[1;97m' # White + +# High Intensity backgrounds +On_IBlack='\033[0;100m' # Black +On_IRed='\033[0;101m' # Red +On_IGreen='\033[0;102m' # Green +On_IYellow='\033[0;103m' # Yellow +On_IBlue='\033[0;104m' # Blue +On_IPurple='\033[0;105m' # Purple +On_ICyan='\033[0;106m' # Cyan +On_IWhite='\033[0;107m' # White diff --git a/proxy_docker/app/tests/mine.sh b/proxy_docker/app/tests/mine.sh new file mode 100755 index 0000000..0276b5f --- /dev/null +++ b/proxy_docker/app/tests/mine.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# Mine +mine() { + local nbblocks=${1:-1} + local minedaddr + + echo ; echo "About to mine ${nbblocks} block(s)..." + minedaddr=$(docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat getnewaddress | tr -d '\r') + echo ; echo "minedaddr=${minedaddr}" + docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat generatetoaddress ${nbblocks} "${minedaddr}" +} + +case "${0}" in *mine.sh) mine $@;; esac diff --git a/proxy_docker/app/script/test-batching.sh b/proxy_docker/app/tests/test-batching.sh similarity index 99% rename from proxy_docker/app/script/test-batching.sh rename to proxy_docker/app/tests/test-batching.sh index 5eea4ff..7dcce5d 100755 --- a/proxy_docker/app/script/test-batching.sh +++ b/proxy_docker/app/tests/test-batching.sh @@ -1,5 +1,7 @@ #!/bin/sh +# docker exec -it $(docker ps -q -f "name=proxy\.") ./tests/test-batching.sh + # curl localhost:8888/listbatchers | jq # curl -d '{}' localhost:8888/getbatcher | jq # curl -d '{}' localhost:8888/getbatchdetails | jq diff --git a/proxy_docker/app/tests/test-manage-missed.sh b/proxy_docker/app/tests/test-manage-missed.sh new file mode 100755 index 0000000..172db1c --- /dev/null +++ b/proxy_docker/app/tests/test-manage-missed.sh @@ -0,0 +1,140 @@ +#!/bin/sh + +. ./colors.sh +. ./mine.sh + +trace() { + if [ "${1}" -le "${TRACING}" ]; then + echo "$(date -u +%FT%TZ) ${2}" 1>&2 + fi +} + +start_test_container() { + docker run -d --rm -it --name tests-manage-missed --network=cyphernodenet -v "$PWD/tests-cb.sh:/tests-cb.sh" alpine +} + +stop_test_container() { + docker stop tests-manage-missed +} + +exec_in_test_container() { + docker exec -it tests-manage-missed $@ +} + +test_manage_missed_0_conf() { + # Missed 0-conf: + # 1. Get new address + # 2. Watch it + # 3. Stop proxy + # 4. sendtoaddress while proxy is offline + # 5. Start proxy + # 6. Call executecallbacks + # 7. Check if 0-conf callback is called + + trace 1 "\n[test_manage_missed_0_conf] ${BCyan}Let's miss a 0-conf!...${Color_Off}" + + trace 2 "[test_manage_missed_0_conf] getnewaddress..." + local response=$(exec_in_test_container curl -d '{"label":"missed0conftest"}' proxy:8888/getnewaddress) + trace 3 "[test_manage_missed_0_conf] response=${response}" + local address=$(echo "${response}" | jq -r ".address") + trace 3 "[test_manage_missed_0_conf] address=${address}" + + trace 2 "[test_manage_missed_0_conf] watch it..." + local data='{"address":"'${address}'","unconfirmedCallbackURL":"'${url1}'","confirmedCallbackURL":"'${url2}'","label":"missed0conftest"}' + trace 3 "[test_manage_missed_0_conf] data=${data}" + response=$(exec_in_test_container curl -d "${data}" proxy:8888/watch) + trace 3 "[test_manage_missed_0_conf] response=${response}" + + trace 3 "[test_manage_missed_0_conf] Shutting down the proxy..." + docker stop $(docker ps -q -f "name=proxy") + + trace 3 "[test_manage_missed_0_conf] Sending coins to watched address while proxy is down..." + docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat sendtoaddress ${address} 0.0001 + + trace 3 "[test_manage_missed_0_conf] Sleeping for 10 seconds to let the proxy restart..." + sleep 10 + + trace 3 "[test_manage_missed_0_conf] Calling executecallbacks..." + exec_in_test_container curl proxy:8888/executecallbacks + +} + +test_manage_missed_1_conf() { + # Missed 1-conf: + # 1. Get new address + # 2. Watch it + # 3. sendtoaddress + # 4. Check if 0-conf callback is called + # 5. Stop proxy + # 6. Mine a new block + # 7. Start proxy + # 8. Call executecallbacks + # 9. Check if 1-conf callback is called + + trace 1 "\n[test_manage_missed_1_conf] ${BCyan}Let's miss a 1-conf!...${Color_Off}" + + trace 2 "[test_manage_missed_1_conf] getnewaddress..." + local response=$(exec_in_test_container curl -d '{"label":"missed0conftest"}' proxy:8888/getnewaddress) + trace 3 "[test_manage_missed_1_conf] response=${response}" + local address=$(echo "${response}" | jq -r ".address") + trace 3 "[test_manage_missed_1_conf] address=${address}" + + trace 2 "[test_manage_missed_1_conf] watch it..." + local data='{"address":"'${address}'","unconfirmedCallbackURL":"'${url3}'","confirmedCallbackURL":"'${url4}'","label":"missed0conftest"}' + trace 3 "[test_manage_missed_1_conf] data=${data}" + response=$(exec_in_test_container curl -d "${data}" proxy:8888/watch) + trace 3 "[test_manage_missed_1_conf] response=${response}" + + trace 3 "[test_manage_missed_1_conf] Sending coins to watched address while proxy is up..." + docker exec -it $(docker ps -q -f "name=cyphernode_bitcoin") bitcoin-cli -rpcwallet=spending01.dat sendtoaddress ${address} 0.0001 + + trace 3 "[test_manage_missed_1_conf] Sleeping for 10 seconds to let the 0-conf callbacks to happen..." + sleep 10 + + trace 3 "[test_manage_missed_1_conf] Shutting down the proxy..." + docker stop $(docker ps -q -f "name=proxy") + + trace 3 "[test_manage_missed_1_conf] Mine a new block..." + mine + + trace 3 "[test_manage_missed_1_conf] Sleeping for 10 seconds to let the proxy restart..." + sleep 10 + + trace 3 "[test_manage_missed_1_conf] Calling executecallbacks..." + exec_in_test_container curl proxy:8888/executecallbacks +} + +wait_for_callbacks() { + trace 1 "[wait_for_callbacks] ${BCyan}Let's start the callback servers!...${Color_Off}" + + docker exec -t tests-manage-missed sh -c "nc -vlp1111 -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'" & + docker exec -t tests-manage-missed sh -c "nc -vlp1112 -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'" & + docker exec -t tests-manage-missed sh -c "nc -vlp1113 -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'" & + docker exec -t tests-manage-missed sh -c "nc -vlp1114 -e sh -c 'echo -en \"HTTP/1.1 200 OK\\\\r\\\\n\\\\r\\\\n\" ; date >&2 ; timeout 1 tee /dev/tty | cat ; echo 1>&2'" & + +} + +TRACING=3 + +start_test_container +wait_for_callbacks + +url1="tests-manage-missed:1111/callback0conf" +url2="tests-manage-missed:1112/callback1conf" +url3="tests-manage-missed:1113/callback0conf" +url4="tests-manage-missed:1114/callback1conf" +trace 2 "url1=${url1}" +trace 2 "url2=${url2}" +trace 2 "url3=${url3}" +trace 2 "url4=${url4}" + +exec_in_test_container apk add curl +# exec_in_test_container ping -c 5 tests-manage-missed +# exec_in_test_container curl -vd 'toto' ${url1}/allo +test_manage_missed_0_conf +test_manage_missed_1_conf + +trace 3 "Waiting for the callbacks to happen..." +wait + +stop_test_container diff --git a/proxy_docker/app/script/tests-cb.sh b/proxy_docker/app/tests/tests-cb.sh old mode 100644 new mode 100755 similarity index 100% rename from proxy_docker/app/script/tests-cb.sh rename to proxy_docker/app/tests/tests-cb.sh diff --git a/proxy_docker/app/tests/tests.sh b/proxy_docker/app/tests/tests.sh new file mode 100755 index 0000000..ce9dce9 --- /dev/null +++ b/proxy_docker/app/tests/tests.sh @@ -0,0 +1,335 @@ +#!/bin/sh + +# . /mine.sh + +# docker run -it --rm -it --name cn-tests --network=cyphernodenet -v "$PWD/mine.sh:/mine.sh" -v "$PWD/tests.sh:/tests.sh" -v "$PWD/tests-cb.sh:/tests-cb.sh" alpine /tests.sh + + +tests() +{ + local address + local address1 + local address2 + local address3 + local response + local transaction + + # getbestblockhash + # (GET) http://proxy:8888/getbestblockhash + + echo "Testing getbestblockhash..." + response=$(curl -s proxy:8888/getbestblockhash) + echo "response=${response}" + local blockhash=$(echo ${response} | jq ".result" | tr -d '\"') + echo "blockhash=${blockhash}" + if [ -z "${blockhash}" ]; then + exit 2 + fi + echo "Tested getbestblockhash." + + # getbestblockinfo + # curl (GET) http://proxy:8888/getbestblockinfo + + echo "Testing getbestblockinfo..." + response=$(curl -s proxy:8888/getbestblockinfo) + echo "response=${response}" + local blockhash2=$(echo ${response} | jq ".result.hash" | tr -d '\"') + echo "blockhash2=${blockhash2}" + if [ "${blockhash2}" != "${blockhash}" ]; then + exit 4 + fi + echo "Tested getbestblockinfo." + + # getblockinfo + # (GET) http://proxy:8888/getblockinfo/000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea + + echo "Testing getblockinfo..." + response=$(curl -s proxy:8888/getblockinfo/${blockhash}) + echo "response=${response}" + blockhash2=$(echo ${response} | jq ".result.hash" | tr -d '\"') + echo "blockhash2=${blockhash2}" + if [ "${blockhash2}" != "${blockhash}" ]; then + exit 6 + fi + echo "Tested getblockinfo." + + # getnewaddress + # (GET) http://proxy:8888/getnewaddress + # returns {"address":"2MuiUu8AyuByAGYRDAqqhdYxt8gXcsQ1Ymw"} + + echo "Testing getnewaddress..." + response=$(curl -s proxy:8888/getnewaddress) + echo "response=${response}" + address1=$(echo ${response} | jq ".address" | tr -d '\"') + echo "address1=${address1}" + if [ -z "${address1}" ]; then + exit 10 + fi + address2=$(curl -s proxy:8888/getnewaddress | jq ".address" | tr -d '\"') + echo "address2=${address2}" + echo "Tested getnewaddress." + + # getbalance + # (GET) http://proxy:8888/getbalance + + echo "Testing getbalance..." + response=$(curl -s proxy:8888/getbalance) + echo "response=${response}" + local balance=$(echo ${response} | jq ".balance") + echo "balance=${balance}" + if [ -z "${balance}" ]; then + exit 12 + fi + echo "Tested getbalance." + + # watch + # POST http://proxy:8888/watch + # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.122.233:1111/callback0conf","confirmedCallbackURL":"192.168.122.233:1111/callback1conf"} + + echo "Testing watch..." + local url1="$(hostname):1111/callback0conf" + local url2="$(hostname):1111/callback1conf" + echo "url1=${url1}" + echo "url2=${url2}" + response=$(curl -s -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"unconfirmedCallbackURL\":\"${url1}\",\"confirmedCallbackURL\":\"${url2}\"}" proxy:8888/watch) + echo "response=${response}" + + local id=$(echo "${response}" | jq ".id" | tr -d '\"') + echo "id=${id}" + local event=$(echo "${response}" | jq ".event" | tr -d '\"') + echo "event=${event}" + if [ "${event}" != "watch" ]; then + exit 15 + fi + address=$(echo "${response}" | jq ".address" | tr -d '\"') + echo "address=${address}" + if [ "${address}" != "${address1}" ]; then + exit 20 + fi + local imported=$(echo "${response}" | jq ".imported" | tr -d '\"') + echo "imported=${imported}" + if [ "${imported}" != "1" ]; then + exit 30 + fi + local inserted=$(echo "${response}" | jq ".inserted" | tr -d '\"') + echo "inserted=${inserted}" + if [ "${inserted}" != "1" ]; then + exit 40 + fi + local unconfirmedCallbackURL=$(echo "${response}" | jq ".unconfirmedCallbackURL" | tr -d '\"') + echo "unconfirmedCallbackURL=${unconfirmedCallbackURL}" + if [ "${unconfirmedCallbackURL}" != "${url1}" ]; then + exit 60 + fi + local confirmedCallbackURL=$(echo "${response}" | jq ".confirmedCallbackURL" | tr -d '\"') + echo "confirmedCallbackURL=${confirmedCallbackURL}" + if [ "${confirmedCallbackURL}" != "${url2}" ]; then + exit 70 + fi + + # Let's watch another address just to be able to test unwatch later and test if found in getactivewatches + response=$(curl -s -H "Content-Type: application/json" -d "{\"address\":\"${address2}\",\"unconfirmedCallbackURL\":\"${url1}2\",\"confirmedCallbackURL\":\"${url2}2\"}" proxy:8888/watch) + echo "response=${response}" + echo "Tested watch." + + # getactivewatches + # (GET) http://proxy:8888/getactivewatches + + echo "Testing getactivewatches..." + response=$(curl -s proxy:8888/getactivewatches) + echo "response=${response}" + response=$(echo ${response} | jq ".watches[]") + echo "response=${response}" + local id2=$(echo ${response} | jq "select(.address == \"${address1}\") | .id" | tr -d '\"') + echo "id2=${id2}" + if [ "${id2}" != "${id}" ]; then + exit 80 + fi + id2=$(echo ${response} | jq "select(.address == \"${address2}\") | .id" | tr -d '\"') + echo "id2=${id2}" + if [ -z "${id2}" ]; then + exit 90 + fi + echo "Tested getactivewatches." + + # unwatch + # (GET) http://proxy:8888/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp + + echo "Testing unwatch..." + response=$(curl -s proxy:8888/unwatch/${address2}) + echo "response=${response}" + event=$(echo "${response}" | jq ".event" | tr -d '\"') + echo "event=${event}" + if [ "${event}" != "unwatch" ]; then + exit 100 + fi + address=$(echo "${response}" | jq ".address" | tr -d '\"') + echo "address=${address}" + if [ "${address}" != "${address2}" ]; then + exit 110 + fi + response=$(curl -s proxy:8888/getactivewatches) + echo "response=${response}" + response=$(echo "${response}" | jq ".watches[]") + echo "response=${response}" + id2=$(echo ${response} | jq "select(.address == \"${address2}\") | .id" | tr -d '\"') + echo "id2=${id2}" + if [ -n "${id2}" ]; then + exit 120 + fi + echo "Tested unwatch." + + # deriveindex + # (GET) http://proxy:8888/deriveindex/25-30 + # {"addresses":[{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}]} + + echo "Testing deriveindex..." + response=$(curl -v proxy:8888/deriveindex/25-30) + echo "response=${response}" + local nbaddr=$(echo "${response}" | jq ".addresses | length") + if [ "${nbaddr}" -ne "6" ]; then + exit 130 + fi + address=$(echo "${response}" | jq ".addresses[2].address" | tr -d '\"') + if [ "${address}" != "2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP" ]; then + exit 140 + fi + echo "Tested deriveindex." + + # derivepubpath + # (GET) http://proxy:8888/derivepubpath + # BODY {"pub32":"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb","path":"0/25-30"} + # {"addresses":[{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}]} + + echo "Testing derivepubpath..." + response=$(curl -v -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" proxy:8888/derivepubpath) + echo "response=${response}" + local nbaddr=$(echo "${response}" | jq ".addresses | length") + if [ "${nbaddr}" -ne "6" ]; then + exit 150 + fi + address=$(echo "${response}" | jq ".addresses[2].address" | tr -d '\"') + if [ "${address}" != "2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP" ]; then + exit 160 + fi + echo "Tested derivepubpath." + + # spend + # POST http://proxy:8888/spend + # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233} + + # By spending to a watched address, we will test the spending feature and trigger the confirmation to test + # confirmations of watched addresses... Cleva!!! + + echo "Testing spend, conf and callbacks..." + response=$(curl -v -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"amount\":0.00001}" proxy:8888/spend) + echo "response=${response}" + echo + echo "Please mine a block" + echo + wait_for_callbacks + echo "Tested spend, conf and callbacks." + + + # gettransaction + # (GET) http://proxy:8888/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648 + + echo "Testing gettransaction..." + transaction=$(echo "${response}" | jq -r ".txid") + response=$(curl -s proxy:8888/gettransaction/${transaction}) + echo "response=${response}" + local txid=$(echo ${response} | jq ".result.txid" | tr -d '\"') + echo "txid=${txid}" + if [ "${txid}" != "${transaction}" ]; then + exit 8 + fi + echo "Tested gettransaction." + + # addtobatch + # POST http://proxy:8888/addtobatch + # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233} + + # By spending to a watched address, we will test the spending feature and trigger the confirmation to test + # confirmations of watched addresses... Cleva!!! + +# echo "Testing addtobatch..." +# response=$(curl -v -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"amount\":0.00001}" proxy:8888/spend) +# echo "response=${response}" +# wait_for_callbacks +# echo "Tested addtobatch ." + + + + + # conf + # (GET) http://proxy:8888/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387 + + # Let's trigger tx confirmation even if not confirmed. Will be funny. Should take care of + # multiple confirmations of the same state. + + + + # executecallbacks + # (GET) http://cyphernode::8080/executecallbacks + + #echo "GET /getbestblockinfo" | nc proxy:8888 - | sed -En "s/^(\{.*)/\1/p" | jq + + + + + # spend + # POST http://proxy:8888/spend + # BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233} + + #curl -v -H "Content-Type: application/json" -d '{"address":"2MsWyaQ8APbnqasFpWopqUKqsdpiVY3EwLE","amount":0.0001}' proxy:8888/spend + + # ln_getinfo + # (GET) http://proxy:8888/ln_getinfo + + echo "Testing ln_getinfo..." + response=$(curl -s proxy:8888/ln_getinfo) + echo "response=${response}" + local port=$(echo ${response} | jq ".binding[] | select(.type == \"ipv4\") | .port") + echo "port=${port}" + if [ "${port}" != "9735" ]; then + exit 170 + fi + echo "Tested ln_getinfo." + + # ln_newaddr + # (GET) http://proxy:8888/ln_newaddr + + echo "Testing ln_newaddr..." + response=$(curl -s proxy:8888/ln_newaddr) + echo "response=${response}" + address=$(echo ${response} | jq ".address") + echo "address=${address}" + if [ -z "${address}" ]; then + exit 180 + fi + echo "Tested ln_newaddr." + + # ln_create_invoice + # POST http://proxy:8888/ln_create_invoice + # BODY {"msatoshi":"10000","label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":"10"} + + #echo "Testing ln_create_invoice..." + #response=$(curl -v -H "Content-Type: application/json" -d "{\"msatoshi\":10000,\"label\":\"koNCcrSvhX3dmyFhW\",\"description\":\"Bylls order #10649\",\"expiry\":10}" proxy:8888/ln_create_invoice) + #echo "response=${response}" + + #echo "Tested ln_create_invoice." + + # ln_pay + + +} + +wait_for_callbacks() +{ + nc -vlp1111 -e ./tests-cb.sh + nc -vlp1111 -e ./tests-cb.sh +} + +apk add curl jq + +tests