This commit is contained in:
woodser 2021-05-04 20:20:01 -04:00
commit 8a38081c04
2800 changed files with 344130 additions and 0 deletions

View file

@ -0,0 +1,28 @@
import sys, os, json
# Writes a Bisq json F2F payment account form for the given country_code to the current working directory.
if len(sys.argv) < 2:
print("usage: editf2faccountform.py country_code")
exit(1)
country_code = str(sys.argv[1]).upper()
acct_form = {
"_COMMENTS_": [
"Do not manually edit the paymentMethodId field.",
"Edit the salt field only if you are recreating a payment account on a new installation and wish to preserve the account age."
],
"paymentMethodId": "F2F",
"accountName": "Face to Face Payment Account",
"city": "Anytown",
"contact": "Me",
"country": country_code,
"extraInfo": "",
"salt": ""
}
target=os.path.dirname(os.path.realpath(__file__)) + '/' + 'f2f-acct.json'
with open (target, 'w') as outfile:
json.dump(acct_form, outfile, indent=2)
outfile.write('\n')
exit(0)

15
apitest/scripts/get-bisq-pid.sh Executable file
View file

@ -0,0 +1,15 @@
#!/bin/bash
# Find the pid of the java process by grepping for the mainClassName and appName,
# then print the 2nd column of the output to stdout.
#
# Doing this from Java is problematic, probably due to limitation of the
# apitest.linux.BashCommand implementation.
MAIN_CLASS_NAME=$1
APP_NAME=$2
# TODO args validation
ps aux | grep java | grep "${MAIN_CLASS_NAME}" | grep "${APP_NAME}" | awk '{print $2}'

View file

@ -0,0 +1,159 @@
#! /bin/bash
# Demonstrates a way to create a limit order (offer) using the API CLI with a local regtest bitcoin node.
#
# A country code argument is used to create a country based face to face payment account for the simulated offer.
#
# Prerequisites:
#
# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10), and bitcoin-core (v0.19, v0.20, v0.21).
#
# - 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`
#
# - Only regtest btc can be bought or sold with the test payment account.
#
# Usage:
#
# This script must be run from the root of the project, e.g.:
#
# `$ apitest/scripts/limit-order-simulation.sh -l 40000 -d buy -c fr -m 3.00 -a 0.125`
#
# Script options: -l <limit-price> -d <direction> -c <country-code> (-m <mkt-price-margin(%)> || -f <fixed-price>) -a <amount(btc)> [-w <price-poll-interval(s)>]
#
# Example:
#
# Create a sell/eur offer to sell 0.125 btc at a fixed-price of 38,000 euros, using a France face to face
# payment account, when the BTC market price rises to or above 40,000 EUR:
#
# `$ apitest/scripts/limit-order-simulation.sh -l 40000 -d sell -c fr -m 0.00 -a 0.125`
APP_BASE_NAME=$(basename "$0")
APP_HOME=$(pwd -P)
APITEST_SCRIPTS_HOME="$APP_HOME/apitest/scripts"
source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh"
source "$APITEST_SCRIPTS_HOME/trade-simulation-utils.sh"
checksetup
parselimitorderopts "$@"
printdate "Started $APP_BASE_NAME with parameters:"
printscriptparams
printbreak
editpaymentaccountform "$COUNTRY_CODE"
exitoncommandalert $?
cat "$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printbreak
# Create F2F payment accounts for $COUNTRY_CODE, and get the $CURRENCY_CODE.
printdate "Creating Alice's face to face $COUNTRY_CODE payment account."
CMD="$CLI_BASE --port=$ALICE_PORT createpaymentacct --payment-account-form=$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printdate "ALICE CLI: $CMD"
CMD_OUTPUT=$(createpaymentacct "$CMD")
exitoncommandalert $?
echo "$CMD_OUTPUT"
ALICE_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
exitoncommandalert $?
CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
exitoncommandalert $?
printdate "ALICE F2F payment-account-id = $ALICE_ACCT_ID, currency-code = $CURRENCY_CODE."
printbreak
printdate "Creating Bob's face to face $COUNTRY_CODE payment account."
CMD="$CLI_BASE --port=$BOB_PORT createpaymentacct --payment-account-form=$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printdate "BOB CLI: $CMD"
CMD_OUTPUT=$(createpaymentacct "$CMD")
exitoncommandalert $?
echo "$CMD_OUTPUT"
BOB_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
exitoncommandalert $?
CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
exitoncommandalert $?
printdate "BOB F2F payment-account-id = $BOB_ACCT_ID, currency-code = $CURRENCY_CODE."
printbreak
# Bob & Alice now have matching payment accounts, now loop until the price limit is reached, then create an offer.
if [ "$DIRECTION" = "BUY" ]
then
printdate "Create a BUY / $CURRENCY_CODE offer when the market price falls to or below $LIMIT_PRICE $CURRENCY_CODE."
else
printdate "Create a SELL / $CURRENCY_CODE offer when the market price rises to or above $LIMIT_PRICE $CURRENCY_CODE."
fi
DONE=0
while : ; do
if [ "$DONE" -ne 0 ]; then
break
fi
CURRENT_PRICE=$(getcurrentprice "$ALICE_PORT" "$CURRENCY_CODE")
exitoncommandalert $?
printdate "Current Market Price: $CURRENT_PRICE"
if [ "$DIRECTION" = "BUY" ] && [ "$CURRENT_PRICE" -le "$LIMIT_PRICE" ]; then
printdate "Limit price reached."
DONE=1
break
fi
if [ "$DIRECTION" = "SELL" ] && [ "$CURRENT_PRICE" -ge "$LIMIT_PRICE" ]; then
printdate "Limit price reached."
DONE=1
break
fi
sleep "$WAIT"
done
printdate "ALICE: Creating $DIRECTION $CURRENCY_CODE offer with payment acct $ALICE_ACCT_ID."
CMD="$CLI_BASE --port=$ALICE_PORT createoffer"
CMD+=" --payment-account=$ALICE_ACCT_ID"
CMD+=" --direction=$DIRECTION"
CMD+=" --currency-code=$CURRENCY_CODE"
CMD+=" --amount=$AMOUNT"
if [ -z "$MKT_PRICE_MARGIN" ]; then
CMD+=" --fixed-price=$FIXED_PRICE"
else
CMD+=" --market-price-margin=$MKT_PRICE_MARGIN"
fi
CMD+=" --security-deposit=50.0"
CMD+=" --fee-currency=BSQ"
printdate "ALICE CLI: $CMD"
OFFER_ID=$(createoffer "$CMD")
exitoncommandalert $?
printdate "ALICE: Created offer with id: $OFFER_ID."
printbreak
sleeptraced 3
# Show Alice's new offer.
printdate "ALICE: Looking at her new $DIRECTION $CURRENCY_CODE offer."
CMD="$CLI_BASE --port=$ALICE_PORT getmyoffer --offer-id=$OFFER_ID"
printdate "ALICE CLI: $CMD"
OFFER=$($CMD)
exitoncommandalert $?
echo "$OFFER"
printbreak
sleeptraced 4
# Generate some btc blocks.
printdate "Generating btc blocks after publishing Alice's offer."
genbtcblocks 3 3
printbreak
# Show Alice's offer in Bob's CLI.
printdate "BOB: Looking at $DIRECTION $CURRENCY_CODE offers."
CMD="$CLI_BASE --port=$BOB_PORT getoffers --direction=$DIRECTION --currency-code=$CURRENCY_CODE"
printdate "BOB CLI: $CMD"
OFFERS=$($CMD)
exitoncommandalert $?
echo "$OFFERS"
exit 0

