mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 15:00:30 +01:00
Merge remote-tracking branch 'bisq-network/hotfix/v1.5.6' into upgrade-javafax-14
This commit is contained in:
commit
7c2b4e02b8
91 changed files with 1887 additions and 749 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -34,3 +34,4 @@ deploy
|
|||
/monitor/monitor-tor/*
|
||||
.java-version
|
||||
.localnet
|
||||
/apitest/src/main/resources/dao-setup*
|
||||
|
|
|
@ -34,57 +34,58 @@
|
|||
#
|
||||
# `$ 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"
|
||||
APITEST_SCRIPTS_HOME="$APP_HOME/apitest/scripts"
|
||||
|
||||
source "${APITEST_SCRIPTS_HOME}/trade-simulation-env.sh"
|
||||
source "${APITEST_SCRIPTS_HOME}/trade-simulation-utils.sh"
|
||||
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:"
|
||||
printdate "Started $APP_BASE_NAME with parameters:"
|
||||
printscriptparams
|
||||
printbreak
|
||||
|
||||
editpaymentaccountform "$COUNTRY_CODE"
|
||||
exitoncommandalert $?
|
||||
cat "${APITEST_SCRIPTS_HOME}/${F2F_ACCT_FORM}"
|
||||
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}")
|
||||
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}")
|
||||
echo "$CMD_OUTPUT"
|
||||
ALICE_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
|
||||
exitoncommandalert $?
|
||||
CURRENCY_CODE=$(getnewpaymentacctcurrency "${CMD_OUTPUT}")
|
||||
CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
|
||||
exitoncommandalert $?
|
||||
printdate "ALICE F2F payment-account-id = ${ALICE_ACCT_ID}, currency-code = ${CURRENCY_CODE}."
|
||||
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}")
|
||||
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}")
|
||||
echo "$CMD_OUTPUT"
|
||||
BOB_ACCT_ID=$(getnewpaymentacctid "$CMD_OUTPUT")
|
||||
exitoncommandalert $?
|
||||
CURRENCY_CODE=$(getnewpaymentacctcurrency "${CMD_OUTPUT}")
|
||||
CURRENCY_CODE=$(getnewpaymentacctcurrency "$CMD_OUTPUT")
|
||||
exitoncommandalert $?
|
||||
printdate "BOB F2F payment-account-id = ${BOB_ACCT_ID}, currency-code = ${CURRENCY_CODE}."
|
||||
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}."
|
||||
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}."
|
||||
printdate "Create a SELL / $CURRENCY_CODE offer when the market price rises to or above $LIMIT_PRICE $CURRENCY_CODE."
|
||||
fi
|
||||
|
||||
DONE=0
|
||||
|
@ -112,48 +113,47 @@ while : ; do
|
|||
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}"
|
||||
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}"
|
||||
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}")
|
||||
printdate "ALICE CLI: $CMD"
|
||||
OFFER_ID=$(createoffer "$CMD")
|
||||
exitoncommandalert $?
|
||||
printdate "ALICE: Created offer with id: ${OFFER_ID}."
|
||||
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}"
|
||||
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}"
|
||||
echo "$OFFER"
|
||||
printbreak
|
||||
sleeptraced 7
|
||||
sleeptraced 4
|
||||
|
||||
# Generate some btc blocks.
|
||||
printdate "Generating btc blocks after publishing Alice's offer."
|
||||
genbtcblocks 3 3
|
||||
printbreak
|
||||
sleeptraced 5
|
||||
|
||||
# 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}"
|
||||
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}"
|
||||
echo "$OFFERS"
|
||||
|
||||
exit 0
|
||||
|
|
119
apitest/scripts/rolling-offer-simulation.sh
Executable file
119
apitest/scripts/rolling-offer-simulation.sh
Executable 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).
|
||||
#
|
||||
# - 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
|
|
@ -8,60 +8,60 @@ 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, arbitration node, and Bob & Alice daemons."
|
||||
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"
|
||||
echo ""
|
||||
echo "Register dispute agents in the arbitration daemon after it initializes."
|
||||
echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=mediator \
|
||||
--registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
|
||||
echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=refundagent \
|
||||
--registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
|
||||
exit 1;
|
||||
}
|
||||
printdate "Checking ${APP_HOME} for some expected directories and files."
|
||||
if [ -d "${APP_HOME}/apitest" ]; then
|
||||
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
|
||||
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."
|
||||
printdate "Checking to see local bitcoind is running, and bitcoin-cli is in PATH."
|
||||
checkbitcoindrunning
|
||||
checkbitcoincliinpath
|
||||
printdate "Checking to see bisq servers are running."
|
||||
if pgrep -f "bisq.seednode.SeedNodeMain" > /dev/null ; then
|
||||
printdate "The seednode is running on host."
|
||||
else
|
||||
printdate "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
|
||||
printdate "The arbitration node is running on host."
|
||||
else
|
||||
printdate "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
|
||||
printdate "Alice's daemon node is running on host."
|
||||
else
|
||||
printdate "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
|
||||
printdate "Bob's daemon node is running on host."
|
||||
else
|
||||
printdate "Error: Bob's daemon node is not running on host, exiting."
|
||||
apitestusage
|
||||
fi
|
||||
checkseednoderunning
|
||||
checkarbnoderunning
|
||||
checkalicenoderunning
|
||||
checkbobnoderunning
|
||||
}
|
||||
|
||||
parseopts() {
|
||||
|
@ -181,12 +181,12 @@ parselimitorderopts() {
|
|||
|
||||
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}")
|
||||
if [[ $APP_HOME == *"+"* ]]; then
|
||||
ESCAPED_APP_HOME=$(escapepluschar "$APP_HOME")
|
||||
else
|
||||
ESCAPED_APP_HOME="${APP_HOME}"
|
||||
ESCAPED_APP_HOME="$APP_HOME"
|
||||
fi
|
||||
if pgrep -f "bitcoind -datadir=${ESCAPED_APP_HOME}/apitest/build/resources/main/Bitcoin-regtest" > /dev/null ; then
|
||||
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."
|
||||
|
@ -194,26 +194,119 @@ checkbitcoindrunning() {
|
|||
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}"
|
||||
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}"
|
||||
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}"
|
||||
echo " BOB_ROLE = $BOB_ROLE"
|
||||
fi
|
||||
|
||||
if [ -n "${ALICE_ROLE+1}" ]; then
|
||||
echo " ALICE_ROLE = ${ALICE_ROLE}"
|
||||
echo " ALICE_ROLE = $ALICE_ROLE"
|
||||
fi
|
||||
|
||||
if [ -n "${WAIT+1}" ]; then
|
||||
echo " WAIT = ${WAIT}"
|
||||
echo " WAIT = $WAIT"
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
# This file must be sourced by the main driver.
|
||||
|
||||
source "${APITEST_SCRIPTS_HOME}/trade-simulation-env.sh"
|
||||
source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh"
|
||||
|
||||
printdate() {
|
||||
echo "[$(date)] $@"
|
||||
|
@ -46,10 +46,10 @@ exitoncommandalert() {
|
|||
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}"
|
||||
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}"
|
||||
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.
|
||||
|
@ -59,7 +59,7 @@ registerdisputeagents() {
|
|||
getbtcoreaddress() {
|
||||
CMD="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest getnewaddress"
|
||||
NEW_ADDRESS=$($CMD)
|
||||
echo "${NEW_ADDRESS}"
|
||||
echo "$NEW_ADDRESS"
|
||||
}
|
||||
|
||||
genbtcblocks() {
|
||||
|
@ -74,7 +74,7 @@ genbtcblocks() {
|
|||
for i in $(seq -f "%02g" 1 "$NUM_BLOCKS")
|
||||
do
|
||||
NEW_BLOCK_HASH=$(genbtcblock "$CMD")
|
||||
printdate "Block Hash #$i:${NEW_BLOCK_HASH}"
|
||||
printdate "Block Hash #$i:$NEW_BLOCK_HASH"
|
||||
sleep "$SECONDS_BETWEEN_BLOCKS"
|
||||
done
|
||||
}
|
||||
|
@ -88,12 +88,12 @@ genbtcblock() {
|
|||
escapepluschar() {
|
||||
STRING="$1"
|
||||
NEW_STRING=$(echo "${STRING//+/\\+}")
|
||||
echo "${NEW_STRING}"
|
||||
echo "$NEW_STRING"
|
||||
}
|
||||
|
||||
printbalances() {
|
||||
PORT="$1"
|
||||
printcmd "${CLI_BASE} --port=${PORT} getbalance"
|
||||
printcmd "$CLI_BASE --port=$PORT getbalance"
|
||||
$CLI_BASE --port="$PORT" getbalance
|
||||
}
|
||||
|
||||
|
@ -102,54 +102,99 @@ getpaymentaccountmethods() {
|
|||
CMD_OUTPUT=$($CMD)
|
||||
commandalert $? "Could not get payment method ids."
|
||||
printdate "Payment Method IDs:"
|
||||
echo "${CMD_OUTPUT}"
|
||||
echo "$CMD_OUTPUT"
|
||||
}
|
||||
|
||||
getpaymentaccountform() {
|
||||
CMD="$1"
|
||||
CMD_OUTPUT=$($CMD)
|
||||
commandalert $? "Could not get new payment account form."
|
||||
echo "${CMD_OUTPUT}"
|
||||
echo "$CMD_OUTPUT"
|
||||
}
|
||||
|
||||
editpaymentaccountform() {
|
||||
COUNTRY_CODE="$1"
|
||||
CMD="python3 ${APITEST_SCRIPTS_HOME}/editf2faccountform.py $COUNTRY_CODE"
|
||||
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}."
|
||||
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')
|
||||
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}"
|
||||
echo "$ACCT_ID"
|
||||
}
|
||||
|
||||
getnewpaymentacctcurrency() {
|
||||
CREATE_PAYMENT_ACCT_OUTPUT="$1"
|
||||
PAYMENT_ACCT_DETAIL=$(echo -e "${CREATE_PAYMENT_ACCT_OUTPUT}" | sed -n '3p')
|
||||
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}"
|
||||
echo "$CURRENCY_CODE"
|
||||
}
|
||||
|
||||
createpaymentacct() {
|
||||
CMD="$1"
|
||||
CMD_OUTPUT=$($CMD)
|
||||
commandalert $? "Could not create new payment account."
|
||||
echo "${CMD_OUTPUT}"
|
||||
echo "$CMD_OUTPUT"
|
||||
}
|
||||
|
||||
getpaymentaccounts() {
|
||||
PORT="$1"
|
||||
printcmd "${CLI_BASE} --port=${PORT} getpaymentaccts"
|
||||
printcmd "$CLI_BASE --port=$PORT getpaymentaccts"
|
||||
CMD="$CLI_BASE --port=$PORT getpaymentaccts"
|
||||
CMD_OUTPUT=$($CMD)
|
||||
commandalert $? "Could not get payment accounts."
|
||||
echo "${CMD_OUTPUT}"
|
||||
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() {
|
||||
|
@ -160,9 +205,388 @@ createoffer() {
|
|||
# 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}"
|
||||
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"
|
||||
MAKER_OR_TAKER="$2"
|
||||
if [ "$MAKER_OR_TAKER" = "MAKER" ]
|
||||
then
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $9}')
|
||||
else
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $10}')
|
||||
fi
|
||||
commandalert $? "Could not parse istradedepositpublished from trade detail."
|
||||
echo "$ANSWER"
|
||||
}
|
||||
|
||||
istradedepositconfirmed() {
|
||||
TRADE_DETAIL="$1"
|
||||
MAKER_OR_TAKER="$2"
|
||||
if [ "$MAKER_OR_TAKER" = "MAKER" ]
|
||||
then
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $10}')
|
||||
else
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $11}')
|
||||
fi
|
||||
commandalert $? "Could not parse istradedepositconfirmed from trade detail."
|
||||
echo "$ANSWER"
|
||||
}
|
||||
|
||||
istradepaymentsent() {
|
||||
TRADE_DETAIL="$1"
|
||||
MAKER_OR_TAKER="$2"
|
||||
if [ "$MAKER_OR_TAKER" = "MAKER" ]
|
||||
then
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $11}')
|
||||
else
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $12}')
|
||||
fi
|
||||
commandalert $? "Could not parse istradepaymentsent from trade detail."
|
||||
echo "$ANSWER"
|
||||
}
|
||||
|
||||
istradepaymentreceived() {
|
||||
TRADE_DETAIL="$1"
|
||||
MAKER_OR_TAKER="$2"
|
||||
if [ "$MAKER_OR_TAKER" = "MAKER" ]
|
||||
then
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $12}')
|
||||
else
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $13}')
|
||||
fi
|
||||
commandalert $? "Could not parse istradepaymentreceived from trade detail."
|
||||
echo "$ANSWER"
|
||||
}
|
||||
|
||||
istradepayoutpublished() {
|
||||
TRADE_DETAIL="$1"
|
||||
MAKER_OR_TAKER="$2"
|
||||
if [ "$MAKER_OR_TAKER" = "MAKER" ]
|
||||
then
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $13}')
|
||||
else
|
||||
ANSWER=$(echo "$TRADE_DETAIL" | awk '{print $14}')
|
||||
fi
|
||||
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" "TAKER")
|
||||
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" "TAKER")
|
||||
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"
|
||||
MAKER_OR_TAKER="$4"
|
||||
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" "$MAKER_OR_TAKER")
|
||||
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"
|
||||
MAKER_OR_TAKER="$4"
|
||||
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" "$MAKER_OR_TAKER")
|
||||
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" "TAKER"
|
||||
else
|
||||
# Alice waits for payment, polling status in maker specific trade detail.
|
||||
waitfortradepaymentsent "$PAYEE_PORT" "$PAYEE" "$OFFER_ID" "MAKER"
|
||||
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" "MAKER"
|
||||
else
|
||||
# Bob waits for payment rcvd confirm from Alice, polling status in taker specific trade detail.
|
||||
waitfortradepaymentreceived "$PAYER_PORT" "$PAYER" "$OFFER_ID" "TAKER"
|
||||
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() {
|
||||
|
|
|
@ -41,191 +41,77 @@
|
|||
|
||||
export APP_BASE_NAME=$(basename "$0")
|
||||
export APP_HOME=$(pwd -P)
|
||||
export APITEST_SCRIPTS_HOME="${APP_HOME}/apitest/scripts"
|
||||
export APITEST_SCRIPTS_HOME="$APP_HOME/apitest/scripts"
|
||||
|
||||
source "${APITEST_SCRIPTS_HOME}/trade-simulation-env.sh"
|
||||
source "${APITEST_SCRIPTS_HOME}/trade-simulation-utils.sh"
|
||||
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:"
|
||||
printdate "Started $APP_BASE_NAME with parameters:"
|
||||
printscriptparams
|
||||
printbreak
|
||||
|
||||
registerdisputeagents
|
||||
|
||||
printdate "Alice looks for the ID of the face to face payment account method (Bob will use same payment method)."
|
||||
CMD="${CLI_BASE} --port=${ALICE_PORT} getpaymentmethods"
|
||||
printdate "ALICE CLI: ${CMD}"
|
||||
getpaymentaccountmethods "$CMD"
|
||||
# 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 "Alice uses the F2F payment method id to create a face to face payment account in country ${COUNTRY_CODE}."
|
||||
CMD="${CLI_BASE} --port=${ALICE_PORT} getpaymentacctform --payment-method-id=F2F"
|
||||
printdate "ALICE CLI: ${CMD}"
|
||||
getpaymentaccountform "$CMD"
|
||||
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
|
||||
|
||||
printdate "Bob & Alice edit their ${COUNTRY_CODE} payment account forms, and renames them to ${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
|
||||
|
||||
printdate "Bob and Alice create their face to face ${COUNTRY_CODE} payment accounts."
|
||||
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}"
|
||||
BOB_ACCT_ID=$(getnewpaymentacctid "${CMD_OUTPUT}")
|
||||
BOB_ACCT_CURRENCY_CODE=$(getnewpaymentacctcurrency "${CMD_OUTPUT}")
|
||||
printdate "BOB F2F payment-account-id = ${BOB_ACCT_ID}, currency-code = ${BOB_ACCT_CURRENCY_CODE}."
|
||||
printbreak
|
||||
|
||||
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}"
|
||||
ALICE_ACCT_ID=$(getnewpaymentacctid "${CMD_OUTPUT}")
|
||||
ALICE_ACCT_CURRENCY_CODE=$(getnewpaymentacctcurrency "${CMD_OUTPUT}")
|
||||
printdate "ALICE F2F payment-account-id = ${ALICE_ACCT_ID}, currency-code = ${ALICE_ACCT_CURRENCY_CODE}."
|
||||
printbreak
|
||||
|
||||
printdate "ALICE ${ALICE_ROLE}: Creating ${DIRECTION} ${ALICE_ACCT_CURRENCY_CODE} offer with payment acct ${ALICE_ACCT_ID}."
|
||||
CURRENT_PRICE=$(getcurrentprice "$ALICE_PORT" "$ALICE_ACCT_CURRENCY_CODE")
|
||||
# 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="$CLI_BASE --port=${ALICE_PORT} createoffer"
|
||||
CMD+=" --payment-account=${ALICE_ACCT_ID}"
|
||||
CMD+=" --direction=${DIRECTION}"
|
||||
CMD+=" --currency-code=${ALICE_ACCT_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"
|
||||
printdate "ALICE CLI: ${CMD}"
|
||||
OFFER_ID=$(createoffer "${CMD}")
|
||||
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}."
|
||||
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}"
|
||||
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}"
|
||||
echo "$OFFER"
|
||||
printbreak
|
||||
sleeptraced 7
|
||||
sleeptraced 3
|
||||
|
||||
# Generate some btc blocks.
|
||||
printdate "Generating btc blocks after publishing Alice's offer."
|
||||
genbtcblocks 3 5
|
||||
genbtcblocks 3 1
|
||||
printbreak
|
||||
sleeptraced 10
|
||||
|
||||
# List offers.
|
||||
printdate "BOB ${BOB_ROLE}: Looking at ${DIRECTION} ${BOB_ACCT_CURRENCY_CODE} offers."
|
||||
CMD="$CLI_BASE --port=${BOB_PORT} getoffers --direction=${DIRECTION} --currency-code=${BOB_ACCT_CURRENCY_CODE}"
|
||||
printdate "BOB CLI: ${CMD}"
|
||||
OFFERS=$($CMD)
|
||||
# Go through the trade protocol.
|
||||
executetrade
|
||||
exitoncommandalert $?
|
||||
echo "${OFFERS}"
|
||||
printbreak
|
||||
sleeptraced 3
|
||||
|
||||
# Take offer.
|
||||
printdate "BOB ${BOB_ROLE}: Taking offer ${OFFER_ID} with payment acct ${BOB_ACCT_ID}."
|
||||
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."
|
||||
|
||||
echo "${TRADE}"
|
||||
printbreak
|
||||
sleeptraced 10
|
||||
|
||||
# Generating some btc blocks
|
||||
printdate "Generating btc blocks after Bob takes Alice's offer."
|
||||
genbtcblocks 3 3
|
||||
printbreak
|
||||
sleeptraced 6
|
||||
|
||||
# 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
|
||||
|
||||
# Confirm payment started.
|
||||
printdate "${PAYER}: Sending fiat payment sent msg."
|
||||
CMD="$CLI_BASE --port=${PAYER_PORT} confirmpaymentstarted --trade-id=${OFFER_ID}"
|
||||
printdate "${PAYER_CLI}: ${CMD}"
|
||||
SENT_MSG=$($CMD)
|
||||
commandalert $? "Could not send confirmpaymentstarted message."
|
||||
|
||||
printdate "${SENT_MSG}"
|
||||
printbreak
|
||||
|
||||
sleeptraced 2
|
||||
printdate "Generating btc blocks after fiat payment sent msg."
|
||||
genbtcblocks 3 5
|
||||
sleeptraced 2
|
||||
|
||||
# Confirm payment received.
|
||||
printdate "${PAYEE}: Sending fiat payment received msg."
|
||||
CMD="$CLI_BASE --port=${PAYEE_PORT} confirmpaymentreceived --trade-id=${OFFER_ID}"
|
||||
printdate "${PAYEE_CLI}: ${CMD}"
|
||||
RCVD_MSG=$($CMD)
|
||||
commandalert $? "Could not send confirmpaymentreceived message."
|
||||
printdate "${RCVD_MSG}"
|
||||
printbreak
|
||||
sleeptraced 4
|
||||
|
||||
# Generate some btc blocks
|
||||
printdate "Generating btc blocks after fiat transfer."
|
||||
genbtcblocks 3 5
|
||||
printbreak
|
||||
sleeptraced 3
|
||||
|
||||
# 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."
|
||||
printdate "${KEEP_FUNDS_MSG}"
|
||||
sleeptraced 5
|
||||
printbreak
|
||||
|
||||
# Get balances after trade completion.
|
||||
|
|
|
@ -55,7 +55,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
|||
import bisq.apitest.config.ApiTestConfig;
|
||||
import bisq.apitest.config.BisqAppConfig;
|
||||
import bisq.apitest.linux.BashCommand;
|
||||
import bisq.apitest.linux.BisqApp;
|
||||
import bisq.apitest.linux.BisqProcess;
|
||||
import bisq.apitest.linux.BitcoinDaemon;
|
||||
import bisq.apitest.linux.LinuxProcess;
|
||||
|
||||
|
@ -367,25 +367,25 @@ public class Scaffold {
|
|||
CountDownLatch countdownLatch)
|
||||
throws IOException, InterruptedException {
|
||||
|
||||
BisqApp bisqApp = createBisqApp(bisqAppConfig);
|
||||
BisqProcess bisqProcess = createBisqProcess(bisqAppConfig);
|
||||
switch (bisqAppConfig) {
|
||||
case seednode:
|
||||
seedNodeTask = new SetupTask(bisqApp, countdownLatch);
|
||||
seedNodeTask = new SetupTask(bisqProcess, countdownLatch);
|
||||
seedNodeTaskFuture = executor.submit(seedNodeTask);
|
||||
break;
|
||||
case arbdaemon:
|
||||
case arbdesktop:
|
||||
arbNodeTask = new SetupTask(bisqApp, countdownLatch);
|
||||
arbNodeTask = new SetupTask(bisqProcess, countdownLatch);
|
||||
arbNodeTaskFuture = executor.submit(arbNodeTask);
|
||||
break;
|
||||
case alicedaemon:
|
||||
case alicedesktop:
|
||||
aliceNodeTask = new SetupTask(bisqApp, countdownLatch);
|
||||
aliceNodeTask = new SetupTask(bisqProcess, countdownLatch);
|
||||
aliceNodeTaskFuture = executor.submit(aliceNodeTask);
|
||||
break;
|
||||
case bobdaemon:
|
||||
case bobdesktop:
|
||||
bobNodeTask = new SetupTask(bisqApp, countdownLatch);
|
||||
bobNodeTask = new SetupTask(bisqProcess, countdownLatch);
|
||||
bobNodeTaskFuture = executor.submit(bobNodeTask);
|
||||
break;
|
||||
default:
|
||||
|
@ -393,18 +393,18 @@ public class Scaffold {
|
|||
}
|
||||
log.info("Giving {} ms for {} to initialize ...", config.bisqAppInitTime, bisqAppConfig.appName);
|
||||
MILLISECONDS.sleep(config.bisqAppInitTime);
|
||||
if (bisqApp.hasStartupExceptions()) {
|
||||
bisqApp.logExceptions(bisqApp.getStartupExceptions(), log);
|
||||
throw new IllegalStateException(bisqApp.getStartupExceptions().get(0));
|
||||
if (bisqProcess.hasStartupExceptions()) {
|
||||
bisqProcess.logExceptions(bisqProcess.getStartupExceptions(), log);
|
||||
throw new IllegalStateException(bisqProcess.getStartupExceptions().get(0));
|
||||
}
|
||||
}
|
||||
|
||||
private BisqApp createBisqApp(BisqAppConfig bisqAppConfig)
|
||||
private BisqProcess createBisqProcess(BisqAppConfig bisqAppConfig)
|
||||
throws IOException, InterruptedException {
|
||||
BisqApp bisqNode = new BisqApp(bisqAppConfig, config);
|
||||
bisqNode.verifyAppNotRunning();
|
||||
bisqNode.verifyAppDataDirInstalled();
|
||||
return bisqNode;
|
||||
BisqProcess bisqProcess = new BisqProcess(bisqAppConfig, config);
|
||||
bisqProcess.verifyAppNotRunning();
|
||||
bisqProcess.verifyAppDataDirInstalled();
|
||||
return bisqProcess;
|
||||
}
|
||||
|
||||
private void verifyStartupCompleted()
|
||||
|
|
|
@ -41,7 +41,7 @@ import bisq.daemon.app.BisqDaemonMain;
|
|||
* Runs a regtest/dao Bisq application instance in the background.
|
||||
*/
|
||||
@Slf4j
|
||||
public class BisqApp extends AbstractLinuxProcess implements LinuxProcess {
|
||||
public class BisqProcess extends AbstractLinuxProcess implements LinuxProcess {
|
||||
|
||||
private final BisqAppConfig bisqAppConfig;
|
||||
private final String baseCurrencyNetwork;
|
||||
|
@ -55,7 +55,7 @@ public class BisqApp extends AbstractLinuxProcess implements LinuxProcess {
|
|||
private final String findBisqPidScript;
|
||||
private final String debugOpts;
|
||||
|
||||
public BisqApp(BisqAppConfig bisqAppConfig, ApiTestConfig config) {
|
||||
public BisqProcess(BisqAppConfig bisqAppConfig, ApiTestConfig config) {
|
||||
super(bisqAppConfig.appName, config);
|
||||
this.bisqAppConfig = bisqAppConfig;
|
||||
this.baseCurrencyNetwork = "BTC_REGTEST";
|
|
@ -392,7 +392,7 @@ configure(project(':desktop')) {
|
|||
apply from: '../gradle/witness/gradle-witness.gradle'
|
||||
apply from: 'package/package.gradle'
|
||||
|
||||
version = '1.5.5-SNAPSHOT'
|
||||
version = '1.5.6-SNAPSHOT'
|
||||
|
||||
jar.manifest.attributes(
|
||||
"Implementation-Title": project.name,
|
||||
|
|
|
@ -30,7 +30,7 @@ public class Version {
|
|||
// VERSION = 0.5.0 introduces proto buffer for the P2P network and local DB and is a not backward compatible update
|
||||
// Therefore all sub versions start again with 1
|
||||
// We use semantic versioning with major, minor and patch
|
||||
public static final String VERSION = "1.5.5";
|
||||
public static final String VERSION = "1.5.6";
|
||||
|
||||
/**
|
||||
* Holds a list of the tagged resource files for optimizing the getData requests.
|
||||
|
|
|
@ -94,7 +94,7 @@ class DesktopUtil {
|
|||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("Error running command. {}", e);
|
||||
log.warn("Error running command. {}", e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
|
||||
package bisq.core.alert;
|
||||
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.network.p2p.storage.payload.ExpirablePayload;
|
||||
import bisq.network.p2p.storage.payload.ProtectedStoragePayload;
|
||||
|
||||
|
@ -51,6 +53,7 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
|
|||
|
||||
private final String message;
|
||||
private final boolean isUpdateInfo;
|
||||
private final boolean isPreReleaseInfo;
|
||||
private final String version;
|
||||
|
||||
@Nullable
|
||||
|
@ -68,9 +71,11 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
|
|||
|
||||
public Alert(String message,
|
||||
boolean isUpdateInfo,
|
||||
boolean isPreReleaseInfo,
|
||||
String version) {
|
||||
this.message = message;
|
||||
this.isUpdateInfo = isUpdateInfo;
|
||||
this.isPreReleaseInfo = isPreReleaseInfo;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
|
@ -82,12 +87,14 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
|
|||
@SuppressWarnings("NullableProblems")
|
||||
public Alert(String message,
|
||||
boolean isUpdateInfo,
|
||||
boolean isPreReleaseInfo,
|
||||
String version,
|
||||
byte[] ownerPubKeyBytes,
|
||||
String signatureAsBase64,
|
||||
Map<String, String> extraDataMap) {
|
||||
this.message = message;
|
||||
this.isUpdateInfo = isUpdateInfo;
|
||||
this.isPreReleaseInfo = isPreReleaseInfo;
|
||||
this.version = version;
|
||||
this.ownerPubKeyBytes = ownerPubKeyBytes;
|
||||
this.signatureAsBase64 = signatureAsBase64;
|
||||
|
@ -103,6 +110,7 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
|
|||
protobuf.Alert.Builder builder = protobuf.Alert.newBuilder()
|
||||
.setMessage(message)
|
||||
.setIsUpdateInfo(isUpdateInfo)
|
||||
.setIsPreReleaseInfo(isPreReleaseInfo)
|
||||
.setVersion(version)
|
||||
.setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes))
|
||||
.setSignatureAsBase64(signatureAsBase64);
|
||||
|
@ -119,6 +127,7 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
|
|||
|
||||
return new Alert(proto.getMessage(),
|
||||
proto.getIsUpdateInfo(),
|
||||
proto.getIsPreReleaseInfo(),
|
||||
proto.getVersion(),
|
||||
proto.getOwnerPubKeyBytes().toByteArray(),
|
||||
proto.getSignatureAsBase64(),
|
||||
|
@ -143,7 +152,28 @@ public final class Alert implements ProtectedStoragePayload, ExpirablePayload {
|
|||
ownerPubKeyBytes = Sig.getPublicKeyBytes(ownerPubKey);
|
||||
}
|
||||
|
||||
public boolean isNewVersion() {
|
||||
return Version.isNewVersion(version);
|
||||
public boolean isNewVersion(Preferences preferences) {
|
||||
// regular release: always notify user
|
||||
// pre-release: if user has set preference to receive pre-release notification
|
||||
if (isUpdateInfo ||
|
||||
(isPreReleaseInfo && preferences.isNotifyOnPreRelease())) {
|
||||
return Version.isNewVersion(version);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isSoftwareUpdateNotification() {
|
||||
return (isUpdateInfo || isPreReleaseInfo);
|
||||
}
|
||||
|
||||
public boolean canShowPopup(Preferences preferences) {
|
||||
// only show popup if its version is newer than current
|
||||
// and only if user has not checked "don't show again"
|
||||
return isNewVersion(preferences) && preferences.showAgain(showAgainKey());
|
||||
}
|
||||
|
||||
public String showAgainKey() {
|
||||
return "Update_" + version;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -65,6 +65,10 @@ class CoreOffersService {
|
|||
private final Supplier<Comparator<Offer>> reversePriceComparator = () -> comparing(Offer::getPrice).reversed();
|
||||
|
||||
private final KeyRing keyRing;
|
||||
// Dependencies on core api services in this package must be kept to an absolute
|
||||
// minimum, but some trading functions require an unlocked wallet's key, so an
|
||||
// exception is made in this case.
|
||||
private final CoreWalletsService coreWalletsService;
|
||||
private final CreateOfferService createOfferService;
|
||||
private final OfferBookService offerBookService;
|
||||
private final OfferFilter offerFilter;
|
||||
|
@ -76,6 +80,7 @@ class CoreOffersService {
|
|||
@Inject
|
||||
public CoreOffersService(CoreContext coreContext,
|
||||
KeyRing keyRing,
|
||||
CoreWalletsService coreWalletsService,
|
||||
CreateOfferService createOfferService,
|
||||
OfferBookService offerBookService,
|
||||
OfferFilter offerFilter,
|
||||
|
@ -83,6 +88,7 @@ class CoreOffersService {
|
|||
OfferUtil offerUtil,
|
||||
User user) {
|
||||
this.keyRing = keyRing;
|
||||
this.coreWalletsService = coreWalletsService;
|
||||
this.createOfferService = createOfferService;
|
||||
this.offerBookService = offerBookService;
|
||||
this.offerFilter = offerFilter;
|
||||
|
@ -144,7 +150,8 @@ class CoreOffersService {
|
|||
String paymentAccountId,
|
||||
String makerFeeCurrencyCode,
|
||||
Consumer<Offer> resultHandler) {
|
||||
|
||||
coreWalletsService.verifyWalletsAreAvailable();
|
||||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||
offerUtil.maybeSetFeePaymentCurrencyPreference(makerFeeCurrencyCode);
|
||||
|
||||
String upperCaseCurrencyCode = currencyCode.toUpperCase();
|
||||
|
|
|
@ -33,6 +33,7 @@ import bisq.core.dao.governance.voteresult.VoteResultException;
|
|||
import bisq.core.dao.state.unconfirmed.UnconfirmedBsqChangeOutputListService;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.payment.AmazonGiftCardAccount;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.RevolutAccount;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
|
@ -179,6 +180,9 @@ public class BisqSetup {
|
|||
private Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Consumer<List<AmazonGiftCardAccount>> amazonGiftCardAccountsUpdateHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Runnable osxKeyLoggerWarningHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
|
@ -250,20 +254,23 @@ public class BisqSetup {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void displayAlertIfPresent(Alert alert, boolean openNewVersionPopup) {
|
||||
if (alert != null) {
|
||||
if (alert.isUpdateInfo()) {
|
||||
user.setDisplayedAlert(alert);
|
||||
final boolean isNewVersion = alert.isNewVersion();
|
||||
newVersionAvailableProperty.set(isNewVersion);
|
||||
String key = "Update_" + alert.getVersion();
|
||||
if (isNewVersion && (preferences.showAgain(key) || openNewVersionPopup) && displayUpdateHandler != null) {
|
||||
displayUpdateHandler.accept(alert, key);
|
||||
if (alert == null)
|
||||
return;
|
||||
|
||||
if (alert.isSoftwareUpdateNotification()) {
|
||||
// only process if the alert version is "newer" than ours
|
||||
if (alert.isNewVersion(preferences)) {
|
||||
user.setDisplayedAlert(alert); // save context to compare later
|
||||
newVersionAvailableProperty.set(true); // shows link in footer bar
|
||||
if ((alert.canShowPopup(preferences) || openNewVersionPopup) && displayUpdateHandler != null) {
|
||||
displayUpdateHandler.accept(alert, alert.showAgainKey());
|
||||
}
|
||||
} else {
|
||||
final Alert displayedAlert = user.getDisplayedAlert();
|
||||
if ((displayedAlert == null || !displayedAlert.equals(alert)) && displayAlertHandler != null)
|
||||
displayAlertHandler.accept(alert);
|
||||
}
|
||||
} else {
|
||||
// it is a normal message alert
|
||||
final Alert displayedAlert = user.getDisplayedAlert();
|
||||
if ((displayedAlert == null || !displayedAlert.equals(alert)) && displayAlertHandler != null)
|
||||
displayAlertHandler.accept(alert);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -453,6 +460,7 @@ public class BisqSetup {
|
|||
filterWarningHandler,
|
||||
voteResultExceptionHandler,
|
||||
revolutAccountsUpdateHandler,
|
||||
amazonGiftCardAccountsUpdateHandler,
|
||||
daoRequiresRestartHandler);
|
||||
|
||||
if (walletsSetup.downloadPercentageProperty().get() == 1) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import bisq.core.notifications.alerts.market.MarketAlerts;
|
|||
import bisq.core.notifications.alerts.price.PriceAlert;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.offer.TriggerPriceService;
|
||||
import bisq.core.payment.AmazonGiftCardAccount;
|
||||
import bisq.core.payment.RevolutAccount;
|
||||
import bisq.core.payment.TradeLimits;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
|
@ -189,6 +190,7 @@ public class DomainInitialisation {
|
|||
Consumer<String> filterWarningHandler,
|
||||
Consumer<VoteResultException> voteResultExceptionHandler,
|
||||
Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler,
|
||||
Consumer<List<AmazonGiftCardAccount>> amazonGiftCardAccountsUpdateHandler,
|
||||
Runnable daoRequiresRestartHandler) {
|
||||
clockWatcher.start();
|
||||
|
||||
|
@ -242,8 +244,8 @@ public class DomainInitialisation {
|
|||
|
||||
priceFeedService.setCurrencyCodeOnInit();
|
||||
|
||||
filterManager.onAllServicesInitialized();
|
||||
filterManager.setFilterWarningHandler(filterWarningHandler);
|
||||
filterManager.onAllServicesInitialized();
|
||||
|
||||
voteResultService.getVoteResultExceptions().addListener((ListChangeListener<VoteResultException>) c -> {
|
||||
c.next();
|
||||
|
@ -267,5 +269,12 @@ public class DomainInitialisation {
|
|||
.filter(RevolutAccount::userNameNotSet)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
if (amazonGiftCardAccountsUpdateHandler != null) {
|
||||
amazonGiftCardAccountsUpdateHandler.accept(user.getPaymentAccountsAsObservable().stream()
|
||||
.filter(paymentAccount -> paymentAccount instanceof AmazonGiftCardAccount)
|
||||
.map(paymentAccount -> (AmazonGiftCardAccount) paymentAccount)
|
||||
.filter(AmazonGiftCardAccount::countryNotSet)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,9 +141,8 @@ public class TxFeeEstimationService {
|
|||
}
|
||||
|
||||
public Tuple2<Coin, Integer> getEstimatedFeeAndTxVsize(Coin amount,
|
||||
FeeService feeService,
|
||||
BtcWalletService btcWalletService) {
|
||||
Coin txFeePerVbyte = feeService.getTxFeePerVbyte();
|
||||
Coin txFeePerVbyte = btcWalletService.getTxFeeForWithdrawalPerVbyte();
|
||||
// We start with min taker fee vsize of 175
|
||||
int estimatedTxVsize = TYPICAL_TX_WITH_1_INPUT_VSIZE;
|
||||
try {
|
||||
|
|
|
@ -80,7 +80,6 @@ import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.PENDING;
|
|||
@Slf4j
|
||||
public class BsqWalletService extends WalletService implements DaoStateListener {
|
||||
|
||||
|
||||
public interface WalletTransactionsChangeListener {
|
||||
|
||||
void onWalletTransactionsChange();
|
||||
|
@ -140,42 +139,7 @@ public class BsqWalletService extends WalletService implements DaoStateListener
|
|||
wallet = walletsSetup.getBsqWallet();
|
||||
if (wallet != null) {
|
||||
wallet.setCoinSelector(bsqCoinSelector);
|
||||
|
||||
wallet.addCoinsReceivedEventListener(walletEventListener);
|
||||
wallet.addCoinsSentEventListener(walletEventListener);
|
||||
wallet.addReorganizeEventListener(walletEventListener);
|
||||
wallet.addTransactionConfidenceEventListener(walletEventListener);
|
||||
|
||||
wallet.addCoinsReceivedEventListener((wallet, tx, prevBalance, newBalance) -> {
|
||||
updateBsqWalletTransactions();
|
||||
});
|
||||
wallet.addCoinsSentEventListener((wallet, tx, prevBalance, newBalance) -> {
|
||||
updateBsqWalletTransactions();
|
||||
});
|
||||
wallet.addReorganizeEventListener(wallet -> {
|
||||
log.warn("onReorganize ");
|
||||
updateBsqWalletTransactions();
|
||||
unconfirmedBsqChangeOutputListService.onReorganize();
|
||||
});
|
||||
wallet.addTransactionConfidenceEventListener((wallet, tx) -> {
|
||||
// We are only interested in updates from unconfirmed txs and confirmed txs at the
|
||||
// time when it gets into a block. Otherwise we would get called
|
||||
// updateBsqWalletTransactions for each tx as the block depth changes for all.
|
||||
if (tx != null && tx.getConfidence() != null && tx.getConfidence().getDepthInBlocks() <= 1 &&
|
||||
daoStateService.isParseBlockChainComplete()) {
|
||||
updateBsqWalletTransactions();
|
||||
}
|
||||
unconfirmedBsqChangeOutputListService.onTransactionConfidenceChanged(tx);
|
||||
});
|
||||
wallet.addKeyChainEventListener(keys -> {
|
||||
updateBsqWalletTransactions();
|
||||
});
|
||||
wallet.addScriptsChangeEventListener((wallet, scripts, isAddingScripts) -> {
|
||||
updateBsqWalletTransactions();
|
||||
});
|
||||
wallet.addChangeEventListener(wallet -> {
|
||||
updateBsqWalletTransactions();
|
||||
});
|
||||
addListenersToWallet();
|
||||
}
|
||||
|
||||
BlockChain chain = walletsSetup.getChain();
|
||||
|
@ -188,6 +152,41 @@ public class BsqWalletService extends WalletService implements DaoStateListener
|
|||
daoStateService.addDaoStateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addListenersToWallet() {
|
||||
super.addListenersToWallet();
|
||||
|
||||
wallet.addCoinsReceivedEventListener((wallet, tx, prevBalance, newBalance) ->
|
||||
updateBsqWalletTransactions()
|
||||
);
|
||||
wallet.addCoinsSentEventListener((wallet, tx, prevBalance, newBalance) ->
|
||||
updateBsqWalletTransactions()
|
||||
);
|
||||
wallet.addReorganizeEventListener(wallet -> {
|
||||
log.warn("onReorganize ");
|
||||
updateBsqWalletTransactions();
|
||||
unconfirmedBsqChangeOutputListService.onReorganize();
|
||||
});
|
||||
wallet.addTransactionConfidenceEventListener((wallet, tx) -> {
|
||||
// We are only interested in updates from unconfirmed txs and confirmed txs at the
|
||||
// time when it gets into a block. Otherwise we would get called
|
||||
// updateBsqWalletTransactions for each tx as the block depth changes for all.
|
||||
if (tx != null && tx.getConfidence() != null && tx.getConfidence().getDepthInBlocks() <= 1 &&
|
||||
daoStateService.isParseBlockChainComplete()) {
|
||||
updateBsqWalletTransactions();
|
||||
}
|
||||
unconfirmedBsqChangeOutputListService.onTransactionConfidenceChanged(tx);
|
||||
});
|
||||
wallet.addKeyChainEventListener(keys ->
|
||||
updateBsqWalletTransactions()
|
||||
);
|
||||
wallet.addScriptsChangeEventListener((wallet, scripts, isAddingScripts) ->
|
||||
updateBsqWalletTransactions()
|
||||
);
|
||||
wallet.addChangeEventListener(wallet ->
|
||||
updateBsqWalletTransactions()
|
||||
);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DaoStateListener
|
||||
|
|
|
@ -99,10 +99,7 @@ public class BtcWalletService extends WalletService {
|
|||
|
||||
walletsSetup.addSetupCompletedHandler(() -> {
|
||||
wallet = walletsSetup.getBtcWallet();
|
||||
wallet.addCoinsReceivedEventListener(walletEventListener);
|
||||
wallet.addCoinsSentEventListener(walletEventListener);
|
||||
wallet.addReorganizeEventListener(walletEventListener);
|
||||
wallet.addTransactionConfidenceEventListener(walletEventListener);
|
||||
addListenersToWallet();
|
||||
|
||||
walletsSetup.getChain().addNewBestBlockListener(block -> chainHeightProperty.set(block.getHeight()));
|
||||
chainHeightProperty.set(walletsSetup.getChain().getBestChainHeight());
|
||||
|
@ -676,8 +673,7 @@ public class BtcWalletService extends WalletService {
|
|||
.filter(e -> {
|
||||
boolean isSegwitOutputScriptType = Script.ScriptType.P2WPKH.equals(e.getAddress().getOutputScriptType());
|
||||
// We need to ensure that we take only addressEntries which matches our segWit flag
|
||||
boolean isMatchingOutputScriptType = isSegwitOutputScriptType == segwit;
|
||||
return isMatchingOutputScriptType;
|
||||
return isSegwitOutputScriptType == segwit;
|
||||
})
|
||||
.findAny();
|
||||
return getOrCreateAddressEntry(context, addressEntry, segwit);
|
||||
|
|
|
@ -72,6 +72,10 @@ import org.bitcoinj.wallet.listeners.WalletReorganizeEventListener;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.collect.ImmutableMultiset;
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.Multiset;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
|
@ -83,9 +87,13 @@ import org.bouncycastle.crypto.params.KeyParameter;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -106,10 +114,13 @@ public abstract class WalletService {
|
|||
protected final Preferences preferences;
|
||||
protected final FeeService feeService;
|
||||
protected final NetworkParameters params;
|
||||
protected final BisqWalletListener walletEventListener = new BisqWalletListener();
|
||||
protected final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
protected final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
protected final CopyOnWriteArraySet<BalanceListener> balanceListeners = new CopyOnWriteArraySet<>();
|
||||
private final BisqWalletListener walletEventListener = new BisqWalletListener();
|
||||
private final CopyOnWriteArraySet<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
private final CopyOnWriteArraySet<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArraySet<>();
|
||||
private final CopyOnWriteArraySet<BalanceListener> balanceListeners = new CopyOnWriteArraySet<>();
|
||||
private final WalletChangeEventListener cacheInvalidationListener;
|
||||
private final AtomicReference<Multiset<Address>> txOutputAddressCache = new AtomicReference<>();
|
||||
private final AtomicReference<SetMultimap<Address, Transaction>> addressToMatchingTxSetCache = new AtomicReference<>();
|
||||
@Getter
|
||||
protected Wallet wallet;
|
||||
@Getter
|
||||
|
@ -131,6 +142,11 @@ public abstract class WalletService {
|
|||
this.feeService = feeService;
|
||||
|
||||
params = walletsSetup.getParams();
|
||||
|
||||
cacheInvalidationListener = wallet -> {
|
||||
txOutputAddressCache.set(null);
|
||||
addressToMatchingTxSetCache.set(null);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
@ -138,13 +154,21 @@ public abstract class WalletService {
|
|||
// Lifecycle
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
protected void addListenersToWallet() {
|
||||
wallet.addCoinsReceivedEventListener(walletEventListener);
|
||||
wallet.addCoinsSentEventListener(walletEventListener);
|
||||
wallet.addReorganizeEventListener(walletEventListener);
|
||||
wallet.addTransactionConfidenceEventListener(walletEventListener);
|
||||
wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener);
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
if (wallet != null) {
|
||||
//noinspection deprecation
|
||||
wallet.removeCoinsReceivedEventListener(walletEventListener);
|
||||
wallet.removeCoinsSentEventListener(walletEventListener);
|
||||
wallet.removeReorganizeEventListener(walletEventListener);
|
||||
wallet.removeTransactionConfidenceEventListener(walletEventListener);
|
||||
wallet.removeChangeEventListener(cacheInvalidationListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -371,15 +395,28 @@ public abstract class WalletService {
|
|||
public TransactionConfidence getConfidenceForAddress(Address address) {
|
||||
List<TransactionConfidence> transactionConfidenceList = new ArrayList<>();
|
||||
if (wallet != null) {
|
||||
Set<Transaction> transactions = wallet.getTransactions(false);
|
||||
if (transactions != null) {
|
||||
transactionConfidenceList.addAll(transactions.stream().map(tx ->
|
||||
getTransactionConfidence(tx, address)).collect(Collectors.toList()));
|
||||
}
|
||||
Set<Transaction> transactions = getAddressToMatchingTxSetMultiset().get(address);
|
||||
transactionConfidenceList.addAll(transactions.stream().map(tx ->
|
||||
getTransactionConfidence(tx, address)).collect(Collectors.toList()));
|
||||
}
|
||||
return getMostRecentConfidence(transactionConfidenceList);
|
||||
}
|
||||
|
||||
private SetMultimap<Address, Transaction> getAddressToMatchingTxSetMultiset() {
|
||||
return addressToMatchingTxSetCache.updateAndGet(set -> set != null ? set : computeAddressToMatchingTxSetMultimap());
|
||||
}
|
||||
|
||||
private SetMultimap<Address, Transaction> computeAddressToMatchingTxSetMultimap() {
|
||||
return wallet.getTransactions(false).stream()
|
||||
.collect(ImmutableSetMultimap.flatteningToImmutableSetMultimap(
|
||||
Function.identity(),
|
||||
(Function<Transaction, Stream<Address>>) (
|
||||
t -> getOutputsWithConnectedOutputs(t).stream()
|
||||
.map(WalletService::getAddressFromOutput)
|
||||
.filter(Objects::nonNull))))
|
||||
.inverse();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TransactionConfidence getConfidenceForTxId(String txId) {
|
||||
if (wallet != null) {
|
||||
|
@ -392,18 +429,18 @@ public abstract class WalletService {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected TransactionConfidence getTransactionConfidence(Transaction tx, Address address) {
|
||||
List<TransactionConfidence> transactionConfidenceList = getOutputsWithConnectedOutputs(tx)
|
||||
.stream()
|
||||
.filter(WalletService::isOutputScriptConvertibleToAddress)
|
||||
@Nullable
|
||||
private TransactionConfidence getTransactionConfidence(Transaction tx, Address address) {
|
||||
List<TransactionConfidence> transactionConfidenceList = getOutputsWithConnectedOutputs(tx).stream()
|
||||
.filter(output -> address != null && address.equals(getAddressFromOutput(output)))
|
||||
.map(o -> tx.getConfidence())
|
||||
.flatMap(o -> Stream.ofNullable(o.getParentTransaction()))
|
||||
.map(Transaction::getConfidence)
|
||||
.collect(Collectors.toList());
|
||||
return getMostRecentConfidence(transactionConfidenceList);
|
||||
}
|
||||
|
||||
|
||||
protected List<TransactionOutput> getOutputsWithConnectedOutputs(Transaction tx) {
|
||||
private List<TransactionOutput> getOutputsWithConnectedOutputs(Transaction tx) {
|
||||
List<TransactionOutput> transactionOutputs = tx.getOutputs();
|
||||
List<TransactionOutput> connectedOutputs = new ArrayList<>();
|
||||
|
||||
|
@ -423,7 +460,7 @@ public abstract class WalletService {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
protected TransactionConfidence getMostRecentConfidence(List<TransactionConfidence> transactionConfidenceList) {
|
||||
private TransactionConfidence getMostRecentConfidence(List<TransactionConfidence> transactionConfidenceList) {
|
||||
TransactionConfidence transactionConfidence = null;
|
||||
for (TransactionConfidence confidence : transactionConfidenceList) {
|
||||
if (confidence != null) {
|
||||
|
@ -490,16 +527,19 @@ public abstract class WalletService {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public int getNumTxOutputsForAddress(Address address) {
|
||||
List<TransactionOutput> transactionOutputs = new ArrayList<>();
|
||||
wallet.getTransactions(false).forEach(t -> transactionOutputs.addAll(t.getOutputs()));
|
||||
int outputs = 0;
|
||||
for (TransactionOutput output : transactionOutputs) {
|
||||
if (isOutputScriptConvertibleToAddress(output) &&
|
||||
address != null &&
|
||||
address.equals(getAddressFromOutput(output)))
|
||||
outputs++;
|
||||
}
|
||||
return outputs;
|
||||
return getTxOutputAddressMultiset().count(address);
|
||||
}
|
||||
|
||||
private Multiset<Address> getTxOutputAddressMultiset() {
|
||||
return txOutputAddressCache.updateAndGet(set -> set != null ? set : computeTxOutputAddressMultiset());
|
||||
}
|
||||
|
||||
private Multiset<Address> computeTxOutputAddressMultiset() {
|
||||
return wallet.getTransactions(false).stream()
|
||||
.flatMap(t -> t.getOutputs().stream())
|
||||
.map(WalletService::getAddressFromOutput)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(ImmutableMultiset.toImmutableMultiset());
|
||||
}
|
||||
|
||||
public boolean isAddressUnused(Address address) {
|
||||
|
@ -595,17 +635,13 @@ public abstract class WalletService {
|
|||
wallet.removeChangeEventListener(listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void addNewBestBlockListener(NewBestBlockListener listener) {
|
||||
//noinspection deprecation
|
||||
final BlockChain chain = walletsSetup.getChain();
|
||||
if (isWalletReady() && chain != null)
|
||||
chain.addNewBestBlockListener(listener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public void removeNewBestBlockListener(NewBestBlockListener listener) {
|
||||
//noinspection deprecation
|
||||
final BlockChain chain = walletsSetup.getChain();
|
||||
if (isWalletReady() && chain != null)
|
||||
chain.removeNewBestBlockListener(listener);
|
||||
|
@ -786,7 +822,6 @@ public abstract class WalletService {
|
|||
// bisqWalletEventListener
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class BisqWalletListener implements WalletCoinsReceivedEventListener, WalletCoinsSentEventListener, WalletReorganizeEventListener, TransactionConfidenceEventListener {
|
||||
@Override
|
||||
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
|
||||
|
@ -806,11 +841,8 @@ public abstract class WalletService {
|
|||
@Override
|
||||
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) {
|
||||
for (AddressConfidenceListener addressConfidenceListener : addressConfidenceListeners) {
|
||||
List<TransactionConfidence> transactionConfidenceList = new ArrayList<>();
|
||||
transactionConfidenceList.add(getTransactionConfidence(tx, addressConfidenceListener.getAddress()));
|
||||
|
||||
TransactionConfidence transactionConfidence = getMostRecentConfidence(transactionConfidenceList);
|
||||
addressConfidenceListener.onTransactionConfidenceChanged(transactionConfidence);
|
||||
TransactionConfidence confidence = getTransactionConfidence(tx, addressConfidenceListener.getAddress());
|
||||
addressConfidenceListener.onTransactionConfidenceChanged(confidence);
|
||||
}
|
||||
txConfidenceListeners.stream()
|
||||
.filter(txConfidenceListener -> tx != null &&
|
||||
|
|
|
@ -103,6 +103,7 @@ public class FilterManager {
|
|||
private final List<String> publicKeys;
|
||||
private ECKey filterSigningKey;
|
||||
private final Set<Filter> invalidFilters = new HashSet<>();
|
||||
private Consumer<String> filterWarningHandler;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -152,6 +153,12 @@ public class FilterManager {
|
|||
.map(protectedStoragePayload -> (Filter) protectedStoragePayload)
|
||||
.forEach(this::onFilterAddedFromNetwork);
|
||||
|
||||
// On mainNet we expect to have received a filter object, if not show a popup to the user to inform the
|
||||
// Bisq devs.
|
||||
if (Config.baseCurrencyNetwork().isMainnet() && getFilter() == null) {
|
||||
filterWarningHandler.accept(Res.get("popup.warning.noFilter"));
|
||||
}
|
||||
|
||||
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
|
||||
@Override
|
||||
public void onAdded(Collection<ProtectedStorageEntry> protectedStorageEntries) {
|
||||
|
@ -216,6 +223,8 @@ public class FilterManager {
|
|||
}
|
||||
|
||||
public void setFilterWarningHandler(Consumer<String> filterWarningHandler) {
|
||||
this.filterWarningHandler = filterWarningHandler;
|
||||
|
||||
addListener(filter -> {
|
||||
if (filter != null && filterWarningHandler != null) {
|
||||
if (filter.getSeedNodes() != null && !filter.getSeedNodes().isEmpty()) {
|
||||
|
|
|
@ -37,7 +37,7 @@ public class CountryUtil {
|
|||
public static List<Country> getAllSepaEuroCountries() {
|
||||
List<Country> list = new ArrayList<>();
|
||||
String[] codes = {"AT", "BE", "CY", "DE", "EE", "FI", "FR", "GR", "IE",
|
||||
"IT", "LV", "LT", "LU", "MC", "MT", "NL", "PT", "SK", "SI", "ES"};
|
||||
"IT", "LV", "LT", "LU", "MC", "MT", "NL", "PT", "SK", "SI", "ES", "AD", "SM", "VA"};
|
||||
populateCountryListByCodes(list, codes);
|
||||
list.sort((a, b) -> a.name.compareTo(b.name));
|
||||
|
||||
|
@ -56,6 +56,16 @@ public class CountryUtil {
|
|||
return list;
|
||||
}
|
||||
|
||||
public static List<Country> getAllAmazonGiftCardCountries() {
|
||||
List<Country> list = new ArrayList<>();
|
||||
String[] codes = {"AU", "CA", "FR", "DE", "IT", "NL", "ES", "GB", "IN", "JP",
|
||||
"SA", "SE", "SG", "TR", "US"};
|
||||
populateCountryListByCodes(list, codes);
|
||||
list.sort((a, b) -> a.name.compareTo(b.name));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static List<Country> getAllSepaInstantEuroCountries() {
|
||||
return getAllSepaEuroCountries();
|
||||
}
|
||||
|
@ -86,7 +96,7 @@ public class CountryUtil {
|
|||
public static List<Country> getAllSepaNonEuroCountries() {
|
||||
List<Country> list = new ArrayList<>();
|
||||
String[] codes = {"BG", "HR", "CZ", "DK", "GB", "HU", "PL", "RO",
|
||||
"SE", "IS", "NO", "LI", "CH"};
|
||||
"SE", "IS", "NO", "LI", "CH", "JE"};
|
||||
populateCountryListByCodes(list, codes);
|
||||
list.sort((a, b) -> a.name.compareTo(b.name));
|
||||
return list;
|
||||
|
@ -133,6 +143,8 @@ public class CountryUtil {
|
|||
}
|
||||
|
||||
public static String getNameAndCode(String countryCode) {
|
||||
if (countryCode.isEmpty())
|
||||
return "";
|
||||
return getNameByCode(countryCode) + " (" + countryCode + ")";
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,9 @@ import bisq.core.monetary.Price;
|
|||
import bisq.core.provider.price.MarketPrice;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
|
||||
import bisq.network.p2p.BootstrapListener;
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.util.MathUtils;
|
||||
|
||||
import org.bitcoinj.utils.Fiat;
|
||||
|
@ -47,17 +50,34 @@ import static bisq.common.util.MathUtils.scaleUpByPowerOf10;
|
|||
@Slf4j
|
||||
@Singleton
|
||||
public class TriggerPriceService {
|
||||
private final P2PService p2PService;
|
||||
private final OpenOfferManager openOfferManager;
|
||||
private final PriceFeedService priceFeedService;
|
||||
private final Map<String, Set<OpenOffer>> openOffersByCurrency = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
public TriggerPriceService(OpenOfferManager openOfferManager, PriceFeedService priceFeedService) {
|
||||
public TriggerPriceService(P2PService p2PService,
|
||||
OpenOfferManager openOfferManager,
|
||||
PriceFeedService priceFeedService) {
|
||||
this.p2PService = p2PService;
|
||||
this.openOfferManager = openOfferManager;
|
||||
this.priceFeedService = priceFeedService;
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
if (p2PService.isBootstrapped()) {
|
||||
onBootstrapComplete();
|
||||
} else {
|
||||
p2PService.addP2PServiceListener(new BootstrapListener() {
|
||||
@Override
|
||||
public void onUpdatedDataReceived() {
|
||||
onBootstrapComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void onBootstrapComplete() {
|
||||
openOfferManager.getObservableList().addListener((ListChangeListener<OpenOffer>) c -> {
|
||||
c.next();
|
||||
if (c.wasAdded()) {
|
||||
|
|
|
@ -17,12 +17,21 @@
|
|||
|
||||
package bisq.core.payment;
|
||||
|
||||
import bisq.core.locale.Country;
|
||||
import bisq.core.locale.CountryUtil;
|
||||
import bisq.core.payment.payload.AmazonGiftCardAccountPayload;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public final class AmazonGiftCardAccount extends PaymentAccount {
|
||||
|
||||
@Nullable
|
||||
private Country country;
|
||||
|
||||
public AmazonGiftCardAccount() {
|
||||
super(PaymentMethod.AMAZON_GIFT_CARD);
|
||||
}
|
||||
|
@ -40,6 +49,24 @@ public final class AmazonGiftCardAccount extends PaymentAccount {
|
|||
getAmazonGiftCardAccountPayload().setEmailOrMobileNr(emailOrMobileNr);
|
||||
}
|
||||
|
||||
public boolean countryNotSet() {
|
||||
return (getAmazonGiftCardAccountPayload()).countryNotSet();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Country getCountry() {
|
||||
if (country == null) {
|
||||
final String countryCode = getAmazonGiftCardAccountPayload().getCountryCode();
|
||||
CountryUtil.findCountryByCode(countryCode).ifPresent(c -> this.country = c);
|
||||
}
|
||||
return country;
|
||||
}
|
||||
|
||||
public void setCountry(@NotNull Country country) {
|
||||
this.country = country;
|
||||
getAmazonGiftCardAccountPayload().setCountryCode(country.code);
|
||||
}
|
||||
|
||||
private AmazonGiftCardAccountPayload getAmazonGiftCardAccountPayload() {
|
||||
return (AmazonGiftCardAccountPayload) paymentAccountPayload;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package bisq.core.payment.payload;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.common.util.JsonExclude;
|
||||
|
||||
import com.google.protobuf.Message;
|
||||
|
||||
|
@ -39,6 +40,10 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public class AmazonGiftCardAccountPayload extends PaymentAccountPayload {
|
||||
private String emailOrMobileNr;
|
||||
// For backward compatibility we need to exclude the new field for the contract json.
|
||||
// We can remove that after a while when risk that users with pre 1.5.5 version is very low.
|
||||
@JsonExclude
|
||||
private String countryCode = "";
|
||||
|
||||
public AmazonGiftCardAccountPayload(String paymentMethod, String id) {
|
||||
super(paymentMethod, id);
|
||||
|
@ -52,6 +57,7 @@ public class AmazonGiftCardAccountPayload extends PaymentAccountPayload {
|
|||
private AmazonGiftCardAccountPayload(String paymentMethodName,
|
||||
String id,
|
||||
String emailOrMobileNr,
|
||||
String countryCode,
|
||||
long maxTradePeriod,
|
||||
Map<String, String> excludeFromJsonDataMap) {
|
||||
super(paymentMethodName,
|
||||
|
@ -59,12 +65,14 @@ public class AmazonGiftCardAccountPayload extends PaymentAccountPayload {
|
|||
maxTradePeriod,
|
||||
excludeFromJsonDataMap);
|
||||
this.emailOrMobileNr = emailOrMobileNr;
|
||||
this.countryCode = countryCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message toProtoMessage() {
|
||||
protobuf.AmazonGiftCardAccountPayload.Builder builder =
|
||||
protobuf.AmazonGiftCardAccountPayload.newBuilder()
|
||||
.setCountryCode(countryCode)
|
||||
.setEmailOrMobileNr(emailOrMobileNr);
|
||||
return getPaymentAccountPayloadBuilder()
|
||||
.setAmazonGiftCardAccountPayload(builder)
|
||||
|
@ -76,6 +84,7 @@ public class AmazonGiftCardAccountPayload extends PaymentAccountPayload {
|
|||
return new AmazonGiftCardAccountPayload(proto.getPaymentMethodId(),
|
||||
proto.getId(),
|
||||
amazonGiftCardAccountPayload.getEmailOrMobileNr(),
|
||||
amazonGiftCardAccountPayload.getCountryCode(),
|
||||
proto.getMaxTradePeriod(),
|
||||
new HashMap<>(proto.getExcludeFromJsonDataMap()));
|
||||
}
|
||||
|
@ -100,4 +109,8 @@ public class AmazonGiftCardAccountPayload extends PaymentAccountPayload {
|
|||
String data = "AmazonGiftCard" + emailOrMobileNr;
|
||||
return super.getAgeWitnessInputData(data.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public boolean countryNotSet() {
|
||||
return countryCode.isEmpty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package bisq.core.support;
|
||||
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.support.messages.ChatMessage;
|
||||
import bisq.core.support.messages.SupportMessage;
|
||||
|
||||
|
@ -191,7 +192,10 @@ public abstract class SupportManager {
|
|||
public ChatMessage sendChatMessage(ChatMessage message) {
|
||||
NodeAddress peersNodeAddress = getPeerNodeAddress(message);
|
||||
PubKeyRing receiverPubKeyRing = getPeerPubKeyRing(message);
|
||||
if (receiverPubKeyRing != null) {
|
||||
if (peersNodeAddress == null || receiverPubKeyRing == null) {
|
||||
UserThread.runAfter(() ->
|
||||
message.setSendMessageError(Res.get("support.receiverNotKnown")), 1);
|
||||
} else {
|
||||
log.info("Send {} to peer {}. tradeId={}, uid={}",
|
||||
message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid());
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ public abstract class SupportSession {
|
|||
|
||||
public abstract String getTradeId();
|
||||
|
||||
public abstract PubKeyRing getClientPubKeyRing();
|
||||
public abstract int getClientId();
|
||||
|
||||
public abstract ObservableList<ChatMessage> getObservableChatMessageList();
|
||||
|
||||
|
|
|
@ -34,8 +34,10 @@ import javafx.beans.property.SimpleIntegerProperty;
|
|||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -53,6 +55,8 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
|
|||
private final Map<String, Subscription> disputeIsClosedSubscriptionsMap = new HashMap<>();
|
||||
@Getter
|
||||
private final IntegerProperty numOpenDisputes = new SimpleIntegerProperty();
|
||||
@Getter
|
||||
private final Set<String> disputedTradeIds = new HashSet<>();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -154,6 +158,7 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
|
|||
disputeIsClosedSubscriptionsMap.get(id).unsubscribe();
|
||||
disputeIsClosedSubscriptionsMap.remove(id);
|
||||
}
|
||||
disputedTradeIds.remove(dispute.getTradeId());
|
||||
});
|
||||
}
|
||||
addedList.forEach(dispute -> {
|
||||
|
@ -168,6 +173,7 @@ public abstract class DisputeListService<T extends DisputeList<Dispute>> impleme
|
|||
});
|
||||
});
|
||||
disputeIsClosedSubscriptionsMap.put(id, disputeStateSubscription);
|
||||
disputedTradeIds.add(dispute.getTradeId());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ import java.security.KeyPair;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -231,6 +232,9 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
|||
return disputeListService.getDisputeList();
|
||||
}
|
||||
|
||||
public Set<String> getDisputedTradeIds() {
|
||||
return disputeListService.getDisputedTradeIds();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
|
|
|
@ -57,9 +57,14 @@ public abstract class DisputeSession extends SupportSession {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PubKeyRing getClientPubKeyRing() {
|
||||
public int getClientId() {
|
||||
// Get pubKeyRing of trader. Arbitrator is considered server for the chat session
|
||||
return dispute != null ? dispute.getTraderPubKeyRing() : null;
|
||||
try {
|
||||
return dispute.getTraderPubKeyRing().hashCode();
|
||||
} catch (NullPointerException e) {
|
||||
log.warn("Unable to get traderPubKeyRing from Dispute - {}", e.toString());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -37,7 +37,7 @@ public class DisputeAgentLookupMap {
|
|||
case "apbp7ubuyezav4hy.onion:9999":
|
||||
return "bisq_knight";
|
||||
case "a56olqlmmpxrn5q34itq5g5tb5d3fg7vxekpbceq7xqvfl3cieocgsyd.onion:9999":
|
||||
return "leo816";
|
||||
return "huey735";
|
||||
case "3z5jnirlccgxzoxc6zwkcgwj66bugvqplzf6z2iyd5oxifiaorhnanqd.onion:9999":
|
||||
return "refundagent2";
|
||||
default:
|
||||
|
|
|
@ -48,12 +48,15 @@ public class TradeChatSession extends SupportSession {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PubKeyRing getClientPubKeyRing() {
|
||||
public int getClientId() {
|
||||
// TODO remove that client-server concept for trade chat
|
||||
// Get pubKeyRing of taker. Maker is considered server for chat sessions
|
||||
if (trade != null && trade.getContract() != null)
|
||||
return trade.getContract().getTakerPubKeyRing();
|
||||
return null;
|
||||
try {
|
||||
return trade.getContract().getTakerPubKeyRing().hashCode();
|
||||
} catch (NullPointerException e) {
|
||||
log.warn("Unable to get takerPubKeyRing from Trade Contract - {}", e.toString());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -309,15 +309,25 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
useStandbyModeProperty.set(prefPayload.isUseStandbyMode());
|
||||
cssThemeProperty.set(prefPayload.getCssTheme());
|
||||
|
||||
// a list of previously-used federated explorers
|
||||
// if user preference references any deprecated explorers we need to select a new valid explorer
|
||||
String deprecatedExplorers = "(bsq.bisq.cc|bsq.vante.me|bsq.emzy.de|bsq.sqrrm.net|bsq.bisq.services|bsq.ninja).*";
|
||||
|
||||
// if no valid Bitcoin block explorer is set, select the 1st valid Bitcoin block explorer
|
||||
ArrayList<BlockChainExplorer> btcExplorers = getBlockChainExplorers();
|
||||
if (getBlockChainExplorer() == null || getBlockChainExplorer().name.length() == 0)
|
||||
if (getBlockChainExplorer() == null ||
|
||||
getBlockChainExplorer().name.length() == 0 ||
|
||||
getBlockChainExplorer().name.matches(deprecatedExplorers)) {
|
||||
setBlockChainExplorer(btcExplorers.get(0));
|
||||
}
|
||||
|
||||
// if no valid BSQ block explorer is set, randomly select a valid BSQ block explorer
|
||||
ArrayList<BlockChainExplorer> bsqExplorers = getBsqBlockChainExplorers();
|
||||
if (getBsqBlockChainExplorer() == null || getBsqBlockChainExplorer().name.length() == 0)
|
||||
if (getBsqBlockChainExplorer() == null ||
|
||||
getBsqBlockChainExplorer().name.length() == 0 ||
|
||||
getBsqBlockChainExplorer().name.matches(deprecatedExplorers)) {
|
||||
setBsqBlockChainExplorer(bsqExplorers.get((new Random()).nextInt(bsqExplorers.size())));
|
||||
}
|
||||
|
||||
tradeCurrenciesAsObservable.addAll(prefPayload.getFiatCurrencies());
|
||||
tradeCurrenciesAsObservable.addAll(prefPayload.getCryptoCurrencies());
|
||||
|
@ -782,6 +792,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
requestPersistence();
|
||||
}
|
||||
|
||||
public void setNotifyOnPreRelease(boolean value) {
|
||||
prefPayload.setNotifyOnPreRelease(value);
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getter
|
||||
|
@ -1095,5 +1110,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||
void setShowOffersMatchingMyAccounts(boolean value);
|
||||
|
||||
void setDenyApiTaker(boolean value);
|
||||
|
||||
void setNotifyOnPreRelease(boolean value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
private boolean hideNonAccountPaymentMethods;
|
||||
private boolean showOffersMatchingMyAccounts;
|
||||
private boolean denyApiTaker;
|
||||
private boolean notifyOnPreRelease;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
|
@ -199,7 +200,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
.collect(Collectors.toList()))
|
||||
.setHideNonAccountPaymentMethods(hideNonAccountPaymentMethods)
|
||||
.setShowOffersMatchingMyAccounts(showOffersMatchingMyAccounts)
|
||||
.setDenyApiTaker(denyApiTaker);
|
||||
.setDenyApiTaker(denyApiTaker)
|
||||
.setNotifyOnPreRelease(notifyOnPreRelease);
|
||||
|
||||
Optional.ofNullable(backupDirectory).ifPresent(builder::setBackupDirectory);
|
||||
Optional.ofNullable(preferredTradeCurrency).ifPresent(e -> builder.setPreferredTradeCurrency((protobuf.TradeCurrency) e.toProtoMessage()));
|
||||
|
@ -296,7 +298,8 @@ public final class PreferencesPayload implements PersistableEnvelope {
|
|||
.collect(Collectors.toList())),
|
||||
proto.getHideNonAccountPaymentMethods(),
|
||||
proto.getShowOffersMatchingMyAccounts(),
|
||||
proto.getDenyApiTaker()
|
||||
proto.getDenyApiTaker(),
|
||||
proto.getNotifyOnPreRelease()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,5 +22,5 @@ OPTIONS
|
|||
EXAMPLES
|
||||
--------
|
||||
To cancel an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
|
||||
$ ./bisq-cli --password=xyz --port=9998 canceloffer -offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 canceloffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
||||
|
|
|
@ -24,4 +24,4 @@ EXAMPLES
|
|||
--------
|
||||
A BTC seller has taken an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea, and has recently
|
||||
received the required fiat payment from the buyer's fiat account:
|
||||
$ ./bisq-cli --password=xyz --port=9998 confirmpaymentreceived -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 confirmpaymentreceived --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
|
|
@ -23,4 +23,4 @@ EXAMPLES
|
|||
--------
|
||||
A BTC buyer has taken an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea, and has recently
|
||||
initiated the required fiat payment to the seller's fiat account:
|
||||
$ ./bisq-cli --password=xyz --port=9998 confirmpaymentstarted -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 confirmpaymentstarted --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
|
|
@ -54,11 +54,29 @@ OPTIONS
|
|||
|
||||
EXAMPLES
|
||||
--------
|
||||
|
||||
To create a BUY 0.125 BTC with EUR offer
|
||||
at the current market price,
|
||||
using a payment account with ID 7413d263-225a-4f1b-837a-1e3094dc0d77,
|
||||
putting up a 30 percent security deposit,
|
||||
and paying the Bisq maker trading fee in BSQ:
|
||||
$ ./bisq-cli --password=xyz --port=9998 createoffer --payment-account=7413d263-225a-4f1b-837a-1e3094dc0d77 --direction=buy --currency-code=eur --amount=0.125 --market-price-margin=0.00 --security-deposit=30.0 --fee-currency=bsq
|
||||
$ ./bisq-cli --password=xyz --port=9998 createoffer --payment-account=7413d263-225a-4f1b-837a-1e3094dc0d77 \
|
||||
--direction=buy \
|
||||
--currency-code=eur \
|
||||
--amount=0.125 \
|
||||
--market-price-margin=0.00 \
|
||||
--security-deposit=30.0 \
|
||||
--fee-currency=bsq
|
||||
|
||||
(TODO another 3 examples: selling @ mkt price, buying a fixed price, selling at fixed price...)
|
||||
To create a SELL 0.006 BTC for USD offer
|
||||
at a fixed price of 40,000 USD,
|
||||
using a payment account with ID 7413d263-225a-4f1b-837a-1e3094dc0d77,
|
||||
putting up a 25 percent security deposit,
|
||||
and paying the Bisq maker trading fee in BTC:
|
||||
$ ./bisq-cli --password=xyz --port=9998 createoffer --payment-account=7413d263-225a-4f1b-837a-1e3094dc0d77 \
|
||||
--direction=sell \
|
||||
--currency-code=usd \
|
||||
--amount=0.006 \
|
||||
--fixed-price=40000 \
|
||||
--security-deposit=25.0 \
|
||||
--fee-currency=btc
|
||||
|
|
|
@ -24,7 +24,7 @@ Show full BSQ and BTC wallet balance information:
|
|||
$ ./bisq-cli --password=xyz --port=9998 getbalance
|
||||
|
||||
Show full BSQ wallet balance information:
|
||||
$ ./bisq-cli --password=xyz --port=9998 getbalance --currency-code=bsq
|
||||
$ ./bisq-cli --password=xyz --port=9998 getbalance --currency-code=bsq
|
||||
|
||||
Show full BTC wallet balance information:
|
||||
$ ./bisq-cli --password=xyz --port=9998 getbalance --currency-code=btc
|
||||
$ ./bisq-cli --password=xyz --port=9998 getbalance --currency-code=btc
|
||||
|
|
|
@ -21,5 +21,5 @@ OPTIONS
|
|||
EXAMPLES
|
||||
--------
|
||||
To view your offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
|
||||
$ ./bisq-cli --password=xyz --port=9998 getmyoffer -offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 getmyoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
||||
|
|
|
@ -22,5 +22,5 @@ OPTIONS
|
|||
EXAMPLES
|
||||
--------
|
||||
To view an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
|
||||
$ ./bisq-cli --password=xyz --port=9998 getoffer -offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 getoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ OPTIONS
|
|||
EXAMPLES
|
||||
--------
|
||||
To see the summary of a trade with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
|
||||
$ ./bisq-cli --password=xyz --port=9998 gettrade -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 gettrade --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
||||
To see the full contract for a trade with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea:
|
||||
$ ./bisq-cli --password=xyz --port=9998 gettrade -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea --show-contract=true
|
||||
$ ./bisq-cli --password=xyz --port=9998 gettrade --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea --show-contract=true
|
||||
|
|
|
@ -24,4 +24,4 @@ EXAMPLES
|
|||
--------
|
||||
To see the summary of a transaction with ID 282dc2a5755219a49ee9f6d46a31a2cbaec6624beba96548180eccb1f004cdd8:
|
||||
$ ./bisq-cli --password=xyz --port=9998 gettransaction \
|
||||
-transaction-id=282dc2a5755219a49ee9f6d46a31a2cbaec6624beba96548180eccb1f004cdd8
|
||||
--transaction-id=282dc2a5755219a49ee9f6d46a31a2cbaec6624beba96548180eccb1f004cdd8
|
||||
|
|
|
@ -28,4 +28,4 @@ EXAMPLES
|
|||
A BTC seller has informed the buyer that fiat payment has been received for trade with ID
|
||||
83e8b2e2-51b6-4f39-a748-3ebd29c22aea, and locked BTC has been released to the buyer.
|
||||
The BTC buyer closes out the trade by keeping the received BTC in her Bisq wallet:
|
||||
$ ./bisq-cli --password=xyz --port=9998 keepfunds -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
$ ./bisq-cli --password=xyz --port=9998 keepfunds --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
|
|
|
@ -7,8 +7,8 @@ sendbsq - send BSQ to an external wallet
|
|||
SYNOPSIS
|
||||
--------
|
||||
sendbsq
|
||||
--address=<btc-address>
|
||||
--amount=<btc-amount>
|
||||
--address=<bsq-address>
|
||||
--amount=<bsq-amount>
|
||||
[--tx-fee-rate=<sats/byte>]
|
||||
|
||||
DESCRIPTION
|
||||
|
|
|
@ -47,5 +47,5 @@ $ ./bisq-cli --password=xyz --port=9998 sendbtc --address=bcrt1qygvsqmyt8jyhtp7l
|
|||
Send 0.005 BTC to address bcrt1qygvsqmyt8jyhtp7l3zwqm7s7v3nar6vkc2luz3 with a transaction
|
||||
fee rate of 40 sats/byte, and save a memo with the send transaction:
|
||||
$ ./bisq-cli --password=xyz --port=9998 sendbtc --address=bcrt1qygvsqmyt8jyhtp7l3zwqm7s7v3nar6vkc2luz3 --amount=0.005 \
|
||||
--tx-fee-rate=40
|
||||
--tx-fee-rate=40 \
|
||||
--memo="note to self"
|
||||
|
|
|
@ -32,4 +32,6 @@ EXAMPLES
|
|||
To take an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea
|
||||
using a payment account with ID fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e,
|
||||
and paying the Bisq trading fee in BSQ:
|
||||
$ ./bisq-cli --password=xyz --port=9998 takeoffer -offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea -payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e -fee-currency=bsq
|
||||
$ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
--payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e \
|
||||
-fee-currency=bsq
|
||||
|
|
|
@ -40,11 +40,11 @@ EXAMPLES
|
|||
A BTC seller has informed the buyer that fiat payment has been received for trade with ID
|
||||
83e8b2e2-51b6-4f39-a748-3ebd29c22aea, and locked BTC has been released to the buyer.
|
||||
The BTC buyer closes out the trade by sending the received BTC to an external BTC wallet:
|
||||
$ ./bisq-cli --password=xyz --port=9998 withdrawfunds -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
$ ./bisq-cli --password=xyz --port=9998 withdrawfunds --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
--address=2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv (bitcoin regtest address)
|
||||
|
||||
|
||||
A seller sends a trade's BTC proceeds to an external wallet, and includes an optional memo:
|
||||
$ ./bisq-cli --password=xyz --port=9998 withdrawfunds -trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
--address=2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv
|
||||
$ ./bisq-cli --password=xyz --port=9998 withdrawfunds --trade-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea \
|
||||
--address=2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv \
|
||||
--memo="note to self"
|
||||
|
|
|
@ -653,10 +653,6 @@ portfolio.pending.step2_buyer.moneyGram.extra=IMPORTANT REQUIREMENT:\nAfter you
|
|||
portfolio.pending.step2_buyer.westernUnion=Please pay {0} to the BTC seller by using Western Union.\n\n
|
||||
portfolio.pending.step2_buyer.westernUnion.extra=IMPORTANT REQUIREMENT:\nAfter you have done the payment send the MTCN (tracking number) and a photo of the receipt by email to the BTC seller.\n\
|
||||
The receipt must clearly show the seller''s full name, city, country and the amount. The seller''s email is: {0}.
|
||||
# suppress inspection "TrailingSpacesInProperty"
|
||||
portfolio.pending.step2_buyer.amazonGiftCard=Please purchase an Amazon eGift Card for {0} at your Amazon account and \
|
||||
use the BTC seller''s email or mobile number as receiver. \
|
||||
In case the trade amount exceeds the permitted amount send multiple cards.\n\n
|
||||
|
||||
# suppress inspection "TrailingSpacesInProperty"
|
||||
portfolio.pending.step2_buyer.postal=Please send {0} by \"US Postal Money Order\" to the BTC seller.\n\n
|
||||
|
@ -1105,6 +1101,7 @@ support.noTickets=There are no open tickets
|
|||
support.sendingMessage=Sending Message...
|
||||
support.receiverNotOnline=Receiver is not online. Message is saved to their mailbox.
|
||||
support.sendMessageError=Sending message failed. Error: {0}
|
||||
support.receiverNotKnown=Receiver not known
|
||||
support.wrongVersion=The offer in that dispute has been created with an older version of Bisq.\n\
|
||||
You cannot close that dispute with your version of the application.\n\n\
|
||||
Please use an older version with protocol version {0}
|
||||
|
@ -1223,6 +1220,7 @@ setting.preferences.useDarkMode=Use dark mode
|
|||
setting.preferences.sortWithNumOffers=Sort market lists with no. of offers/trades
|
||||
setting.preferences.onlyShowPaymentMethodsFromAccount=Hide non-supported payment methods
|
||||
setting.preferences.denyApiTaker=Deny takers using the API
|
||||
setting.preferences.notifyOnPreRelease=Receive pre-release notifications
|
||||
setting.preferences.resetAllFlags=Reset all \"Don't show again\" flags
|
||||
settings.preferences.languageChange=To apply the language change to all screens requires a restart.
|
||||
settings.preferences.supportLanguageWarning=In case of a dispute, please note that mediation is handled in {0} and arbitration in {1}.
|
||||
|
@ -2658,7 +2656,9 @@ selectDepositTxWindow.select=Select deposit transaction
|
|||
sendAlertMessageWindow.headline=Send global notification
|
||||
sendAlertMessageWindow.alertMsg=Alert message
|
||||
sendAlertMessageWindow.enterMsg=Enter message
|
||||
sendAlertMessageWindow.isUpdate=Is update notification
|
||||
sendAlertMessageWindow.isSoftwareUpdate=Software download notification
|
||||
sendAlertMessageWindow.isUpdate=Is full release
|
||||
sendAlertMessageWindow.isPreRelease=Is pre-release
|
||||
sendAlertMessageWindow.version=New version no.
|
||||
sendAlertMessageWindow.send=Send notification
|
||||
sendAlertMessageWindow.remove=Remove notification
|
||||
|
@ -2841,6 +2841,7 @@ popup.warning.mandatoryUpdate.dao=Please update to the latest Bisq version. \
|
|||
Please check out the Bisq Forum for more information.
|
||||
popup.warning.disable.dao=The Bisq DAO and BSQ are temporary disabled. \
|
||||
Please check out the Bisq Forum for more information.
|
||||
popup.warning.noFilter=We did not receive a filter object from the seed nodes. This is a not expected situation. Please inform the Bisq developers.
|
||||
popup.warning.burnBTC=This transaction is not possible, as the mining fees of {0} would exceed the amount to transfer of {1}. \
|
||||
Please wait until the mining fees are low again or until you''ve accumulated more BTC to transfer.
|
||||
|
||||
|
@ -3198,6 +3199,8 @@ payment.select.altcoin=Select or search Altcoin
|
|||
payment.secret=Secret question
|
||||
payment.answer=Answer
|
||||
payment.wallet=Wallet ID
|
||||
payment.amazon.site=Buy giftcard at
|
||||
payment.ask=Ask in Trader Chat
|
||||
payment.uphold.accountId=Username or email or phone no.
|
||||
payment.moneyBeam.accountId=Email or phone no.
|
||||
payment.venmo.venmoUserName=Venmo username
|
||||
|
@ -3308,6 +3311,13 @@ payment.account.revolut.addUserNameInfo={0}\n\
|
|||
This will not affect your account age signing status.
|
||||
payment.revolut.addUserNameInfo.headLine=Update Revolut account
|
||||
|
||||
payment.amazonGiftCard.upgrade=Amazon gift cards payment method requires the country to be specified.
|
||||
payment.account.amazonGiftCard.addCountryInfo={0}\n\
|
||||
Your existing Amazon Gift Card account ({1}) does not have a Country specified.\n\
|
||||
Please enter your Amazon Gift Card Country to update your account data.\n\
|
||||
This will not affect your account age status.
|
||||
payment.amazonGiftCard.upgrade.headLine=Update Amazon Gift Card account
|
||||
|
||||
payment.usPostalMoneyOrder.info=Trading using US Postal Money Orders (USPMO) on Bisq requires that you understand the following:\n\
|
||||
\n\
|
||||
- BTC buyers must write the BTC Seller’s name in both the Payer and the Payee’s fields & take a high-resolution photo of the USPMO and envelope with proof of tracking before sending.\n\
|
||||
|
|
|
@ -41,7 +41,7 @@ public class UserPayloadModelVOTest {
|
|||
public void testRoundtripFull() {
|
||||
UserPayload vo = new UserPayload();
|
||||
vo.setAccountId("accountId");
|
||||
vo.setDisplayedAlert(new Alert("message", true, "version", new byte[]{12, -64, 12}, "string", null));
|
||||
vo.setDisplayedAlert(new Alert("message", true, false, "version", new byte[]{12, -64, 12}, "string", null));
|
||||
vo.setDevelopersFilter(new Filter(Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
Lists.newArrayList(),
|
||||
|
|
|
@ -6,12 +6,24 @@ import bisq.proto.grpc.DisputeAgentsGrpc;
|
|||
import bisq.proto.grpc.RegisterDisputeAgentReply;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
@Slf4j
|
||||
class GrpcDisputeAgentsService extends DisputeAgentsGrpc.DisputeAgentsImplBase {
|
||||
|
||||
|
@ -36,4 +48,21 @@ class GrpcDisputeAgentsService extends DisputeAgentsGrpc.DisputeAgentsImplBase {
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
// You can only register mainnet dispute agents in the UI.
|
||||
// Do not limit devs' ability to register test agents.
|
||||
put("registerDisputeAgent", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,23 @@ import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
|||
import bisq.proto.grpc.GetTradeStatisticsReply;
|
||||
import bisq.proto.grpc.GetTradeStatisticsRequest;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
class GrpcGetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
|
||||
|
||||
private final CoreApi coreApi;
|
||||
|
@ -39,4 +50,19 @@ class GrpcGetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStati
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getTradeStatistics", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,24 @@ import bisq.proto.grpc.GetMethodHelpReply;
|
|||
import bisq.proto.grpc.GetMethodHelpRequest;
|
||||
import bisq.proto.grpc.HelpGrpc;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
@Slf4j
|
||||
class GrpcHelpService extends HelpGrpc.HelpImplBase {
|
||||
|
||||
|
@ -53,4 +65,19 @@ class GrpcHelpService extends HelpGrpc.HelpImplBase {
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getMethodHelp", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,16 +36,27 @@ import bisq.proto.grpc.GetOffersReply;
|
|||
import bisq.proto.grpc.GetOffersRequest;
|
||||
import bisq.proto.grpc.OffersGrpc;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.core.api.model.OfferInfo.toOfferInfo;
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
@Slf4j
|
||||
class GrpcOffersService extends OffersGrpc.OffersImplBase {
|
||||
|
@ -171,4 +182,24 @@ class GrpcOffersService extends OffersGrpc.OffersImplBase {
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getOffer", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getMyOffer", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getOffers", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getMyOffers", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("createOffer", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("cancelOffer", new GrpcCallRateMeter(1, MINUTES));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,12 +31,24 @@ import bisq.proto.grpc.GetPaymentMethodsReply;
|
|||
import bisq.proto.grpc.GetPaymentMethodsRequest;
|
||||
import bisq.proto.grpc.PaymentAccountsGrpc;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
|
||||
class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImplBase {
|
||||
|
||||
|
@ -110,4 +122,22 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("createPaymentAccount", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("getPaymentAccounts", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getPaymentMethods", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getPaymentAccountForm", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,24 @@ import bisq.proto.grpc.MarketPriceReply;
|
|||
import bisq.proto.grpc.MarketPriceRequest;
|
||||
import bisq.proto.grpc.PriceGrpc;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
@Slf4j
|
||||
class GrpcPriceService extends PriceGrpc.PriceImplBase {
|
||||
|
||||
|
@ -55,4 +67,19 @@ class GrpcPriceService extends PriceGrpc.PriceImplBase {
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getMarketPrice", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,15 +60,15 @@ public class GrpcServer {
|
|||
GrpcWalletsService walletsService) {
|
||||
this.server = ServerBuilder.forPort(config.apiPort)
|
||||
.executor(UserThread.getExecutor())
|
||||
.addService(disputeAgentsService)
|
||||
.addService(helpService)
|
||||
.addService(offersService)
|
||||
.addService(paymentAccountsService)
|
||||
.addService(priceService)
|
||||
.addService(tradeStatisticsService)
|
||||
.addService(tradesService)
|
||||
.addService(interceptForward(disputeAgentsService, disputeAgentsService.interceptors()))
|
||||
.addService(interceptForward(helpService, helpService.interceptors()))
|
||||
.addService(interceptForward(offersService, offersService.interceptors()))
|
||||
.addService(interceptForward(paymentAccountsService, paymentAccountsService.interceptors()))
|
||||
.addService(interceptForward(priceService, priceService.interceptors()))
|
||||
.addService(interceptForward(tradeStatisticsService, tradeStatisticsService.interceptors()))
|
||||
.addService(interceptForward(tradesService, tradesService.interceptors()))
|
||||
.addService(interceptForward(versionService, versionService.interceptors()))
|
||||
.addService(walletsService)
|
||||
.addService(interceptForward(walletsService, walletsService.interceptors()))
|
||||
.intercept(passwordAuthInterceptor)
|
||||
.build();
|
||||
coreContext.setApiUser(true);
|
||||
|
|
|
@ -35,13 +35,25 @@ import bisq.proto.grpc.TradesGrpc;
|
|||
import bisq.proto.grpc.WithdrawFundsReply;
|
||||
import bisq.proto.grpc.WithdrawFundsRequest;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.core.api.model.TradeInfo.toTradeInfo;
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
@Slf4j
|
||||
class GrpcTradesService extends TradesGrpc.TradesImplBase {
|
||||
|
@ -142,4 +154,24 @@ class GrpcTradesService extends TradesGrpc.TradesImplBase {
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getTrade", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("takeOffer", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("confirmPaymentStarted", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("confirmPaymentReceived", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("keepFunds", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("withdrawFunds", new GrpcCallRateMeter(1, MINUTES));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,13 +74,11 @@ public class GrpcVersionService extends GetVersionGrpc.GetVersionImplBase {
|
|||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
@SuppressWarnings("unused") // Defined as a usage example.
|
||||
CallRateMeteringInterceptor defaultCallRateMeteringInterceptor =
|
||||
new CallRateMeteringInterceptor(new HashMap<>() {{
|
||||
put("getVersion", new GrpcCallRateMeter(100, SECONDS));
|
||||
}});
|
||||
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(Optional::empty /* Optional.of(defaultCallRateMeteringInterceptor) */);
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getVersion", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ import bisq.proto.grpc.UnsetTxFeeRatePreferenceReply;
|
|||
import bisq.proto.grpc.UnsetTxFeeRatePreferenceRequest;
|
||||
import bisq.proto.grpc.WalletsGrpc;
|
||||
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
@ -61,7 +62,9 @@ import javax.inject.Inject;
|
|||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -69,6 +72,14 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static bisq.core.api.model.TxInfo.toTxInfo;
|
||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
|
||||
|
||||
import bisq.daemon.grpc.interceptor.CallRateMeteringInterceptor;
|
||||
import bisq.daemon.grpc.interceptor.GrpcCallRateMeter;
|
||||
|
||||
@Slf4j
|
||||
class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
||||
|
@ -330,4 +341,36 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
|||
exceptionHandler.handleException(cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
||||
final ServerInterceptor[] interceptors() {
|
||||
Optional<ServerInterceptor> rateMeteringInterceptor = rateMeteringInterceptor();
|
||||
return rateMeteringInterceptor.map(serverInterceptor ->
|
||||
new ServerInterceptor[]{serverInterceptor}).orElseGet(() -> new ServerInterceptor[0]);
|
||||
}
|
||||
|
||||
final Optional<ServerInterceptor> rateMeteringInterceptor() {
|
||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put("getBalances", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getAddressBalance", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getFundingAddresses", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getUnusedBsqAddress", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("sendBsq", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("sendBtc", new GrpcCallRateMeter(1, MINUTES));
|
||||
put("getTxFeeRate", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("setTxFeeRatePreference", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("unsetTxFeeRatePreference", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("getTransaction", new GrpcCallRateMeter(1, SECONDS));
|
||||
|
||||
// Trying to set or remove a wallet password several times before the 1st attempt has time to
|
||||
// persist the change to disk may corrupt the wallet, so allow only 1 attempt per 5 seconds.
|
||||
put("setWalletPassword", new GrpcCallRateMeter(1, SECONDS, 5));
|
||||
put("removeWalletPassword", new GrpcCallRateMeter(1, SECONDS, 5));
|
||||
|
||||
put("lockWallet", new GrpcCallRateMeter(1, SECONDS));
|
||||
put("unlockWallet", new GrpcCallRateMeter(1, SECONDS));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import io.grpc.StatusRuntimeException;
|
|||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
@ -124,4 +125,10 @@ public final class CallRateMeteringInterceptor implements ServerInterceptor {
|
|||
rateMetersString + "\n\t" + "}" + "\n"
|
||||
+ "}";
|
||||
}
|
||||
|
||||
public static CallRateMeteringInterceptor valueOf(Map<String, GrpcCallRateMeter> rateMeters) {
|
||||
return new CallRateMeteringInterceptor(new HashMap<>() {{
|
||||
putAll(rateMeters);
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ public class GrpcServiceRateMeteringConfig {
|
|||
this.methodRateMeters = methodRateMeters;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public GrpcServiceRateMeteringConfig addMethodCallRateMeter(String methodName,
|
||||
int maxCalls,
|
||||
TimeUnit timeUnit) {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
# pull base image
|
||||
FROM openjdk:8-jdk
|
||||
ENV version 1.5.5-SNAPSHOT
|
||||
ENV version 1.5.6-SNAPSHOT
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends openjfx && rm -rf /var/lib/apt/lists/* &&
|
||||
apt-get install -y vim fakeroot
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
# - Update version below
|
||||
# - Ensure JAVA_HOME below is pointing to OracleJDK 10 directory
|
||||
|
||||
version=1.5.5-SNAPSHOT
|
||||
version=1.5.6-SNAPSHOT
|
||||
version_base=$(echo $version | awk -F'[_-]' '{print $1}')
|
||||
if [ ! -f "$JAVA_HOME/bin/javapackager" ]; then
|
||||
if [ -d "/usr/lib/jvm/jdk-10.0.2" ]; then
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
# Prior to running this script:
|
||||
# - Update version below
|
||||
|
||||
version=1.5.5-SNAPSHOT
|
||||
version=1.5.6-SNAPSHOT
|
||||
base_dir=$( cd "$(dirname "$0")" ; pwd -P )/../../..
|
||||
package_dir=$base_dir/desktop/package
|
||||
release_dir=$base_dir/desktop/release/$version
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.5.5</string>
|
||||
<string>1.5.6</string>
|
||||
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.5.5</string>
|
||||
<string>1.5.6</string>
|
||||
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Bisq</string>
|
||||
|
|
|
@ -6,7 +6,7 @@ mkdir -p deploy
|
|||
|
||||
set -e
|
||||
|
||||
version="1.5.5-SNAPSHOT"
|
||||
version="1.5.6-SNAPSHOT"
|
||||
|
||||
cd ..
|
||||
./gradlew :desktop:build -x test shadowJar
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
cd ../../
|
||||
|
||||
version="1.5.5-SNAPSHOT"
|
||||
version="1.5.6-SNAPSHOT"
|
||||
|
||||
target_dir="releases/$version"
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
cd $(dirname $0)/../../../
|
||||
|
||||
version=1.5.5
|
||||
version=1.5.6
|
||||
|
||||
find . -type f \( -name "finalize.sh" \
|
||||
-o -name "create_app.sh" \
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
cd $(dirname $0)/../../../.
|
||||
|
||||
oldVersion=1.5.4
|
||||
newVersion=1.5.5
|
||||
oldVersion=1.5.5
|
||||
newVersion=1.5.6
|
||||
|
||||
find . -type f \( -name "finalize.sh" \
|
||||
-o -name "create_app.sh" \
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
@echo off
|
||||
|
||||
set version=1.5.5-SNAPSHOT
|
||||
set version=1.5.6-SNAPSHOT
|
||||
if not exist "%JAVA_HOME%\bin\javapackager.exe" (
|
||||
if not exist "%ProgramFiles%\Java\jdk-10.0.2" (
|
||||
echo Javapackager not found. Update JAVA_HOME variable to point to OracleJDK.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
@echo off
|
||||
|
||||
set version=1.5.5-SNAPSHOT
|
||||
set version=1.5.6-SNAPSHOT
|
||||
set release_dir=%~dp0..\..\..\releases\%version%
|
||||
set package_dir=%~dp0..
|
||||
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.desktop.components;
|
||||
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
||||
import bisq.core.btc.listeners.AddressConfidenceListener;
|
||||
import bisq.core.btc.listeners.BalanceListener;
|
||||
import bisq.core.btc.wallet.BtcWalletService;
|
||||
import bisq.core.util.coin.CoinFormatter;
|
||||
|
||||
import org.bitcoinj.core.Address;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import com.jfoenix.controls.JFXTextField;
|
||||
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.effect.BlurType;
|
||||
import javafx.scene.effect.DropShadow;
|
||||
import javafx.scene.effect.Effect;
|
||||
import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.paint.Color;
|
||||
|
||||
public class BalanceWithConfirmationTextField extends AnchorPane {
|
||||
|
||||
private static BtcWalletService walletService;
|
||||
private BalanceListener balanceListener;
|
||||
private AddressConfidenceListener confidenceListener;
|
||||
|
||||
public static void setWalletService(BtcWalletService walletService) {
|
||||
BalanceWithConfirmationTextField.walletService = walletService;
|
||||
}
|
||||
|
||||
private final TextField textField;
|
||||
private final Tooltip progressIndicatorTooltip;
|
||||
private final TxConfidenceIndicator txConfidenceIndicator;
|
||||
|
||||
private final Effect fundedEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.GREEN, 4, 0.0, 0, 0);
|
||||
private final Effect notFundedEffect = new DropShadow(BlurType.THREE_PASS_BOX, Color.ORANGERED, 4, 0.0, 0, 0);
|
||||
private CoinFormatter formatter;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public BalanceWithConfirmationTextField() {
|
||||
textField = new JFXTextField();
|
||||
textField.setFocusTraversable(false);
|
||||
textField.setEditable(false);
|
||||
|
||||
txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
txConfidenceIndicator.setFocusTraversable(false);
|
||||
txConfidenceIndicator.setPrefSize(24, 24);
|
||||
txConfidenceIndicator.setId("funds-confidence");
|
||||
txConfidenceIndicator.setLayoutY(1);
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
txConfidenceIndicator.setVisible(false);
|
||||
|
||||
progressIndicatorTooltip = new Tooltip("-");
|
||||
Tooltip.install(txConfidenceIndicator, progressIndicatorTooltip);
|
||||
|
||||
AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0);
|
||||
AnchorPane.setRightAnchor(textField, 55.0);
|
||||
AnchorPane.setLeftAnchor(textField, 0.0);
|
||||
|
||||
getChildren().addAll(textField, txConfidenceIndicator);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
walletService.removeAddressConfidenceListener(confidenceListener);
|
||||
}
|
||||
|
||||
public void setup(Address address, CoinFormatter formatter) {
|
||||
this.formatter = formatter;
|
||||
confidenceListener = new AddressConfidenceListener(address) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
updateConfidence(confidence);
|
||||
}
|
||||
};
|
||||
walletService.addAddressConfidenceListener(confidenceListener);
|
||||
updateConfidence(walletService.getConfidenceForAddress(address));
|
||||
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balance, Transaction tx) {
|
||||
updateBalance(balance);
|
||||
}
|
||||
};
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
updateBalance(walletService.getBalanceForAddress(address));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void updateConfidence(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, progressIndicatorTooltip, txConfidenceIndicator);
|
||||
if (confidence != null) {
|
||||
if (txConfidenceIndicator.getProgress() != 0) {
|
||||
txConfidenceIndicator.setVisible(true);
|
||||
AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0);
|
||||
AnchorPane.setRightAnchor(textField, 35.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBalance(Coin balance) {
|
||||
textField.setText(formatter.formatCoinWithCode(balance));
|
||||
if (balance.isPositive())
|
||||
textField.setEffect(fundedEffect);
|
||||
else
|
||||
textField.setEffect(notFundedEffect);
|
||||
}
|
||||
|
||||
}
|
|
@ -18,10 +18,11 @@
|
|||
package bisq.desktop.components.paymentmethods;
|
||||
|
||||
import bisq.desktop.components.InputTextField;
|
||||
import bisq.desktop.util.FormBuilder;
|
||||
import bisq.desktop.util.Layout;
|
||||
|
||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||
import bisq.core.locale.Country;
|
||||
import bisq.core.locale.CountryUtil;
|
||||
import bisq.core.locale.CurrencyUtil;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.locale.TradeCurrency;
|
||||
|
@ -33,25 +34,41 @@ import bisq.core.payment.payload.PaymentMethod;
|
|||
import bisq.core.util.coin.CoinFormatter;
|
||||
import bisq.core.util.validation.InputValidator;
|
||||
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.GridPane;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField;
|
||||
import static bisq.desktop.util.FormBuilder.addInputTextField;
|
||||
import static bisq.desktop.util.FormBuilder.addTopLabelTextField;
|
||||
import static bisq.desktop.util.FormBuilder.*;
|
||||
|
||||
@Slf4j
|
||||
public class AmazonGiftCardForm extends PaymentMethodForm {
|
||||
private InputTextField accountNrInputTextField;
|
||||
ComboBox<Country> countryCombo;
|
||||
private final AmazonGiftCardAccount amazonGiftCardAccount;
|
||||
|
||||
public static int addFormForBuyer(GridPane gridPane, int gridRow, PaymentAccountPayload paymentAccountPayload) {
|
||||
FormBuilder.addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, Res.get("payment.email.mobile"),
|
||||
((AmazonGiftCardAccountPayload) paymentAccountPayload).getEmailOrMobileNr());
|
||||
AmazonGiftCardAccountPayload amazonGiftCardAccountPayload = (AmazonGiftCardAccountPayload) paymentAccountPayload;
|
||||
|
||||
addTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1, Res.get("payment.amazon.site"),
|
||||
countryToAmazonSite(amazonGiftCardAccountPayload.getCountryCode()),
|
||||
Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE);
|
||||
addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow, Res.get("payment.email.mobile"),
|
||||
amazonGiftCardAccountPayload.getEmailOrMobileNr());
|
||||
String countryText = CountryUtil.getNameAndCode(amazonGiftCardAccountPayload.getCountryCode());
|
||||
if (countryText.isEmpty()) {
|
||||
countryText = Res.get("payment.ask");
|
||||
}
|
||||
addCompactTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1,
|
||||
Res.get("shared.country"),
|
||||
countryText);
|
||||
return gridRow;
|
||||
}
|
||||
|
||||
|
@ -66,11 +83,6 @@ public class AmazonGiftCardForm extends PaymentMethodForm {
|
|||
this.amazonGiftCardAccount = (AmazonGiftCardAccount) paymentAccount;
|
||||
}
|
||||
|
||||
public void addTradeCurrency() {
|
||||
addTradeCurrencyComboBox();
|
||||
currencyComboBox.setItems(FXCollections.observableArrayList(CurrencyUtil.getAllAmazonGiftCardCurrencies()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFormForAddAccount() {
|
||||
gridRowFrom = gridRow + 1;
|
||||
|
@ -82,13 +94,33 @@ public class AmazonGiftCardForm extends PaymentMethodForm {
|
|||
updateFromInputs();
|
||||
});
|
||||
|
||||
addTradeCurrency();
|
||||
countryCombo = addComboBox(gridPane, ++gridRow, Res.get("shared.country"));
|
||||
countryCombo.setPromptText(Res.get("payment.select.country"));
|
||||
countryCombo.setItems(FXCollections.observableArrayList(CountryUtil.getAllAmazonGiftCardCountries()));
|
||||
TextField ccyField = addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.currency"), "").second;
|
||||
countryCombo.setConverter(new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Country country) {
|
||||
return country.name + " (" + country.code + ")";
|
||||
}
|
||||
@Override
|
||||
public Country fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
countryCombo.setOnAction(e -> {
|
||||
Country countryCode = countryCombo.getValue();
|
||||
amazonGiftCardAccount.setCountry(countryCode);
|
||||
TradeCurrency currency = CurrencyUtil.getCurrencyByCountryCode(countryCode.code);
|
||||
paymentAccount.setSingleTradeCurrency(currency);
|
||||
ccyField.setText(currency.getNameAndCode());
|
||||
updateFromInputs();
|
||||
});
|
||||
|
||||
addLimitations(false);
|
||||
addAccountNameTextFieldWithAutoFillToggleButton();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void autoFillNameTextField() {
|
||||
setAccountNameWithString(accountNrInputTextField.getText());
|
||||
|
@ -121,9 +153,33 @@ public class AmazonGiftCardForm extends PaymentMethodForm {
|
|||
Res.get("payment.email.mobile"), accountNr).second;
|
||||
field.setMouseTransparent(false);
|
||||
|
||||
addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("payment.country"),
|
||||
amazonGiftCardAccount.getCountry() != null ? amazonGiftCardAccount.getCountry().name : "");
|
||||
String nameAndCode = singleTradeCurrency != null ? singleTradeCurrency.getNameAndCode() : "";
|
||||
addCompactTopLabelTextField(gridPane, ++gridRow, Res.get("shared.currency"), nameAndCode);
|
||||
|
||||
addLimitations(true);
|
||||
}
|
||||
|
||||
private static String countryToAmazonSite(String countryCode) {
|
||||
HashMap<String, String> mapCountryToSite = new HashMap<>() {{
|
||||
put("AU", "https://www.amazon.au");
|
||||
put("CA", "https://www.amazon.ca");
|
||||
put("FR", "https://www.amazon.fr");
|
||||
put("DE", "https://www.amazon.de");
|
||||
put("IT", "https://www.amazon.it");
|
||||
put("NL", "https://www.amazon.nl");
|
||||
put("ES", "https://www.amazon.es");
|
||||
put("UK", "https://www.amazon.co.uk");
|
||||
put("IN", "https://www.amazon.in");
|
||||
put("JP", "https://www.amazon.co.jp");
|
||||
put("SA", "https://www.amazon.sa");
|
||||
put("SE", "https://www.amazon.se");
|
||||
put("SG", "https://www.amazon.sg");
|
||||
put("TR", "https://www.amazon.tr");
|
||||
put("US", "https://www.amazon.com");
|
||||
put("", Res.get("payment.ask"));
|
||||
}};
|
||||
return mapCountryToSite.get(countryCode);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package bisq.desktop.main;
|
|||
|
||||
import bisq.desktop.app.BisqApp;
|
||||
import bisq.desktop.common.model.ViewModel;
|
||||
import bisq.desktop.components.BalanceWithConfirmationTextField;
|
||||
import bisq.desktop.components.TxIdTextField;
|
||||
import bisq.desktop.main.overlays.Overlay;
|
||||
import bisq.desktop.main.overlays.notifications.NotificationCenter;
|
||||
|
@ -27,6 +26,7 @@ import bisq.desktop.main.overlays.popups.Popup;
|
|||
import bisq.desktop.main.overlays.windows.DisplayAlertMessageWindow;
|
||||
import bisq.desktop.main.overlays.windows.TacWindow;
|
||||
import bisq.desktop.main.overlays.windows.TorNetworkSettingsWindow;
|
||||
import bisq.desktop.main.overlays.windows.UpdateAmazonGiftCardAccountWindow;
|
||||
import bisq.desktop.main.overlays.windows.UpdateRevolutAccountWindow;
|
||||
import bisq.desktop.main.overlays.windows.WalletPasswordWindow;
|
||||
import bisq.desktop.main.overlays.windows.downloadupdate.DisplayUpdateDownloadWindow;
|
||||
|
@ -51,6 +51,7 @@ import bisq.core.locale.Res;
|
|||
import bisq.core.offer.OpenOffer;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.payment.AliPayAccount;
|
||||
import bisq.core.payment.AmazonGiftCardAccount;
|
||||
import bisq.core.payment.CryptoCurrencyAccount;
|
||||
import bisq.core.payment.RevolutAccount;
|
||||
import bisq.core.payment.payload.AssetsAccountPayload;
|
||||
|
@ -207,7 +208,6 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
|||
TxIdTextField.setPreferences(preferences);
|
||||
|
||||
TxIdTextField.setWalletService(btcWalletService);
|
||||
BalanceWithConfirmationTextField.setWalletService(btcWalletService);
|
||||
|
||||
GUIUtil.setFeeService(feeService);
|
||||
GUIUtil.setPreferences(preferences);
|
||||
|
@ -410,6 +410,10 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
|||
// We copy the array as we will mutate it later
|
||||
showRevolutAccountUpdateWindow(new ArrayList<>(revolutAccountList));
|
||||
});
|
||||
bisqSetup.setAmazonGiftCardAccountsUpdateHandler(amazonGiftCardAccountList -> {
|
||||
// We copy the array as we will mutate it later
|
||||
showAmazonGiftCardAccountUpdateWindow(new ArrayList<>(amazonGiftCardAccountList));
|
||||
});
|
||||
bisqSetup.setOsxKeyLoggerWarningHandler(() -> {
|
||||
String key = "osxKeyLoggerWarning";
|
||||
if (preferences.showAgain(key)) {
|
||||
|
@ -487,6 +491,17 @@ public class MainViewModel implements ViewModel, BisqSetup.BisqSetupListener {
|
|||
}
|
||||
}
|
||||
|
||||
private void showAmazonGiftCardAccountUpdateWindow(List<AmazonGiftCardAccount> amazonGiftCardAccountList) {
|
||||
if (!amazonGiftCardAccountList.isEmpty()) {
|
||||
AmazonGiftCardAccount amazonGiftCardAccount = amazonGiftCardAccountList.get(0);
|
||||
amazonGiftCardAccountList.remove(0);
|
||||
new UpdateAmazonGiftCardAccountWindow(amazonGiftCardAccount, user).onClose(() -> {
|
||||
// We delay a bit in case we have multiple account for better UX
|
||||
UserThread.runAfter(() -> showAmazonGiftCardAccountUpdateWindow(amazonGiftCardAccountList), 300, TimeUnit.MILLISECONDS);
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupP2PNumPeersWatcher() {
|
||||
p2PService.getNumConnectedPeers().addListener((observable, oldValue, newValue) -> {
|
||||
int numPeers = (int) newValue;
|
||||
|
|
|
@ -32,67 +32,84 @@ import org.bitcoinj.core.Coin;
|
|||
import org.bitcoinj.core.Transaction;
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import javafx.scene.control.Tooltip;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
class DepositListItem {
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private final StringProperty balance = new SimpleStringProperty();
|
||||
private final BtcWalletService walletService;
|
||||
private Coin balanceAsCoin;
|
||||
private final TxConfidenceIndicator txConfidenceIndicator;
|
||||
private final Tooltip tooltip;
|
||||
private final String addressString;
|
||||
private String usage = "-";
|
||||
private TxConfidenceListener txConfidenceListener;
|
||||
private BalanceListener balanceListener;
|
||||
private int numTxOutputs = 0;
|
||||
private final Supplier<LazyFields> lazyFieldsSupplier;
|
||||
|
||||
public DepositListItem(AddressEntry addressEntry, BtcWalletService walletService, CoinFormatter formatter) {
|
||||
private static class LazyFields {
|
||||
TxConfidenceIndicator txConfidenceIndicator;
|
||||
Tooltip tooltip;
|
||||
}
|
||||
|
||||
private LazyFields lazy() {
|
||||
return lazyFieldsSupplier.get();
|
||||
}
|
||||
|
||||
DepositListItem(AddressEntry addressEntry, BtcWalletService walletService, CoinFormatter formatter) {
|
||||
this.walletService = walletService;
|
||||
|
||||
addressString = addressEntry.getAddressString();
|
||||
|
||||
// confidence
|
||||
txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
txConfidenceIndicator.setId("funds-confidence");
|
||||
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
txConfidenceIndicator.setTooltip(tooltip);
|
||||
Address address = addressEntry.getAddress();
|
||||
TransactionConfidence confidence = walletService.getConfidenceForAddress(address);
|
||||
|
||||
final Address address = addressEntry.getAddress();
|
||||
walletService.addBalanceListener(new BalanceListener(address) {
|
||||
// confidence
|
||||
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
|
||||
txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
txConfidenceIndicator.setId("funds-confidence");
|
||||
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
txConfidenceIndicator.setTooltip(tooltip);
|
||||
if (confidence != null) {
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
}
|
||||
}});
|
||||
|
||||
if (confidence != null) {
|
||||
txConfidenceListener = new TxConfidenceListener(confidence.getTransactionHash().toString()) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
}
|
||||
};
|
||||
walletService.addTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
balanceListener = new BalanceListener(address) {
|
||||
@Override
|
||||
public void onBalanceChanged(Coin balanceAsCoin, Transaction tx) {
|
||||
DepositListItem.this.balanceAsCoin = balanceAsCoin;
|
||||
DepositListItem.this.balance.set(formatter.formatCoin(balanceAsCoin));
|
||||
GUIUtil.updateConfidence(walletService.getConfidenceForTxId(tx.getTxId().toString()), tooltip, txConfidenceIndicator);
|
||||
var confidence = walletService.getConfidenceForTxId(tx.getTxId().toString());
|
||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
updateUsage(address);
|
||||
}
|
||||
});
|
||||
};
|
||||
walletService.addBalanceListener(balanceListener);
|
||||
|
||||
balanceAsCoin = walletService.getBalanceForAddress(address);
|
||||
balance.set(formatter.formatCoin(balanceAsCoin));
|
||||
|
||||
updateUsage(address);
|
||||
|
||||
TransactionConfidence confidence = walletService.getConfidenceForAddress(address);
|
||||
if (confidence != null) {
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
|
||||
txConfidenceListener = new TxConfidenceListener(confidence.getTransactionHash().toString()) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
}
|
||||
};
|
||||
walletService.addTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUsage(Address address) {
|
||||
|
@ -102,10 +119,11 @@ class DepositListItem {
|
|||
|
||||
public void cleanup() {
|
||||
walletService.removeTxConfidenceListener(txConfidenceListener);
|
||||
walletService.removeBalanceListener(balanceListener);
|
||||
}
|
||||
|
||||
public TxConfidenceIndicator getTxConfidenceIndicator() {
|
||||
return txConfidenceIndicator;
|
||||
return lazy().txConfidenceIndicator;
|
||||
}
|
||||
|
||||
public String getAddressString() {
|
||||
|
|
|
@ -36,13 +36,11 @@ import org.bitcoinj.core.TransactionOutput;
|
|||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
||||
@Slf4j
|
||||
class TransactionAwareTrade implements TransactionAwareTradable {
|
||||
private final Trade trade;
|
||||
|
@ -65,32 +63,31 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
|
||||
@Override
|
||||
public boolean isRelatedToTransaction(Transaction transaction) {
|
||||
String txId = transaction.getTxId().toString();
|
||||
Sha256Hash hash = transaction.getTxId();
|
||||
String txId = hash.toString();
|
||||
|
||||
boolean isTakerOfferFeeTx = txId.equals(trade.getTakerFeeTxId());
|
||||
boolean isOfferFeeTx = isOfferFeeTx(txId);
|
||||
boolean isDepositTx = isDepositTx(txId);
|
||||
boolean isPayoutTx = isPayoutTx(txId);
|
||||
boolean isDepositTx = isDepositTx(hash);
|
||||
boolean isPayoutTx = isPayoutTx(hash);
|
||||
boolean isDisputedPayoutTx = isDisputedPayoutTx(txId);
|
||||
boolean isDelayedPayoutTx = isDelayedPayoutTx(txId);
|
||||
boolean isDelayedPayoutTx = transaction.getLockTime() != 0 && isDelayedPayoutTx(txId);
|
||||
boolean isRefundPayoutTx = isRefundPayoutTx(txId);
|
||||
|
||||
return isTakerOfferFeeTx || isOfferFeeTx || isDepositTx || isPayoutTx ||
|
||||
isDisputedPayoutTx || isDelayedPayoutTx || isRefundPayoutTx;
|
||||
}
|
||||
|
||||
private boolean isPayoutTx(String txId) {
|
||||
private boolean isPayoutTx(Sha256Hash txId) {
|
||||
return Optional.ofNullable(trade.getPayoutTx())
|
||||
.map(Transaction::getTxId)
|
||||
.map(Sha256Hash::toString)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
||||
private boolean isDepositTx(String txId) {
|
||||
private boolean isDepositTx(Sha256Hash txId) {
|
||||
return Optional.ofNullable(trade.getDepositTx())
|
||||
.map(Transaction::getTxId)
|
||||
.map(Sha256Hash::toString)
|
||||
.map(hash -> hash.equals(txId))
|
||||
.orElse(false);
|
||||
}
|
||||
|
@ -104,9 +101,11 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
|
||||
private boolean isDisputedPayoutTx(String txId) {
|
||||
String delegateId = trade.getId();
|
||||
|
||||
ObservableList<Dispute> disputes = arbitrationManager.getDisputesAsObservableList();
|
||||
return disputes.stream()
|
||||
|
||||
boolean isAnyDisputeRelatedToThis = arbitrationManager.getDisputedTradeIds().contains(trade.getId());
|
||||
|
||||
return isAnyDisputeRelatedToThis && disputes.stream()
|
||||
.anyMatch(dispute -> {
|
||||
String disputePayoutTxId = dispute.getDisputePayoutTxId();
|
||||
boolean isDisputePayoutTx = txId.equals(disputePayoutTxId);
|
||||
|
@ -139,42 +138,37 @@ class TransactionAwareTrade implements TransactionAwareTradable {
|
|||
if (parentTransaction == null) {
|
||||
return false;
|
||||
}
|
||||
return isDepositTx(parentTransaction.getTxId().toString());
|
||||
return isDepositTx(parentTransaction.getTxId());
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isRefundPayoutTx(String txId) {
|
||||
String tradeId = trade.getId();
|
||||
ObservableList<Dispute> disputes = refundManager.getDisputesAsObservableList();
|
||||
AtomicBoolean isRefundTx = new AtomicBoolean(false);
|
||||
AtomicBoolean isDisputeRelatedToThis = new AtomicBoolean(false);
|
||||
disputes.forEach(dispute -> {
|
||||
String disputeTradeId = dispute.getTradeId();
|
||||
isDisputeRelatedToThis.set(tradeId.equals(disputeTradeId));
|
||||
if (isDisputeRelatedToThis.get()) {
|
||||
Transaction tx = btcWalletService.getTransaction(txId);
|
||||
if (tx != null) {
|
||||
tx.getOutputs().forEach(txo -> {
|
||||
if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||
try {
|
||||
Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||
Contract contract = checkNotNull(trade.getContract());
|
||||
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||
contract.getBuyerPayoutAddressString() :
|
||||
contract.getSellerPayoutAddressString();
|
||||
if (receiverAddress != null && myPayoutAddressString.equals(receiverAddress.toString())) {
|
||||
isRefundTx.set(true);
|
||||
}
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
|
||||
boolean isAnyDisputeRelatedToThis = refundManager.getDisputedTradeIds().contains(tradeId);
|
||||
|
||||
if (isAnyDisputeRelatedToThis) {
|
||||
Transaction tx = btcWalletService.getTransaction(txId);
|
||||
if (tx != null) {
|
||||
for (TransactionOutput txo : tx.getOutputs()) {
|
||||
if (btcWalletService.isTransactionOutputMine(txo)) {
|
||||
try {
|
||||
Address receiverAddress = txo.getScriptPubKey().getToAddress(btcWalletService.getParams());
|
||||
Contract contract = checkNotNull(trade.getContract());
|
||||
String myPayoutAddressString = contract.isMyRoleBuyer(pubKeyRing) ?
|
||||
contract.getBuyerPayoutAddressString() :
|
||||
contract.getSellerPayoutAddressString();
|
||||
if (receiverAddress != null && myPayoutAddressString.equals(receiverAddress.toString())) {
|
||||
return true;
|
||||
}
|
||||
} catch (RuntimeException ignore) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return isRefundTx.get() && isDisputeRelatedToThis.get();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -40,10 +40,13 @@ import org.bitcoinj.core.Transaction;
|
|||
import org.bitcoinj.core.TransactionConfidence;
|
||||
import org.bitcoinj.core.TransactionOutput;
|
||||
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import javafx.scene.control.Tooltip;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -57,8 +60,6 @@ class TransactionsListItem {
|
|||
private String dateString;
|
||||
private final Date date;
|
||||
private final String txId;
|
||||
private final TxConfidenceIndicator txConfidenceIndicator;
|
||||
private final Tooltip tooltip;
|
||||
@Nullable
|
||||
private Tradable tradable;
|
||||
private String details = "";
|
||||
|
@ -72,16 +73,26 @@ class TransactionsListItem {
|
|||
private int confirmations = 0;
|
||||
@Getter
|
||||
private final boolean isDustAttackTx;
|
||||
private boolean initialTxConfidenceVisibility = true;
|
||||
private final Supplier<LazyFields> lazyFieldsSupplier;
|
||||
|
||||
private static class LazyFields {
|
||||
TxConfidenceIndicator txConfidenceIndicator;
|
||||
Tooltip tooltip;
|
||||
}
|
||||
|
||||
private LazyFields lazy() {
|
||||
return lazyFieldsSupplier.get();
|
||||
}
|
||||
|
||||
// used at exportCSV
|
||||
TransactionsListItem() {
|
||||
date = null;
|
||||
btcWalletService = null;
|
||||
txConfidenceIndicator = null;
|
||||
tooltip = null;
|
||||
txId = null;
|
||||
formatter = null;
|
||||
isDustAttackTx = false;
|
||||
lazyFieldsSupplier = null;
|
||||
}
|
||||
|
||||
TransactionsListItem(Transaction transaction,
|
||||
|
@ -181,26 +192,6 @@ class TransactionsListItem {
|
|||
//addressString = "";
|
||||
}
|
||||
|
||||
// confidence
|
||||
txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
txConfidenceIndicator.setId("funds-confidence");
|
||||
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
txConfidenceIndicator.setTooltip(tooltip);
|
||||
|
||||
txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}
|
||||
};
|
||||
btcWalletService.addTxConfidenceListener(txConfidenceListener);
|
||||
TransactionConfidence confidence = transaction.getConfidence();
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
|
||||
|
||||
if (optionalTradable.isPresent()) {
|
||||
tradable = optionalTradable.get();
|
||||
detailsAvailable = true;
|
||||
|
@ -225,7 +216,7 @@ class TransactionsListItem {
|
|||
details = Res.get("funds.tx.multiSigPayout", tradeId);
|
||||
|
||||
if (amountAsCoin.isZero()) {
|
||||
txConfidenceIndicator.setVisible(false);
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else {
|
||||
Trade.DisputeState disputeState = trade.getDisputeState();
|
||||
|
@ -234,7 +225,7 @@ class TransactionsListItem {
|
|||
details = Res.get("funds.tx.disputePayout", tradeId);
|
||||
} else {
|
||||
details = Res.get("funds.tx.disputeLost", tradeId);
|
||||
txConfidenceIndicator.setVisible(false);
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else if (disputeState == Trade.DisputeState.REFUND_REQUEST_CLOSED ||
|
||||
disputeState == Trade.DisputeState.REFUND_REQUESTED ||
|
||||
|
@ -249,12 +240,12 @@ class TransactionsListItem {
|
|||
// left our wallet nor we received funds. So we set indicator invisible.
|
||||
amountAsCoin = Coin.ZERO;
|
||||
details = Res.get("funds.tx.collateralForRefund", tradeId);
|
||||
txConfidenceIndicator.setVisible(false);
|
||||
initialTxConfidenceVisibility = false;
|
||||
}
|
||||
} else {
|
||||
if (transactionAwareTrade.isDelayedPayoutTx(txId)) {
|
||||
details = Res.get("funds.tx.timeLockedPayoutTx", tradeId);
|
||||
txConfidenceIndicator.setVisible(false);
|
||||
initialTxConfidenceVisibility = false;
|
||||
} else {
|
||||
details = Res.get("funds.tx.unknown", tradeId);
|
||||
}
|
||||
|
@ -265,7 +256,7 @@ class TransactionsListItem {
|
|||
} else {
|
||||
if (amountAsCoin.isZero()) {
|
||||
details = Res.get("funds.tx.noFundsFromDispute");
|
||||
txConfidenceIndicator.setVisible(false);
|
||||
initialTxConfidenceVisibility = false;
|
||||
} else if (withdrawalFromBSQWallet) {
|
||||
details = Res.get("funds.tx.withdrawnFromBSQWallet");
|
||||
} else if (!txFeeForBsqPayment) {
|
||||
|
@ -282,6 +273,29 @@ class TransactionsListItem {
|
|||
if (isDustAttackTx) {
|
||||
details = Res.get("funds.tx.dustAttackTx");
|
||||
}
|
||||
|
||||
// confidence
|
||||
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
|
||||
txConfidenceIndicator = new TxConfidenceIndicator();
|
||||
txConfidenceIndicator.setId("funds-confidence");
|
||||
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||
txConfidenceIndicator.setProgress(0);
|
||||
txConfidenceIndicator.setTooltip(tooltip);
|
||||
txConfidenceIndicator.setVisible(initialTxConfidenceVisibility);
|
||||
|
||||
TransactionConfidence confidence = transaction.getConfidence();
|
||||
GUIUtil.updateConfidence(confidence, tooltip, txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}});
|
||||
|
||||
txConfidenceListener = new TxConfidenceListener(txId) {
|
||||
@Override
|
||||
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
GUIUtil.updateConfidence(confidence, lazy().tooltip, lazy().txConfidenceIndicator);
|
||||
confirmations = confidence.getDepthInBlocks();
|
||||
}
|
||||
};
|
||||
btcWalletService.addTxConfidenceListener(txConfidenceListener);
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
|
@ -290,7 +304,7 @@ class TransactionsListItem {
|
|||
|
||||
|
||||
public TxConfidenceIndicator getTxConfidenceIndicator() {
|
||||
return txConfidenceIndicator;
|
||||
return lazy().txConfidenceIndicator;
|
||||
}
|
||||
|
||||
public final String getDateString() {
|
||||
|
@ -344,6 +358,7 @@ class TransactionsListItem {
|
|||
return String.valueOf(confirmations);
|
||||
}
|
||||
|
||||
public String getMemo() { return memo; }
|
||||
public String getMemo() {
|
||||
return memo;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -240,8 +240,10 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
volumeAxisYWidth = (double) newValue;
|
||||
layoutChart();
|
||||
};
|
||||
tradeStatisticsByCurrencyListener = c -> nrOfTradeStatisticsLabel.setText(Res.get("market.trades.nrOfTrades",
|
||||
model.selectedTradeStatistics.size()));
|
||||
tradeStatisticsByCurrencyListener = c -> {
|
||||
nrOfTradeStatisticsLabel.setText(Res.get("market.trades.nrOfTrades", model.selectedTradeStatistics.size()));
|
||||
fillList();
|
||||
};
|
||||
parentHeightListener = (observable, oldValue, newValue) -> layout();
|
||||
|
||||
priceColumnLabelListener = (o, oldVal, newVal) -> priceColumn.setGraphic(new AutoTooltipLabel(newVal));
|
||||
|
@ -308,7 +310,6 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
CurrencyListItem selectedItem = currencyComboBox.getSelectionModel().getSelectedItem();
|
||||
if (selectedItem != null) {
|
||||
model.onSetTradeCurrency(selectedItem.tradeCurrency);
|
||||
fillList();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -396,14 +397,12 @@ public class TradesChartsView extends ActivatableViewAndModel<VBox, TradesCharts
|
|||
}
|
||||
|
||||
private void fillList() {
|
||||
ObservableList<TradeStatistics3ListItem> tradeStatistics3ListItems = FXCollections.observableList(
|
||||
model.selectedTradeStatistics.stream()
|
||||
.map(tradeStatistics -> new TradeStatistics3ListItem(tradeStatistics,
|
||||
coinFormatter,
|
||||
model.showAllTradeCurrenciesProperty.get()))
|
||||
.collect(Collectors.toList()));
|
||||
listItems.clear();
|
||||
listItems.addAll(tradeStatistics3ListItems);
|
||||
List<TradeStatistics3ListItem> tradeStatistics3ListItems = model.selectedTradeStatistics.stream()
|
||||
.map(tradeStatistics -> new TradeStatistics3ListItem(tradeStatistics,
|
||||
coinFormatter,
|
||||
model.showAllTradeCurrenciesProperty.get()))
|
||||
.collect(Collectors.toList());
|
||||
listItems.setAll(tradeStatistics3ListItems);
|
||||
}
|
||||
|
||||
private void exportToCsv() {
|
||||
|
|
|
@ -792,6 +792,8 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
// if not reset here. Not clear why...
|
||||
triggerPriceValidationResult.set(new InputValidator.ValidationResult(true));
|
||||
|
||||
if (dataModel.getPrice().get() == null) // fix NPE @ bisq/issues/5166
|
||||
return;
|
||||
InputValidator.ValidationResult result = PriceUtil.isTriggerPriceValid(triggerPriceAsString,
|
||||
dataModel.getPrice().get(),
|
||||
dataModel.isSellOffer(),
|
||||
|
|
|
@ -50,7 +50,7 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
|
|||
|
||||
checkNotNull(alert, "alertMessage must not be null");
|
||||
|
||||
if (alert.isUpdateInfo()) {
|
||||
if (alert.isSoftwareUpdateNotification()) {
|
||||
information("");
|
||||
headLine = Res.get("displayAlertMessageWindow.update.headline");
|
||||
} else {
|
||||
|
@ -78,7 +78,7 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
|
|||
private void addContent() {
|
||||
checkNotNull(alert, "alertMessage must not be null");
|
||||
addMultilineLabel(gridPane, ++rowIndex, alert.getMessage(), 10);
|
||||
if (alert.isUpdateInfo()) {
|
||||
if (alert.isSoftwareUpdateNotification()) {
|
||||
String url = "https://bisq.network/downloads";
|
||||
HyperlinkWithIcon hyperlinkWithIcon = FormBuilder.addLabelHyperlinkWithIcon(gridPane, ++rowIndex,
|
||||
Res.get("displayAlertMessageWindow.update.download"), url, url).second;
|
||||
|
|
|
@ -38,7 +38,6 @@ import bisq.core.btc.wallet.TxBroadcaster;
|
|||
import bisq.core.dao.DaoFacade;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.support.SupportType;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.DisputeList;
|
||||
|
@ -105,7 +104,6 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
private final TradeWalletService tradeWalletService;
|
||||
private final BtcWalletService btcWalletService;
|
||||
private final TxFeeEstimationService txFeeEstimationService;
|
||||
private final FeeService feeService;
|
||||
private final DaoFacade daoFacade;
|
||||
private Dispute dispute;
|
||||
private Optional<Runnable> finalizeDisputeHandlerOptional = Optional.empty();
|
||||
|
@ -143,7 +141,6 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
TradeWalletService tradeWalletService,
|
||||
BtcWalletService btcWalletService,
|
||||
TxFeeEstimationService txFeeEstimationService,
|
||||
FeeService feeService,
|
||||
DaoFacade daoFacade) {
|
||||
|
||||
this.formatter = formatter;
|
||||
|
@ -152,7 +149,6 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
this.tradeWalletService = tradeWalletService;
|
||||
this.btcWalletService = btcWalletService;
|
||||
this.txFeeEstimationService = txFeeEstimationService;
|
||||
this.feeService = feeService;
|
||||
this.daoFacade = daoFacade;
|
||||
|
||||
type = Type.Confirmation;
|
||||
|
@ -672,7 +668,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
Coin sellerPayoutAmount = disputeResult.getSellerPayoutAmount();
|
||||
String sellerPayoutAddressString = contract.getSellerPayoutAddressString();
|
||||
Coin outputAmount = buyerPayoutAmount.add(sellerPayoutAmount);
|
||||
Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxVsize(outputAmount, feeService, btcWalletService);
|
||||
Tuple2<Coin, Integer> feeTuple = txFeeEstimationService.getEstimatedFeeAndTxVsize(outputAmount, btcWalletService);
|
||||
Coin fee = feeTuple.first;
|
||||
Integer txVsize = feeTuple.second;
|
||||
double feePerVbyte = CoinUtil.getFeePerVbyte(fee, txVsize);
|
||||
|
|
|
@ -39,7 +39,9 @@ import javafx.scene.Scene;
|
|||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.RadioButton;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
@ -49,6 +51,7 @@ import javafx.geometry.Insets;
|
|||
|
||||
import static bisq.desktop.util.FormBuilder.addInputTextField;
|
||||
import static bisq.desktop.util.FormBuilder.addLabelCheckBox;
|
||||
import static bisq.desktop.util.FormBuilder.addRadioButton;
|
||||
import static bisq.desktop.util.FormBuilder.addTopLabelTextArea;
|
||||
|
||||
public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
|
||||
|
@ -107,13 +110,26 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
|
|||
TextArea alertMessageTextArea = labelTextAreaTuple2.second;
|
||||
Label first = labelTextAreaTuple2.first;
|
||||
first.setMinWidth(150);
|
||||
CheckBox isUpdateCheckBox = addLabelCheckBox(gridPane, ++rowIndex,
|
||||
Res.get("sendAlertMessageWindow.isUpdate"));
|
||||
CheckBox isSoftwareUpdateCheckBox = addLabelCheckBox(gridPane, ++rowIndex,
|
||||
Res.get("sendAlertMessageWindow.isSoftwareUpdate"));
|
||||
HBox hBoxRelease = new HBox();
|
||||
hBoxRelease.setSpacing(10);
|
||||
GridPane.setRowIndex(hBoxRelease, ++rowIndex);
|
||||
|
||||
ToggleGroup toggleGroup = new ToggleGroup();
|
||||
RadioButton isUpdateCheckBox = addRadioButton(gridPane, rowIndex, toggleGroup, Res.get("sendAlertMessageWindow.isUpdate"));
|
||||
RadioButton isPreReleaseCheckBox = addRadioButton(gridPane, rowIndex, toggleGroup, Res.get("sendAlertMessageWindow.isPreRelease"));
|
||||
hBoxRelease.getChildren().addAll(new Label(""), isUpdateCheckBox, isPreReleaseCheckBox);
|
||||
gridPane.getChildren().add(hBoxRelease);
|
||||
|
||||
isSoftwareUpdateCheckBox.setSelected(true);
|
||||
isUpdateCheckBox.setSelected(true);
|
||||
|
||||
InputTextField versionInputTextField = FormBuilder.addInputTextField(gridPane, ++rowIndex,
|
||||
Res.get("sendAlertMessageWindow.version"));
|
||||
versionInputTextField.disableProperty().bind(isUpdateCheckBox.selectedProperty().not());
|
||||
versionInputTextField.disableProperty().bind(isSoftwareUpdateCheckBox.selectedProperty().not());
|
||||
isUpdateCheckBox.disableProperty().bind(isSoftwareUpdateCheckBox.selectedProperty().not());
|
||||
isPreReleaseCheckBox.disableProperty().bind(isSoftwareUpdateCheckBox.selectedProperty().not());
|
||||
|
||||
Button sendButton = new AutoTooltipButton(Res.get("sendAlertMessageWindow.send"));
|
||||
sendButton.getStyleClass().add("action-button");
|
||||
|
@ -121,8 +137,9 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
|
|||
sendButton.setOnAction(e -> {
|
||||
final String version = versionInputTextField.getText();
|
||||
boolean versionOK = false;
|
||||
final boolean isUpdate = isUpdateCheckBox.isSelected();
|
||||
if (isUpdate) {
|
||||
final boolean isUpdate = (isSoftwareUpdateCheckBox.isSelected() && isUpdateCheckBox.isSelected());
|
||||
final boolean isPreRelease = (isSoftwareUpdateCheckBox.isSelected() && isPreReleaseCheckBox.isSelected());
|
||||
if (isUpdate || isPreRelease) {
|
||||
final String[] split = version.split("\\.");
|
||||
versionOK = split.length == 3;
|
||||
if (!versionOK) // Do not translate as only used by devs
|
||||
|
@ -130,10 +147,10 @@ public class SendAlertMessageWindow extends Overlay<SendAlertMessageWindow> {
|
|||
.onClose(this::blurAgain)
|
||||
.show();
|
||||
}
|
||||
if (!isUpdate || versionOK) {
|
||||
if (!isSoftwareUpdateCheckBox.isSelected() || versionOK) {
|
||||
if (alertMessageTextArea.getText().length() > 0 && keyInputTextField.getText().length() > 0) {
|
||||
if (alertManager.addAlertMessageIfKeyIsValid(
|
||||
new Alert(alertMessageTextArea.getText(), isUpdate, version),
|
||||
new Alert(alertMessageTextArea.getText(), isUpdate, isPreRelease, version),
|
||||
keyInputTextField.getText())
|
||||
)
|
||||
hide();
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.desktop.main.overlays.windows;
|
||||
|
||||
import bisq.desktop.main.overlays.Overlay;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.locale.Country;
|
||||
import bisq.core.payment.AmazonGiftCardAccount;
|
||||
import bisq.core.user.User;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.control.ComboBox;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import javafx.util.StringConverter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static bisq.core.locale.CountryUtil.findCountryByCode;
|
||||
import static bisq.core.locale.CountryUtil.getAllAmazonGiftCardCountries;
|
||||
import static bisq.desktop.util.FormBuilder.addComboBox;
|
||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextField;
|
||||
import static bisq.desktop.util.FormBuilder.addLabel;
|
||||
|
||||
public class UpdateAmazonGiftCardAccountWindow extends Overlay<UpdateAmazonGiftCardAccountWindow> {
|
||||
private final AmazonGiftCardAccount amazonGiftCardAccount;
|
||||
private final User user;
|
||||
private ComboBox<Country> countryCombo;
|
||||
|
||||
public UpdateAmazonGiftCardAccountWindow(AmazonGiftCardAccount amazonGiftCardAccount, User user) {
|
||||
super();
|
||||
this.amazonGiftCardAccount = amazonGiftCardAccount;
|
||||
this.user = user;
|
||||
type = Type.Attention;
|
||||
hideCloseButton = true;
|
||||
actionButtonText = Res.get("shared.save");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupKeyHandler(Scene scene) {
|
||||
// We do not support enter or escape here
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
if (headLine == null)
|
||||
headLine = Res.get("payment.amazonGiftCard.upgrade.headLine");
|
||||
|
||||
width = 868;
|
||||
createGridPane();
|
||||
addHeadLine();
|
||||
addContent();
|
||||
addButtons();
|
||||
applyStyles();
|
||||
display();
|
||||
// when there is only one possible country to choose from just go ahead and choose it. e.g. UK, US, JP etc.
|
||||
if (countryCombo.getItems().size() == 1) {
|
||||
countryCombo.setValue(countryCombo.getItems().get(0));
|
||||
UserThread.runAfter(() -> actionButton.fire(), 300, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private void addContent() {
|
||||
addLabel(gridPane, ++rowIndex, Res.get("payment.account.amazonGiftCard.addCountryInfo", Res.get("payment.amazonGiftCard.upgrade"), amazonGiftCardAccount.getAccountName()));
|
||||
addCompactTopLabelTextField(gridPane, ++rowIndex, Res.get("shared.currency"), amazonGiftCardAccount.getSingleTradeCurrency().getNameAndCode());
|
||||
countryCombo = addComboBox(gridPane, ++rowIndex, Res.get("shared.country"));
|
||||
countryCombo.setPromptText(Res.get("payment.select.country"));
|
||||
countryCombo.setItems(FXCollections.observableArrayList(getAppropriateCountries(amazonGiftCardAccount.getSingleTradeCurrency().getCode())));
|
||||
countryCombo.setConverter(new StringConverter<>() {
|
||||
@Override
|
||||
public String toString(Country country) {
|
||||
return country.name + " (" + country.code + ")";
|
||||
}
|
||||
@Override
|
||||
public Country fromString(String s) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
countryCombo.setOnAction(e -> {
|
||||
Country countryCode = countryCombo.getValue();
|
||||
actionButton.setDisable(countryCode == null || countryCode.code == null || countryCode.code.length() < 1);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addButtons() {
|
||||
super.addButtons();
|
||||
Country countryCode = countryCombo.getValue();
|
||||
if (countryCode == null || countryCode.code == null || countryCode.code.isEmpty())
|
||||
actionButton.setDisable(true);
|
||||
// We do not allow close in case the field is not correctly added
|
||||
actionButton.setOnAction(event -> {
|
||||
Country chosenCountryCode = countryCombo.getValue();
|
||||
if (chosenCountryCode != null && chosenCountryCode.code != null && !chosenCountryCode.code.isEmpty()) {
|
||||
amazonGiftCardAccount.setCountry(chosenCountryCode);
|
||||
user.requestPersistence();
|
||||
closeHandlerOptional.ifPresent(Runnable::run);
|
||||
hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static List<Country> getAppropriateCountries(String currency) {
|
||||
List<Country> list = new ArrayList<>();
|
||||
if (currency.equalsIgnoreCase("EUR")) {
|
||||
// Eurozone countries using EUR
|
||||
list = getAllAmazonGiftCardCountries();
|
||||
list = list.stream().filter(e -> e.code.matches("FR|DE|IT|NL|ES")).collect(Collectors.toList());
|
||||
} else {
|
||||
// non-Eurozone with own ccy
|
||||
HashMap<String, String> mapCcyToCountry = new HashMap<>(Map.of(
|
||||
"AUD", "AU",
|
||||
"CAD", "CA",
|
||||
"GBP", "GB",
|
||||
"INR", "IN",
|
||||
"JPY", "JP",
|
||||
"SAR", "SA",
|
||||
"SEK", "SE",
|
||||
"SGD", "SG",
|
||||
"TRY", "TR",
|
||||
"USD", "US"
|
||||
));
|
||||
Optional<Country> found = findCountryByCode(mapCcyToCountry.get(currency));
|
||||
if (found.isPresent())
|
||||
list.add(found.get());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
|
@ -63,7 +63,6 @@ import bisq.core.network.MessageState;
|
|||
import bisq.core.offer.Offer;
|
||||
import bisq.core.payment.PaymentAccount;
|
||||
import bisq.core.payment.PaymentAccountUtil;
|
||||
import bisq.core.payment.payload.AmazonGiftCardAccountPayload;
|
||||
import bisq.core.payment.payload.AssetsAccountPayload;
|
||||
import bisq.core.payment.payload.CashByMailAccountPayload;
|
||||
import bisq.core.payment.payload.CashDepositAccountPayload;
|
||||
|
@ -569,9 +568,6 @@ public class BuyerStep2View extends TradeStepView {
|
|||
Res.get("portfolio.pending.step2_buyer.fasterPaymentsHolderNameInfo") + "\n\n" +
|
||||
refTextWarn + "\n\n" +
|
||||
fees;
|
||||
} else if (paymentAccountPayload instanceof AmazonGiftCardAccountPayload) {
|
||||
message += Res.get("portfolio.pending.step2_buyer.amazonGiftCard", amount) +
|
||||
refTextWarn;
|
||||
} else if (paymentAccountPayload instanceof CashByMailAccountPayload ||
|
||||
paymentAccountPayload instanceof HalCashAccountPayload) {
|
||||
message += Res.get("portfolio.pending.step2_buyer.pay", amount);
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
package bisq.desktop.main.presentation;
|
||||
|
||||
import bisq.desktop.components.BalanceWithConfirmationTextField;
|
||||
import bisq.desktop.components.TxIdTextField;
|
||||
import bisq.desktop.main.shared.PriceFeedComboBoxItem;
|
||||
import bisq.desktop.util.GUIUtil;
|
||||
|
@ -98,7 +97,6 @@ public class MarketPricePresentation {
|
|||
|
||||
// TODO
|
||||
TxIdTextField.setWalletService(btcWalletService);
|
||||
BalanceWithConfirmationTextField.setWalletService(btcWalletService);
|
||||
|
||||
GUIUtil.setFeeService(feeService);
|
||||
}
|
||||
|
|
|
@ -120,7 +120,8 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
private ComboBox<TradeCurrency> preferredTradeCurrencyComboBox;
|
||||
|
||||
private ToggleButton showOwnOffersInOfferBook, useAnimations, useDarkMode, sortMarketCurrenciesNumerically,
|
||||
avoidStandbyMode, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle;
|
||||
avoidStandbyMode, useCustomFee, autoConfirmXmrToggle, hideNonAccountPaymentMethodsToggle, denyApiTakerToggle,
|
||||
notifyOnPreReleaseToggle;
|
||||
private int gridRow = 0;
|
||||
private int displayCurrenciesGridRowIndex = 0;
|
||||
private InputTextField transactionFeeInputTextField, ignoreTradersListInputTextField, ignoreDustThresholdInputTextField,
|
||||
|
@ -612,6 +613,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
sortMarketCurrenciesNumerically = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.sortWithNumOffers"));
|
||||
hideNonAccountPaymentMethodsToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.onlyShowPaymentMethodsFromAccount"));
|
||||
denyApiTakerToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.denyApiTaker"));
|
||||
notifyOnPreReleaseToggle = addSlideToggleButton(root, ++gridRow, Res.get("setting.preferences.notifyOnPreRelease"));
|
||||
resetDontShowAgainButton = addButton(root, ++gridRow, Res.get("setting.preferences.resetAllFlags"), 0);
|
||||
resetDontShowAgainButton.getStyleClass().add("compact-button");
|
||||
resetDontShowAgainButton.setMaxWidth(Double.MAX_VALUE);
|
||||
|
@ -954,6 +956,9 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
denyApiTakerToggle.setSelected(preferences.isDenyApiTaker());
|
||||
denyApiTakerToggle.setOnAction(e -> preferences.setDenyApiTaker(denyApiTakerToggle.isSelected()));
|
||||
|
||||
notifyOnPreReleaseToggle.setSelected(preferences.isNotifyOnPreRelease());
|
||||
notifyOnPreReleaseToggle.setOnAction(e -> preferences.setNotifyOnPreRelease(notifyOnPreReleaseToggle.isSelected()));
|
||||
|
||||
resetDontShowAgainButton.setOnAction(e -> preferences.resetDontShowAgain());
|
||||
|
||||
editCustomBtcExplorer.setOnAction(e -> {
|
||||
|
@ -1133,6 +1138,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
|||
sortMarketCurrenciesNumerically.setOnAction(null);
|
||||
hideNonAccountPaymentMethodsToggle.setOnAction(null);
|
||||
denyApiTakerToggle.setOnAction(null);
|
||||
notifyOnPreReleaseToggle.setOnAction(null);
|
||||
showOwnOffersInOfferBook.setOnAction(null);
|
||||
resetDontShowAgainButton.setOnAction(null);
|
||||
if (displayStandbyModeFeature) {
|
||||
|
|
|
@ -648,7 +648,7 @@ public class ChatView extends AnchorPane {
|
|||
ChatMessage message = new ChatMessage(
|
||||
supportManager.getSupportType(),
|
||||
supportSession.getTradeId(),
|
||||
supportSession.getClientPubKeyRing().hashCode(),
|
||||
supportSession.getClientId(),
|
||||
supportSession.isClient(),
|
||||
text,
|
||||
supportManager.getMyAddress(),
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.bitcoinj.core.Transaction;
|
|||
|
||||
import javafx.collections.FXCollections;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -41,8 +41,6 @@ import static org.mockito.Mockito.when;
|
|||
public class TransactionAwareTradeTest {
|
||||
private static final Sha256Hash XID = Sha256Hash.wrap("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
|
||||
|
||||
|
||||
|
||||
private Transaction transaction;
|
||||
private ArbitrationManager arbitrationManager;
|
||||
private Trade delegate;
|
||||
|
@ -95,7 +93,10 @@ public class TransactionAwareTradeTest {
|
|||
when(dispute.getTradeId()).thenReturn(tradeId);
|
||||
|
||||
when(arbitrationManager.getDisputesAsObservableList())
|
||||
.thenReturn(FXCollections.observableArrayList(Collections.singleton(dispute)));
|
||||
.thenReturn(FXCollections.observableArrayList(Set.of(dispute)));
|
||||
|
||||
when(arbitrationManager.getDisputedTradeIds())
|
||||
.thenReturn(Set.of(tradeId));
|
||||
|
||||
when(delegate.getId()).thenReturn(tradeId);
|
||||
|
||||
|
|
|
@ -620,6 +620,7 @@ message Alert {
|
|||
string signature_as_base64 = 4;
|
||||
bytes owner_pub_key_bytes = 5;
|
||||
map<string, string> extra_data = 6;
|
||||
bool is_pre_release_info = 7;
|
||||
}
|
||||
|
||||
message Arbitrator {
|
||||
|
@ -1074,6 +1075,7 @@ message WesternUnionAccountPayload {
|
|||
|
||||
message AmazonGiftCardAccountPayload {
|
||||
string email_or_mobile_nr = 1;
|
||||
string country_code = 2;
|
||||
}
|
||||
|
||||
message SepaAccountPayload {
|
||||
|
@ -1638,6 +1640,7 @@ message PreferencesPayload {
|
|||
bool hide_non_account_payment_methods = 58;
|
||||
bool show_offers_matching_my_accounts = 59;
|
||||
bool deny_api_taker = 60;
|
||||
bool notify_on_pre_release = 61;
|
||||
}
|
||||
|
||||
message AutoConfirmSettings {
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.5.5-SNAPSHOT
|
||||
1.5.6-SNAPSHOT
|
||||
|
|
|
@ -47,7 +47,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public class SeedNodeMain extends ExecutableForAppWithP2p {
|
||||
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
|
||||
private static final String VERSION = "1.5.5";
|
||||
private static final String VERSION = "1.5.6";
|
||||
private SeedNode seedNode;
|
||||
private Timer checkConnectionLossTime;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue