From cf64f55cb1b35d34d5e5c8406113865dc4129ae7 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 31 Dec 2020 15:54:13 -0300 Subject: [PATCH] Add simple api/cli regtest trading script This is a work in progress, but when done, will resovle issue https://github.com/bisq-network/bisq/issues/4257. --- apitest/scripts/simple-trading-script.sh | 209 ++++++++++++++++++++ apitest/scripts/trading-script-env.sh | 239 +++++++++++++++++++++++ 2 files changed, 448 insertions(+) create mode 100755 apitest/scripts/simple-trading-script.sh create mode 100755 apitest/scripts/trading-script-env.sh diff --git a/apitest/scripts/simple-trading-script.sh b/apitest/scripts/simple-trading-script.sh new file mode 100755 index 00000000000..5fb05632842 --- /dev/null +++ b/apitest/scripts/simple-trading-script.sh @@ -0,0 +1,209 @@ +#! /bin/bash + +# +# Script for running simple fiat <-> btc trading scenarios using the API CLI on regtest. +# +# Prerequisites: +# +# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10). +# +# - Bisq must be fully built with apitest dao setup files installed. +# Build command: `./gradlew clean build :apitest:installDaoSetup` +# +# - All supporting nodes must be run locally, in dev/dao/regtest mode: +# bitcoind, seednode, arbdaemon, alicedaemon, bobdaemon +# These should be run using the apitest harness. From the root project dir, run: +# `./bisq-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false"` +# +# - The regtest arbitration daemon (arbdaemon) must have registered dispute agents, run: +# `./bisq-cli --password=xyz --port=9997 registerdisputeagent mediator 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a` +# `./bisq-cli --password=xyz --port=9997 registerdisputeagent refundagent 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a` +# +# - Only regtest btc can be bought or sold with the default dummy eur account. +# The CLI supports creating other fiat payment accts but this script doesn't (todo). +# +# Usage: +# +# This script must be run from the root of the project, e.g.: +# +# `apitest/scripts/simple-trading-script.sh -d buy -c eur -m 3.00 -a 0.125` +# +# Script options: -d -c -m - f -a +# +# Examples: +# +# Create a buy/eur offer to buy 0.125 btc at a mkt-price-margin of 0%: +# +# `apitest/scripts/simple-trading-script.sh -d buy -c usd -m 0.00 -a 0.125` +# +# Create a sell/eur offer to sell 0.125 btc at a fixed-price of 23,000 euros: +# +# `apitest/scripts/simple-trading-script.sh -d sell -c usd -f 23000 -a 0.125` + +APP_BASE_NAME=`basename "$0"` +APP_HOME="`pwd -P`" +APITEST_SCRIPTS_HOME="${APP_HOME}/apitest/scripts" + +# Source the env and some helper functions. +. ${APITEST_SCRIPTS_HOME}/trading-script-env.sh + +checksetup +parseopts "$@" + +echo "[`date`] Started ${APP_BASE_NAME} with parameters:" +printscriptparams + + +# get balances +echo "[`date`] Bob & Alice's balances before trade:" +printcmd "ALICE CLI: ${CLI_BASE} --port=${ALICE_PORT} getbalance" +printbalances $ALICE_PORT +printbreak +printcmd "BOB CLI: ${CLI_BASE} --port=${BOB_PORT} getbalance" +printbalances $BOB_PORT +printbreak + +printcmd "ALICE CLI: ${CLI_BASE} --port=${ALICE_PORT} getpaymentaccts" +ALICE_ACCT_ID=$(getdummyacctid ${ALICE_PORT}) +echo "[`date`] ALICE ${ALICE_ROLE}: Fiat Acct ID: ${ALICE_ACCT_ID}" +printbreak + +printcmd "BOB CLI: ${CLI_BASE} --port=${BOB_PORT} getpaymentaccts" +BOB_ACCT_ID=$(getdummyacctid ${BOB_PORT}) +echo "[`date`] Bob's Fiat Acct ID: ${BOB_ACCT_ID}" +printbreak + +echo "[`date`] ALICE ${ALICE_ROLE}: Creating ${DIRECTION} ${CURRENCY_CODE} offer with payment acct ${ALICE_ACCT_ID}." +if [ -z "${MKT_PRICE_MARGIN}" ]; then + printcmd "ALICE CLI: $CLI_BASE --port=${ALICE_PORT} createoffer ${ALICE_ACCT_ID} ${DIRECTION} ${CURRENCY_CODE} ${AMOUNT} ${AMOUNT} false ${FIXED_PRICE} 0.15 BSQ" +else + printcmd "ALICE CLI: $CLI_BASE --port=${ALICE_PORT} createoffer ${ALICE_ACCT_ID} ${DIRECTION} ${CURRENCY_CODE} ${AMOUNT} ${AMOUNT} true ${MKT_PRICE_MARGIN} 0.15 BSQ" +fi +OFFER_ID=$(createoffer ${ALICE_PORT} ${ALICE_ACCT_ID}) +echo "[`date`] ALICE ${ALICE_ROLE}: Created offer with id: ${OFFER_ID}" +printbreak +sleep 10 + +# Generate a btc block +echo "Generating btc block after publishing Alice's offer." +genbtcblock +printbreak +sleep 10 + +printcmd "BOB (CLI): $CLI_BASE --port=${BOB_PORT} getoffers ${DIRECTION} ${CURRENCY_CODE}" +getoffers ${BOB_PORT} +printbreak + +echo "[`date`] BOB ${BOB_ROLE}: Taking offer ${OFFER_ID} with payment acct ${BOB_ACCT_ID}." +printcmd "BOB CLI: $CLI_BASE --port=${BOB_PORT} takeoffer ${OFFER_ID} ${BOB_ACCT_ID} BSQ" +TRADE=$(takeoffer ${BOB_PORT} ${OFFER_ID} ${BOB_ACCT_ID}) +echo "[`date`] ${TRADE}" +printbreak +sleep 10 + +# Generate a btc block +echo "Generating btc block after Bob takes Alice's offer." +genbtcblock +printbreak +sleep 5 + + +if [ $DIRECTION = "SELL" ] +then + # Confirm payment started + echo "[`date`] BOB ${BOB_ROLE}: Sending fiat payment sent msg." + printcmd "BOB CLI: $CLI_BASE --port=${BOB_PORT} confirmpaymentstarted ${OFFER_ID}" + SENT_MSG=$(confirmpaymentstarted ${BOB_PORT} ${OFFER_ID}) + echo "[`date`] ${SENT_MSG}" + printbreak + + sleep 10 + + # Confirm payment received + echo "[`date`] ALICE ${ALICE_ROLE}: Sending fiat payment recieved msg." + printcmd "ALICE CLI: $CLI_BASE --port=${ALICE_PORT} confirmpaymentreceived ${OFFER_ID}" + SENT_MSG=$(confirmpaymentreceived ${ALICE_PORT} ${OFFER_ID}) + echo "[`date`] ${RCVD_MSG}" + printbreak + + sleep 10 +else + # Confirm payment started + echo "[`date`] ALICE ${ALICE_ROLE}: Sending fiat payment sent msg." + printcmd "ALICE CLI: $CLI_BASE --port=${ALICE_PORT} confirmpaymentstarted ${OFFER_ID}" + SENT_MSG=$(confirmpaymentstarted ${ALICE_PORT} ${OFFER_ID}) + echo "[`date`] ALICE ${ALICE_ROLE}: ${SENT_MSG}" + printbreak + + sleep 10 + + # Confirm payment received + echo "[`date`] BOB ${BOB_ROLE}: Sending fiat payment recieved msg." + printcmd "BOB CLI: $CLI_BASE --port=${BOB_PORT} confirmpaymentreceived ${OFFER_ID}" + RCVD_MSG=$(confirmpaymentreceived ${BOB_PORT} ${OFFER_ID}) + echo "[`date`] ${RCVD_MSG}" + printbreak + + sleep 10 +fi + + +# Generate a btc block +echo "[`date`] Generating btc block after fiat transfer." +genbtcblock +printbreak +sleep 3 + + +if [ $DIRECTION = "SELL" ] +then + # Complete Alice's side of trade + echo "[`date`] ALICE (taker): Closing trade by keeping funds in Bisq wallet." + printcmd "ALICE CLI: $CLI_BASE --port=${ALICE_PORT} keepfunds ${OFFER_ID}" + KEEP_FUNDS_MSG=$(keepfunds ${ALICE_PORT} ${OFFER_ID}) + echo "[`date`] ${KEEP_FUNDS_MSG}" + sleep 5 +else + # Complete Bob's side of trade + echo "[`date`] BOB ${BOB_ROLE}: Closing trade by keeping funds in Bisq wallet." + printcmd "BOB CLI: $CLI_BASE --port=${BOB_PORT} keepfunds ${OFFER_ID}" + KEEP_FUNDS_MSG=$(keepfunds ${BOB_PORT} ${OFFER_ID}) + echo "[`date`] ${KEEP_FUNDS_MSG}" + sleep 5 +fi +printbreak + + +# get balances +echo "[`date`] Bob & Alice's balances after trade:" +printcmd "[`date`] ALICE CLI: ${CLI_BASE} --port=${ALICE_PORT} getbalance" +printbalances $ALICE_PORT +printbreak +printcmd "[`date`] BOB CLI: ${CLI_BASE} --port=${BOB_PORT} getbalance" +printbalances $BOB_PORT +printbreak + + +exit 0 + + + + + + + + + + + + + + + + + + + + + + diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh new file mode 100755 index 00000000000..1d0be9fc226 --- /dev/null +++ b/apitest/scripts/trading-script-env.sh @@ -0,0 +1,239 @@ +#! /bin/bash + +# This file must be sourced by the main driver. + + +export CLI_BASE="./bisq-cli --password=xyz" +export ALICE_PORT=9998 +export BOB_PORT=9999 + +echo "[`date`] Started ${APP_HOME}/${APP_BASE_NAME}." + +checksetup() { + apitestusage() { + echo "The apitest harness must be running a local bitcoin regtest node, a seednode, arbitration node, and Bob & Alice daemons." + echo "" + echo "From the project's root dir, start all supporting nodes from a terminal:" + echo "./bisq-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false" + echo "" + echo "Register dispute agents in the arbitration daemon after it initializes." + echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent mediator 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a" + echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent refundagent 6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a" + exit 1; + } + echo "[`date`] Checking ${APP_HOME} for some expected directories and files." + if [ -d "${APP_HOME}/apitest" ]; then + echo "[`date`] Subproject apitest exists."; + else + echo "[`date`] Error: Subproject apitest not found, maybe because you are not running the script from the project root dir." + exit 1 + fi + if [ -f "${APP_HOME}/bisq-cli" ]; then + echo "[`date`] The bisq-cli script exists."; + else + echo "[`date`] Error: The bisq-cli script not found, maybe because you are not running the script from the project root dir." + exit 1 + fi + echo "[`date`] Checking to see local bitcoind is running." + if pgrep -f "bitcoind -datadir=${APP_HOME}/apitest/build/resources/main/Bitcoin-regtest" > /dev/null ; then + echo "[`date`] The regtest bitcoind node is running on host." + else + echo "[`date`] Error: regtest bitcoind node is not running on host, exiting." + apitestusage + fi + echo "[`date`] Checking to see bisq servers are running." + if pgrep -f "bisq.seednode.SeedNodeMain" > /dev/null ; then + echo "[`date`] The seednode is running on host." + else + echo "[`date`] Error: seed is not running on host, exiting." + apitestusage + fi + if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Arb_dao" > /dev/null ; then + echo "[`date`] The arbitration node is running on host." + else + echo "[`date`] Error: arbitration node is not running on host, exiting." + apitestusage + fi + if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Alice_dao" > /dev/null ; then + echo "[`date`] Alice's daemon node is running on host." + else + echo "[`date`] Error: Alice's daemon node is not running on host, exiting." + apitestusage + fi + if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Bob_dao" > /dev/null ; then + echo "[`date`] Bob's daemon node is running on host." + else + echo "[`date`] Error: Bob's daemon node is not running on host, exiting." + apitestusage + fi +} + +parseopts() { + usage() { + echo "Usage: $0 [-d buy|sell] [-c ] [-f || -m ] [-a ]" 1>&2 + exit 1; + } + + local OPTIND o d c f m a + while getopts "d:c:f:m:a:" o; do + case "${o}" in + d) d=$(echo ${OPTARG} | tr '[:lower:]' '[:upper:]') + ((d == "BUY" || d == "SELL")) || usage + export DIRECTION=${d} + ;; + c) c=$(echo ${OPTARG} | tr '[:lower:]' '[:upper:]') + export CURRENCY_CODE=${c} + ;; + f) f=${OPTARG} + export FIXED_PRICE=${f} + ;; + m) m=${OPTARG} + export MKT_PRICE_MARGIN=${m} + ;; + a) a=${OPTARG} + export AMOUNT=${a} + ;; + *) usage ;; + esac + done + shift $((OPTIND-1)) + + if [ -z "${d}" ] || [ -z "${c}" ] || [ -z "${a}" ]; then + usage + fi + + if [ -z "${f}" ] && [ -z "${m}" ]; then + usage + fi + + if [ -n "${f}" ] && [ -n "${m}" ]; then + echo "[`date`] Must use margin-from-price param (-m) or fixed-price param (-f), not both." + usage + fi + + if [ $DIRECTION = "SELL" ] + then + export BOB_ROLE="Bob (taker/buyer)" + export ALICE_ROLE="Alice (maker/seller)" + else + export BOB_ROLE="Bob (taker/seller)" + export ALICE_ROLE="Alice (maker/buyer)" + fi +} + + +printscriptparams() { + echo " DIRECTION = ${DIRECTION}" + echo " CURRENCY_CODE = ${CURRENCY_CODE}" + echo " FIXED_PRICE = ${FIXED_PRICE}" + echo " MKT_PRICE_MARGIN = ${MKT_PRICE_MARGIN}" + echo " AMOUNT = ${AMOUNT}" + echo " BOB_ROLE = ${BOB_ROLE}" + echo " ALICE_ROLE = ${ALICE_ROLE}" +} + +printbreak() { + echo "" + echo "" +} + + +printbalances() { + PORT=$1 + $CLI_BASE --port=${PORT} getbalance +} + +printcmd() { + echo "[`date`] $@" +} + +getdummyacctid() { + PORT=$1 + DUMMY_ACCTS=$(${CLI_BASE} --port=${PORT} getpaymentaccts) + DUMMY_ACCT_1=$(echo -e "${DUMMY_ACCTS}" | sed -n '2p') + DUMMY_ACCT_2=$(echo -e "${DUMMY_ACCTS}" | sed -n '3p') + if [[ "$DUMMY_ACCT_1=" == *"PerfectMoney dummy"* ]]; then + DUMMY_ACCT=$DUMMY_ACCT_1 + else + DUMMY_ACCT=$DUMMY_ACCT_2 + fi + ACCT_ID=$(echo -e $DUMMY_ACCT | awk '{print $NF}') + echo "${ACCT_ID}" +} + +createoffer() { + PORT=$1 + ACCT_ID=$2 + if [ -z "${MKT_PRICE_MARGIN}" ]; then + OFFER_DESC=$($CLI_BASE --port=${PORT} createoffer ${ACCT_ID} ${DIRECTION} ${CURRENCY_CODE} ${AMOUNT} ${AMOUNT} false ${FIXED_PRICE} 0.15 BSQ) + else + OFFER_DESC=$($CLI_BASE --port=${PORT} createoffer ${ACCT_ID} ${DIRECTION} ${CURRENCY_CODE} ${AMOUNT} ${AMOUNT} true ${MKT_PRICE_MARGIN} 0.15 BSQ) + fi + OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p') + NEW_OFFER_ID=$(echo -e ${OFFER_DETAIL} | awk '{print $NF}') + echo "${NEW_OFFER_ID}" +} + + +getoffers() { + PORT=$1 + OFFERS=$($CLI_BASE --port=${PORT} getoffers ${DIRECTION} ${CURRENCY_CODE}) + echo "${OFFERS}" +} + +getfirstofferid() { + PORT=$1 + OFFERS=$($CLI_BASE --port=${PORT} getoffers ${DIRECTION} ${CURRENCY_CODE}) + FIRST_OFFER=$(echo -e "${OFFERS}" | sed -n '2p') + FIRST_OFFER_ID=$(echo -e ${FIRST_OFFER} | awk '{print $NF}') + echo "${FIRST_OFFER_ID}" +} + +takeoffer() { + PORT=$1 + OFFER_ID=$2 + ACCT_ID=$3 + TRADE=$($CLI_BASE --port=${PORT} takeoffer ${OFFER_ID} ${ACCT_ID} BSQ) + echo "${TRADE}" +} + +confirmpaymentstarted() { + PORT=$1 + OFFER_ID=$2 + SENT_MSG=$($CLI_BASE --port=${PORT} confirmpaymentstarted ${OFFER_ID}) + echo "${SENT_MSG}" +} + +confirmpaymentreceived() { + PORT=$1 + OFFER_ID=$2 + RCVD_MSG=$($CLI_BASE --port=${PORT} confirmpaymentreceived ${OFFER_ID}) + echo "${RCVD_MSG}" +} + +keepfunds() { + PORT=$1 + OFFER_ID=$2 + KEEP_FUNDS_MSG=$($CLI_BASE --port=${PORT} keepfunds ${OFFER_ID}) + echo "${KEEP_FUNDS_MSG}" +} + +genbtcblock() { + bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv" +} + + +readYesOrNo() { + question=$1 + echo -n "$question Yes or No: " + read answer + answer=`echo $answer | tr [a-z] [A-Z]` + if [ $answer = Y ] + then + echo You answered yes: $answer + else + echo You answered no: $answer + fi + return 0 +} +