236
apitest/scripts/mainnet-test.sh Executable file
View file

@ -0,0 +1,236 @@
#!/usr/bin/env bats
#
# Smoke tests for bisq-cli running against a live bisq-daemon (on mainnet)
#
# Prerequisites:
#
# - bats-core 1.2.0+ must be installed (brew install bats-core on macOS)
# see https://github.com/bats-core/bats-core
#
# - Run `./bisq-daemon --apiPassword=xyz --appDataDir=$TESTDIR` where $TESTDIR
# is empty or otherwise contains an unencrypted wallet with a 0 BTC balance
#
# Usage:
#
# This script must be run from the root of the project, e.g.:
#
# bats apitest/scripts/mainnet-test.sh
@test "test unsupported method error" {
run ./bisq-cli --password=xyz bogus
[ "$status" -eq 1 ]
echo "actual output: $output" >&2 # printed only on test failure
[ "$output" = "Error: 'bogus' is not a supported method" ]
}
@test "test unrecognized option error" {
run ./bisq-cli --bogus getversion
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: missing required 'password' option" ]
}
@test "test missing required password option error" {
run ./bisq-cli getversion
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: missing required 'password' option" ]
}
@test "test incorrect password error" {
run ./bisq-cli --password=bogus getversion
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: incorrect 'password' rpc header value" ]
}
@test "test getversion call with quoted password" {
load 'version-parser'
run ./bisq-cli --password="xyz" getversion
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "$CURRENT_VERSION" ]
}
@test "test getversion" {
# Wait 1 second before calling getversion again.
sleep 1
load 'version-parser'
run ./bisq-cli --password=xyz getversion
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "$CURRENT_VERSION" ]
}
@test "test setwalletpassword \"a b c\"" {
run ./bisq-cli --password=xyz setwalletpassword --wallet-password="a b c"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet encrypted" ]
sleep 1
}
@test "test unlockwallet without password & timeout args" {
run ./bisq-cli --password=xyz unlockwallet
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: no password specified" ]
}
@test "test unlockwallet without timeout arg" {
run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c"
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: no unlock timeout specified" ]
}
@test "test unlockwallet \"a b c\" 8" {
run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c" --timeout=8
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet unlocked" ]
}
@test "test getbalance while wallet unlocked for 8s" {
run ./bisq-cli --password=xyz getbalance
[ "$status" -eq 0 ]
sleep 8
}
@test "test unlockwallet \"a b c\" 6" {
run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c" --timeout=6
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet unlocked" ]
}
@test "test lockwallet before unlockwallet timeout=6s expires" {
run ./bisq-cli --password=xyz lockwallet
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet locked" ]
}
@test "test setwalletpassword incorrect old pwd error" {
run ./bisq-cli --password=xyz setwalletpassword --wallet-password="z z z" --new-wallet-password="d e f"
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: incorrect old password" ]
}
@test "test setwalletpassword oldpwd newpwd" {
# Wait 5 seconds before calling setwalletpassword again.
sleep 5
run ./bisq-cli --password=xyz setwalletpassword --wallet-password="a b c" --new-wallet-password="d e f"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet encrypted with new password" ]
sleep 1
}
@test "test getbalance wallet locked error" {
run ./bisq-cli --password=xyz getbalance
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: wallet is locked" ]
}
@test "test removewalletpassword" {
run ./bisq-cli --password=xyz removewalletpassword --wallet-password="d e f"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet decrypted" ]
sleep 3
}
@test "test getbalance when wallet available & unlocked with 0 btc balance" {
run ./bisq-cli --password=xyz getbalance
[ "$status" -eq 0 ]
}
@test "test getfundingaddresses" {
run ./bisq-cli --password=xyz getfundingaddresses
[ "$status" -eq 0 ]
}
@test "test getunusedbsqaddress" {
run ./bisq-cli --password=xyz getunusedbsqaddress
[ "$status" -eq 0 ]
}
@test "test getaddressbalance missing address argument" {
run ./bisq-cli --password=xyz getaddressbalance
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: no address specified" ]
}
@test "test getaddressbalance bogus address argument" {
# Wait 1 second before calling getaddressbalance again.
sleep 1
run ./bisq-cli --password=xyz getaddressbalance --address=bogus
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: address bogus not found in wallet" ]
}
@test "test getpaymentmethods" {
run ./bisq-cli --password=xyz getpaymentmethods
[ "$status" -eq 0 ]
}
@test "test getpaymentaccts" {
run ./bisq-cli --password=xyz getpaymentaccts
[ "$status" -eq 0 ]
}
@test "test getoffers missing direction argument" {
run ./bisq-cli --password=xyz getoffers
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: no direction (buy|sell) specified" ]
}
@test "test getoffers sell eur check return status" {
# Wait 1 second before calling getoffers again.
sleep 1
run ./bisq-cli --password=xyz getoffers --direction=sell --currency-code=eur
[ "$status" -eq 0 ]
}
@test "test getoffers buy eur check return status" {
# Wait 1 second before calling getoffers again.
sleep 1
run ./bisq-cli --password=xyz getoffers --direction=buy --currency-code=eur
[ "$status" -eq 0 ]
}
@test "test getoffers sell gbp check return status" {
# Wait 1 second before calling getoffers again.
sleep 1
run ./bisq-cli --password=xyz getoffers --direction=sell --currency-code=gbp
[ "$status" -eq 0 ]
}
@test "test help displayed on stderr if no options or arguments" {
run ./bisq-cli
[ "$status" -eq 1 ]
[ "${lines[0]}" = "Bisq RPC Client" ]
[ "${lines[1]}" = "Usage: bisq-cli [options] <method> [params]" ]
# TODO add asserts after help text is modified for new endpoints
}
@test "test --help option" {
run ./bisq-cli --help
[ "$status" -eq 0 ]
[ "${lines[0]}" = "Bisq RPC Client" ]
[ "${lines[1]}" = "Usage: bisq-cli [options] <method> [params]" ]
# TODO add asserts after help text is modified for new endpoints
}
@test "test takeoffer method --help" {
run ./bisq-cli --password=xyz takeoffer --help
[ "$status" -eq 0 ]
[ "${lines[0]}" = "takeoffer" ]
}

View file

@ -0,0 +1,119 @@
#! /bin/bash
# Demonstrates a way to always keep one offer in the market, using the API CLI with a local regtest bitcoin node.
# Alice creates an offer, waits for Bob to take it, and completes the trade protocol with him. Then Alice
# creates a new offer...
#
# Stop the script by entering ^C.
#
# A country code argument is used to create a country based face to face payment account for the simulated offer.
#
# Prerequisites:
#
# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10), and bitcoin-core (v0.19, v0.20, v0.21).
#
# - 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`
#
# - Only regtest btc can be bought or sold with the test payment account.
#
# Usage:
#
# This script must be run from the root of the project, e.g.:
#
# `$ apitest/scripts/rolling-offer-simulation.sh -d buy -c us -m 2.00 -a 0.125`
#
# Script options: -d <direction> -c <country-code> (-m <mkt-price-margin(%)> || -f <fixed-price>) -a <amount(btc)>
#
# Example:
#
# Create a buy/usd offer to sell 0.1 btc at 2% above market price, using a US face to face payment account:
#
# `$ apitest/scripts/rolling-offer-simulation.sh -d sell -c us -m 2.00 -a 0.1`
APP_BASE_NAME=$(basename "$0")
APP_HOME=$(pwd -P)
APITEST_SCRIPTS_HOME="$APP_HOME/apitest/scripts"
source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh"
source "$APITEST_SCRIPTS_HOME/trade-simulation-utils.sh"
checksetup
parseopts "$@"
printdate "Started $APP_BASE_NAME with parameters:"
printscriptparams
printbreak
registerdisputeagents
showcreatepaymentacctsteps "Alice" "$ALICE_PORT"
CMD="$CLI_BASE --port=$ALICE_PORT createpaymentacct --payment-account-form=$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printdate "ALICE CLI: $CMD"
CMD_OUTPUT=$(createpaymentacct "$CMD")
echo "$CMD_OUTPUT"
printbreak
export ALICE_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
export CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
printdate "Alice's F2F payment-account-id: $ALICE_ACCT_ID, currency-code: $CURRENCY_CODE"
exitoncommandalert $?
printbreak
printdate "Bob creates his F2F payment account."
CMD="$CLI_BASE --port=$BOB_PORT createpaymentacct --payment-account-form=$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printdate "BOB CLI: $CMD"
CMD_OUTPUT=$(createpaymentacct "$CMD")
echo "$CMD_OUTPUT"
printbreak
export BOB_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
export CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
printdate "Bob's F2F payment-account-id: $BOB_ACCT_ID, currency-code: $CURRENCY_CODE"
exitoncommandalert $?
printbreak
while : ; do
printdate "ALICE $ALICE_ROLE: Creating $DIRECTION $CURRENCY_CODE offer with payment acct $ALICE_ACCT_ID."
CURRENT_PRICE=$(getcurrentprice "$ALICE_PORT" "$CURRENCY_CODE")
exitoncommandalert $?
printdate "Current Market Price: $CURRENT_PRICE"
CMD=$(gencreateoffercommand "$ALICE_PORT" "$ALICE_ACCT_ID")
printdate "ALICE CLI: $CMD"
OFFER_ID=$(createoffer "$CMD")
exitoncommandalert $?
printdate "ALICE $ALICE_ROLE: Created offer with id: $OFFER_ID."
printbreak
sleeptraced 3
# Show Alice's new offer.
printdate "ALICE $ALICE_ROLE: Looking at her new $DIRECTION $CURRENCY_CODE offer."
CMD="$CLI_BASE --port=$ALICE_PORT getmyoffer --offer-id=$OFFER_ID"
printdate "ALICE CLI: $CMD"
OFFER=$($CMD)
exitoncommandalert $?
echo "$OFFER"
printbreak
sleeptraced 3
# Generate some btc blocks.
printdate "Generating btc blocks after publishing Alice's offer."
genbtcblocks 3 2
printbreak
RANDOM_WAIT=$(echo $[$RANDOM % 10 + 1])
printdate "Bob will take Alice's offer in $RANDOM_WAIT seconds..."
sleeptraced "$RANDOM_WAIT"
executetrade
exitoncommandalert $?
printbreak
done
exit 0

View file

@ -0,0 +1,312 @@
#! /bin/bash
# This file must be sourced by the main driver.
export CLI_BASE="./bisq-cli --password=xyz"
export ARBITRATOR_PORT=9997
export ALICE_PORT=9998
export BOB_PORT=9999
export F2F_ACCT_FORM="f2f-acct.json"
checkos() {
LINUX=FALSE
DARWIN=FALSE
UNAME=$(uname)
case "$UNAME" in
Linux* )
export LINUX=TRUE
;;
Darwin* )
export DARWIN=TRUE
;;
esac
if [[ "$LINUX" == "TRUE" ]]; then
printdate "Running on supported Linux OS."
elif [[ "$DARWIN" == "TRUE" ]]; then
printdate "Running on supported Mac OS."
else
printdate "Script cannot run on $OSTYPE OS, only Linux and OSX are supported."
exit 1
fi
}
checksetup() {
checkos
apitestusage() {
echo "The apitest harness must be running a local bitcoin regtest node, a seednode, an arbitration node,"
echo "Bob & Alice daemons, and bitcoin-core's bitcoin-cli must be in the system PATH."
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"
exit 1;
}
printdate "Checking $APP_HOME for some expected directories and files."
if [ -d "$APP_HOME/apitest" ]; then
printdate "Subproject apitest exists.";
else
printdate "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
printdate "The bisq-cli script exists.";
else
printdate "Error: The bisq-cli script not found, maybe because you are not running the script from the project root dir."
exit 1
fi
printdate "Checking to see local bitcoind is running, and bitcoin-cli is in PATH."
checkbitcoindrunning
checkbitcoincliinpath
printdate "Checking to see bisq servers are running."
checkseednoderunning
checkarbnoderunning
checkalicenoderunning
checkbobnoderunning
}
parseopts() {
usage() {
echo "Usage: $0 [-d buy|sell] [-c <country-code>] [-f <fixed-price> || -m <margin-from-price>] [-a <amount in btc>]" 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 COUNTRY_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
printdate "Must use margin-from-price param (-m) or fixed-price param (-f), not both."
usage
fi
if [ "$DIRECTION" = "SELL" ]
then
export BOB_ROLE="(taker/buyer)"
export ALICE_ROLE="(maker/seller)"
else
export BOB_ROLE="(taker/seller)"
export ALICE_ROLE="(maker/buyer)"
fi
}
parselimitorderopts() {
usage() {
echo "Usage: $0 [-l limit-price] [-d buy|sell] [-c <country-code>] [-f <fixed-price> || -m <margin-from-price>] [-a <amount in btc>] [-w <price-poll-interval(s)>]" 1>&2
exit 1;
}
local OPTIND o l d c f m a w
while getopts "l:d:c:f:m:a:w:" o; do
case "${o}" in
l) l=${OPTARG}
export LIMIT_PRICE=${l}
;;
d) d=$(echo "${OPTARG}" | tr '[:lower:]' '[:upper:]')
((d == "BUY" || d == "SELL")) || usage
export DIRECTION=${d}
;;
c) c=$(echo "${OPTARG}"| tr '[:lower:]' '[:upper:]')
export COUNTRY_CODE=${c}
;;
f) f=${OPTARG}
export FIXED_PRICE=${f}
;;
m) m=${OPTARG}
export MKT_PRICE_MARGIN=${m}
;;
a) a=${OPTARG}
export AMOUNT=${a}
;;
w) w=${OPTARG}
export WAIT=${w}
;;
*) usage ;;
esac
done
shift $((OPTIND-1))
if [ -z "${l}" ]; then
usage
fi
if [ -z "${d}" ] || [ -z "${c}" ] || [ -z "${a}" ]; then
usage
fi
if [ -z "${f}" ] && [ -z "${m}" ]; then
usage
fi
if [ -n "${f}" ] && [ -n "${m}" ]; then
printdate "Must use margin-from-price param (-m) or fixed-price param (-f), not both."
usage
fi
if [ -z "${w}" ]; then
WAIT=120
elif [ "$w" -lt 20 ]; then
printdate "The -w <price-poll-interval(s)> option is too low, minimum allowed is 20s. Using default 120s."
WAIT=120
fi
}
checkbitcoindrunning() {
# There may be a '+' char in the path and we have to escape it for pgrep.
if [[ $APP_HOME == *"+"* ]]; then
ESCAPED_APP_HOME=$(escapepluschar "$APP_HOME")
else
ESCAPED_APP_HOME="$APP_HOME"
fi
if pgrep -f "bitcoind -datadir=$ESCAPED_APP_HOME/apitest/build/resources/main/Bitcoin-regtest" > /dev/null ; then
printdate "The regtest bitcoind node is running on host."
else
printdate "Error: regtest bitcoind node is not running on host, exiting."
apitestusage
fi
}
checkbitcoincliinpath() {
if which bitcoin-cli > /dev/null ; then
printdate "The bitcoin-cli binary is in the system PATH."
else
printdate "Error: bitcoin-cli binary is not in the system PATH, exiting."
apitestusage
fi
}
checkseednoderunning() {
if [[ "$LINUX" == "TRUE" ]]; then
if pgrep -f "bisq.seednode.SeedNodeMain" > /dev/null ; then
printdate "The seed node is running on host."
else
printdate "Error: seed node is not running on host, exiting."
apitestusage
fi
elif [[ "$DARWIN" == "TRUE" ]]; then
if ps -A | awk '/[S]eedNodeMain/ {print $1}' > /dev/null ; then
printdate "The seednode is running on host."
else
printdate "Error: seed node is not running on host, exiting."
apitestusage
fi
else
printdate "Error: seed node is not running on host, exiting."
apitestusage
fi
}
checkarbnoderunning() {
if [[ "$LINUX" == "TRUE" ]]; then
if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Arb_dao" > /dev/null ; then
printdate "The arbitration node is running on host."
else
printdate "Error: arbitration node is not running on host, exiting."
apitestusage
fi
elif [[ "$DARWIN" == "TRUE" ]]; then
if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Arb_dao/ {print $1}' > /dev/null ; then
printdate "The arbitration node is running on host."
else
printdate "Error: arbitration node is not running on host, exiting."
apitestusage
fi
else
printdate "Error: arbitration node is not running on host, exiting."
apitestusage
fi
}
checkalicenoderunning() {
if [[ "$LINUX" == "TRUE" ]]; then
if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Alice_dao" > /dev/null ; then
printdate "Alice's node is running on host."
else
printdate "Error: Alice's node is not running on host, exiting."
apitestusage
fi
elif [[ "$DARWIN" == "TRUE" ]]; then
if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Alice_dao/ {print $1}' > /dev/null ; then
printdate "Alice's node node is running on host."
else
printdate "Error: Alice's node is not running on host, exiting."
apitestusage
fi
else
printdate "Error: Alice's node is not running on host, exiting."
apitestusage
fi
}
checkbobnoderunning() {
if [[ "$LINUX" == "TRUE" ]]; then
if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Alice_dao" > /dev/null ; then
printdate "Bob's node is running on host."
else
printdate "Error: Bob's node is not running on host, exiting."
apitestusage
fi
elif [[ "$DARWIN" == "TRUE" ]]; then
if ps -A | awk '/[b]isq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Alice_dao/ {print $1}' > /dev/null ; then
printdate "Bob's node node is running on host."
else
printdate "Error: Bob's node is not running on host, exiting."
apitestusage
fi
else
printdate "Error: Bob's node is not running on host, exiting."
apitestusage
fi
}
printscriptparams() {
if [ -n "${LIMIT_PRICE+1}" ]; then
echo " LIMIT_PRICE = $LIMIT_PRICE"
fi
echo " DIRECTION = $DIRECTION"
echo " COUNTRY_CODE = $COUNTRY_CODE"
echo " FIXED_PRICE = $FIXED_PRICE"
echo " MKT_PRICE_MARGIN = $MKT_PRICE_MARGIN"
echo " AMOUNT = $AMOUNT"
if [ -n "${BOB_ROLE+1}" ]; then
echo " BOB_ROLE = $BOB_ROLE"
fi
if [ -n "${ALICE_ROLE+1}" ]; then
echo " ALICE_ROLE = $ALICE_ROLE"
fi
if [ -n "${WAIT+1}" ]; then
echo " WAIT = $WAIT"
fi
}

View file

@ -0,0 +1,571 @@
#! /bin/bash
# This file must be sourced by the main driver.
source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh"
printdate() {
echo "[$(date)] $@"
}
printbreak() {
echo ""
echo ""
}
printcmd() {
echo -en "$@\n"
}
sleeptraced() {
PERIOD="$1"
printdate "sleeping for $PERIOD"
sleep "$PERIOD"
}
commandalert() {
# Used in a script function when it needs to fail early with an error message, & pass the error code to the caller.
# usage: commandalert <$?> <msg-prefix>
if [ "$1" -ne 0 ]
then
printdate "Error: $2" >&2
exit "$1"
fi
}
# TODO rename exitonalert ?
exitoncommandalert() {
# Used in a parent script when you need it to fail immediately, with no error message.
# usage: exitoncommandalert <$?>
if [ "$1" -ne 0 ]
then
exit "$1"
fi
}
registerdisputeagents() {
# Silently register dev dispute agents. It's easy to forget.
REG_KEY="6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
CMD="$CLI_BASE --port=$ARBITRATOR_PORT registerdisputeagent --dispute-agent-type=mediator --registration-key=$REG_KEY"
SILENT=$($CMD)
commandalert $? "Could not register dev/test mediator."
CMD="$CLI_BASE --port=$ARBITRATOR_PORT registerdisputeagent --dispute-agent-type=refundagent --registration-key=$REG_KEY"
SILENT=$($CMD)
commandalert $? "Could not register dev/test refundagent."
# Do something with $SILENT to keep codacy happy.
echo "$SILENT" > /dev/null
}
getbtcoreaddress() {
CMD="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest getnewaddress"
NEW_ADDRESS=$($CMD)
echo "$NEW_ADDRESS"
}
genbtcblocks() {
NUM_BLOCKS="$1"
SECONDS_BETWEEN_BLOCKS="$2"
ADDR_PARAM="$(getbtcoreaddress)"
CMD_PREFIX="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1"
# Print the generatetoaddress command with double quoted address param, to make it cut & pastable from the console.
printdate "$CMD_PREFIX \"$ADDR_PARAM\""
# Now create the full generatetoaddress command to be run now.
CMD="$CMD_PREFIX $ADDR_PARAM"
for i in $(seq -f "%02g" 1 "$NUM_BLOCKS")
do
NEW_BLOCK_HASH=$(genbtcblock "$CMD")
printdate "Block Hash #$i:$NEW_BLOCK_HASH"
sleep "$SECONDS_BETWEEN_BLOCKS"
done
}
genbtcblock() {
CMD="$1"
NEW_BLOCK_HASH=$($CMD | sed -n '2p')
echo "$NEW_BLOCK_HASH"
}
escapepluschar() {
STRING="$1"
NEW_STRING=$(echo "${STRING//+/\\+}")
echo "$NEW_STRING"
}
printbalances() {
PORT="$1"
printcmd "$CLI_BASE --port=$PORT getbalance"
$CLI_BASE --port="$PORT" getbalance
}
getpaymentaccountmethods() {
CMD="$1"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not get payment method ids."
printdate "Payment Method IDs:"
echo "$CMD_OUTPUT"
}
getpaymentaccountform() {
CMD="$1"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not get new payment account form."
echo "$CMD_OUTPUT"
}
editpaymentaccountform() {
COUNTRY_CODE="$1"
CMD="python3 $APITEST_SCRIPTS_HOME/editf2faccountform.py $COUNTRY_CODE"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not edit payment account form."
printdate "Saved payment account form as $F2F_ACCT_FORM."
}
getnewpaymentacctid() {
CREATE_PAYMENT_ACCT_OUTPUT="$1"
PAYMENT_ACCT_DETAIL=$(echo -e "$CREATE_PAYMENT_ACCT_OUTPUT" | sed -n '3p')
ACCT_ID=$(echo -e "$PAYMENT_ACCT_DETAIL" | awk '{print $NF}')
echo "$ACCT_ID"
}
getnewpaymentacctcurrency() {
CREATE_PAYMENT_ACCT_OUTPUT="$1"
PAYMENT_ACCT_DETAIL=$(echo -e "$CREATE_PAYMENT_ACCT_OUTPUT" | sed -n '3p')
# This is brittle; it requires the account name field to have N words,
# e.g, "Face to Face Payment Account" as defined in editf2faccountform.py.
CURRENCY_CODE=$(echo -e "$PAYMENT_ACCT_DETAIL" | awk '{print $6}')
echo "$CURRENCY_CODE"
}
createpaymentacct() {
CMD="$1"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not create new payment account."
echo "$CMD_OUTPUT"
}
getpaymentaccounts() {
PORT="$1"
printcmd "$CLI_BASE --port=$PORT getpaymentaccts"
CMD="$CLI_BASE --port=$PORT getpaymentaccts"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not get payment accounts."
echo "$CMD_OUTPUT"
}
showcreatepaymentacctsteps() {
USER="$1"
PORT="$2"
printdate "$USER looks for the ID of the face to face payment account method (Bob will use same payment method)."
CMD="$CLI_BASE --port=$PORT getpaymentmethods"
printdate "$USER CLI: $CMD"
PAYMENT_ACCT_METHODS=$(getpaymentaccountmethods "$CMD")
echo "$PAYMENT_ACCT_METHODS"
printbreak
printdate "$USER uses the F2F payment method id to create a face to face payment account in country $COUNTRY_CODE."
CMD="$CLI_BASE --port=$PORT getpaymentacctform --payment-method-id=F2F"
printdate "$USER CLI: $CMD"
getpaymentaccountform "$CMD"
printbreak
printdate "$USER edits the $COUNTRY_CODE payment account form, and (optionally) renames it as $F2F_ACCT_FORM"
editpaymentaccountform "$COUNTRY_CODE"
cat "$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
# Remove the autogenerated json template because we are going to use one created by a python script in the next step.
CMD="rm -v $APP_HOME/f2f_*.json"
DELETE_JSON_TEMPLATE=$($CMD)
printdate "$DELETE_JSON_TEMPLATE"
printbreak
}
gencreateoffercommand() {
PORT="$1"
ACCT_ID="$2"
CMD="$CLI_BASE --port=$PORT createoffer"
CMD+=" --payment-account=$ACCT_ID"
CMD+=" --direction=$DIRECTION"
CMD+=" --currency-code=$CURRENCY_CODE"
CMD+=" --amount=$AMOUNT"
if [ -z "$MKT_PRICE_MARGIN" ]; then
CMD+=" --fixed-price=$FIXED_PRICE"
else
CMD+=" --market-price-margin=$MKT_PRICE_MARGIN"
fi
CMD+=" --security-deposit=15.0"
CMD+=" --fee-currency=BSQ"
echo "$CMD"
}
createoffer() {
CREATE_OFFER_CMD="$1"
OFFER_DESC=$($CREATE_OFFER_CMD)
# If the CLI command exited with an error, print the CLI error, and
# return from this function now, passing the error status code to the caller.
commandalert $? "Could not create offer."
OFFER_DETAIL=$(echo -e "$OFFER_DESC" | sed -n '2p')
NEW_OFFER_ID=$(echo -e "$OFFER_DETAIL" | awk '{print $NF}')
echo "$NEW_OFFER_ID"
}
getfirstofferid() {
PORT="$1"
CMD="$CLI_BASE --port=$PORT getoffers --direction=$DIRECTION --currency-code=$CURRENCY_CODE"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not get current $DIRECTION / $CURRENCY_CODE offers."
FIRST_OFFER_DETAIL=$(echo -e "$CMD_OUTPUT" | sed -n '2p')
FIRST_OFFER_ID=$(echo -e "$FIRST_OFFER_DETAIL" | awk '{print $NF}')
commandalert $? "Could parse the offer-id from the first listed offer."
echo "$FIRST_OFFER_ID"
}
gettrade() {
GET_TRADE_CMD="$1"
TRADE_DESC=$($GET_TRADE_CMD)
commandalert $? "Could not get trade."
echo "$TRADE_DESC"
}
gettradedetail() {
TRADE_DESC="$1"
# Get 2nd line of gettrade cmd output, and squeeze multi space delimiters into one space.
TRADE_DETAIL=$(echo "$TRADE_DESC" | sed -n '2p' | tr -s ' ')
commandalert $? "Could not get trade detail (line 2 of gettrade output)."
echo "$TRADE_DETAIL"
}
istradedepositpublished() {
TRADE_DETAIL="$1"
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $10}')
commandalert $? "Could not parse istradedepositpublished from trade detail."
echo "$ANSWER"
}
istradedepositconfirmed() {
TRADE_DETAIL="$1"
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $11}')
commandalert $? "Could not parse istradedepositconfirmed from trade detail."
echo "$ANSWER"
}
istradepaymentsent() {
TRADE_DETAIL="$1"
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $13}')
commandalert $? "Could not parse istradepaymentsent from trade detail."
echo "$ANSWER"
}
istradepaymentreceived() {
TRADE_DETAIL="$1"
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $14}')
commandalert $? "Could not parse istradepaymentreceived from trade detail."
echo "$ANSWER"
}
istradepayoutpublished() {
TRADE_DETAIL="$1"
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $15}')
commandalert $? "Could not parse istradepayoutpublished from trade detail."
echo "$ANSWER"
}
waitfortradedepositpublished() {
# Loops until Bob's trade deposit is published. (Bob is always the trade taker.)
OFFER_ID="$1"
DONE=0
while : ; do
if [ "$DONE" -ne 0 ]; then
break
fi
printdate "BOB $BOB_ROLE: Looking at his trade with id $OFFER_ID."
CMD="$CLI_BASE --port=$BOB_PORT gettrade --trade-id=$OFFER_ID"
printdate "BOB CLI: $CMD"
GETTRADE_CMD_OUTPUT=$(gettrade "$CMD")
exitoncommandalert $?
echo "$GETTRADE_CMD_OUTPUT"
printbreak
TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT")
exitoncommandalert $?
IS_TRADE_DEPOSIT_PUBLISHED=$(istradedepositpublished "$TRADE_DETAIL")
exitoncommandalert $?
printdate "BOB $BOB_ROLE: Has taker's trade deposit been published? $IS_TRADE_DEPOSIT_PUBLISHED"
if [ "$IS_TRADE_DEPOSIT_PUBLISHED" = "YES" ]
then
DONE=1
else
RANDOM_WAIT=$(echo $[$RANDOM % 3 + 1])
sleeptraced "$RANDOM_WAIT"
fi
printbreak
done
}
waitfortradedepositconfirmed() {
# Loops until Bob's trade deposit is confirmed. (Bob is always the trade taker.)
OFFER_ID="$1"
DONE=0
while : ; do
if [ "$DONE" -ne 0 ]; then
break
fi
printdate "BOB $BOB_ROLE: Looking at his trade with id $OFFER_ID."
CMD="$CLI_BASE --port=$BOB_PORT gettrade --trade-id=$OFFER_ID"
printdate "BOB CLI: $CMD"
GETTRADE_CMD_OUTPUT=$(gettrade "$CMD")
exitoncommandalert $?
echo "$GETTRADE_CMD_OUTPUT"
printbreak
TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT")
exitoncommandalert $?
IS_TRADE_DEPOSIT_CONFIRMED=$(istradedepositconfirmed "$TRADE_DETAIL")
exitoncommandalert $?
printdate "BOB $BOB_ROLE: Has taker's trade deposit been confirmed? $IS_TRADE_DEPOSIT_CONFIRMED"
printbreak
if [ "$IS_TRADE_DEPOSIT_CONFIRMED" = "YES" ]
then
DONE=1
else
printdate "Generating btc block while Bob waits for trade deposit to be confirmed."
genbtcblocks 1 0
RANDOM_WAIT=$(echo $[$RANDOM % 3 + 1])
sleeptraced "$RANDOM_WAIT"
fi
done
}
waitfortradepaymentsent() {
# Loops until buyer's trade payment has been sent.
PORT="$1"
SELLER="$2"
OFFER_ID="$3"
DONE=0
while : ; do
if [ "$DONE" -ne 0 ]; then
break
fi
printdate "$SELLER: Looking at trade with id $OFFER_ID."
CMD="$CLI_BASE --port=$PORT gettrade --trade-id=$OFFER_ID"
printdate "$SELLER CLI: $CMD"
GETTRADE_CMD_OUTPUT=$(gettrade "$CMD")
exitoncommandalert $?
echo "$GETTRADE_CMD_OUTPUT"
printbreak
TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT")
exitoncommandalert $?
IS_TRADE_PAYMENT_SENT=$(istradepaymentsent "$TRADE_DETAIL")
exitoncommandalert $?
printdate "$SELLER: Has buyer's fiat payment been initiated? $IS_TRADE_PAYMENT_SENT"
if [ "$IS_TRADE_PAYMENT_SENT" = "YES" ]
then
DONE=1
else
RANDOM_WAIT=$(echo $[$RANDOM % 3 + 1])
sleeptraced "$RANDOM_WAIT"
fi
printbreak
done
}
waitfortradepaymentreceived() {
# Loops until buyer's trade payment has been received.
PORT="$1"
SELLER="$2"
OFFER_ID="$3"
DONE=0
while : ; do
if [ "$DONE" -ne 0 ]; then
break
fi
printdate "$SELLER: Looking at trade with id $OFFER_ID."
CMD="$CLI_BASE --port=$PORT gettrade --trade-id=$OFFER_ID"
printdate "$SELLER CLI: $CMD"
GETTRADE_CMD_OUTPUT=$(gettrade "$CMD")
exitoncommandalert $?
echo "$GETTRADE_CMD_OUTPUT"
printbreak
TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT")
exitoncommandalert $?
# When the seller receives a 'payment sent' message, it is assumed funds (fiat) have already been deposited.
# In a real trade, there is usually a delay between receipt of a 'payment sent' message, and the funds deposit,
# but we do not need to simulate that in this regtest script.
IS_TRADE_PAYMENT_SENT=$(istradepaymentreceived "$TRADE_DETAIL")
exitoncommandalert $?
printdate "$SELLER: Has buyer's payment been transferred to seller's fiat account? $IS_TRADE_PAYMENT_SENT"
if [ "$IS_TRADE_PAYMENT_SENT" = "YES" ]
then
DONE=1
else
RANDOM_WAIT=$(echo $[$RANDOM % 3 + 1])
sleeptraced "$RANDOM_WAIT"
fi
printbreak
done
}
delayconfirmpaymentstarted() {
# Confirm payment started after a random delay. This should be run in the background
# while the payee polls the trade status, waiting for the message before confirming
# payment has been received.
PAYER="$1"
PORT="$2"
OFFER_ID="$3"
RANDOM_WAIT=$(echo $[$RANDOM % 5 + 1])
printdate "$PAYER: Sending fiat payment sent message to seller in $RANDOM_WAIT seconds..."
sleeptraced "$RANDOM_WAIT"
CMD="$CLI_BASE --port=$PORT confirmpaymentstarted --trade-id=$OFFER_ID"
printdate "$PAYER_CLI: $CMD"
SENT_MSG=$($CMD)
commandalert $? "Could not send confirmpaymentstarted message."
# Print the confirmpaymentstarted command's console output.
printdate "$SENT_MSG"
printbreak
}
delayconfirmpaymentreceived() {
# Confirm payment received after a random delay. This should be run in the background
# while the payer polls the trade status, waiting for the confirmation from the seller
# that funds have been received.
PAYEE="$1"
PORT="$2"
OFFER_ID="$3"
RANDOM_WAIT=$(echo $[$RANDOM % 5 + 1])
printdate "$PAYEE: Sending fiat payment sent message to seller in $RANDOM_WAIT seconds..."
sleeptraced "$RANDOM_WAIT"
CMD="$CLI_BASE --port=$PORT confirmpaymentreceived --trade-id=$OFFER_ID"
printdate "$PAYEE_CLI: $CMD"
RCVD_MSG=$($CMD)
commandalert $? "Could not send confirmpaymentstarted message."
# Print the confirmpaymentstarted command's console output.
printdate "$RCVD_MSG"
printbreak
}
# This is a large function that should be broken up if it ever makes sense to not treat a trade
# execution simulation as an atomic operation. But we are not testing api methods here, just
# demonstrating how to use them to get through the trade protocol. It should work for any trade
# between Bob & Alice, as long as Alice is maker, Bob is taker, and the offer to be taken is the
# first displayed in Bob's getoffers command output.
executetrade() {
# Bob list available offers.
printdate "BOB $BOB_ROLE: Looking at $DIRECTION $CURRENCY_CODE offers."
CMD="$CLI_BASE --port=$BOB_PORT getoffers --direction=$DIRECTION --currency-code=$CURRENCY_CODE"
printdate "BOB CLI: $CMD"
OFFERS=$($CMD)
exitoncommandalert $?
echo "$OFFERS"
printbreak
OFFER_ID=$(getfirstofferid "$BOB_PORT")
exitoncommandalert $?
printdate "First offer found: $OFFER_ID"
# Take Alice's offer.
CMD="$CLI_BASE --port=$BOB_PORT takeoffer --offer-id=$OFFER_ID --payment-account=$BOB_ACCT_ID --fee-currency=bsq"
printdate "BOB CLI: $CMD"
TRADE=$($CMD)
commandalert $? "Could not take offer."
# Print the takeoffer command's console output.
printdate "$TRADE"
printbreak
waitfortradedepositpublished "$OFFER_ID"
waitfortradedepositconfirmed "$OFFER_ID"
# Send payment sent and received messages.
if [ "$DIRECTION" = "BUY" ]
then
PAYER="ALICE $ALICE_ROLE"
PAYER_PORT=$ALICE_PORT
PAYER_CLI="ALICE CLI"
PAYEE="BOB $BOB_ROLE"
PAYEE_PORT=$BOB_PORT
PAYEE_CLI="BOB CLI"
else
PAYER="BOB $BOB_ROLE"
PAYER_PORT=$BOB_PORT
PAYER_CLI="BOB CLI"
PAYEE="ALICE $ALICE_ROLE"
PAYEE_PORT=$ALICE_PORT
PAYEE_CLI="ALICE CLI"
fi
# Asynchronously send a confirm payment started message after a random delay.
delayconfirmpaymentstarted "$PAYER" "$PAYER_PORT" "$OFFER_ID" &
if [ "$DIRECTION" = "BUY" ]
then
# Bob waits for payment, polling status in taker specific trade detail.
waitfortradepaymentsent "$PAYEE_PORT" "$PAYEE" "$OFFER_ID"
else
# Alice waits for payment, polling status in maker specific trade detail.
waitfortradepaymentsent "$PAYEE_PORT" "$PAYEE" "$OFFER_ID"
fi
# Asynchronously send a confirm payment received message after a random delay.
delayconfirmpaymentreceived "$PAYEE" "$PAYEE_PORT" "$OFFER_ID" &
if [ "$DIRECTION" = "BUY" ]
then
# Alice waits for payment rcvd confirm from Bob, polling status in maker specific trade detail.
waitfortradepaymentreceived "$PAYER_PORT" "$PAYER" "$OFFER_ID"
else
# Bob waits for payment rcvd confirm from Alice, polling status in taker specific trade detail.
waitfortradepaymentreceived "$PAYER_PORT" "$PAYER" "$OFFER_ID"
fi
# Generate some btc blocks
printdate "Generating btc blocks after fiat transfer."
genbtcblocks 2 2
printbreak
# Complete the trade on the seller side.
if [ "$DIRECTION" = "BUY" ]
then
printdate "BOB $BOB_ROLE: Closing trade by keeping funds in Bisq wallet."
CMD="$CLI_BASE --port=$BOB_PORT keepfunds --trade-id=$OFFER_ID"
printdate "BOB CLI: $CMD"
else
printdate "ALICE (taker): Closing trade by keeping funds in Bisq wallet."
CMD="$CLI_BASE --port=$ALICE_PORT keepfunds --trade-id=$OFFER_ID"
printdate "ALICE CLI: $CMD"
fi
KEEP_FUNDS_MSG=$($CMD)
commandalert $? "Could close trade with keepfunds command."
# Print the keepfunds command's console output.
printdate "$KEEP_FUNDS_MSG"
sleeptraced 3
printbreak
printdate "Trade $OFFER_ID complete."
}
getcurrentprice() {
PORT="$1"
CURRENCY_CODE="$2"
CMD="$CLI_BASE --port=$PORT getbtcprice --currency-code=$CURRENCY_CODE"
CMD_OUTPUT=$($CMD)
commandalert $? "Could not get current market $CURRENCY_CODE price."
FLOOR=$(echo "$CMD_OUTPUT" | cut -d'.' -f 1)
commandalert $? "Could not get the floor of the current market $CURRENCY_CODE price."
INTEGER=$(echo "$FLOOR" | tr -cd '[[:digit:]]')
commandalert $? "Could not convert the current market $CURRENCY_CODE price string to an integer."
echo "$INTEGER"
}

View file

@ -0,0 +1,126 @@
#! /bin/bash
# Runs fiat <-> btc trading scenarios using the API CLI with a local regtest bitcoin node.
#
# A country code argument is used to create a country based face to face payment account for the simulated
# trade, and the maker's face to face payment account's currency code is used when creating the offer.
#
# Prerequisites:
#
# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10), and bitcoin-core (v0.19, v0.20, or v0.21).
#
# - 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`
#
# - Only regtest btc can be bought or sold with the test payment account.
#
# Usage:
#
# This script must be run from the root of the project, e.g.:
#
# `$ apitest/scripts/trade-simulation.sh -d buy -c fr -m 3.00 -a 0.125`
#
# Script options: -d <direction> -c <country-code> -m <mkt-price-margin(%)> - f <fixed-price> -a <amount(btc)>
#
# Examples:
#
# Create a buy/eur offer to buy 0.125 btc at a mkt-price-margin of 0%, using an Italy face to face payment account:
#
# `$ apitest/scripts/trade-simulation.sh -d buy -c it -m 0.00 -a 0.125`
#
# Create a sell/eur offer to sell 0.125 btc at a fixed-price of 38,000 euros, using a France face to face
# payment account:
#
# `$ apitest/scripts/trade-simulation.sh -d sell -c fr -f 38000 -a 0.125`
export APP_BASE_NAME=$(basename "$0")
export APP_HOME=$(pwd -P)
export APITEST_SCRIPTS_HOME="$APP_HOME/apitest/scripts"
source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh"
source "$APITEST_SCRIPTS_HOME/trade-simulation-utils.sh"
checksetup
parseopts "$@"
printdate "Started $APP_BASE_NAME with parameters:"
printscriptparams
printbreak
registerdisputeagents
# Demonstrate how to create a country based, face to face account.
showcreatepaymentacctsteps "Alice" "$ALICE_PORT"
CMD="$CLI_BASE --port=$ALICE_PORT createpaymentacct --payment-account-form=$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printdate "ALICE CLI: $CMD"
CMD_OUTPUT=$(createpaymentacct "$CMD")
echo "$CMD_OUTPUT"
printbreak
export ALICE_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
export CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
printdate "Alice's F2F payment-account-id: $ALICE_ACCT_ID, currency-code: $CURRENCY_CODE"
exitoncommandalert $?
printbreak
printdate "Bob creates his F2F payment account."
CMD="$CLI_BASE --port=$BOB_PORT createpaymentacct --payment-account-form=$APITEST_SCRIPTS_HOME/$F2F_ACCT_FORM"
printdate "BOB CLI: $CMD"
CMD_OUTPUT=$(createpaymentacct "$CMD")
echo "$CMD_OUTPUT"
printbreak
export BOB_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
export CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
printdate "Bob's F2F payment-account-id: $BOB_ACCT_ID, currency-code: $CURRENCY_CODE"
exitoncommandalert $?
printbreak
# Alice creates an offer.
printdate "ALICE $ALICE_ROLE: Creating $DIRECTION $CURRENCY_CODE offer with payment acct $ALICE_ACCT_ID."
CURRENT_PRICE=$(getcurrentprice "$ALICE_PORT" "$CURRENCY_CODE")
exitoncommandalert $?
printdate "Current Market Price: $CURRENT_PRICE"
CMD=$(gencreateoffercommand "$ALICE_PORT" "$ALICE_ACCT_ID")
printdate "ALICE CLI: $CMD"
OFFER_ID=$(createoffer "$CMD")
exitoncommandalert $?
printdate "ALICE $ALICE_ROLE: Created offer with id: $OFFER_ID."
printbreak
sleeptraced 3
# Show Alice's new offer.
printdate "ALICE $ALICE_ROLE: Looking at her new $DIRECTION $CURRENCY_CODE offer."
CMD="$CLI_BASE --port=$ALICE_PORT getmyoffer --offer-id=$OFFER_ID"
printdate "ALICE CLI: $CMD"
OFFER=$($CMD)
exitoncommandalert $?
echo "$OFFER"
printbreak
sleeptraced 3
# Generate some btc blocks.
printdate "Generating btc blocks after publishing Alice's offer."
genbtcblocks 3 1
printbreak
# Go through the trade protocol.
executetrade
exitoncommandalert $?
printbreak
# Get balances after trade completion.
printdate "Bob & Alice's balances after trade:"
printdate "ALICE CLI:"
printbalances "$ALICE_PORT"
printbreak
printdate "BOB CLI:"
printbalances "$BOB_PORT"
printbreak
exit 0

View file

@ -0,0 +1,5 @@
#!/bin/bash
# Bats helper script for parsing current version from Version.java.
export CURRENT_VERSION=$(grep "String VERSION =" common/src/main/java/bisq/common/app/Version.java | sed 's/[^0-9.]*//g')