support local, stagenet, and mainnet xmr network configuration (#335)
remove btc wallet disable local zmq
This commit is contained in:
parent
b4112e50e9
commit
e2208355b1
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -12,4 +12,4 @@
|
|||||||
*.jpg binary
|
*.jpg binary
|
||||||
*.jpeg binary
|
*.jpeg binary
|
||||||
*.png binary
|
*.png binary
|
||||||
p2p/src/main/resources/*XMR_TESTNET filter=lfs diff=lfs merge=lfs -text
|
p2p/src/main/resources/*XMR_LOCAL filter=lfs diff=lfs merge=lfs -text
|
||||||
|
226
Makefile
226
Makefile
@ -27,74 +27,180 @@ deploy:
|
|||||||
screen -dmS localnet
|
screen -dmS localnet
|
||||||
# deploy each node in its own named screen window
|
# deploy each node in its own named screen window
|
||||||
for target in \
|
for target in \
|
||||||
seednode \
|
seednode-local \
|
||||||
alice-desktop \
|
alice-desktop-local \
|
||||||
bob-desktop \
|
bob-desktop-local \
|
||||||
arbitrator-desktop; do \
|
arbitrator-desktop-local; do \
|
||||||
screen -S localnet -X screen -t $$target; \
|
screen -S localnet -X screen -t $$target; \
|
||||||
screen -S localnet -p $$target -X stuff "make $$target\n"; \
|
screen -S localnet -p $$target -X stuff "make $$target\n"; \
|
||||||
done;
|
done;
|
||||||
# give bitcoind rpc server time to start
|
# give bitcoind rpc server time to start
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
||||||
seednode:
|
monerod-local1:
|
||||||
|
./.localnet/monerod \
|
||||||
|
--testnet \
|
||||||
|
--no-igd \
|
||||||
|
--hide-my-port \
|
||||||
|
--data-dir .localnet/xmr_local/node1 \
|
||||||
|
--p2p-bind-ip 127.0.0.1 \
|
||||||
|
--p2p-bind-port 48080 \
|
||||||
|
--rpc-bind-port 48081 \
|
||||||
|
--no-zmq \
|
||||||
|
--add-exclusive-node 127.0.0.1:28080 \
|
||||||
|
--rpc-access-control-origins http://localhost:8080 \
|
||||||
|
--fixed-difficulty 100
|
||||||
|
|
||||||
|
monerod-local2:
|
||||||
|
./.localnet/monerod \
|
||||||
|
--testnet \
|
||||||
|
--no-igd \
|
||||||
|
--hide-my-port \
|
||||||
|
--data-dir .localnet/xmr_local/node2 \
|
||||||
|
--p2p-bind-ip 127.0.0.1 \
|
||||||
|
--rpc-bind-ip 0.0.0.0 \
|
||||||
|
--no-zmq \
|
||||||
|
--confirm-external-bind \
|
||||||
|
--add-exclusive-node 127.0.0.1:48080 \
|
||||||
|
--rpc-access-control-origins http://localhost:8080 \
|
||||||
|
--fixed-difficulty 100
|
||||||
|
|
||||||
|
monerod-stagenet:
|
||||||
|
./.localnet/monerod \
|
||||||
|
--stagenet \
|
||||||
|
--bootstrap-daemon-address auto \
|
||||||
|
--rpc-access-control-origins http://localhost:8080 \
|
||||||
|
|
||||||
|
funding-wallet-local:
|
||||||
|
./.localnet/monero-wallet-rpc \
|
||||||
|
--testnet \
|
||||||
|
--daemon-address http://localhost:28081 \
|
||||||
|
--rpc-bind-port 28084 \
|
||||||
|
--rpc-login rpc_user:abc123 \
|
||||||
|
--rpc-access-control-origins http://localhost:8080 \
|
||||||
|
--wallet-dir ./.localnet
|
||||||
|
|
||||||
|
funding-wallet-stagenet:
|
||||||
|
./.localnet/monero-wallet-rpc \
|
||||||
|
--stagenet \
|
||||||
|
--daemon-address http://localhost:38081 \
|
||||||
|
--rpc-bind-port 38084 \
|
||||||
|
--rpc-login rpc_user:abc123 \
|
||||||
|
--rpc-access-control-origins http://localhost:8080 \
|
||||||
|
--wallet-dir ./.localnet
|
||||||
|
|
||||||
|
seednode-local:
|
||||||
./haveno-seednode \
|
./haveno-seednode \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=true \
|
||||||
--nodePort=2002 \
|
--nodePort=2002 \
|
||||||
|
--appName=haveno-XMR_LOCAL_Seed_2002 \
|
||||||
|
|
||||||
|
seednode-stagenet:
|
||||||
|
./haveno-seednode \
|
||||||
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=false \
|
||||||
|
--nodePort=2002 \
|
||||||
--appName=haveno-XMR_STAGENET_Seed_2002 \
|
--appName=haveno-XMR_STAGENET_Seed_2002 \
|
||||||
|
|
||||||
arbitrator-desktop:
|
arbitrator-desktop-local:
|
||||||
# Arbitrator and mediator need to be registerd in the UI after launching it.
|
# Arbitrator needs to be registered before making trades
|
||||||
|
./haveno-desktop \
|
||||||
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=4444 \
|
||||||
|
--appName=haveno-XMR_LOCAL_arbitrator \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=9998
|
||||||
|
|
||||||
|
arbitrator-desktop-stagenet:
|
||||||
|
# Arbitrator needs to be registered before making trades
|
||||||
./haveno-desktop \
|
./haveno-desktop \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=false \
|
||||||
--nodePort=4444 \
|
--nodePort=4444 \
|
||||||
--appName=haveno-XMR_STAGENET_arbitrator \
|
--appName=haveno-XMR_STAGENET_arbitrator \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
--apiPort=9998
|
--apiPort=9998
|
||||||
|
|
||||||
arbitrator-desktop2:
|
arbitrator-desktop2-local:
|
||||||
# Arbitrator and mediator need to be registerd in the UI after launching it.
|
# Arbitrator needs to be registered before making trades
|
||||||
./haveno-desktop \
|
./haveno-desktop \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=true \
|
||||||
--nodePort=7777 \
|
--nodePort=7777 \
|
||||||
--appName=haveno-XMR_STAGENET_arbitrator2 \
|
--appName=haveno-XMR_LOCAL_arbitrator2 \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
--apiPort=10001
|
--apiPort=10001
|
||||||
|
|
||||||
arbitrator-daemon:
|
arbitrator-daemon-local:
|
||||||
# Arbitrator and mediator need to be registerd in the UI before launching the daemon!
|
# Arbitrator needs to be registered before making trades
|
||||||
|
./haveno-daemon \
|
||||||
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=4444 \
|
||||||
|
--appName=haveno-XMR_LOCAL_arbitrator \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=9998 \
|
||||||
|
--passwordRequired=false
|
||||||
|
|
||||||
|
arbitrator-daemon-stagenet:
|
||||||
|
# Arbitrator needs to be registered before making trades
|
||||||
./haveno-daemon \
|
./haveno-daemon \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=false \
|
||||||
--nodePort=4444 \
|
--nodePort=4444 \
|
||||||
--appName=haveno-XMR_STAGENET_arbitrator \
|
--appName=haveno-XMR_STAGENET_arbitrator \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
--apiPort=9998 \
|
--apiPort=9998 \
|
||||||
--passwordRequired=false
|
--passwordRequired=false
|
||||||
|
|
||||||
alice-desktop:
|
alice-desktop-local:
|
||||||
|
./haveno-desktop \
|
||||||
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=5555 \
|
||||||
|
--appName=haveno-XMR_LOCAL_Alice \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=9999 \
|
||||||
|
--walletRpcBindPort=38091
|
||||||
|
|
||||||
|
alice-desktop-stagenet:
|
||||||
./haveno-desktop \
|
./haveno-desktop \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=false \
|
||||||
--nodePort=5555 \
|
--nodePort=5555 \
|
||||||
--appName=haveno-XMR_STAGENET_Alice \
|
--appName=haveno-XMR_STAGENET_Alice \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
--apiPort=9999 \
|
--apiPort=9999 \
|
||||||
--walletRpcBindPort=38091
|
--walletRpcBindPort=38091
|
||||||
|
|
||||||
alice-daemon:
|
alice-daemon-local:
|
||||||
|
./haveno-daemon \
|
||||||
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=5555 \
|
||||||
|
--appName=haveno-XMR_LOCAL_Alice \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=9999 \
|
||||||
|
--walletRpcBindPort=38091 \
|
||||||
|
--passwordRequired=false
|
||||||
|
|
||||||
|
alice-daemon-stagenet:
|
||||||
./haveno-daemon \
|
./haveno-daemon \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=false \
|
||||||
--nodePort=5555 \
|
--nodePort=5555 \
|
||||||
--appName=haveno-XMR_STAGENET_Alice \
|
--appName=haveno-XMR_STAGENET_Alice \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
@ -102,22 +208,45 @@ alice-daemon:
|
|||||||
--walletRpcBindPort=38091 \
|
--walletRpcBindPort=38091 \
|
||||||
--passwordRequired=false
|
--passwordRequired=false
|
||||||
|
|
||||||
bob-desktop:
|
bob-desktop-local:
|
||||||
|
./haveno-desktop \
|
||||||
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=6666 \
|
||||||
|
--appName=haveno-XMR_LOCAL_Bob \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=10000 \
|
||||||
|
--walletRpcBindPort=38092
|
||||||
|
|
||||||
|
bob-desktop-stagenet:
|
||||||
./haveno-desktop \
|
./haveno-desktop \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=false \
|
||||||
--nodePort=6666 \
|
--nodePort=6666 \
|
||||||
--appName=haveno-XMR_STAGENET_Bob \
|
--appName=haveno-XMR_STAGENET_Bob \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
--apiPort=10000 \
|
--apiPort=10000 \
|
||||||
--walletRpcBindPort=38092
|
--walletRpcBindPort=38092
|
||||||
|
|
||||||
bob-daemon:
|
bob-daemon-local:
|
||||||
|
./haveno-daemon \
|
||||||
|
--baseCurrencyNetwork=XMR_LOCAL \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=6666 \
|
||||||
|
--appName=haveno-XMR_LOCAL_Bob \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=10000 \
|
||||||
|
--walletRpcBindPort=38092 \
|
||||||
|
--passwordRequired=false
|
||||||
|
|
||||||
|
bob-daemon-stagenet:
|
||||||
./haveno-daemon \
|
./haveno-daemon \
|
||||||
--baseCurrencyNetwork=XMR_STAGENET \
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
--useLocalhostForP2P=true \
|
--useLocalhostForP2P=true \
|
||||||
--useDevPrivilegeKeys=true \
|
--useDevPrivilegeKeys=false \
|
||||||
--nodePort=6666 \
|
--nodePort=6666 \
|
||||||
--appName=haveno-XMR_STAGENET_Bob \
|
--appName=haveno-XMR_STAGENET_Bob \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
@ -125,55 +254,6 @@ bob-daemon:
|
|||||||
--walletRpcBindPort=38092 \
|
--walletRpcBindPort=38092 \
|
||||||
--passwordRequired=false
|
--passwordRequired=false
|
||||||
|
|
||||||
monero-shared:
|
|
||||||
./.localnet/monerod \
|
|
||||||
--stagenet \
|
|
||||||
--no-igd \
|
|
||||||
--hide-my-port \
|
|
||||||
--data-dir .localnet/stagenet \
|
|
||||||
--add-exclusive-node 136.244.105.131:58080 \
|
|
||||||
--rpc-login superuser:abctesting123 \
|
|
||||||
--rpc-access-control-origins http://localhost:8080 \
|
|
||||||
|
|
||||||
monero-private1:
|
|
||||||
./.localnet/monerod \
|
|
||||||
--stagenet \
|
|
||||||
--no-igd \
|
|
||||||
--hide-my-port \
|
|
||||||
--data-dir .localnet/stagenet/node1 \
|
|
||||||
--p2p-bind-ip 127.0.0.1 \
|
|
||||||
--p2p-bind-port 48080 \
|
|
||||||
--rpc-bind-port 48081 \
|
|
||||||
--zmq-rpc-bind-port 48082 \
|
|
||||||
--add-exclusive-node 127.0.0.1:38080 \
|
|
||||||
--rpc-login superuser:abctesting123 \
|
|
||||||
--rpc-access-control-origins http://localhost:8080 \
|
|
||||||
--fixed-difficulty 100
|
|
||||||
|
|
||||||
monero-private2:
|
|
||||||
./.localnet/monerod \
|
|
||||||
--stagenet \
|
|
||||||
--no-igd \
|
|
||||||
--hide-my-port \
|
|
||||||
--data-dir .localnet/stagenet/node2 \
|
|
||||||
--p2p-bind-ip 127.0.0.1 \
|
|
||||||
--rpc-bind-ip 0.0.0.0 \
|
|
||||||
--confirm-external-bind \
|
|
||||||
--add-exclusive-node 127.0.0.1:48080 \
|
|
||||||
--rpc-login superuser:abctesting123 \
|
|
||||||
--rpc-access-control-origins http://localhost:8080 \
|
|
||||||
--fixed-difficulty 100
|
|
||||||
|
|
||||||
funding-wallet:
|
|
||||||
./.localnet/monero-wallet-rpc \
|
|
||||||
--stagenet \
|
|
||||||
--daemon-address http://localhost:38081 \
|
|
||||||
--daemon-login superuser:abctesting123 \
|
|
||||||
--rpc-bind-port 38084 \
|
|
||||||
--rpc-login rpc_user:abc123 \
|
|
||||||
--rpc-access-control-origins http://localhost:8080 \
|
|
||||||
--wallet-dir ./.localnet
|
|
||||||
|
|
||||||
bitcoind:
|
bitcoind:
|
||||||
./.localnet/bitcoind \
|
./.localnet/bitcoind \
|
||||||
-regtest \
|
-regtest \
|
||||||
|
@ -313,7 +313,7 @@ configure(project(':p2p')) {
|
|||||||
// If they have not, e.g. because Git LFS is not installed, they will be text files
|
// If they have not, e.g. because Git LFS is not installed, they will be text files
|
||||||
// containing a sha256 hash of the remote object, indicating we should stop the
|
// containing a sha256 hash of the remote object, indicating we should stop the
|
||||||
// build and inform the user how to fix the problem.
|
// build and inform the user how to fix the problem.
|
||||||
if (file('src/main/resources/AccountAgeWitnessStore_XMR_TESTNET_placeholder').text.contains("oid sha256:"))
|
if (file('src/main/resources/AccountAgeWitnessStore_XMR_LOCAL_placeholder').text.contains("oid sha256:"))
|
||||||
throw new GradleException("p2p data store files have not been synchronized. " +
|
throw new GradleException("p2p data store files have not been synchronized. " +
|
||||||
"To fix this, ensure you have Git LFS installed and run `git lfs pull`. " +
|
"To fix this, ensure you have Git LFS installed and run `git lfs pull`. " +
|
||||||
"See docs/build.md for more information.")
|
"See docs/build.md for more information.")
|
||||||
|
@ -106,7 +106,7 @@ public class Version {
|
|||||||
return p2pMessageVersion;
|
return p2pMessageVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The version for the crypto network (XMR_Mainnet = 0, XMR_Testnet = 1, XMR_Regtest = 2, ...)
|
// The version for the crypto network (XMR_Mainnet = 0, XMR_LOCAL = 1, XMR_Regtest = 2, ...)
|
||||||
private static int BASE_CURRENCY_NETWORK;
|
private static int BASE_CURRENCY_NETWORK;
|
||||||
|
|
||||||
public static void setBaseCryptoNetworkId(int baseCryptoNetworkId) {
|
public static void setBaseCryptoNetworkId(int baseCryptoNetworkId) {
|
||||||
|
@ -20,15 +20,14 @@ package bisq.common.config;
|
|||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.bitcoinj.params.MainNetParams;
|
import org.bitcoinj.params.MainNetParams;
|
||||||
import org.bitcoinj.params.RegTestParams;
|
import org.bitcoinj.params.RegTestParams;
|
||||||
import org.bitcoinj.params.TestNet3Params;
|
|
||||||
import org.bitcoinj.utils.MonetaryFormat;
|
import org.bitcoinj.utils.MonetaryFormat;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public enum BaseCurrencyNetwork {
|
public enum BaseCurrencyNetwork {
|
||||||
XMR_MAINNET(new XmrMainNetParams(), "XMR", "MAINNET", "Monero"), // TODO (woodser): network params are part of bitcoinj and shouldn't be needed. only used to get MonetaryFormat? replace with MonetaryFormat if so
|
XMR_MAINNET(new XmrMainNetParams(), "XMR", "MAINNET", "Monero"), // TODO (woodser): network params are part of bitcoinj and shouldn't be needed. only used to get MonetaryFormat? replace with MonetaryFormat if so
|
||||||
XMR_TESTNET(new XmrTestNet3Params(), "XMR", "TESTNET", "Monero"),
|
XMR_STAGENET(new XmrStageNetParams(), "XMR", "STAGENET", "Monero"),
|
||||||
XMR_STAGENET(new XmrRegTestParams(), "XMR", "STAGENET", "Monero");
|
XMR_LOCAL(new XmrTestNetParams(), "XMR", "TESTNET", "Monero");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final NetworkParameters parameters;
|
private final NetworkParameters parameters;
|
||||||
@ -51,7 +50,7 @@ public enum BaseCurrencyNetwork {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTestnet() {
|
public boolean isTestnet() {
|
||||||
return "XMR_TESTNET".equals(name());
|
return "XMR_LOCAL".equals(name());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStagenet() {
|
public boolean isStagenet() {
|
||||||
@ -71,14 +70,14 @@ public enum BaseCurrencyNetwork {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class XmrTestNet3Params extends TestNet3Params {
|
private static class XmrTestNetParams extends RegTestParams {
|
||||||
@Override
|
@Override
|
||||||
public MonetaryFormat getMonetaryFormat() {
|
public MonetaryFormat getMonetaryFormat() {
|
||||||
return XMR_MONETARY_FORMAT;
|
return XMR_MONETARY_FORMAT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class XmrRegTestParams extends RegTestParams {
|
private static class XmrStageNetParams extends RegTestParams {
|
||||||
@Override
|
@Override
|
||||||
public MonetaryFormat getMonetaryFormat() {
|
public MonetaryFormat getMonetaryFormat() {
|
||||||
return XMR_MONETARY_FORMAT;
|
return XMR_MONETARY_FORMAT;
|
||||||
|
@ -76,7 +76,7 @@ public class FileUtil {
|
|||||||
|
|
||||||
public static void deleteRollingBackup(File dir, String fileName) {
|
public static void deleteRollingBackup(File dir, String fileName) {
|
||||||
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
File backupDir = new File(Paths.get(dir.getAbsolutePath(), "backup").toString());
|
||||||
if (!backupDir.exists()) throw new RuntimeException("backup directory does not exist: " + backupDir);
|
if (!backupDir.exists()) return;
|
||||||
String dirName = "backups_" + fileName;
|
String dirName = "backups_" + fileName;
|
||||||
if (dirName.contains(".")) dirName = dirName.replace(".", "_");
|
if (dirName.contains(".")) dirName = dirName.replace(".", "_");
|
||||||
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
|
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());
|
||||||
|
@ -548,15 +548,11 @@ public class CoreApi {
|
|||||||
String paymentAccountId,
|
String paymentAccountId,
|
||||||
Consumer<Trade> resultHandler,
|
Consumer<Trade> resultHandler,
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
try {
|
Offer offer = coreOffersService.getOffer(offerId);
|
||||||
Offer offer = coreOffersService.getOffer(offerId);
|
coreTradesService.takeOffer(offer,
|
||||||
coreTradesService.takeOffer(offer,
|
paymentAccountId,
|
||||||
paymentAccountId,
|
resultHandler,
|
||||||
resultHandler,
|
errorMessageHandler);
|
||||||
errorMessageHandler);
|
|
||||||
} catch (Exception e) {
|
|
||||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void confirmPaymentStarted(String tradeId) {
|
public void confirmPaymentStarted(String tradeId) {
|
||||||
|
@ -17,8 +17,7 @@
|
|||||||
|
|
||||||
package bisq.core.api;
|
package bisq.core.api;
|
||||||
|
|
||||||
import bisq.core.btc.model.AddressEntry;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
import bisq.core.support.SupportType;
|
import bisq.core.support.SupportType;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
@ -45,7 +44,6 @@ import java.util.Optional;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import static bisq.common.app.DevEnv.DEV_PRIVILEGE_PRIV_KEY;
|
|
||||||
import static bisq.core.support.SupportType.ARBITRATION;
|
import static bisq.core.support.SupportType.ARBITRATION;
|
||||||
import static bisq.core.support.SupportType.MEDIATION;
|
import static bisq.core.support.SupportType.MEDIATION;
|
||||||
import static bisq.core.support.SupportType.REFUND;
|
import static bisq.core.support.SupportType.REFUND;
|
||||||
@ -61,7 +59,7 @@ class CoreDisputeAgentsService {
|
|||||||
private final User user;
|
private final User user;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
private final KeyRing keyRing;
|
private final KeyRing keyRing;
|
||||||
private final BtcWalletService btcWalletService;
|
private final XmrWalletService xmrWalletService;
|
||||||
private final ArbitratorManager arbitratorManager;
|
private final ArbitratorManager arbitratorManager;
|
||||||
private final MediatorManager mediatorManager;
|
private final MediatorManager mediatorManager;
|
||||||
private final RefundAgentManager refundAgentManager;
|
private final RefundAgentManager refundAgentManager;
|
||||||
@ -73,7 +71,7 @@ class CoreDisputeAgentsService {
|
|||||||
public CoreDisputeAgentsService(User user,
|
public CoreDisputeAgentsService(User user,
|
||||||
Config config,
|
Config config,
|
||||||
KeyRing keyRing,
|
KeyRing keyRing,
|
||||||
BtcWalletService btcWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
ArbitratorManager arbitratorManager,
|
ArbitratorManager arbitratorManager,
|
||||||
MediatorManager mediatorManager,
|
MediatorManager mediatorManager,
|
||||||
RefundAgentManager refundAgentManager,
|
RefundAgentManager refundAgentManager,
|
||||||
@ -81,7 +79,7 @@ class CoreDisputeAgentsService {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
this.btcWalletService = btcWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.arbitratorManager = arbitratorManager;
|
this.arbitratorManager = arbitratorManager;
|
||||||
this.mediatorManager = mediatorManager;
|
this.mediatorManager = mediatorManager;
|
||||||
this.refundAgentManager = refundAgentManager;
|
this.refundAgentManager = refundAgentManager;
|
||||||
@ -98,9 +96,6 @@ class CoreDisputeAgentsService {
|
|||||||
|| !config.useLocalhostForP2P)
|
|| !config.useLocalhostForP2P)
|
||||||
throw new IllegalStateException("dispute agents must be registered in a Bisq UI");
|
throw new IllegalStateException("dispute agents must be registered in a Bisq UI");
|
||||||
|
|
||||||
if (!registrationKey.equals(DEV_PRIVILEGE_PRIV_KEY))
|
|
||||||
throw new IllegalArgumentException("invalid registration key");
|
|
||||||
|
|
||||||
Optional<SupportType> supportType = getSupportType(disputeAgentType);
|
Optional<SupportType> supportType = getSupportType(disputeAgentType);
|
||||||
if (supportType.isPresent()) {
|
if (supportType.isPresent()) {
|
||||||
ECKey ecKey;
|
ECKey ecKey;
|
||||||
@ -112,6 +107,7 @@ class CoreDisputeAgentsService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecKey = arbitratorManager.getRegistrationKey(registrationKey);
|
ecKey = arbitratorManager.getRegistrationKey(registrationKey);
|
||||||
|
if (ecKey == null) throw new IllegalStateException("invalid registration key");
|
||||||
signature = arbitratorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
signature = arbitratorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||||
registerArbitrator(nodeAddress, languageCodes, ecKey, signature);
|
registerArbitrator(nodeAddress, languageCodes, ecKey, signature);
|
||||||
return;
|
return;
|
||||||
@ -121,6 +117,7 @@ class CoreDisputeAgentsService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecKey = mediatorManager.getRegistrationKey(registrationKey);
|
ecKey = mediatorManager.getRegistrationKey(registrationKey);
|
||||||
|
if (ecKey == null) throw new IllegalStateException("invalid registration key");
|
||||||
signature = mediatorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
signature = mediatorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||||
registerMediator(nodeAddress, languageCodes, ecKey, signature);
|
registerMediator(nodeAddress, languageCodes, ecKey, signature);
|
||||||
return;
|
return;
|
||||||
@ -130,6 +127,7 @@ class CoreDisputeAgentsService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecKey = refundAgentManager.getRegistrationKey(registrationKey);
|
ecKey = refundAgentManager.getRegistrationKey(registrationKey);
|
||||||
|
if (ecKey == null) throw new IllegalStateException("invalid registration key");
|
||||||
signature = refundAgentManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
signature = refundAgentManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||||
registerRefundAgent(nodeAddress, languageCodes, ecKey, signature);
|
registerRefundAgent(nodeAddress, languageCodes, ecKey, signature);
|
||||||
return;
|
return;
|
||||||
@ -145,11 +143,9 @@ class CoreDisputeAgentsService {
|
|||||||
List<String> languageCodes,
|
List<String> languageCodes,
|
||||||
ECKey ecKey,
|
ECKey ecKey,
|
||||||
String signature) {
|
String signature) {
|
||||||
AddressEntry arbitratorAddressEntry = btcWalletService.getArbitratorAddressEntry(); // TODO (woodser): switch to XMR; no reason for arbitrator to have BTC address / pub key
|
|
||||||
Arbitrator arbitrator = new Arbitrator(
|
Arbitrator arbitrator = new Arbitrator(
|
||||||
p2PService.getAddress(),
|
p2PService.getAddress(),
|
||||||
arbitratorAddressEntry.getPubKey(),
|
xmrWalletService.getWallet().getPrimaryAddress(), // TODO: how is this used?
|
||||||
arbitratorAddressEntry.getAddressString(),
|
|
||||||
keyRing.getPubKeyRing(),
|
keyRing.getPubKeyRing(),
|
||||||
new ArrayList<>(languageCodes),
|
new ArrayList<>(languageCodes),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package bisq.core.api;
|
package bisq.core.api;
|
||||||
|
|
||||||
import bisq.common.UserThread;
|
import bisq.common.config.BaseCurrencyNetwork;
|
||||||
|
import bisq.common.config.Config;
|
||||||
import bisq.core.btc.model.EncryptedConnectionList;
|
import bisq.core.btc.model.EncryptedConnectionList;
|
||||||
import bisq.core.btc.setup.DownloadListener;
|
import bisq.core.btc.setup.DownloadListener;
|
||||||
import bisq.core.btc.setup.WalletsSetup;
|
import bisq.core.btc.setup.WalletsSetup;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.LongProperty;
|
import javafx.beans.property.LongProperty;
|
||||||
@ -18,13 +22,15 @@ import javafx.beans.property.SimpleLongProperty;
|
|||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.common.MoneroConnectionManager;
|
import monero.common.MoneroConnectionManager;
|
||||||
import monero.common.MoneroConnectionManagerListener;
|
import monero.common.MoneroConnectionManagerListener;
|
||||||
|
import monero.common.MoneroError;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
import monero.daemon.MoneroDaemon;
|
import monero.common.TaskLooper;
|
||||||
import monero.daemon.MoneroDaemonRpc;
|
import monero.daemon.MoneroDaemonRpc;
|
||||||
|
import monero.daemon.model.MoneroDaemonInfo;
|
||||||
import monero.daemon.model.MoneroPeer;
|
import monero.daemon.model.MoneroPeer;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -32,16 +38,37 @@ import monero.daemon.model.MoneroPeer;
|
|||||||
public final class CoreMoneroConnectionsService {
|
public final class CoreMoneroConnectionsService {
|
||||||
|
|
||||||
private static final int MIN_BROADCAST_CONNECTIONS = 0; // TODO: 0 for stagenet, 5+ for mainnet
|
private static final int MIN_BROADCAST_CONNECTIONS = 0; // TODO: 0 for stagenet, 5+ for mainnet
|
||||||
private static final long DAEMON_REFRESH_PERIOD_MS = 15000L; // check connection periodically in ms
|
private static final long REFRESH_PERIOD_LOCAL_MS = 5000; // refresh period when connected to local node
|
||||||
private static final long DAEMON_INFO_POLL_PERIOD_MS = 20000L; // collect daemon info periodically in ms
|
private static final long REFRESH_PERIOD_REMOTE_MS = 20000; // refresh period when connected to remote node
|
||||||
|
|
||||||
// TODO (woodser): support each network type, move to config, remove localhost authentication
|
// default Monero nodes
|
||||||
private static final List<MoneroRpcConnection> DEFAULT_CONNECTIONS = Arrays.asList(
|
private static final Map<BaseCurrencyNetwork, List<MoneroRpcConnection>> DEFAULT_CONNECTIONS;
|
||||||
new MoneroRpcConnection("http://127.0.0.1:38081", "superuser", "abctesting123").setPriority(1), // localhost is first priority, use loopback address to match url generated by local node service
|
static {
|
||||||
new MoneroRpcConnection("http://haveno.exchange:38081", "", "").setPriority(2)
|
DEFAULT_CONNECTIONS = new HashMap<BaseCurrencyNetwork, List<MoneroRpcConnection>>();
|
||||||
);
|
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_LOCAL, Arrays.asList(
|
||||||
|
new MoneroRpcConnection("http://127.0.0.1:28081").setPriority(1)
|
||||||
|
));
|
||||||
|
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_STAGENET, Arrays.asList(
|
||||||
|
new MoneroRpcConnection("http://127.0.0.1:38081").setPriority(1), // localhost is first priority, use loopback address to match url generated by local node service
|
||||||
|
new MoneroRpcConnection("http://stagenet.melo.tools:38081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://node.sethforprivacy.com:38089").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://node2.sethforprivacy.com:38089").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://stagenet.community.rino.io:38081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://ct36dsbe3oubpbebpxmiqz4uqk6zb6nhmkhoekileo4fts23rvuse2qd.onion:38081").setPriority(2)
|
||||||
|
));
|
||||||
|
DEFAULT_CONNECTIONS.put(BaseCurrencyNetwork.XMR_MAINNET, Arrays.asList(
|
||||||
|
new MoneroRpcConnection("http://127.0.0.1:18081").setPriority(1),
|
||||||
|
new MoneroRpcConnection("http://node.community.rino.io:18081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://xmr-node.cakewallet.com:18081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://xmr-node-eu.cakewallet.com:18081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://xmr-node-usa-east.cakewallet.com:18081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://xmr-node-uk.cakewallet.com:18081").setPriority(2),
|
||||||
|
new MoneroRpcConnection("http://node.sethforprivacy.com:18089").setPriority(2)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
|
private final CoreContext coreContext;
|
||||||
private final CoreAccountService accountService;
|
private final CoreAccountService accountService;
|
||||||
private final CoreMoneroNodeService nodeService;
|
private final CoreMoneroNodeService nodeService;
|
||||||
private final MoneroConnectionManager connectionManager;
|
private final MoneroConnectionManager connectionManager;
|
||||||
@ -51,15 +78,20 @@ public final class CoreMoneroConnectionsService {
|
|||||||
private final LongProperty chainHeight = new SimpleLongProperty(0);
|
private final LongProperty chainHeight = new SimpleLongProperty(0);
|
||||||
private final DownloadListener downloadListener = new DownloadListener();
|
private final DownloadListener downloadListener = new DownloadListener();
|
||||||
|
|
||||||
private MoneroDaemon daemon;
|
private MoneroDaemonRpc daemon;
|
||||||
|
@Getter
|
||||||
|
private MoneroDaemonInfo lastInfo;
|
||||||
private boolean isInitialized = false;
|
private boolean isInitialized = false;
|
||||||
|
private TaskLooper updateDaemonLooper;;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public CoreMoneroConnectionsService(WalletsSetup walletsSetup,
|
public CoreMoneroConnectionsService(CoreContext coreContext,
|
||||||
|
WalletsSetup walletsSetup,
|
||||||
CoreAccountService accountService,
|
CoreAccountService accountService,
|
||||||
CoreMoneroNodeService nodeService,
|
CoreMoneroNodeService nodeService,
|
||||||
MoneroConnectionManager connectionManager,
|
MoneroConnectionManager connectionManager,
|
||||||
EncryptedConnectionList connectionList) {
|
EncryptedConnectionList connectionList) {
|
||||||
|
this.coreContext = coreContext;
|
||||||
this.accountService = accountService;
|
this.accountService = accountService;
|
||||||
this.nodeService = nodeService;
|
this.nodeService = nodeService;
|
||||||
this.connectionManager = connectionManager;
|
this.connectionManager = connectionManager;
|
||||||
@ -96,7 +128,7 @@ public final class CoreMoneroConnectionsService {
|
|||||||
|
|
||||||
// ------------------------ CONNECTION MANAGEMENT -------------------------
|
// ------------------------ CONNECTION MANAGEMENT -------------------------
|
||||||
|
|
||||||
public MoneroDaemon getDaemon() {
|
public MoneroDaemonRpc getDaemon() {
|
||||||
accountService.checkAccountOpen();
|
accountService.checkAccountOpen();
|
||||||
return this.daemon;
|
return this.daemon;
|
||||||
}
|
}
|
||||||
@ -171,7 +203,7 @@ public final class CoreMoneroConnectionsService {
|
|||||||
public void startCheckingConnection(Long refreshPeriod) {
|
public void startCheckingConnection(Long refreshPeriod) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
accountService.checkAccountOpen();
|
accountService.checkAccountOpen();
|
||||||
connectionManager.startCheckingConnection(refreshPeriod == null ? DAEMON_REFRESH_PERIOD_MS : refreshPeriod);
|
connectionManager.startCheckingConnection(refreshPeriod == null ? getDefaultRefreshPeriodMs() : refreshPeriod);
|
||||||
connectionList.setRefreshPeriod(refreshPeriod);
|
connectionList.setRefreshPeriod(refreshPeriod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -199,14 +231,28 @@ public final class CoreMoneroConnectionsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getDefaultRefreshPeriodMs() {
|
||||||
|
if (daemon == null) return REFRESH_PERIOD_LOCAL_MS;
|
||||||
|
else {
|
||||||
|
boolean isLocal = CoreMoneroNodeService.isLocalHost(daemon.getRpcConnection().getUri());
|
||||||
|
if (isLocal) {
|
||||||
|
updateDaemonInfo();
|
||||||
|
if (lastInfo.isBusySyncing() || (lastInfo.getHeightWithoutBootstrap() != null && lastInfo.getHeightWithoutBootstrap() > 0 && lastInfo.getHeightWithoutBootstrap() < lastInfo.getHeight())) return REFRESH_PERIOD_REMOTE_MS; // refresh slower if syncing or bootstrapped
|
||||||
|
else return REFRESH_PERIOD_LOCAL_MS; // TODO: announce faster refresh after done syncing
|
||||||
|
} else {
|
||||||
|
return REFRESH_PERIOD_REMOTE_MS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------- APP METHODS ------------------------------
|
// ----------------------------- APP METHODS ------------------------------
|
||||||
|
|
||||||
public boolean isChainHeightSyncedWithinTolerance() {
|
public boolean isChainHeightSyncedWithinTolerance() {
|
||||||
if (daemon == null) return false;
|
if (daemon == null) return false;
|
||||||
Long targetHeight = daemon.getSyncInfo().getTargetHeight();
|
Long targetHeight = lastInfo.getTargetHeight(); // the last time the node thought it was behind the network and was in active sync mode to catch up
|
||||||
if (targetHeight == 0) return true; // monero-daemon-rpc sync_info's target_height returns 0 when node is fully synced
|
if (targetHeight == 0) return true; // monero-daemon-rpc sync_info's target_height returns 0 when node is fully synced
|
||||||
long currentHeight = chainHeight.get();
|
long currentHeight = chainHeight.get();
|
||||||
if (Math.abs(targetHeight - currentHeight) <= 3) {
|
if (targetHeight - currentHeight <= 3) { // synced if not more than 3 blocks behind target height
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), targetHeight);
|
log.warn("Our chain height: {} is out of sync with peer nodes chain height: {}", chainHeight.get(), targetHeight);
|
||||||
@ -263,7 +309,7 @@ public final class CoreMoneroConnectionsService {
|
|||||||
log.info("Read " + connectionList.getConnections().size() + " connections from disk");
|
log.info("Read " + connectionList.getConnections().size() + " connections from disk");
|
||||||
|
|
||||||
// add default connections
|
// add default connections
|
||||||
for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS) {
|
for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS.get(Config.baseCurrencyNetwork())) {
|
||||||
if (connectionList.hasConnection(connection.getUri())) continue;
|
if (connectionList.hasConnection(connection.getUri())) continue;
|
||||||
addConnection(connection);
|
addConnection(connection);
|
||||||
}
|
}
|
||||||
@ -276,7 +322,7 @@ public final class CoreMoneroConnectionsService {
|
|||||||
connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
|
connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
|
||||||
long refreshPeriod = connectionList.getRefreshPeriod();
|
long refreshPeriod = connectionList.getRefreshPeriod();
|
||||||
if (refreshPeriod > 0) connectionManager.startCheckingConnection(refreshPeriod);
|
if (refreshPeriod > 0) connectionManager.startCheckingConnection(refreshPeriod);
|
||||||
else if (refreshPeriod == 0) connectionManager.startCheckingConnection(DAEMON_REFRESH_PERIOD_MS);
|
else if (refreshPeriod == 0) connectionManager.startCheckingConnection();
|
||||||
else checkConnection();
|
else checkConnection();
|
||||||
|
|
||||||
// run once
|
// run once
|
||||||
@ -301,33 +347,37 @@ public final class CoreMoneroConnectionsService {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// start local node if last connection is local and offline
|
|
||||||
currentConnectionUri.ifPresent(uri -> {
|
|
||||||
try {
|
|
||||||
if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) {
|
|
||||||
nodeService.startMoneroNode();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("Unable to start local monero node: " + e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// poll daemon periodically
|
|
||||||
startPollingDaemon();
|
|
||||||
isInitialized = true;
|
isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if offline, connect to local node if available
|
// start local node if offline and last connection is local
|
||||||
if (!connectionManager.isConnected() && nodeService.isMoneroNodeRunning()) {
|
currentConnectionUri.ifPresent(uri -> {
|
||||||
|
try {
|
||||||
|
if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) {
|
||||||
|
nodeService.startMoneroNode();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Unable to start local monero node: " + e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// connect to local node if available
|
||||||
|
if (nodeService.isMoneroNodeRunning() && (connectionManager.getAutoSwitch() || !connectionManager.isConnected())) {
|
||||||
MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri());
|
MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri());
|
||||||
if (connection == null) connection = nodeService.getDaemon().getRpcConnection();
|
if (connection != null) {
|
||||||
connection.checkConnection(connectionManager.getTimeout());
|
connection.checkConnection(connectionManager.getTimeout());
|
||||||
setConnection(connection);
|
setConnection(connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the daemon based on the connection
|
// if using legacy desktop app, connect to best available connection
|
||||||
if (getConnection() != null) daemon = new MoneroDaemonRpc(connectionManager.getConnection());
|
if (!coreContext.isApiUser()) {
|
||||||
updateDaemonInfo();
|
connectionManager.setAutoSwitch(true);
|
||||||
|
connectionManager.setConnection(connectionManager.getBestAvailableConnection());
|
||||||
|
}
|
||||||
|
|
||||||
|
// update connection
|
||||||
|
onConnectionChanged(connectionManager.getConnection());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,23 +392,35 @@ public final class CoreMoneroConnectionsService {
|
|||||||
connectionList.addConnection(currentConnection);
|
connectionList.addConnection(currentConnection);
|
||||||
connectionList.setCurrentConnectionUri(currentConnection.getUri());
|
connectionList.setCurrentConnectionUri(currentConnection.getUri());
|
||||||
}
|
}
|
||||||
|
startPollingDaemon();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startPollingDaemon() {
|
private void startPollingDaemon() {
|
||||||
UserThread.runPeriodically(() -> {
|
if (updateDaemonLooper != null) updateDaemonLooper.stop();
|
||||||
|
updateDaemonLooper = new TaskLooper(() -> {
|
||||||
updateDaemonInfo();
|
updateDaemonInfo();
|
||||||
}, DAEMON_INFO_POLL_PERIOD_MS / 1000l);
|
});
|
||||||
|
updateDaemonLooper.start(getDefaultRefreshPeriodMs());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDaemonInfo() {
|
private void updateDaemonInfo() {
|
||||||
try {
|
try {
|
||||||
|
log.trace("Updating daemon info");
|
||||||
if (daemon == null) throw new RuntimeException("No daemon connection");
|
if (daemon == null) throw new RuntimeException("No daemon connection");
|
||||||
peers.set(getOnlinePeers());
|
lastInfo = daemon.getInfo();
|
||||||
|
//System.out.println(JsonUtils.serialize(lastInfo));
|
||||||
|
//System.out.println(JsonUtils.serialize(daemon.getSyncInfo()));
|
||||||
|
chainHeight.set(lastInfo.getTargetHeight() == 0 ? lastInfo.getHeight() : lastInfo.getTargetHeight());
|
||||||
|
try {
|
||||||
|
peers.set(getOnlinePeers());
|
||||||
|
} catch (MoneroError err) {
|
||||||
|
peers.set(new ArrayList<MoneroPeer>()); // TODO: peers unknown due to restricted RPC call
|
||||||
|
}
|
||||||
numPeers.set(peers.get().size());
|
numPeers.set(peers.get().size());
|
||||||
chainHeight.set(daemon.getHeight());
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Could not update daemon info: " + e.getMessage());
|
log.warn("Could not update daemon info: " + e.getMessage());
|
||||||
|
if (connectionManager.getAutoSwitch()) connectionManager.setConnection(connectionManager.getBestAvailableConnection());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,14 +18,13 @@ package bisq.core.api;
|
|||||||
|
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.xmr.MoneroNodeSettings;
|
import bisq.core.xmr.MoneroNodeSettings;
|
||||||
|
import bisq.common.config.BaseCurrencyNetwork;
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -35,7 +34,7 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import monero.common.MoneroError;
|
||||||
import monero.daemon.MoneroDaemonRpc;
|
import monero.daemon.MoneroDaemonRpc;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,7 +48,7 @@ public class CoreMoneroNodeService {
|
|||||||
private static final String LOCALHOST = "localhost";
|
private static final String LOCALHOST = "localhost";
|
||||||
private static final String MONERO_NETWORK_TYPE = Config.baseCurrencyNetwork().getNetwork().toLowerCase();
|
private static final String MONERO_NETWORK_TYPE = Config.baseCurrencyNetwork().getNetwork().toLowerCase();
|
||||||
private static final String MONEROD_PATH = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + "monerod";
|
private static final String MONEROD_PATH = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + "monerod";
|
||||||
private static final String MONEROD_DATADIR = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + MONERO_NETWORK_TYPE;
|
private static final String MONEROD_DATADIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + Config.baseCurrencyNetwork().toString().toLowerCase() + File.separator + "node1" : null;
|
||||||
|
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private final List<MoneroNodeServiceListener> listeners = new ArrayList<>();
|
private final List<MoneroNodeServiceListener> listeners = new ArrayList<>();
|
||||||
@ -59,8 +58,7 @@ public class CoreMoneroNodeService {
|
|||||||
MONEROD_PATH,
|
MONEROD_PATH,
|
||||||
"--" + MONERO_NETWORK_TYPE,
|
"--" + MONERO_NETWORK_TYPE,
|
||||||
"--no-igd",
|
"--no-igd",
|
||||||
"--hide-my-port",
|
"--hide-my-port"
|
||||||
"--rpc-login", "superuser:abctesting123" // TODO: remove authentication
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// client to the local Monero node
|
// client to the local Monero node
|
||||||
@ -69,21 +67,24 @@ public class CoreMoneroNodeService {
|
|||||||
@Inject
|
@Inject
|
||||||
public CoreMoneroNodeService(Preferences preferences) {
|
public CoreMoneroNodeService(Preferences preferences) {
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
int rpcPort = 18081; // mainnet
|
Integer rpcPort = null;
|
||||||
if (Config.baseCurrencyNetwork().isTestnet()) {
|
if (Config.baseCurrencyNetwork().isMainnet()) rpcPort = 18081;
|
||||||
rpcPort = 28081;
|
else if (Config.baseCurrencyNetwork().isTestnet()) rpcPort = 28081;
|
||||||
} else if (Config.baseCurrencyNetwork().isStagenet()) {
|
else if (Config.baseCurrencyNetwork().isStagenet()) rpcPort = 38081;
|
||||||
rpcPort = 38081;
|
else throw new RuntimeException("Base network is not local testnet, stagenet, or mainnet");
|
||||||
}
|
this.daemon = new MoneroDaemonRpc("http://" + LOOPBACK_HOST + ":" + rpcPort);
|
||||||
this.daemon = new MoneroDaemonRpc("http://" + LOOPBACK_HOST + ":" + rpcPort, "superuser", "abctesting123"); // TODO: remove authentication
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the given URI is on local host. // TODO: move to utils
|
* Returns whether the given URI is on local host. // TODO: move to utils
|
||||||
*/
|
*/
|
||||||
public static boolean isLocalHost(String uri) throws URISyntaxException {
|
public static boolean isLocalHost(String uri) {
|
||||||
String host = new URI(uri).getHost();
|
try {
|
||||||
return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST);
|
String host = new URI(uri).getHost();
|
||||||
|
return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(MoneroNodeServiceListener listener) {
|
public void addListener(MoneroNodeServiceListener listener) {
|
||||||
@ -105,7 +106,13 @@ public class CoreMoneroNodeService {
|
|||||||
* Returns whether a local monero node is running.
|
* Returns whether a local monero node is running.
|
||||||
*/
|
*/
|
||||||
public boolean isMoneroNodeRunning() {
|
public boolean isMoneroNodeRunning() {
|
||||||
return daemon.isConnected();
|
//return daemon.isConnected(); // TODO: daemonRpc.isConnected() should use getVersion() instead of getHeight() which throws when unsynced
|
||||||
|
try {
|
||||||
|
daemon.getVersion();
|
||||||
|
return true;
|
||||||
|
} catch (MoneroError e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public MoneroNodeSettings getMoneroNodeSettings() {
|
public MoneroNodeSettings getMoneroNodeSettings() {
|
||||||
@ -135,7 +142,7 @@ public class CoreMoneroNodeService {
|
|||||||
if (dataDir == null || dataDir.isEmpty()) {
|
if (dataDir == null || dataDir.isEmpty()) {
|
||||||
dataDir = MONEROD_DATADIR;
|
dataDir = MONEROD_DATADIR;
|
||||||
}
|
}
|
||||||
args.add("--data-dir=" + dataDir);
|
if (dataDir != null) args.add("--data-dir=" + dataDir);
|
||||||
|
|
||||||
var bootstrapUrl = settings.getBootstrapUrl();
|
var bootstrapUrl = settings.getBootstrapUrl();
|
||||||
if (bootstrapUrl != null && !bootstrapUrl.isEmpty()) {
|
if (bootstrapUrl != null && !bootstrapUrl.isEmpty()) {
|
||||||
|
@ -500,7 +500,7 @@ class CoreWalletsService {
|
|||||||
|
|
||||||
// Throws a RuntimeException if wallets are encrypted and locked.
|
// Throws a RuntimeException if wallets are encrypted and locked.
|
||||||
void verifyEncryptedWalletIsUnlocked() {
|
void verifyEncryptedWalletIsUnlocked() {
|
||||||
if (walletsManager.areWalletsEncrypted() && tempAesKey == null)
|
if (walletsManager.areWalletsEncrypted() && !accountService.isAccountOpen())
|
||||||
throw new IllegalStateException("wallet is locked");
|
throw new IllegalStateException("wallet is locked");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ public class WalletAppSetup {
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
});
|
});
|
||||||
btcInfoBinding.subscribe((observable, oldValue, newValue) -> getBtcInfo().set(newValue));
|
btcInfoBinding.subscribe((observable, oldValue, newValue) -> UserThread.execute(() -> btcInfo.set(newValue)));
|
||||||
|
|
||||||
walletsSetup.initialize(null,
|
walletsSetup.initialize(null,
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -224,7 +224,7 @@ public abstract class ExecutableForAppWithP2p extends HavenoExecutable {
|
|||||||
log.warn("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
|
log.warn("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
|
||||||
"We are over our memory limit ({}) and trigger a shutdown. usedMemory: {} MB. freeMemory: {} MB" +
|
"We are over our memory limit ({}) and trigger a shutdown. usedMemory: {} MB. freeMemory: {} MB" +
|
||||||
"\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n",
|
"\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n",
|
||||||
(int) maxMemory, usedMemory, Profiler.getFreeMemoryInMB());
|
maxMemory, usedMemory, Profiler.getFreeMemoryInMB());
|
||||||
shutDown(gracefulShutDownHandler);
|
shutDown(gracefulShutDownHandler);
|
||||||
}
|
}
|
||||||
}, 5);
|
}, 5);
|
||||||
|
@ -18,44 +18,29 @@
|
|||||||
package bisq.core.btc.setup;
|
package bisq.core.btc.setup;
|
||||||
|
|
||||||
import bisq.core.btc.nodes.LocalBitcoinNode;
|
import bisq.core.btc.nodes.LocalBitcoinNode;
|
||||||
import bisq.core.btc.nodes.ProxySocketFactory;
|
|
||||||
import bisq.core.btc.wallet.HavenoRiskAnalysis;
|
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
import bisq.common.file.FileUtil;
|
import bisq.common.file.FileUtil;
|
||||||
|
|
||||||
import org.bitcoinj.core.BlockChain;
|
import org.bitcoinj.core.BlockChain;
|
||||||
import org.bitcoinj.core.CheckpointManager;
|
|
||||||
import org.bitcoinj.core.Context;
|
import org.bitcoinj.core.Context;
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.bitcoinj.core.PeerAddress;
|
import org.bitcoinj.core.PeerAddress;
|
||||||
import org.bitcoinj.core.PeerGroup;
|
import org.bitcoinj.core.PeerGroup;
|
||||||
import org.bitcoinj.core.listeners.DownloadProgressTracker;
|
import org.bitcoinj.core.listeners.DownloadProgressTracker;
|
||||||
import org.bitcoinj.crypto.KeyCrypter;
|
import org.bitcoinj.crypto.KeyCrypter;
|
||||||
import org.bitcoinj.net.BlockingClientManager;
|
|
||||||
import org.bitcoinj.net.discovery.DnsDiscovery;
|
|
||||||
import org.bitcoinj.net.discovery.PeerDiscovery;
|
import org.bitcoinj.net.discovery.PeerDiscovery;
|
||||||
import org.bitcoinj.script.Script;
|
import org.bitcoinj.script.Script;
|
||||||
import org.bitcoinj.store.BlockStore;
|
import org.bitcoinj.store.BlockStore;
|
||||||
import org.bitcoinj.store.BlockStoreException;
|
|
||||||
import org.bitcoinj.store.SPVBlockStore;
|
import org.bitcoinj.store.SPVBlockStore;
|
||||||
import org.bitcoinj.wallet.DeterministicKeyChain;
|
import org.bitcoinj.wallet.DeterministicKeyChain;
|
||||||
import org.bitcoinj.wallet.DeterministicSeed;
|
import org.bitcoinj.wallet.DeterministicSeed;
|
||||||
import org.bitcoinj.wallet.KeyChainGroup;
|
|
||||||
import org.bitcoinj.wallet.KeyChainGroupStructure;
|
|
||||||
import org.bitcoinj.wallet.Protos;
|
|
||||||
import org.bitcoinj.wallet.Wallet;
|
import org.bitcoinj.wallet.Wallet;
|
||||||
import org.bitcoinj.wallet.WalletExtension;
|
|
||||||
import org.bitcoinj.wallet.WalletProtobufSerializer;
|
|
||||||
|
|
||||||
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
|
||||||
|
|
||||||
import com.google.common.io.Closeables;
|
import com.google.common.io.Closeables;
|
||||||
import com.google.common.util.concurrent.AbstractIdleService;
|
import com.google.common.util.concurrent.AbstractIdleService;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
|
||||||
|
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
@ -63,11 +48,8 @@ import javafx.beans.property.SimpleBooleanProperty;
|
|||||||
import org.bouncycastle.crypto.params.KeyParameter;
|
import org.bouncycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
import java.net.Proxy;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@ -107,9 +89,6 @@ import static com.google.common.base.Preconditions.checkState;
|
|||||||
*/
|
*/
|
||||||
public class WalletConfig extends AbstractIdleService {
|
public class WalletConfig extends AbstractIdleService {
|
||||||
|
|
||||||
private static final int TOR_SOCKET_TIMEOUT = 120 * 1000; // 1 sec used in bitcoinj, but since bisq uses Tor we allow more.
|
|
||||||
private static final int TOR_VERSION_EXCHANGE_TIMEOUT = 125 * 1000; // 5 sec used in bitcoinj, but since bisq uses Tor we allow more.
|
|
||||||
|
|
||||||
protected static final Logger log = LoggerFactory.getLogger(WalletConfig.class);
|
protected static final Logger log = LoggerFactory.getLogger(WalletConfig.class);
|
||||||
|
|
||||||
protected final NetworkParameters params;
|
protected final NetworkParameters params;
|
||||||
@ -264,233 +243,16 @@ public class WalletConfig extends AbstractIdleService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startUp() throws Exception {
|
protected void startUp() throws Exception {
|
||||||
// Runs in a separate thread.
|
onSetupCompleted();
|
||||||
Context.propagate(context);
|
|
||||||
try {
|
|
||||||
File chainFile = new File(directory, filePrefix + ".spvchain");
|
|
||||||
boolean chainFileExists = chainFile.exists();
|
|
||||||
|
|
||||||
String btcPrefix = "_BTC";
|
|
||||||
vBtcWalletFile = new File(directory, filePrefix + btcPrefix + ".wallet");
|
|
||||||
boolean shouldReplayWallet = (vBtcWalletFile.exists() && !chainFileExists) || restoreFromSeed != null;
|
|
||||||
vBtcWallet = createOrLoadWallet(shouldReplayWallet, vBtcWalletFile);
|
|
||||||
vBtcWallet.allowSpendingUnconfirmedTransactions();
|
|
||||||
vBtcWallet.setRiskAnalyzer(new HavenoRiskAnalysis.Analyzer());
|
|
||||||
|
|
||||||
// Initiate Bitcoin network objects (block store, blockchain and peer group)
|
|
||||||
vStore = new SPVBlockStore(params, chainFile);
|
|
||||||
if (!chainFileExists || restoreFromSeed != null) {
|
|
||||||
if (checkpoints == null) {
|
|
||||||
checkpoints = CheckpointManager.openStream(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkpoints != null) {
|
|
||||||
// Initialize the chain file with a checkpoint to speed up first-run sync.
|
|
||||||
long time;
|
|
||||||
if (restoreFromSeed != null) {
|
|
||||||
time = restoreFromSeed.getCreationTimeSeconds();
|
|
||||||
if (chainFileExists) {
|
|
||||||
log.info("Clearing the chain file in preparation for restore.");
|
|
||||||
vStore.clear();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
time = vBtcWallet.getEarliestKeyCreationTime();
|
|
||||||
}
|
|
||||||
if (time > 0)
|
|
||||||
CheckpointManager.checkpoint(params, checkpoints, vStore, time);
|
|
||||||
else
|
|
||||||
log.warn("Creating a new uncheckpointed block store due to a wallet with a creation time of zero: this will result in a very slow chain sync");
|
|
||||||
} else if (chainFileExists) {
|
|
||||||
log.info("Clearing the chain file in preparation for restore.");
|
|
||||||
vStore.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vChain = new BlockChain(params, vStore);
|
|
||||||
vPeerGroup = createPeerGroup();
|
|
||||||
if (minBroadcastConnections > 0)
|
|
||||||
vPeerGroup.setMinBroadcastConnections(minBroadcastConnections);
|
|
||||||
if (this.userAgent != null)
|
|
||||||
vPeerGroup.setUserAgent(userAgent, version);
|
|
||||||
|
|
||||||
// Set up peer addresses or discovery first, so if wallet extensions try to broadcast a transaction
|
|
||||||
// before we're actually connected the broadcast waits for an appropriate number of connections.
|
|
||||||
if (peerAddresses != null) {
|
|
||||||
for (PeerAddress addr : peerAddresses) vPeerGroup.addAddress(addr);
|
|
||||||
int maxConnections = Math.min(numConnectionsForBtc, peerAddresses.length);
|
|
||||||
log.info("We try to connect to {} btc nodes", maxConnections);
|
|
||||||
vPeerGroup.setMaxConnections(maxConnections);
|
|
||||||
vPeerGroup.setAddPeersFromAddressMessage(false);
|
|
||||||
peerAddresses = null;
|
|
||||||
} else if (!params.getId().equals(NetworkParameters.ID_REGTEST)) {
|
|
||||||
vPeerGroup.addPeerDiscovery(discovery != null ? discovery : new DnsDiscovery(params));
|
|
||||||
}
|
|
||||||
vChain.addWallet(vBtcWallet);
|
|
||||||
vPeerGroup.addWallet(vBtcWallet);
|
|
||||||
onSetupCompleted();
|
|
||||||
|
|
||||||
if (migratedWalletToSegwit.get()) {
|
|
||||||
startPeerGroup();
|
|
||||||
} else {
|
|
||||||
migratedWalletToSegwit.addListener((observable, oldValue, newValue) -> {
|
|
||||||
if (newValue) {
|
|
||||||
startPeerGroup();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (BlockStoreException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startPeerGroup() {
|
|
||||||
Futures.addCallback((ListenableFuture<?>) vPeerGroup.startAsync(), new FutureCallback<Object>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable Object result) {
|
|
||||||
//completeExtensionInitiations(vPeerGroup);
|
|
||||||
DownloadProgressTracker tracker = new DownloadProgressTracker();
|
|
||||||
vPeerGroup.startBlockChainDownload(tracker);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Throwable t) {
|
|
||||||
throw new RuntimeException(t);
|
|
||||||
|
|
||||||
}
|
|
||||||
}, MoreExecutors.directExecutor());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Wallet createOrLoadWallet(boolean shouldReplayWallet,
|
|
||||||
File walletFile) throws Exception {
|
|
||||||
Wallet wallet;
|
|
||||||
|
|
||||||
maybeMoveOldWalletOutOfTheWay(walletFile);
|
|
||||||
|
|
||||||
if (walletFile.exists()) {
|
|
||||||
wallet = loadWallet(shouldReplayWallet, walletFile);
|
|
||||||
} else {
|
|
||||||
wallet = createWallet();
|
|
||||||
//wallet.freshReceiveKey();
|
|
||||||
|
|
||||||
// Currently the only way we can be sure that an extension is aware of its containing wallet is by
|
|
||||||
// deserializing the extension (see WalletExtension#deserializeWalletExtension(Wallet, byte[]))
|
|
||||||
// Hence, we first save and then load wallet to ensure any extensions are correctly initialized.
|
|
||||||
wallet.saveToFile(walletFile);
|
|
||||||
wallet = loadWallet(false, walletFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setupAutoSave(wallet, walletFile);
|
|
||||||
|
|
||||||
return wallet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setupAutoSave(Wallet wallet, File walletFile) {
|
protected void setupAutoSave(Wallet wallet, File walletFile) {
|
||||||
wallet.autosaveToFile(walletFile, 5, TimeUnit.SECONDS, null);
|
wallet.autosaveToFile(walletFile, 5, TimeUnit.SECONDS, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Wallet loadWallet(boolean shouldReplayWallet, File walletFile) throws Exception {
|
|
||||||
Wallet wallet;
|
|
||||||
try (FileInputStream walletStream = new FileInputStream(walletFile)) {
|
|
||||||
WalletExtension[] extArray = new WalletExtension[]{};
|
|
||||||
Protos.Wallet proto = WalletProtobufSerializer.parseToProto(walletStream);
|
|
||||||
final WalletProtobufSerializer serializer;
|
|
||||||
serializer = new WalletProtobufSerializer();
|
|
||||||
// Hack to convert bitcoinj 0.14 wallets to bitcoinj 0.15 format
|
|
||||||
serializer.setKeyChainFactory(new HavenoKeyChainFactory());
|
|
||||||
wallet = serializer.readWallet(params, extArray, proto);
|
|
||||||
if (shouldReplayWallet)
|
|
||||||
wallet.reset();
|
|
||||||
maybeAddSegwitKeychain(wallet, null);
|
|
||||||
}
|
|
||||||
return wallet;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Wallet createWallet() {
|
|
||||||
Script.ScriptType preferredOutputScriptType = Script.ScriptType.P2WPKH;
|
|
||||||
KeyChainGroupStructure structure = new HavenoKeyChainGroupStructure();
|
|
||||||
KeyChainGroup.Builder kcgBuilder = KeyChainGroup.builder(params, structure);
|
|
||||||
if (restoreFromSeed != null) {
|
|
||||||
kcgBuilder.fromSeed(restoreFromSeed, preferredOutputScriptType);
|
|
||||||
} else {
|
|
||||||
// new wallet
|
|
||||||
// btc wallet uses a new random seed.
|
|
||||||
kcgBuilder.fromRandom(preferredOutputScriptType);
|
|
||||||
}
|
|
||||||
return new Wallet(params, kcgBuilder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void maybeMoveOldWalletOutOfTheWay(File walletFile) {
|
|
||||||
if (restoreFromSeed == null) return;
|
|
||||||
if (!walletFile.exists()) return;
|
|
||||||
int counter = 1;
|
|
||||||
File newName;
|
|
||||||
do {
|
|
||||||
newName = new File(walletFile.getParent(), "Backup " + counter + " for " + walletFile.getName());
|
|
||||||
counter++;
|
|
||||||
} while (newName.exists());
|
|
||||||
log.info("Renaming old wallet file {} to {}", walletFile, newName);
|
|
||||||
if (!walletFile.renameTo(newName)) {
|
|
||||||
// This should not happen unless something is really messed up.
|
|
||||||
throw new RuntimeException("Failed to rename wallet for restore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private PeerGroup createPeerGroup() {
|
|
||||||
PeerGroup peerGroup;
|
|
||||||
// no proxy case.
|
|
||||||
if (socks5Proxy == null) {
|
|
||||||
peerGroup = new PeerGroup(params, vChain);
|
|
||||||
} else {
|
|
||||||
// proxy case (tor).
|
|
||||||
Proxy proxy = new Proxy(Proxy.Type.SOCKS,
|
|
||||||
new InetSocketAddress(socks5Proxy.getInetAddress(), socks5Proxy.getPort()));
|
|
||||||
|
|
||||||
ProxySocketFactory proxySocketFactory = new ProxySocketFactory(proxy);
|
|
||||||
// We don't use tor mode if we have a local node running
|
|
||||||
BlockingClientManager blockingClientManager = localBitcoinNode.shouldBeUsed() ?
|
|
||||||
new BlockingClientManager() :
|
|
||||||
new BlockingClientManager(proxySocketFactory);
|
|
||||||
|
|
||||||
peerGroup = new PeerGroup(params, vChain, blockingClientManager);
|
|
||||||
|
|
||||||
blockingClientManager.setConnectTimeoutMillis(TOR_SOCKET_TIMEOUT);
|
|
||||||
peerGroup.setConnectTimeoutMillis(TOR_VERSION_EXCHANGE_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!localBitcoinNode.shouldBeUsed())
|
|
||||||
peerGroup.setUseLocalhostPeerWhenPossible(false);
|
|
||||||
|
|
||||||
return peerGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void shutDown() throws Exception {
|
protected void shutDown() throws Exception {
|
||||||
// Runs in a separate thread.
|
|
||||||
try {
|
|
||||||
Context.propagate(context);
|
|
||||||
|
|
||||||
vBtcWallet.saveToFile(vBtcWalletFile);
|
|
||||||
vBtcWallet = null;
|
|
||||||
log.info("BtcWallet saved to file");
|
|
||||||
|
|
||||||
vStore.close();
|
|
||||||
vStore = null;
|
|
||||||
log.info("SPV file closed");
|
|
||||||
|
|
||||||
vChain = null;
|
|
||||||
|
|
||||||
// vPeerGroup.stop has no timeout and can take very long (10 sec. in my test). So we call it at the end.
|
|
||||||
// We might get likely interrupted by the parent call timeout.
|
|
||||||
if (vPeerGroup.isRunning()) {
|
|
||||||
vPeerGroup.stop();
|
|
||||||
log.info("PeerGroup stopped");
|
|
||||||
} else {
|
|
||||||
log.info("PeerGroup not stopped because it was not running");
|
|
||||||
}
|
|
||||||
vPeerGroup = null;
|
|
||||||
} catch (BlockStoreException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public NetworkParameters params() {
|
public NetworkParameters params() {
|
||||||
|
@ -196,31 +196,11 @@ public class WalletsSetup {
|
|||||||
//We are here in the btcj thread Thread[ STARTING,5,main]
|
//We are here in the btcj thread Thread[ STARTING,5,main]
|
||||||
super.onSetupCompleted();
|
super.onSetupCompleted();
|
||||||
|
|
||||||
final PeerGroup peerGroup = walletConfig.peerGroup();
|
|
||||||
|
|
||||||
// We don't want to get our node white list polluted with nodes from AddressMessage calls.
|
|
||||||
if (preferences.getBitcoinNodes() != null && !preferences.getBitcoinNodes().isEmpty())
|
|
||||||
peerGroup.setAddPeersFromAddressMessage(false);
|
|
||||||
|
|
||||||
// Need to be Threading.SAME_THREAD executor otherwise BitcoinJ will skip that listener
|
|
||||||
peerGroup.addPreMessageReceivedEventListener(Threading.SAME_THREAD, (peer, message) -> {
|
|
||||||
if (message instanceof RejectMessage) {
|
|
||||||
UserThread.execute(() -> {
|
|
||||||
RejectMessage rejectMessage = (RejectMessage) message;
|
|
||||||
String msg = rejectMessage.toString();
|
|
||||||
log.warn(msg);
|
|
||||||
exceptionHandler.handleException(new RejectedTxException(msg, rejectMessage));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return message;
|
|
||||||
});
|
|
||||||
|
|
||||||
// run external startup handlers
|
// run external startup handlers
|
||||||
setupTaskHandlers.forEach(Runnable::run);
|
setupTaskHandlers.forEach(Runnable::run);
|
||||||
|
|
||||||
// Map to user thread
|
// Map to user thread
|
||||||
UserThread.execute(() -> {
|
UserThread.execute(() -> {
|
||||||
addressEntryList.onWalletReady(walletConfig.btcWallet());
|
|
||||||
timeoutTimer.stop();
|
timeoutTimer.stop();
|
||||||
setupCompletedHandlers.forEach(Runnable::run);
|
setupCompletedHandlers.forEach(Runnable::run);
|
||||||
});
|
});
|
||||||
|
@ -45,7 +45,6 @@ import org.bitcoinj.core.TransactionOutput;
|
|||||||
import org.bitcoinj.crypto.DeterministicKey;
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||||
import org.bitcoinj.script.Script;
|
import org.bitcoinj.script.Script;
|
||||||
import org.bitcoinj.script.ScriptBuilder;
|
|
||||||
import org.bitcoinj.script.ScriptPattern;
|
import org.bitcoinj.script.ScriptPattern;
|
||||||
import org.bitcoinj.wallet.SendRequest;
|
import org.bitcoinj.wallet.SendRequest;
|
||||||
import org.bitcoinj.wallet.Wallet;
|
import org.bitcoinj.wallet.Wallet;
|
||||||
@ -97,12 +96,13 @@ public class BtcWalletService extends WalletService {
|
|||||||
|
|
||||||
this.addressEntryList = addressEntryList;
|
this.addressEntryList = addressEntryList;
|
||||||
|
|
||||||
|
// TODO: set and use chainHeightProperty in XmrWalletService
|
||||||
walletsSetup.addSetupCompletedHandler(() -> {
|
walletsSetup.addSetupCompletedHandler(() -> {
|
||||||
wallet = walletsSetup.getBtcWallet();
|
// wallet = walletsSetup.getBtcWallet();
|
||||||
addListenersToWallet();
|
// addListenersToWallet();
|
||||||
|
//
|
||||||
walletsSetup.getChain().addNewBestBlockListener(block -> chainHeightProperty.set(block.getHeight()));
|
// walletsSetup.getChain().addNewBestBlockListener(block -> chainHeightProperty.set(block.getHeight()));
|
||||||
chainHeightProperty.set(walletsSetup.getChain().getBestChainHeight());
|
// chainHeightProperty.set(walletsSetup.getChain().getBestChainHeight());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,11 +160,13 @@ public abstract class WalletService {
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
protected void addListenersToWallet() {
|
protected void addListenersToWallet() {
|
||||||
wallet.addCoinsReceivedEventListener(walletEventListener);
|
if (wallet != null) {
|
||||||
wallet.addCoinsSentEventListener(walletEventListener);
|
wallet.addCoinsReceivedEventListener(walletEventListener);
|
||||||
wallet.addReorganizeEventListener(walletEventListener);
|
wallet.addCoinsSentEventListener(walletEventListener);
|
||||||
wallet.addTransactionConfidenceEventListener(walletEventListener);
|
wallet.addReorganizeEventListener(walletEventListener);
|
||||||
wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener);
|
wallet.addTransactionConfidenceEventListener(walletEventListener);
|
||||||
|
wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutDown() {
|
public void shutDown() {
|
||||||
|
@ -44,6 +44,7 @@ public class WalletsManager {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(WalletsManager.class);
|
private static final Logger log = LoggerFactory.getLogger(WalletsManager.class);
|
||||||
|
|
||||||
private final BtcWalletService btcWalletService;
|
private final BtcWalletService btcWalletService;
|
||||||
|
private final XmrWalletService xmrWalletService;
|
||||||
private final TradeWalletService tradeWalletService;
|
private final TradeWalletService tradeWalletService;
|
||||||
private final WalletsSetup walletsSetup;
|
private final WalletsSetup walletsSetup;
|
||||||
|
|
||||||
@ -53,9 +54,11 @@ public class WalletsManager {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WalletsManager(BtcWalletService btcWalletService,
|
public WalletsManager(BtcWalletService btcWalletService,
|
||||||
|
XmrWalletService xmrWalletService,
|
||||||
TradeWalletService tradeWalletService,
|
TradeWalletService tradeWalletService,
|
||||||
WalletsSetup walletsSetup) {
|
WalletsSetup walletsSetup) {
|
||||||
this.btcWalletService = btcWalletService;
|
this.btcWalletService = btcWalletService;
|
||||||
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.tradeWalletService = tradeWalletService;
|
this.tradeWalletService = tradeWalletService;
|
||||||
this.walletsSetup = walletsSetup;
|
this.walletsSetup = walletsSetup;
|
||||||
}
|
}
|
||||||
@ -96,12 +99,11 @@ public class WalletsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean areWalletsEncrypted() {
|
public boolean areWalletsEncrypted() {
|
||||||
return areWalletsAvailable() &&
|
return xmrWalletService.isWalletEncrypted();
|
||||||
btcWalletService.isEncrypted();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean areWalletsAvailable() {
|
public boolean areWalletsAvailable() {
|
||||||
return btcWalletService.isWalletReady();
|
return xmrWalletService.isWalletReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyCrypterScrypt getKeyCrypterScrypt() {
|
public KeyCrypterScrypt getKeyCrypterScrypt() {
|
||||||
|
@ -42,8 +42,9 @@ import java.util.stream.Stream;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import monero.common.MoneroError;
|
import monero.common.MoneroError;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
|
import monero.common.MoneroRpcError;
|
||||||
import monero.common.MoneroUtils;
|
import monero.common.MoneroUtils;
|
||||||
import monero.daemon.MoneroDaemon;
|
import monero.daemon.MoneroDaemonRpc;
|
||||||
import monero.daemon.model.MoneroNetworkType;
|
import monero.daemon.model.MoneroNetworkType;
|
||||||
import monero.daemon.model.MoneroOutput;
|
import monero.daemon.model.MoneroOutput;
|
||||||
import monero.daemon.model.MoneroSubmitTxResult;
|
import monero.daemon.model.MoneroSubmitTxResult;
|
||||||
@ -69,7 +70,7 @@ public class XmrWalletService {
|
|||||||
|
|
||||||
// Monero configuration
|
// Monero configuration
|
||||||
// TODO: don't hard code configuration, inject into classes?
|
// TODO: don't hard code configuration, inject into classes?
|
||||||
private static final MoneroNetworkType MONERO_NETWORK_TYPE = MoneroNetworkType.STAGENET;
|
private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType();
|
||||||
private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
|
private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
|
||||||
private static final String MONERO_WALLET_RPC_DIR = System.getProperty("user.dir") + File.separator + ".localnet"; // .localnet contains monero-wallet-rpc and wallet files
|
private static final String MONERO_WALLET_RPC_DIR = System.getProperty("user.dir") + File.separator + ".localnet"; // .localnet contains monero-wallet-rpc and wallet files
|
||||||
private static final String MONERO_WALLET_RPC_PATH = MONERO_WALLET_RPC_DIR + File.separator + "monero-wallet-rpc";
|
private static final String MONERO_WALLET_RPC_PATH = MONERO_WALLET_RPC_DIR + File.separator + "monero-wallet-rpc";
|
||||||
@ -77,7 +78,6 @@ public class XmrWalletService {
|
|||||||
private static final String MONERO_WALLET_RPC_DEFAULT_PASSWORD = "password"; // only used if account password is null
|
private static final String MONERO_WALLET_RPC_DEFAULT_PASSWORD = "password"; // only used if account password is null
|
||||||
private static final String MONERO_WALLET_NAME = "haveno_XMR";
|
private static final String MONERO_WALLET_NAME = "haveno_XMR";
|
||||||
private static final String MONERO_MULTISIG_WALLET_PREFIX = "xmr_multisig_trade_";
|
private static final String MONERO_MULTISIG_WALLET_PREFIX = "xmr_multisig_trade_";
|
||||||
private static final long MONERO_WALLET_SYNC_PERIOD = 5000l;
|
|
||||||
|
|
||||||
private final CoreAccountService accountService;
|
private final CoreAccountService accountService;
|
||||||
private final CoreMoneroConnectionsService connectionsService;
|
private final CoreMoneroConnectionsService connectionsService;
|
||||||
@ -155,8 +155,16 @@ public class XmrWalletService {
|
|||||||
checkState(state == State.STARTING || state == State.RUNNING, "Cannot call until startup is complete");
|
checkState(state == State.STARTING || state == State.RUNNING, "Cannot call until startup is complete");
|
||||||
return wallet;
|
return wallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isWalletReady() {
|
||||||
|
return getWallet() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWalletEncrypted() {
|
||||||
|
return accountService.getPassword() != null;
|
||||||
|
}
|
||||||
|
|
||||||
public MoneroDaemon getDaemon() {
|
public MoneroDaemonRpc getDaemon() {
|
||||||
return connectionsService.getDaemon();
|
return connectionsService.getDaemon();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,14 +261,14 @@ public class XmrWalletService {
|
|||||||
// get expected mining fee
|
// get expected mining fee
|
||||||
MoneroTxWallet miningFeeTx = wallet.createTx(new MoneroTxConfig()
|
MoneroTxWallet miningFeeTx = wallet.createTx(new MoneroTxConfig()
|
||||||
.setAccountIndex(0)
|
.setAccountIndex(0)
|
||||||
.addDestination(TradeUtils.FEE_ADDRESS, tradeFee)
|
.addDestination(TradeUtils.getTradeFeeAddress(), tradeFee)
|
||||||
.addDestination(returnAddress, depositAmount));
|
.addDestination(returnAddress, depositAmount));
|
||||||
BigInteger miningFee = miningFeeTx.getFee();
|
BigInteger miningFee = miningFeeTx.getFee();
|
||||||
|
|
||||||
// create reserve tx
|
// create reserve tx
|
||||||
MoneroTxWallet reserveTx = wallet.createTx(new MoneroTxConfig()
|
MoneroTxWallet reserveTx = wallet.createTx(new MoneroTxConfig()
|
||||||
.setAccountIndex(0)
|
.setAccountIndex(0)
|
||||||
.addDestination(TradeUtils.FEE_ADDRESS, tradeFee)
|
.addDestination(TradeUtils.getTradeFeeAddress(), tradeFee)
|
||||||
.addDestination(returnAddress, depositAmount.add(miningFee.multiply(BigInteger.valueOf(3l))))); // add thrice the mining fee // TODO (woodser): really require more funds on top of security deposit?
|
.addDestination(returnAddress, depositAmount.add(miningFee.multiply(BigInteger.valueOf(3l))))); // add thrice the mining fee // TODO (woodser): really require more funds on top of security deposit?
|
||||||
|
|
||||||
// freeze inputs
|
// freeze inputs
|
||||||
@ -288,7 +296,7 @@ public class XmrWalletService {
|
|||||||
// create deposit tx
|
// create deposit tx
|
||||||
MoneroTxWallet depositTx = wallet.createTx(new MoneroTxConfig()
|
MoneroTxWallet depositTx = wallet.createTx(new MoneroTxConfig()
|
||||||
.setAccountIndex(0)
|
.setAccountIndex(0)
|
||||||
.addDestination(TradeUtils.FEE_ADDRESS, tradeFee)
|
.addDestination(TradeUtils.getTradeFeeAddress(), tradeFee)
|
||||||
.addDestination(multisigAddress, depositAmount));
|
.addDestination(multisigAddress, depositAmount));
|
||||||
|
|
||||||
// freeze deposit inputs
|
// freeze deposit inputs
|
||||||
@ -315,36 +323,31 @@ public class XmrWalletService {
|
|||||||
* @param miningFeePadding verifies depositAmount has additional funds to cover mining fee increase
|
* @param miningFeePadding verifies depositAmount has additional funds to cover mining fee increase
|
||||||
*/
|
*/
|
||||||
public void verifyTradeTx(String depositAddress, BigInteger depositAmount, BigInteger tradeFee, String txHash, String txHex, String txKey, List<String> keyImages, boolean miningFeePadding) {
|
public void verifyTradeTx(String depositAddress, BigInteger depositAmount, BigInteger tradeFee, String txHash, String txHex, String txKey, List<String> keyImages, boolean miningFeePadding) {
|
||||||
boolean submittedToPool = false;
|
MoneroDaemonRpc daemon = getDaemon();
|
||||||
MoneroDaemon daemon = getDaemon();
|
|
||||||
MoneroWallet wallet = getWallet();
|
MoneroWallet wallet = getWallet();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// get tx from daemon
|
// verify tx not submitted to pool
|
||||||
MoneroTx tx = daemon.getTx(txHash);
|
MoneroTx tx = daemon.getTx(txHash);
|
||||||
|
if (tx != null) throw new RuntimeException("Tx is already submitted");
|
||||||
// if tx is not submitted, submit but do not relay
|
|
||||||
if (tx == null) {
|
// submit tx to pool
|
||||||
MoneroSubmitTxResult result = daemon.submitTxHex(txHex, true); // TODO (woodser): invert doNotRelay flag to relay for library consistency?
|
MoneroSubmitTxResult result = daemon.submitTxHex(txHex, true); // TODO (woodser): invert doNotRelay flag to relay for library consistency?
|
||||||
if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result));
|
if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result));
|
||||||
submittedToPool = true;
|
tx = daemon.getTx(txHash);
|
||||||
tx = daemon.getTx(txHash);
|
|
||||||
} else if (tx.isRelayed()) {
|
|
||||||
throw new RuntimeException("Trade tx must not be relayed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify reserved key images
|
// verify reserved key images
|
||||||
if (keyImages != null) {
|
if (keyImages != null) {
|
||||||
Set<String> txKeyImages = new HashSet<String>();
|
Set<String> txKeyImages = new HashSet<String>();
|
||||||
for (MoneroOutput input : tx.getInputs()) txKeyImages.add(input.getKeyImage().getHex());
|
for (MoneroOutput input : tx.getInputs()) txKeyImages.add(input.getKeyImage().getHex());
|
||||||
if (!txKeyImages.equals(new HashSet<String>(keyImages))) throw new Error("Reserve tx's inputs do not match claimed key images");
|
if (!txKeyImages.equals(new HashSet<String>(keyImages))) throw new Error("Reserve tx's inputs do not match claimed key images");
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify the unlock height
|
// verify the unlock height
|
||||||
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
|
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
|
||||||
|
|
||||||
// verify trade fee
|
// verify trade fee
|
||||||
String feeAddress = TradeUtils.FEE_ADDRESS;
|
String feeAddress = TradeUtils.getTradeFeeAddress();
|
||||||
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
|
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
|
||||||
if (!check.isGood()) throw new RuntimeException("Invalid proof of trade fee");
|
if (!check.isGood()) throw new RuntimeException("Invalid proof of trade fee");
|
||||||
if (!check.getReceivedAmount().equals(tradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + check.getReceivedAmount());
|
if (!check.getReceivedAmount().equals(tradeFee)) throw new RuntimeException("Trade fee is incorrect amount, expected " + tradeFee + " but was " + check.getReceivedAmount());
|
||||||
@ -364,9 +367,12 @@ public class XmrWalletService {
|
|||||||
if (miningFeePadding) depositThreshold = depositThreshold.add(feeThreshold.multiply(BigInteger.valueOf(3l))); // prove reserve of at least deposit amount + (3 * min mining fee)
|
if (miningFeePadding) depositThreshold = depositThreshold.add(feeThreshold.multiply(BigInteger.valueOf(3l))); // prove reserve of at least deposit amount + (3 * min mining fee)
|
||||||
if (check.getReceivedAmount().compareTo(depositThreshold) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + depositThreshold + " but was " + check.getReceivedAmount());
|
if (check.getReceivedAmount().compareTo(depositThreshold) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + depositThreshold + " but was " + check.getReceivedAmount());
|
||||||
} finally {
|
} finally {
|
||||||
|
try {
|
||||||
// flush tx from pool if we added it
|
daemon.flushTxPool(txHash); // flush tx from pool
|
||||||
if (submittedToPool) daemon.flushTxPool(txHash);
|
} catch (MoneroRpcError err) {
|
||||||
|
System.out.println(daemon.getRpcConnection());
|
||||||
|
throw err.getCode() == -32601 ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,11 +399,13 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void tryInitMainWallet() {
|
private void tryInitMainWallet() {
|
||||||
|
|
||||||
|
// open or create wallet
|
||||||
MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword());
|
MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword());
|
||||||
if (MoneroUtils.walletExists(xmrWalletFile.getPath())) {
|
if (MoneroUtils.walletExists(xmrWalletFile.getPath())) {
|
||||||
wallet = openWallet(walletConfig, rpcBindPort);
|
wallet = openWallet(walletConfig, rpcBindPort);
|
||||||
} else if (connectionsService.getConnection() != null && Boolean.TRUE.equals(connectionsService.getConnection().isConnected())) {
|
} else if (connectionsService.getConnection() != null && Boolean.TRUE.equals(connectionsService.getConnection().isConnected())) {
|
||||||
wallet = createWallet(walletConfig, rpcBindPort); // wallet requires connection to daemon to correctly set height
|
wallet = createWallet(walletConfig, rpcBindPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wallet is not initialized until connected to a daemon
|
// wallet is not initialized until connected to a daemon
|
||||||
@ -410,9 +418,15 @@ public class XmrWalletService {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Monero wallet path: " + wallet.getPath());
|
if (connectionsService.getDaemon() == null) System.out.println("Daemon: null");
|
||||||
System.out.println("Monero wallet address: " + wallet.getPrimaryAddress());
|
else {
|
||||||
|
System.out.println("Daemon uri: " + connectionsService.getDaemon().getRpcConnection().getUri());
|
||||||
|
System.out.println("Daemon height: " + connectionsService.getDaemon().getInfo().getHeight());
|
||||||
|
}
|
||||||
System.out.println("Monero wallet uri: " + wallet.getRpcConnection().getUri());
|
System.out.println("Monero wallet uri: " + wallet.getRpcConnection().getUri());
|
||||||
|
System.out.println("Monero wallet path: " + wallet.getPath());
|
||||||
|
System.out.println("Monero wallet seed: " + wallet.getMnemonic());
|
||||||
|
System.out.println("Monero wallet primary address: " + wallet.getPrimaryAddress());
|
||||||
System.out.println("Monero wallet height: " + wallet.getHeight());
|
System.out.println("Monero wallet height: " + wallet.getHeight());
|
||||||
System.out.println("Monero wallet balance: " + wallet.getBalance(0));
|
System.out.println("Monero wallet balance: " + wallet.getBalance(0));
|
||||||
System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0));
|
System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0));
|
||||||
@ -433,8 +447,10 @@ public class XmrWalletService {
|
|||||||
|
|
||||||
// create wallet
|
// create wallet
|
||||||
try {
|
try {
|
||||||
|
log.info("Creating wallet " + config.getPath());
|
||||||
walletRpc.createWallet(config);
|
walletRpc.createWallet(config);
|
||||||
walletRpc.startSyncing(MONERO_WALLET_SYNC_PERIOD);
|
log.info("Syncing wallet " + config.getPath());
|
||||||
|
walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
|
||||||
return walletRpc;
|
return walletRpc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -450,8 +466,10 @@ public class XmrWalletService {
|
|||||||
|
|
||||||
// open wallet
|
// open wallet
|
||||||
try {
|
try {
|
||||||
|
log.info("Opening wallet " + config.getPath());
|
||||||
walletRpc.openWallet(config);
|
walletRpc.openWallet(config);
|
||||||
walletRpc.startSyncing(MONERO_WALLET_SYNC_PERIOD);
|
log.info("Syncing wallet " + config.getPath());
|
||||||
|
walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
|
||||||
return walletRpc;
|
return walletRpc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@ -489,10 +507,16 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setWalletDaemonConnections(MoneroRpcConnection connection) {
|
private void setWalletDaemonConnections(MoneroRpcConnection connection) {
|
||||||
log.info("Setting wallet daemon connections: " + (connection == null ? null : connection.getUri()));
|
log.info("Setting wallet daemon connection: " + (connection == null ? null : connection.getUri()));
|
||||||
if (wallet == null) tryInitMainWallet();
|
if (wallet == null) tryInitMainWallet();
|
||||||
if (wallet != null) wallet.setDaemonConnection(connection);
|
if (wallet != null) {
|
||||||
for (MoneroWallet multisigWallet : multisigWallets.values()) multisigWallet.setDaemonConnection(connection);
|
wallet.setDaemonConnection(connection);
|
||||||
|
wallet.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
|
||||||
|
}
|
||||||
|
for (MoneroWallet multisigWallet : multisigWallets.values()) {
|
||||||
|
multisigWallet.setDaemonConnection(connection);
|
||||||
|
multisigWallet.startSyncing(connectionsService.getDefaultRefreshPeriodMs()); // TODO: optimize when multisig wallets are open and syncing
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyBalanceListeners() {
|
private void notifyBalanceListeners() {
|
||||||
@ -803,6 +827,19 @@ public class XmrWalletService {
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
public static MoneroNetworkType getMoneroNetworkType() {
|
||||||
|
switch (Config.baseCurrencyNetwork()) {
|
||||||
|
case XMR_LOCAL:
|
||||||
|
return MoneroNetworkType.TESTNET;
|
||||||
|
case XMR_STAGENET:
|
||||||
|
return MoneroNetworkType.STAGENET;
|
||||||
|
case XMR_MAINNET:
|
||||||
|
return MoneroNetworkType.MAINNET;
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void printTxs(String tracePrefix, MoneroTxWallet... txs) {
|
public static void printTxs(String tracePrefix, MoneroTxWallet... txs) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
for (MoneroTxWallet tx : txs) sb.append('\n' + tx.toString());
|
for (MoneroTxWallet tx : txs) sb.append('\n' + tx.toString());
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
package bisq.core.offer;
|
package bisq.core.offer;
|
||||||
|
|
||||||
import bisq.core.btc.TxFeeEstimationService;
|
import bisq.core.btc.TxFeeEstimationService;
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
import bisq.core.btc.wallet.Restrictions;
|
import bisq.core.btc.wallet.Restrictions;
|
||||||
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.CurrencyUtil;
|
import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
@ -69,7 +69,7 @@ public class CreateOfferService {
|
|||||||
private final P2PService p2PService;
|
private final P2PService p2PService;
|
||||||
private final PubKeyRingProvider pubKeyRingProvider;
|
private final PubKeyRingProvider pubKeyRingProvider;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final BtcWalletService btcWalletService;
|
private final XmrWalletService xmrWalletService;
|
||||||
private final TradeStatisticsManager tradeStatisticsManager;
|
private final TradeStatisticsManager tradeStatisticsManager;
|
||||||
private final ArbitratorManager arbitratorManager;
|
private final ArbitratorManager arbitratorManager;
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ public class CreateOfferService {
|
|||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
PubKeyRingProvider pubKeyRingProvider,
|
PubKeyRingProvider pubKeyRingProvider,
|
||||||
User user,
|
User user,
|
||||||
BtcWalletService btcWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
TradeStatisticsManager tradeStatisticsManager,
|
TradeStatisticsManager tradeStatisticsManager,
|
||||||
ArbitratorManager arbitratorManager) {
|
ArbitratorManager arbitratorManager) {
|
||||||
this.offerUtil = offerUtil;
|
this.offerUtil = offerUtil;
|
||||||
@ -94,7 +94,7 @@ public class CreateOfferService {
|
|||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.pubKeyRingProvider = pubKeyRingProvider;
|
this.pubKeyRingProvider = pubKeyRingProvider;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.btcWalletService = btcWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||||
this.arbitratorManager = arbitratorManager;
|
this.arbitratorManager = arbitratorManager;
|
||||||
}
|
}
|
||||||
@ -167,8 +167,6 @@ public class CreateOfferService {
|
|||||||
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
||||||
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
|
||||||
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
|
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
|
||||||
Coin txFeeFromFeeService = getEstimatedFeeAndTxVsize(amount, direction, buyerSecurityDepositAsDouble, sellerSecurityDeposit).first;
|
|
||||||
Coin txFeeToUse = txFee.isPositive() ? txFee : txFeeFromFeeService;
|
|
||||||
Coin makerFeeAsCoin = offerUtil.getMakerFee(amount);
|
Coin makerFeeAsCoin = offerUtil.getMakerFee(amount);
|
||||||
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
|
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
|
||||||
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
|
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
|
||||||
@ -195,6 +193,7 @@ public class CreateOfferService {
|
|||||||
|
|
||||||
// select signing arbitrator
|
// select signing arbitrator
|
||||||
Arbitrator arbitrator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, arbitratorManager);
|
Arbitrator arbitrator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, arbitratorManager);
|
||||||
|
if (arbitrator == null) throw new RuntimeException("No arbitrators available");
|
||||||
|
|
||||||
OfferPayload offerPayload = new OfferPayload(offerId,
|
OfferPayload offerPayload = new OfferPayload(offerId,
|
||||||
creationTime,
|
creationTime,
|
||||||
@ -216,8 +215,8 @@ public class CreateOfferService {
|
|||||||
bankId,
|
bankId,
|
||||||
acceptedBanks,
|
acceptedBanks,
|
||||||
Version.VERSION,
|
Version.VERSION,
|
||||||
btcWalletService.getLastBlockSeenHeight(), // TODO (woodser): switch to XMR
|
xmrWalletService.getWallet().getHeight(),
|
||||||
txFeeToUse.value,
|
0, // todo: remove txFeeToUse from data model
|
||||||
makerFeeAsCoin.value,
|
makerFeeAsCoin.value,
|
||||||
buyerSecurityDepositAsCoin.value,
|
buyerSecurityDepositAsCoin.value,
|
||||||
sellerSecurityDepositAsCoin.value,
|
sellerSecurityDepositAsCoin.value,
|
||||||
|
@ -34,7 +34,7 @@ public class OfferRestrictions {
|
|||||||
private static final Date REQUIRE_TOR_NODE_ADDRESS_V3_DATE = Utilities.getUTCDate(2021, GregorianCalendar.AUGUST, 15);
|
private static final Date REQUIRE_TOR_NODE_ADDRESS_V3_DATE = Utilities.getUTCDate(2021, GregorianCalendar.AUGUST, 15);
|
||||||
|
|
||||||
public static boolean requiresNodeAddressUpdate() {
|
public static boolean requiresNodeAddressUpdate() {
|
||||||
return new Date().after(REQUIRE_TOR_NODE_ADDRESS_V3_DATE) && !Config.baseCurrencyNetwork().isStagenet();
|
return new Date().after(REQUIRE_TOR_NODE_ADDRESS_V3_DATE) && Config.baseCurrencyNetwork().isMainnet();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Coin TOLERATED_SMALL_TRADE_AMOUNT = Coin.parseCoin("1.0");
|
public static Coin TOLERATED_SMALL_TRADE_AMOUNT = Coin.parseCoin("1.0");
|
||||||
|
@ -33,7 +33,6 @@ import bisq.core.provider.price.PriceFeedService;
|
|||||||
import bisq.core.trade.statistics.ReferralIdService;
|
import bisq.core.trade.statistics.ReferralIdService;
|
||||||
import bisq.core.user.AutoConfirmSettings;
|
import bisq.core.user.AutoConfirmSettings;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.util.AveragePriceUtil;
|
|
||||||
import bisq.core.util.coin.CoinFormatter;
|
import bisq.core.util.coin.CoinFormatter;
|
||||||
import bisq.core.util.coin.CoinUtil;
|
import bisq.core.util.coin.CoinUtil;
|
||||||
|
|
||||||
@ -68,7 +67,6 @@ import static bisq.core.btc.wallet.Restrictions.getMinBuyerSecurityDepositAsPerc
|
|||||||
import static bisq.core.offer.OfferPayload.*;
|
import static bisq.core.offer.OfferPayload.*;
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static java.lang.String.format;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class holds utility methods for creating, editing and taking an Offer.
|
* This class holds utility methods for creating, editing and taking an Offer.
|
||||||
|
@ -24,7 +24,6 @@ import bisq.core.btc.wallet.TradeWalletService;
|
|||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.exceptions.TradePriceOutOfToleranceException;
|
import bisq.core.exceptions.TradePriceOutOfToleranceException;
|
||||||
import bisq.core.filter.FilterManager;
|
import bisq.core.filter.FilterManager;
|
||||||
import bisq.core.locale.Res;
|
|
||||||
import bisq.core.offer.availability.DisputeAgentSelection;
|
import bisq.core.offer.availability.DisputeAgentSelection;
|
||||||
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
||||||
import bisq.core.offer.messages.OfferAvailabilityResponse;
|
import bisq.core.offer.messages.OfferAvailabilityResponse;
|
||||||
@ -72,7 +71,6 @@ import bisq.common.persistence.PersistenceManager;
|
|||||||
import bisq.common.proto.network.NetworkEnvelope;
|
import bisq.common.proto.network.NetworkEnvelope;
|
||||||
import bisq.common.proto.persistable.PersistedDataHost;
|
import bisq.common.proto.persistable.PersistedDataHost;
|
||||||
import bisq.common.util.Tuple2;
|
import bisq.common.util.Tuple2;
|
||||||
import bisq.common.util.Utilities;
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@ -1004,12 +1002,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
availabilityResult = AvailabilityResult.OFFER_TAKEN;
|
availabilityResult = AvailabilityResult.OFFER_TAKEN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (btcWalletService.isUnconfirmedTransactionsLimitHit()) {
|
|
||||||
errorMessage = Res.get("shared.unconfirmedTransactionsLimitReached");
|
|
||||||
log.warn(errorMessage);
|
|
||||||
availabilityResult = AvailabilityResult.UNCONF_TX_LIMIT_HIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
||||||
availabilityResult,
|
availabilityResult,
|
||||||
makerSignature,
|
makerSignature,
|
||||||
|
@ -46,7 +46,7 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
|
|||||||
checkCoinNotNullOrZero(offer.getMakerFee(), "MakerFee");
|
checkCoinNotNullOrZero(offer.getMakerFee(), "MakerFee");
|
||||||
checkCoinNotNullOrZero(offer.getBuyerSecurityDeposit(), "buyerSecurityDeposit");
|
checkCoinNotNullOrZero(offer.getBuyerSecurityDeposit(), "buyerSecurityDeposit");
|
||||||
checkCoinNotNullOrZero(offer.getSellerSecurityDeposit(), "sellerSecurityDeposit");
|
checkCoinNotNullOrZero(offer.getSellerSecurityDeposit(), "sellerSecurityDeposit");
|
||||||
checkCoinNotNullOrZero(offer.getTxFee(), "txFee");
|
//checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model
|
||||||
checkCoinNotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
|
checkCoinNotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
|
||||||
|
|
||||||
// We remove those checks to be more flexible with future changes.
|
// We remove those checks to be more flexible with future changes.
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
package bisq.core.offer.takeoffer;
|
package bisq.core.offer.takeoffer;
|
||||||
|
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||||
import bisq.core.btc.model.AddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.CurrencyUtil;
|
import bisq.core.locale.CurrencyUtil;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
@ -45,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import static bisq.core.btc.model.AddressEntry.Context.OFFER_FUNDING;
|
import static bisq.core.btc.model.XmrAddressEntry.Context.OFFER_FUNDING;
|
||||||
import static bisq.core.offer.OfferDirection.SELL;
|
import static bisq.core.offer.OfferDirection.SELL;
|
||||||
import static bisq.core.util.VolumeUtil.getAdjustedVolumeForHalCash;
|
import static bisq.core.util.VolumeUtil.getAdjustedVolumeForHalCash;
|
||||||
import static bisq.core.util.VolumeUtil.getRoundedFiatVolume;
|
import static bisq.core.util.VolumeUtil.getRoundedFiatVolume;
|
||||||
@ -59,14 +59,14 @@ import static org.bitcoinj.core.Coin.valueOf;
|
|||||||
public class TakeOfferModel implements Model {
|
public class TakeOfferModel implements Model {
|
||||||
// Immutable
|
// Immutable
|
||||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
private final BtcWalletService btcWalletService;
|
private final XmrWalletService xmrWalletService;
|
||||||
private final FeeService feeService;
|
private final FeeService feeService;
|
||||||
private final OfferUtil offerUtil;
|
private final OfferUtil offerUtil;
|
||||||
private final PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
|
|
||||||
// Mutable
|
// Mutable
|
||||||
@Getter
|
@Getter
|
||||||
private AddressEntry addressEntry;
|
private XmrAddressEntry addressEntry;
|
||||||
@Getter
|
@Getter
|
||||||
private Coin amount;
|
private Coin amount;
|
||||||
private Offer offer;
|
private Offer offer;
|
||||||
@ -91,18 +91,18 @@ public class TakeOfferModel implements Model {
|
|||||||
@Getter
|
@Getter
|
||||||
private Coin balance;
|
private Coin balance;
|
||||||
@Getter
|
@Getter
|
||||||
private boolean isBtcWalletFunded;
|
private boolean isXmrWalletFunded;
|
||||||
@Getter
|
@Getter
|
||||||
private Volume volume;
|
private Volume volume;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
|
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
|
||||||
BtcWalletService btcWalletService,
|
XmrWalletService xmrWalletService,
|
||||||
FeeService feeService,
|
FeeService feeService,
|
||||||
OfferUtil offerUtil,
|
OfferUtil offerUtil,
|
||||||
PriceFeedService priceFeedService) {
|
PriceFeedService priceFeedService) {
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.btcWalletService = btcWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.feeService = feeService;
|
this.feeService = feeService;
|
||||||
this.offerUtil = offerUtil;
|
this.offerUtil = offerUtil;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
@ -114,7 +114,7 @@ public class TakeOfferModel implements Model {
|
|||||||
this.clearModel();
|
this.clearModel();
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.paymentAccount = paymentAccount;
|
this.paymentAccount = paymentAccount;
|
||||||
this.addressEntry = btcWalletService.getOrCreateAddressEntry(offer.getId(), OFFER_FUNDING); // TODO (woodser): replace with xmr or remove
|
this.addressEntry = xmrWalletService.getOrCreateAddressEntry(offer.getId(), OFFER_FUNDING); // TODO (woodser): replace with xmr or remove
|
||||||
validateModelInputs();
|
validateModelInputs();
|
||||||
|
|
||||||
this.useSavingsWallet = useSavingsWallet;
|
this.useSavingsWallet = useSavingsWallet;
|
||||||
@ -200,18 +200,10 @@ public class TakeOfferModel implements Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateBalance() {
|
private void updateBalance() {
|
||||||
Coin tradeWalletBalance = btcWalletService.getBalanceForAddress(addressEntry.getAddress());
|
totalAvailableBalance = xmrWalletService.getSavingWalletBalance();
|
||||||
if (useSavingsWallet) {
|
if (totalToPayAsCoin != null) balance = minCoin(totalToPayAsCoin, totalAvailableBalance);
|
||||||
Coin savingWalletBalance = btcWalletService.getSavingWalletBalance();
|
|
||||||
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
|
|
||||||
if (totalToPayAsCoin != null)
|
|
||||||
balance = minCoin(totalToPayAsCoin, totalAvailableBalance);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
balance = tradeWalletBalance;
|
|
||||||
}
|
|
||||||
missingCoin = offerUtil.getBalanceShortage(totalToPayAsCoin, balance);
|
missingCoin = offerUtil.getBalanceShortage(totalToPayAsCoin, balance);
|
||||||
isBtcWalletFunded = offerUtil.isBalanceSufficient(totalToPayAsCoin, balance);
|
isXmrWalletFunded = offerUtil.isBalanceSufficient(totalToPayAsCoin, balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getMaxTradeLimit() {
|
private long getMaxTradeLimit() {
|
||||||
@ -264,7 +256,7 @@ public class TakeOfferModel implements Model {
|
|||||||
this.addressEntry = null;
|
this.addressEntry = null;
|
||||||
this.amount = null;
|
this.amount = null;
|
||||||
this.balance = null;
|
this.balance = null;
|
||||||
this.isBtcWalletFunded = false;
|
this.isXmrWalletFunded = false;
|
||||||
this.missingCoin = ZERO;
|
this.missingCoin = ZERO;
|
||||||
this.offer = null;
|
this.offer = null;
|
||||||
this.paymentAccount = null;
|
this.paymentAccount = null;
|
||||||
@ -299,7 +291,7 @@ public class TakeOfferModel implements Model {
|
|||||||
", balance=" + balance + "\n" +
|
", balance=" + balance + "\n" +
|
||||||
", volume=" + volume + "\n" +
|
", volume=" + volume + "\n" +
|
||||||
", fundsNeededForTrade=" + getFundsNeededForTrade() + "\n" +
|
", fundsNeededForTrade=" + getFundsNeededForTrade() + "\n" +
|
||||||
", isBtcWalletFunded=" + isBtcWalletFunded + "\n" +
|
", isXmrWalletFunded=" + isXmrWalletFunded + "\n" +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -568,10 +568,10 @@ public abstract class PaymentAccount implements PersistablePayload {
|
|||||||
throw new IllegalArgumentException("Not implemented");
|
throw new IllegalArgumentException("Not implemented");
|
||||||
case CITY:
|
case CITY:
|
||||||
field.setComponent(PaymentAccountFormField.Component.TEXT);
|
field.setComponent(PaymentAccountFormField.Component.TEXT);
|
||||||
field.setLabel(Res.get("Contact"));
|
field.setLabel("City");
|
||||||
case CONTACT:
|
case CONTACT:
|
||||||
field.setComponent(PaymentAccountFormField.Component.TEXT);
|
field.setComponent(PaymentAccountFormField.Component.TEXT);
|
||||||
field.setLabel("City");
|
field.setLabel("Contact info");
|
||||||
case COUNTRY:
|
case COUNTRY:
|
||||||
field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
|
field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
|
||||||
field.setLabel("Country");
|
field.setLabel("Country");
|
||||||
|
@ -86,7 +86,6 @@ public final class StrikeAccount extends CountryBasedPaymentAccount {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public @NotNull List<Country> getSupportedCountries() {
|
public @NotNull List<Country> getSupportedCountries() {
|
||||||
System.out.println("STIKE RETURNING SUPPORTED COUNTRIES: " + SUPPORTED_COUNTRIES);
|
|
||||||
return SUPPORTED_COUNTRIES;
|
return SUPPORTED_COUNTRIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,11 +416,6 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||||||
boolean senderIsCosigner = senderIsWinner || disputeResult.isLoserPublisher();
|
boolean senderIsCosigner = senderIsWinner || disputeResult.isLoserPublisher();
|
||||||
boolean receiverIsArbitrator = pubKeyRing.equals(dispute.getAgentPubKeyRing());
|
boolean receiverIsArbitrator = pubKeyRing.equals(dispute.getAgentPubKeyRing());
|
||||||
|
|
||||||
System.out.println("TESTING PUB KEY RINGS");
|
|
||||||
System.out.println(pubKeyRing);
|
|
||||||
System.out.println(dispute.getAgentPubKeyRing());
|
|
||||||
System.out.println("Receiver is arbitrator: " + receiverIsArbitrator);
|
|
||||||
|
|
||||||
if (!senderIsCosigner) {
|
if (!senderIsCosigner) {
|
||||||
log.warn("Received ArbitratorPayoutTxRequest but sender is not co-signer for trade id " + tradeId);
|
log.warn("Received ArbitratorPayoutTxRequest but sender is not co-signer for trade id " + tradeId);
|
||||||
return;
|
return;
|
||||||
|
@ -24,7 +24,6 @@ import bisq.network.p2p.NodeAddress;
|
|||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
import bisq.common.proto.ProtoUtil;
|
import bisq.common.proto.ProtoUtil;
|
||||||
import bisq.common.util.CollectionUtils;
|
import bisq.common.util.CollectionUtils;
|
||||||
import bisq.common.util.Utilities;
|
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
@ -43,12 +42,10 @@ import javax.annotation.Nullable;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
@Getter
|
@Getter
|
||||||
public final class Arbitrator extends DisputeAgent {
|
public final class Arbitrator extends DisputeAgent {
|
||||||
private final byte[] btcPubKey;
|
private final String xmrAddress;
|
||||||
private final String btcAddress;
|
|
||||||
|
|
||||||
public Arbitrator(NodeAddress nodeAddress,
|
public Arbitrator(NodeAddress nodeAddress,
|
||||||
byte[] btcPubKey,
|
String xmrAddress,
|
||||||
String btcAddress,
|
|
||||||
PubKeyRing pubKeyRing,
|
PubKeyRing pubKeyRing,
|
||||||
List<String> languageCodes,
|
List<String> languageCodes,
|
||||||
long registrationDate,
|
long registrationDate,
|
||||||
@ -68,8 +65,7 @@ public final class Arbitrator extends DisputeAgent {
|
|||||||
info,
|
info,
|
||||||
extraDataMap);
|
extraDataMap);
|
||||||
|
|
||||||
this.btcPubKey = btcPubKey;
|
this.xmrAddress = xmrAddress;
|
||||||
this.btcAddress = btcAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -80,8 +76,7 @@ public final class Arbitrator extends DisputeAgent {
|
|||||||
public protobuf.StoragePayload toProtoMessage() {
|
public protobuf.StoragePayload toProtoMessage() {
|
||||||
protobuf.Arbitrator.Builder builder = protobuf.Arbitrator.newBuilder()
|
protobuf.Arbitrator.Builder builder = protobuf.Arbitrator.newBuilder()
|
||||||
.setNodeAddress(nodeAddress.toProtoMessage())
|
.setNodeAddress(nodeAddress.toProtoMessage())
|
||||||
.setBtcPubKey(ByteString.copyFrom(btcPubKey))
|
.setXmrAddress(xmrAddress)
|
||||||
.setBtcAddress(btcAddress)
|
|
||||||
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
||||||
.addAllLanguageCodes(languageCodes)
|
.addAllLanguageCodes(languageCodes)
|
||||||
.setRegistrationDate(registrationDate)
|
.setRegistrationDate(registrationDate)
|
||||||
@ -95,8 +90,7 @@ public final class Arbitrator extends DisputeAgent {
|
|||||||
|
|
||||||
public static Arbitrator fromProto(protobuf.Arbitrator proto) {
|
public static Arbitrator fromProto(protobuf.Arbitrator proto) {
|
||||||
return new Arbitrator(NodeAddress.fromProto(proto.getNodeAddress()),
|
return new Arbitrator(NodeAddress.fromProto(proto.getNodeAddress()),
|
||||||
proto.getBtcPubKey().toByteArray(),
|
proto.getXmrAddress(),
|
||||||
proto.getBtcAddress(),
|
|
||||||
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
||||||
new ArrayList<>(proto.getLanguageCodesList()),
|
new ArrayList<>(proto.getLanguageCodesList()),
|
||||||
proto.getRegistrationDate(),
|
proto.getRegistrationDate(),
|
||||||
@ -115,8 +109,7 @@ public final class Arbitrator extends DisputeAgent {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Arbitrator{" +
|
return "Arbitrator{" +
|
||||||
"\n btcPubKey=" + Utilities.bytesAsHexString(btcPubKey) +
|
",\n xmrAddress='" + xmrAddress + '\'' +
|
||||||
",\n btcAddress='" + btcAddress + '\'' +
|
|
||||||
"\n} " + super.toString();
|
"\n} " + super.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,13 @@ import bisq.core.support.dispute.agent.DisputeAgentManager;
|
|||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
|
|
||||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -49,21 +48,26 @@ public class ArbitratorManager extends DisputeAgentManager<Arbitrator> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getPubKeyList() {
|
protected List<String> getPubKeyList() {
|
||||||
return List.of("0365c6af94681dbee69de1851f98d4684063bf5c2d64b1c73ed5d90434f375a054",
|
switch (Config.baseCurrencyNetwork()) {
|
||||||
"031c502a60f9dbdb5ae5e438a79819e4e1f417211dd537ac12c9bc23246534c4bd",
|
case XMR_LOCAL:
|
||||||
"02c1e5a242387b6d5319ce27246cea6edaaf51c3550591b528d2578a4753c56c2c",
|
throw new RuntimeException("No arbitrator pub key list for local XMR testnet. Set useDevPrivilegeKeys=true");
|
||||||
"025c319faf7067d9299590dd6c97fe7e56cd4dac61205ccee1cd1fc390142390a2",
|
case XMR_STAGENET:
|
||||||
"038f6e24c2bfe5d51d0a290f20a9a657c270b94ef2b9c12cd15ca3725fa798fc55",
|
return List.of(
|
||||||
"0255256ff7fb615278c4544a9bbd3f5298b903b8a011cd7889be19b6b1c45cbefe",
|
"03bb559ce207a4deb51d4c705076c95b85ad8581d35936b2a422dcb504eaf7cdb0",
|
||||||
"024a3a37289f08c910fbd925ebc72b946f33feaeff451a4738ee82037b4cda2e95",
|
"026c581ad773d987e6bd10785ac7f7e0e64864aedeb8bce5af37046de812a37854",
|
||||||
"02a88b75e9f0f8afba1467ab26799dcc38fd7a6468fb2795444b425eb43e2c10bd",
|
"025b058c9f2c60d839669dbfa5578cf5a8117d60e6b70e2f0946f8a691273c6a36",
|
||||||
"02349a51512c1c04c67118386f4d27d768c5195a83247c150a4b722d161722ba81",
|
"036c7d3f4bf05ef39b9d1b0a5d453a18210de36220c3d83cd16e59bd6132b037ad",
|
||||||
"03f718a2e0dc672c7cdec0113e72c3322efc70412bb95870750d25c32cd98de17d",
|
"030f7122a10ff73cd73808bddace95be77a94189c8a0eb24586265e125ce5ce6b9",
|
||||||
"028ff47ee2c56e66313928975c58fa4f1b19a0f81f3a96c4e9c9c3c6768075509e",
|
"03aa23e062afa0dda465f46986f8aa8d0374ad3e3f256141b05681dcb1e39c3859",
|
||||||
"02b517c0cbc3a49548f448ddf004ed695c5a1c52ec110be1bfd65fa0ca0761c94b",
|
"02d3beb1293ca2ca14e6d42ca8bd18089a62aac62fd6bb23923ee6ead46ac60fba",
|
||||||
"03df837a3a0f3d858e82f3356b71d1285327f101f7c10b404abed2abc1c94e7169",
|
"03fa0f38f27bdd324db6f933f7e57851dadf3b911e4db6b19dd0950492c4525a31",
|
||||||
"0203a90fb2ab698e524a5286f317a183a84327b8f8c3f7fa4a98fec9e1cefd6b72",
|
"02a1a458df5acf4ab08fdca748e28f33a955a30854c8c1a831ee733dca7f0d2fcd",
|
||||||
"023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5");
|
"0374dd70f3fa6e47ec5ab97932e1cec6233e98e6ae3129036b17118650c44fd3de");
|
||||||
|
case XMR_MAINNET:
|
||||||
|
return new ArrayList<String>();
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -362,9 +362,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
@Setter
|
@Setter
|
||||||
private NodeAddress arbitratorNodeAddress;
|
private NodeAddress arbitratorNodeAddress;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Setter
|
|
||||||
private byte[] arbitratorBtcPubKey;
|
|
||||||
@Nullable
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private PubKeyRing arbitratorPubKeyRing;
|
private PubKeyRing arbitratorPubKeyRing;
|
||||||
@ -603,7 +600,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
Optional.ofNullable(contractHash).ifPresent(e -> builder.setContractHash(ByteString.copyFrom(contractHash)));
|
Optional.ofNullable(contractHash).ifPresent(e -> builder.setContractHash(ByteString.copyFrom(contractHash)));
|
||||||
Optional.ofNullable(arbitratorNodeAddress).ifPresent(e -> builder.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage()));
|
Optional.ofNullable(arbitratorNodeAddress).ifPresent(e -> builder.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage()));
|
||||||
Optional.ofNullable(refundAgentNodeAddress).ifPresent(e -> builder.setRefundAgentNodeAddress(refundAgentNodeAddress.toProtoMessage()));
|
Optional.ofNullable(refundAgentNodeAddress).ifPresent(e -> builder.setRefundAgentNodeAddress(refundAgentNodeAddress.toProtoMessage()));
|
||||||
Optional.ofNullable(arbitratorBtcPubKey).ifPresent(e -> builder.setArbitratorBtcPubKey(ByteString.copyFrom(arbitratorBtcPubKey)));
|
|
||||||
Optional.ofNullable(takerPaymentAccountId).ifPresent(builder::setTakerPaymentAccountId);
|
Optional.ofNullable(takerPaymentAccountId).ifPresent(builder::setTakerPaymentAccountId);
|
||||||
Optional.ofNullable(errorMessage).ifPresent(builder::setErrorMessage);
|
Optional.ofNullable(errorMessage).ifPresent(builder::setErrorMessage);
|
||||||
Optional.ofNullable(arbitratorPubKeyRing).ifPresent(e -> builder.setArbitratorPubKeyRing(arbitratorPubKeyRing.toProtoMessage()));
|
Optional.ofNullable(arbitratorPubKeyRing).ifPresent(e -> builder.setArbitratorPubKeyRing(arbitratorPubKeyRing.toProtoMessage()));
|
||||||
@ -633,7 +629,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
trade.setContractHash(ProtoUtil.byteArrayOrNullFromProto(proto.getContractHash()));
|
trade.setContractHash(ProtoUtil.byteArrayOrNullFromProto(proto.getContractHash()));
|
||||||
trade.setArbitratorNodeAddress(proto.hasArbitratorNodeAddress() ? NodeAddress.fromProto(proto.getArbitratorNodeAddress()) : null);
|
trade.setArbitratorNodeAddress(proto.hasArbitratorNodeAddress() ? NodeAddress.fromProto(proto.getArbitratorNodeAddress()) : null);
|
||||||
trade.setRefundAgentNodeAddress(proto.hasRefundAgentNodeAddress() ? NodeAddress.fromProto(proto.getRefundAgentNodeAddress()) : null);
|
trade.setRefundAgentNodeAddress(proto.hasRefundAgentNodeAddress() ? NodeAddress.fromProto(proto.getRefundAgentNodeAddress()) : null);
|
||||||
trade.setArbitratorBtcPubKey(ProtoUtil.byteArrayOrNullFromProto(proto.getArbitratorBtcPubKey()));
|
|
||||||
trade.setTakerPaymentAccountId(ProtoUtil.stringOrNullFromProto(proto.getTakerPaymentAccountId()));
|
trade.setTakerPaymentAccountId(ProtoUtil.stringOrNullFromProto(proto.getTakerPaymentAccountId()));
|
||||||
trade.setErrorMessage(ProtoUtil.stringOrNullFromProto(proto.getErrorMessage()));
|
trade.setErrorMessage(ProtoUtil.stringOrNullFromProto(proto.getErrorMessage()));
|
||||||
trade.setArbitratorPubKeyRing(proto.hasArbitratorPubKeyRing() ? PubKeyRing.fromProto(proto.getArbitratorPubKeyRing()) : null);
|
trade.setArbitratorPubKeyRing(proto.hasArbitratorPubKeyRing() ? PubKeyRing.fromProto(proto.getArbitratorPubKeyRing()) : null);
|
||||||
@ -670,7 +665,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
|
|
||||||
public void initialize(ProcessModelServiceProvider serviceProvider) {
|
public void initialize(ProcessModelServiceProvider serviceProvider) {
|
||||||
serviceProvider.getArbitratorManager().getDisputeAgentByNodeAddress(arbitratorNodeAddress).ifPresent(arbitrator -> {
|
serviceProvider.getArbitratorManager().getDisputeAgentByNodeAddress(arbitratorNodeAddress).ifPresent(arbitrator -> {
|
||||||
arbitratorBtcPubKey = arbitrator.getBtcPubKey();
|
|
||||||
arbitratorPubKeyRing = arbitrator.getPubKeyRing();
|
arbitratorPubKeyRing = arbitrator.getPubKeyRing();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1439,19 +1433,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
getDelayedPayoutTxBytes() == null;
|
getDelayedPayoutTxBytes() == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getArbitratorBtcPubKey() {
|
|
||||||
// In case we are already in a trade the arbitrator can have been revoked and we still can complete the trade
|
|
||||||
// Only new trades cannot start without any arbitrator
|
|
||||||
if (arbitratorBtcPubKey == null) {
|
|
||||||
Arbitrator arbitrator = processModel.getUser().getAcceptedArbitratorByAddress(arbitratorNodeAddress);
|
|
||||||
checkNotNull(arbitrator, "arbitrator must not be null");
|
|
||||||
arbitratorBtcPubKey = arbitrator.getBtcPubKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
checkNotNull(arbitratorBtcPubKey, "ArbitratorPubKey must not be null");
|
|
||||||
return arbitratorBtcPubKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Private
|
// Private
|
||||||
@ -1526,7 +1507,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
",\n contract=" + contract +
|
",\n contract=" + contract +
|
||||||
",\n contractAsJson='" + contractAsJson + '\'' +
|
",\n contractAsJson='" + contractAsJson + '\'' +
|
||||||
",\n contractHash=" + Utilities.bytesAsHexString(contractHash) +
|
",\n contractHash=" + Utilities.bytesAsHexString(contractHash) +
|
||||||
",\n arbitratorBtcPubKey=" + Utilities.bytesAsHexString(arbitratorBtcPubKey) +
|
|
||||||
",\n takerPaymentAccountId='" + takerPaymentAccountId + '\'' +
|
",\n takerPaymentAccountId='" + takerPaymentAccountId + '\'' +
|
||||||
",\n errorMessage='" + errorMessage + '\'' +
|
",\n errorMessage='" + errorMessage + '\'' +
|
||||||
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +
|
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +
|
||||||
|
@ -17,11 +17,11 @@
|
|||||||
|
|
||||||
package bisq.core.trade;
|
package bisq.core.trade;
|
||||||
|
|
||||||
|
import bisq.common.config.Config;
|
||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
import bisq.common.crypto.Sig;
|
import bisq.common.crypto.Sig;
|
||||||
import bisq.common.util.Tuple2;
|
import bisq.common.util.Tuple2;
|
||||||
import bisq.common.util.Utilities;
|
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.offer.OfferPayload;
|
import bisq.core.offer.OfferPayload;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
@ -32,15 +32,28 @@ import java.util.concurrent.CountDownLatch;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of utilities for trading.
|
* Collection of utilities for trading.
|
||||||
*
|
|
||||||
* TODO (woodser): combine with TradeUtil.java ?
|
|
||||||
*/
|
*/
|
||||||
public class TradeUtils {
|
public class TradeUtils {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Address to collect Haveno trade fees. TODO (woodser): move to config constants
|
* Get address to collect trade fees.
|
||||||
|
*
|
||||||
|
* TODO: move to config constants?
|
||||||
|
*
|
||||||
|
* @return the address which collects trade fees
|
||||||
*/
|
*/
|
||||||
public static String FEE_ADDRESS = "52FnB7ABUrKJzVQRpbMNrqDFWbcKLjFUq8Rgek7jZEuB6WE2ZggXaTf4FK6H8gQymvSrruHHrEuKhMN3qTMiBYzREKsmRKM";
|
public static String getTradeFeeAddress() {
|
||||||
|
switch (Config.baseCurrencyNetwork()) {
|
||||||
|
case XMR_LOCAL:
|
||||||
|
return "Bd37nTGHjL3RvPxc9dypzpWiXQrPzxxG4RsWAasD9CV2iZ1xfFZ7mzTKNDxWBfsqQSUimctAsGtTZ8c8bZJy35BYL9jYj88";
|
||||||
|
case XMR_STAGENET:
|
||||||
|
return "52FnB7ABUrKJzVQRpbMNrqDFWbcKLjFUq8Rgek7jZEuB6WE2ZggXaTf4FK6H8gQymvSrruHHrEuKhMN3qTMiBYzREKsmRKM";
|
||||||
|
case XMR_MAINNET:
|
||||||
|
throw new RuntimeException("Mainnet fee address not implemented");
|
||||||
|
default:
|
||||||
|
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the arbitrator signature for an offer is valid.
|
* Check if the arbitrator signature for an offer is valid.
|
||||||
|
@ -39,7 +39,7 @@ public class MakerSetsLockTime extends TradeTask {
|
|||||||
|
|
||||||
// 10 days for altcoins, 20 days for other payment methods
|
// 10 days for altcoins, 20 days for other payment methods
|
||||||
// For regtest dev environment we use 5 blocks
|
// For regtest dev environment we use 5 blocks
|
||||||
int delay = Config.baseCurrencyNetwork().isStagenet() ?
|
int delay = Config.baseCurrencyNetwork().isTestnet() ?
|
||||||
5 :
|
5 :
|
||||||
Restrictions.getLockTime(processModel.getOffer().getPaymentMethod().isBlockchain());
|
Restrictions.getLockTime(processModel.getOffer().getPaymentMethod().isBlockchain());
|
||||||
|
|
||||||
|
@ -778,9 +778,10 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||||||
switch (baseCurrencyNetwork) {
|
switch (baseCurrencyNetwork) {
|
||||||
case XMR_MAINNET:
|
case XMR_MAINNET:
|
||||||
return prefPayload.getBlockChainExplorerMainNet();
|
return prefPayload.getBlockChainExplorerMainNet();
|
||||||
case XMR_TESTNET:
|
|
||||||
case XMR_STAGENET:
|
case XMR_STAGENET:
|
||||||
return prefPayload.getBlockChainExplorerTestNet();
|
return prefPayload.getBlockChainExplorerTestNet();
|
||||||
|
case XMR_LOCAL:
|
||||||
|
return prefPayload.getBlockChainExplorerTestNet(); // TODO: no testnet explorer for private testnet
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork);
|
throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork);
|
||||||
}
|
}
|
||||||
@ -791,9 +792,10 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
|||||||
switch (baseCurrencyNetwork) {
|
switch (baseCurrencyNetwork) {
|
||||||
case XMR_MAINNET:
|
case XMR_MAINNET:
|
||||||
return BTC_MAIN_NET_EXPLORERS;
|
return BTC_MAIN_NET_EXPLORERS;
|
||||||
case XMR_TESTNET:
|
|
||||||
case XMR_STAGENET:
|
case XMR_STAGENET:
|
||||||
return BTC_TEST_NET_EXPLORERS;
|
return BTC_TEST_NET_EXPLORERS;
|
||||||
|
case XMR_LOCAL:
|
||||||
|
return BTC_TEST_NET_EXPLORERS; // TODO: no testnet explorer for private testnet
|
||||||
default:
|
default:
|
||||||
throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork);
|
throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork);
|
||||||
}
|
}
|
||||||
|
@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.util;
|
|
||||||
|
|
||||||
import bisq.core.filter.FilterManager;
|
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class FeeReceiverSelector {
|
|
||||||
public static final String BTC_FEE_RECEIVER_ADDRESS = "38bZBj5peYS3Husdz7AH3gEUiUbYRD951t";
|
|
||||||
|
|
||||||
public static String getMostRecentAddress() {
|
|
||||||
return Config.baseCurrencyNetwork().isMainnet() ? BTC_FEE_RECEIVER_ADDRESS :
|
|
||||||
Config.baseCurrencyNetwork().isTestnet() ? "2N4mVTpUZAnhm9phnxB7VrHB4aBhnWrcUrV" :
|
|
||||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getAddress(FilterManager filterManager) {
|
|
||||||
return getAddress(filterManager, new Random());
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static String getAddress(FilterManager filterManager, Random rnd) {
|
|
||||||
List<String> feeReceivers = Optional.ofNullable(filterManager.getFilter())
|
|
||||||
.flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses()))
|
|
||||||
.orElse(List.of());
|
|
||||||
|
|
||||||
List<Long> amountList = new ArrayList<>();
|
|
||||||
List<String> receiverAddressList = new ArrayList<>();
|
|
||||||
|
|
||||||
feeReceivers.forEach(e -> {
|
|
||||||
try {
|
|
||||||
String[] tokens = e.split("#");
|
|
||||||
amountList.add(Coin.parseCoin(tokens[1]).longValue()); // total amount the victim should receive
|
|
||||||
receiverAddressList.add(tokens[0]); // victim's receiver address
|
|
||||||
} catch (RuntimeException ignore) {
|
|
||||||
// If input format is not as expected we ignore entry
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!amountList.isEmpty()) {
|
|
||||||
return receiverAddressList.get(weightedSelection(amountList, rnd));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no fee address receiver is defined via filter we use the hard coded recent address
|
|
||||||
return getMostRecentAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static int weightedSelection(List<Long> weights, Random rnd) {
|
|
||||||
long sum = weights.stream().mapToLong(n -> n).sum();
|
|
||||||
long target = rnd.longs(0, sum).findFirst().orElseThrow();
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < weights.size() && target >= 0; i++) {
|
|
||||||
target -= weights.get(i);
|
|
||||||
}
|
|
||||||
return i - 1;
|
|
||||||
}
|
|
||||||
}
|
|
@ -2391,7 +2391,7 @@ formatter.asTaker={0} {1} as taker
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Monero Mainnet
|
XMR_MAINNET=Monero Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Monero Testnet
|
XMR_LOCAL=Monero Local Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Monero Stagenet
|
XMR_STAGENET=Monero Stagenet
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} jako příjemce
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Monero Mainnet
|
XMR_MAINNET=Monero Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Monero Testnet
|
XMR_LOCAL=Monero Local Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Monero Stagenet
|
XMR_STAGENET=Monero Stagenet
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} als Abnehmer
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Bitcoin-Hauptnetzwerk
|
XMR_MAINNET=Bitcoin-Hauptnetzwerk
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Bitcoin-Testnetzwerk
|
XMR_LOCAL=Bitcoin-Testnetzwerk
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Bitcoin-Regtest
|
XMR_STAGENET=Bitcoin-Regtest
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} como tomador
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Red principal de Monero
|
XMR_MAINNET=Red principal de Monero
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Red de prueba de Monero
|
XMR_LOCAL=Red de prueba de Monero
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Stagenet Monero
|
XMR_STAGENET=Stagenet Monero
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} به عنوان پذیرنده
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Monero Mainnet
|
XMR_MAINNET=Monero Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Monero Testnet
|
XMR_LOCAL=Monero Local Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Monero Stagenet
|
XMR_STAGENET=Monero Stagenet
|
||||||
|
|
||||||
|
@ -1824,7 +1824,7 @@ formatter.asTaker={0} {1} en tant que taker
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Monero Mainnet
|
XMR_MAINNET=Monero Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Monero Testnet
|
XMR_LOCAL=Monero Local Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Monero Stagenet
|
XMR_STAGENET=Monero Stagenet
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} come taker
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Mainnet Bitcoin
|
XMR_MAINNET=Mainnet Bitcoin
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Testnet Bitcoin
|
XMR_LOCAL=Testnet Bitcoin
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Regtest Bitcoin
|
XMR_STAGENET=Regtest Bitcoin
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1}のテイカー
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Monero Mainnet
|
XMR_MAINNET=Monero Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Monero Testnet
|
XMR_LOCAL=Monero Local Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Monero Stagenet
|
XMR_STAGENET=Monero Stagenet
|
||||||
|
|
||||||
|
@ -1831,7 +1831,7 @@ formatter.asTaker={0} {1} como aceitador
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Mainnet do Monero
|
XMR_MAINNET=Mainnet do Monero
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Testnet do Monero
|
XMR_LOCAL=Testnet do Monero
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Stagenet do Monero
|
XMR_STAGENET=Stagenet do Monero
|
||||||
|
|
||||||
|
@ -1821,7 +1821,7 @@ formatter.asTaker={0} {1} como aceitador
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Mainnet de Monero
|
XMR_MAINNET=Mainnet de Monero
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Testnet de Monero
|
XMR_LOCAL=Testnet de Monero
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Stagenet Monero
|
XMR_STAGENET=Stagenet Monero
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} как тейкер
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=XMR Mainnet
|
XMR_MAINNET=XMR Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=XMR Testnet
|
XMR_LOCAL=XMR Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=XMR Stagenet
|
XMR_STAGENET=XMR Stagenet
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} ในฐานะคนรับ
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Monero Mainnet
|
XMR_MAINNET=Monero Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Monero Testnet
|
XMR_LOCAL=Monero Local Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Monero Stagenet
|
XMR_STAGENET=Monero Stagenet
|
||||||
|
|
||||||
|
@ -1825,7 +1825,7 @@ formatter.asTaker={0} {1} như người nhận
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=Bitcoin Mainnet
|
XMR_MAINNET=Bitcoin Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=Bitcoin Testnet
|
XMR_LOCAL=Bitcoin Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=Bitcoin Regtest
|
XMR_STAGENET=Bitcoin Regtest
|
||||||
|
|
||||||
|
@ -1827,7 +1827,7 @@ formatter.asTaker={0} {1} 是买家
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=XMR Mainnet
|
XMR_MAINNET=XMR Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=XMR Testnet
|
XMR_LOCAL=XMR Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=XMR Stagenet
|
XMR_STAGENET=XMR Stagenet
|
||||||
|
|
||||||
|
@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} 是買家
|
|||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_MAINNET=XMR Mainnet
|
XMR_MAINNET=XMR Mainnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_TESTNET=XMR Testnet
|
XMR_LOCAL=XMR Testnet
|
||||||
# suppress inspection "UnusedProperty"
|
# suppress inspection "UnusedProperty"
|
||||||
XMR_STAGENET=XMR Stagenet
|
XMR_STAGENET=XMR Stagenet
|
||||||
|
|
||||||
|
3
core/src/main/resources/xmr_local.seednodes
Normal file
3
core/src/main/resources/xmr_local.seednodes
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# nodeaddress.onion:port [(@owner,@backup)]
|
||||||
|
localhost:2002 (@devtest1)
|
||||||
|
localhost:3002 (@devtest2)
|
@ -1,3 +1,3 @@
|
|||||||
# nodeaddress.onion:port [(@owner,@backup)]
|
# nodeaddress.onion:port [(@owner)]
|
||||||
localhost:2002 (@devtest1)
|
localhost:2002 (@devtest1) # TODO: replace with hosted stagenet seednodes
|
||||||
localhost:3002 (@devtest2)
|
localhost:3002 (@devtest2)
|
@ -1,2 +0,0 @@
|
|||||||
# nodeaddress.onion:port [(@owner)]
|
|
||||||
placeholder.onion:8001
|
|
@ -56,11 +56,11 @@ public class ArbitratorManagerTest {
|
|||||||
add("es");
|
add("es");
|
||||||
}};
|
}};
|
||||||
|
|
||||||
Arbitrator one = new Arbitrator(new NodeAddress("arbitrator:1"), null, null, null,
|
Arbitrator one = new Arbitrator(new NodeAddress("arbitrator:1"), null, null,
|
||||||
languagesOne, 0L, null, "", null,
|
languagesOne, 0L, null, "", null,
|
||||||
null, null);
|
null, null);
|
||||||
|
|
||||||
Arbitrator two = new Arbitrator(new NodeAddress("arbitrator:2"), null, null, null,
|
Arbitrator two = new Arbitrator(new NodeAddress("arbitrator:2"), null, null,
|
||||||
languagesTwo, 0L, null, "", null,
|
languagesTwo, 0L, null, "", null,
|
||||||
null, null);
|
null, null);
|
||||||
|
|
||||||
@ -92,11 +92,11 @@ public class ArbitratorManagerTest {
|
|||||||
add("es");
|
add("es");
|
||||||
}};
|
}};
|
||||||
|
|
||||||
Arbitrator one = new Arbitrator(new NodeAddress("arbitrator:1"), null, null, null,
|
Arbitrator one = new Arbitrator(new NodeAddress("arbitrator:1"), null, null,
|
||||||
languagesOne, 0L, null, "", null,
|
languagesOne, 0L, null, "", null,
|
||||||
null, null);
|
null, null);
|
||||||
|
|
||||||
Arbitrator two = new Arbitrator(new NodeAddress("arbitrator:2"), null, null, null,
|
Arbitrator two = new Arbitrator(new NodeAddress("arbitrator:2"), null, null,
|
||||||
languagesTwo, 0L, null, "", null,
|
languagesTwo, 0L, null, "", null,
|
||||||
null, null);
|
null, null);
|
||||||
|
|
||||||
|
@ -44,8 +44,7 @@ public class ArbitratorTest {
|
|||||||
|
|
||||||
public static Arbitrator getArbitratorMock() {
|
public static Arbitrator getArbitratorMock() {
|
||||||
return new Arbitrator(new NodeAddress("host", 1000),
|
return new Arbitrator(new NodeAddress("host", 1000),
|
||||||
getBytes(100),
|
"xmraddress",
|
||||||
"btcaddress",
|
|
||||||
new PubKeyRing(getBytes(100), getBytes(100)),
|
new PubKeyRing(getBytes(100), getBytes(100)),
|
||||||
Lists.newArrayList(),
|
Lists.newArrayList(),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
|
@ -79,7 +79,7 @@ public class CurrencyUtilTest {
|
|||||||
|
|
||||||
// For testnet its ok
|
// For testnet its ok
|
||||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
|
assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
|
||||||
BaseCurrencyNetwork.XMR_TESTNET).get().getTickerSymbol(), "MOCK_COIN");
|
BaseCurrencyNetwork.XMR_LOCAL).get().getTickerSymbol(), "MOCK_COIN");
|
||||||
assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork());
|
assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork());
|
||||||
|
|
||||||
// For regtest its still found
|
// For regtest its still found
|
||||||
@ -90,7 +90,7 @@ public class CurrencyUtilTest {
|
|||||||
// We test if we are not on mainnet to get the mainnet coin
|
// We test if we are not on mainnet to get the mainnet coin
|
||||||
Coin ether = new Ether();
|
Coin ether = new Ether();
|
||||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
|
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
|
||||||
BaseCurrencyNetwork.XMR_TESTNET).get().getTickerSymbol(), "ETH");
|
BaseCurrencyNetwork.XMR_LOCAL).get().getTickerSymbol(), "ETH");
|
||||||
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
|
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
|
||||||
BaseCurrencyNetwork.XMR_STAGENET).get().getTickerSymbol(), "ETH");
|
BaseCurrencyNetwork.XMR_STAGENET).get().getTickerSymbol(), "ETH");
|
||||||
assertEquals(Coin.Network.MAINNET, ether.getNetwork());
|
assertEquals(Coin.Network.MAINNET, ether.getNetwork());
|
||||||
|
@ -136,6 +136,7 @@ class GrpcTradesService extends TradesImplBase {
|
|||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
});
|
});
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
|
cause.printStackTrace();
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
exceptionHandler.handleException(log, cause, responseObserver);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -357,23 +357,25 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||||||
settingsButtonWithBadge.getStyleClass().add("new");
|
settingsButtonWithBadge.getStyleClass().add("new");
|
||||||
|
|
||||||
navigation.addListener((viewPath, data) -> {
|
navigation.addListener((viewPath, data) -> {
|
||||||
if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0)
|
UserThread.execute(() -> {
|
||||||
return;
|
if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
Class<? extends View> viewClass = viewPath.tip();
|
Class<? extends View> viewClass = viewPath.tip();
|
||||||
View view = viewLoader.load(viewClass);
|
View view = viewLoader.load(viewClass);
|
||||||
contentContainer.getChildren().setAll(view.getRoot());
|
contentContainer.getChildren().setAll(view.getRoot());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
navButtons.getToggles().stream()
|
navButtons.getToggles().stream()
|
||||||
.filter(toggle -> toggle instanceof NavButton)
|
.filter(toggle -> toggle instanceof NavButton)
|
||||||
.filter(button -> viewClass == ((NavButton) button).viewClass)
|
.filter(button -> viewClass == ((NavButton) button).viewClass)
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElseThrow(() -> new HavenoException("No button matching %s found", viewClass))
|
.orElseThrow(() -> new HavenoException("No button matching %s found", viewClass))
|
||||||
.setSelected(true);
|
.setSelected(true);
|
||||||
} catch (HavenoException e) {
|
} catch (HavenoException e) {
|
||||||
navigation.navigateTo(MainView.class, MarketView.class, OfferBookChartView.class);
|
navigation.navigateTo(MainView.class, MarketView.class, OfferBookChartView.class);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
VBox splashScreen = createSplashScreen();
|
VBox splashScreen = createSplashScreen();
|
||||||
@ -431,13 +433,14 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||||||
return new ListCell<>() {
|
return new ListCell<>() {
|
||||||
@Override
|
@Override
|
||||||
protected void updateItem(PriceFeedComboBoxItem item, boolean empty) {
|
protected void updateItem(PriceFeedComboBoxItem item, boolean empty) {
|
||||||
super.updateItem(item, empty);
|
UserThread.execute(() -> {
|
||||||
|
super.updateItem(item, empty);
|
||||||
if (!empty && item != null) {
|
if (!empty && item != null) {
|
||||||
textProperty().bind(item.displayStringProperty);
|
textProperty().bind(item.displayStringProperty);
|
||||||
} else {
|
} else {
|
||||||
textProperty().unbind();
|
textProperty().unbind();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -763,8 +766,10 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
|
|||||||
buttonWithBadge.textProperty().bind(badgeNumber);
|
buttonWithBadge.textProperty().bind(badgeNumber);
|
||||||
buttonWithBadge.setEnabled(badgeEnabled.get());
|
buttonWithBadge.setEnabled(badgeEnabled.get());
|
||||||
badgeEnabled.addListener((observable, oldValue, newValue) -> {
|
badgeEnabled.addListener((observable, oldValue, newValue) -> {
|
||||||
buttonWithBadge.setEnabled(newValue);
|
UserThread.execute(() -> {
|
||||||
buttonWithBadge.refreshBadge();
|
buttonWithBadge.setEnabled(newValue);
|
||||||
|
buttonWithBadge.refreshBadge();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
buttonWithBadge.setPosition(Pos.TOP_RIGHT);
|
buttonWithBadge.setPosition(Pos.TOP_RIGHT);
|
||||||
|
@ -20,7 +20,7 @@ package bisq.desktop.main.account.register;
|
|||||||
import bisq.desktop.common.model.ActivatableViewModel;
|
import bisq.desktop.common.model.ActivatableViewModel;
|
||||||
import bisq.desktop.util.GUIUtil;
|
import bisq.desktop.util.GUIUtil;
|
||||||
|
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.locale.LanguageUtil;
|
import bisq.core.locale.LanguageUtil;
|
||||||
import bisq.core.support.dispute.agent.DisputeAgent;
|
import bisq.core.support.dispute.agent.DisputeAgent;
|
||||||
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
||||||
@ -51,7 +51,7 @@ public abstract class AgentRegistrationViewModel<R extends DisputeAgent, T exten
|
|||||||
private final T disputeAgentManager;
|
private final T disputeAgentManager;
|
||||||
protected final User user;
|
protected final User user;
|
||||||
protected final P2PService p2PService;
|
protected final P2PService p2PService;
|
||||||
protected final BtcWalletService walletService;
|
protected final XmrWalletService xmrWalletService;
|
||||||
protected final KeyRing keyRing;
|
protected final KeyRing keyRing;
|
||||||
|
|
||||||
final BooleanProperty registrationEditDisabled = new SimpleBooleanProperty(true);
|
final BooleanProperty registrationEditDisabled = new SimpleBooleanProperty(true);
|
||||||
@ -73,12 +73,12 @@ public abstract class AgentRegistrationViewModel<R extends DisputeAgent, T exten
|
|||||||
public AgentRegistrationViewModel(T disputeAgentManager,
|
public AgentRegistrationViewModel(T disputeAgentManager,
|
||||||
User user,
|
User user,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
BtcWalletService walletService,
|
XmrWalletService xmrWalletService,
|
||||||
KeyRing keyRing) {
|
KeyRing keyRing) {
|
||||||
this.disputeAgentManager = disputeAgentManager;
|
this.disputeAgentManager = disputeAgentManager;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.p2PService = p2PService;
|
this.p2PService = p2PService;
|
||||||
this.walletService = walletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
|
|
||||||
mapChangeListener = change -> {
|
mapChangeListener = change -> {
|
||||||
|
@ -19,8 +19,7 @@ package bisq.desktop.main.account.register.arbitrator;
|
|||||||
|
|
||||||
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
|
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
|
||||||
|
|
||||||
import bisq.core.btc.model.AddressEntry;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
@ -30,7 +29,6 @@ import bisq.network.p2p.P2PService;
|
|||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
@ -40,19 +38,17 @@ public class ArbitratorRegistrationViewModel extends AgentRegistrationViewModel<
|
|||||||
public ArbitratorRegistrationViewModel(ArbitratorManager arbitratorManager,
|
public ArbitratorRegistrationViewModel(ArbitratorManager arbitratorManager,
|
||||||
User user,
|
User user,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
BtcWalletService walletService,
|
XmrWalletService xmrWalletService,
|
||||||
KeyRing keyRing) {
|
KeyRing keyRing) {
|
||||||
super(arbitratorManager, user, p2PService, walletService, keyRing);
|
super(arbitratorManager, user, p2PService, xmrWalletService, keyRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Arbitrator getDisputeAgent(String registrationSignature,
|
protected Arbitrator getDisputeAgent(String registrationSignature,
|
||||||
String emailAddress) {
|
String emailAddress) {
|
||||||
AddressEntry arbitratorAddressEntry = walletService.getArbitratorAddressEntry();
|
|
||||||
return new Arbitrator(
|
return new Arbitrator(
|
||||||
p2PService.getAddress(),
|
p2PService.getAddress(),
|
||||||
arbitratorAddressEntry.getPubKey(),
|
xmrWalletService.getWallet().getPrimaryAddress(), // TODO: how is arbitrator address used?
|
||||||
arbitratorAddressEntry.getAddressString(),
|
|
||||||
keyRing.getPubKeyRing(),
|
keyRing.getPubKeyRing(),
|
||||||
new ArrayList<>(languageCodes),
|
new ArrayList<>(languageCodes),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
|
@ -19,7 +19,7 @@ package bisq.desktop.main.account.register.mediator;
|
|||||||
|
|
||||||
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
|
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
|
||||||
|
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
@ -39,9 +39,9 @@ class MediatorRegistrationViewModel extends AgentRegistrationViewModel<Mediator,
|
|||||||
public MediatorRegistrationViewModel(MediatorManager mediatorManager,
|
public MediatorRegistrationViewModel(MediatorManager mediatorManager,
|
||||||
User user,
|
User user,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
BtcWalletService walletService,
|
XmrWalletService xmrWalletService,
|
||||||
KeyRing keyRing) {
|
KeyRing keyRing) {
|
||||||
super(mediatorManager, user, p2PService, walletService, keyRing);
|
super(mediatorManager, user, p2PService, xmrWalletService, keyRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -20,7 +20,7 @@ package bisq.desktop.main.account.register.refundagent;
|
|||||||
|
|
||||||
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
|
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
|
||||||
|
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.support.dispute.refund.refundagent.RefundAgent;
|
import bisq.core.support.dispute.refund.refundagent.RefundAgent;
|
||||||
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
@ -40,9 +40,9 @@ public class RefundAgentRegistrationViewModel extends AgentRegistrationViewModel
|
|||||||
public RefundAgentRegistrationViewModel(RefundAgentManager arbitratorManager,
|
public RefundAgentRegistrationViewModel(RefundAgentManager arbitratorManager,
|
||||||
User user,
|
User user,
|
||||||
P2PService p2PService,
|
P2PService p2PService,
|
||||||
BtcWalletService walletService,
|
XmrWalletService xmrWalletService,
|
||||||
KeyRing keyRing) {
|
KeyRing keyRing) {
|
||||||
super(arbitratorManager, user, p2PService, walletService, keyRing);
|
super(arbitratorManager, user, p2PService, xmrWalletService, keyRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,7 +52,6 @@ import bisq.core.util.coin.CoinUtil;
|
|||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
import bisq.common.util.MathUtils;
|
import bisq.common.util.MathUtils;
|
||||||
import bisq.common.util.Tuple2;
|
|
||||||
import bisq.common.util.Utilities;
|
import bisq.common.util.Utilities;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
@ -263,9 +262,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
|||||||
|
|
||||||
priceFeedService.setCurrencyCode(tradeCurrencyCode.get());
|
priceFeedService.setCurrencyCode(tradeCurrencyCode.get());
|
||||||
|
|
||||||
// We request to get the actual estimated fee
|
|
||||||
requestTxFee(null);
|
|
||||||
|
|
||||||
// Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values)
|
// Set the default values (in rare cases if the fee request was not done yet we get the hard coded default values)
|
||||||
// But offer creation happens usually after that so we should have already the value from the estimation service.
|
// But offer creation happens usually after that so we should have already the value from the estimation service.
|
||||||
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
|
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
|
||||||
@ -317,16 +313,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
|||||||
paymentAccount);
|
paymentAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This works only if we have already funds in the wallet
|
|
||||||
public void updateEstimatedFeeAndTxVsize() {
|
|
||||||
Tuple2<Coin, Integer> estimatedFeeAndTxVsize = createOfferService.getEstimatedFeeAndTxVsize(amount.get(),
|
|
||||||
direction,
|
|
||||||
buyerSecurityDeposit.get(),
|
|
||||||
createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDeposit.get()));
|
|
||||||
txFeeFromFeeService = estimatedFeeAndTxVsize.first;
|
|
||||||
feeTxVsize = estimatedFeeAndTxVsize.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
|
||||||
openOfferManager.placeOffer(offer,
|
openOfferManager.placeOffer(offer,
|
||||||
useSavingsWallet,
|
useSavingsWallet,
|
||||||
@ -439,15 +425,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
|
|||||||
this.marketPriceMargin = marketPriceMargin;
|
this.marketPriceMargin = marketPriceMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestTxFee(@Nullable Runnable actionHandler) {
|
|
||||||
feeService.requestFees(() -> {
|
|
||||||
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
|
|
||||||
calculateTotalToPay();
|
|
||||||
if (actionHandler != null)
|
|
||||||
actionHandler.run();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Getters
|
// Getters
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -37,7 +37,6 @@ import bisq.core.monetary.Price;
|
|||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferDirection;
|
import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
import bisq.core.offer.OfferRestrictions;
|
import bisq.core.offer.OfferRestrictions;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
@ -674,8 +673,6 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onShowPayFundsScreen(Runnable actionHandler) {
|
void onShowPayFundsScreen(Runnable actionHandler) {
|
||||||
dataModel.updateEstimatedFeeAndTxVsize();
|
|
||||||
dataModel.requestTxFee(actionHandler);
|
|
||||||
showPayFundsScreenDisplayed.set(true);
|
showPayFundsScreenDisplayed.set(true);
|
||||||
updateSpinnerInfo();
|
updateSpinnerInfo();
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import bisq.desktop.main.overlays.popups.Popup;
|
|||||||
import bisq.desktop.util.GUIUtil;
|
import bisq.desktop.util.GUIUtil;
|
||||||
|
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
import bisq.core.account.witness.AccountAgeWitnessService;
|
||||||
import bisq.core.btc.TxFeeEstimationService;
|
|
||||||
import bisq.core.btc.listeners.XmrBalanceListener;
|
import bisq.core.btc.listeners.XmrBalanceListener;
|
||||||
import bisq.core.btc.model.XmrAddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.btc.wallet.Restrictions;
|
import bisq.core.btc.wallet.Restrictions;
|
||||||
@ -36,7 +35,6 @@ import bisq.core.monetary.Price;
|
|||||||
import bisq.core.monetary.Volume;
|
import bisq.core.monetary.Volume;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferDirection;
|
import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.PaymentAccountUtil;
|
import bisq.core.payment.PaymentAccountUtil;
|
||||||
@ -52,7 +50,6 @@ import bisq.core.util.VolumeUtil;
|
|||||||
import bisq.core.util.coin.CoinUtil;
|
import bisq.core.util.coin.CoinUtil;
|
||||||
|
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
import bisq.common.util.Tuple2;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
@ -93,7 +90,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||||||
private final MempoolService mempoolService;
|
private final MempoolService mempoolService;
|
||||||
private final FilterManager filterManager;
|
private final FilterManager filterManager;
|
||||||
final Preferences preferences;
|
final Preferences preferences;
|
||||||
private final TxFeeEstimationService txFeeEstimationService;
|
|
||||||
private final PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||||
private final Navigation navigation;
|
private final Navigation navigation;
|
||||||
@ -138,7 +134,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||||||
MempoolService mempoolService,
|
MempoolService mempoolService,
|
||||||
FilterManager filterManager,
|
FilterManager filterManager,
|
||||||
Preferences preferences,
|
Preferences preferences,
|
||||||
TxFeeEstimationService txFeeEstimationService,
|
|
||||||
PriceFeedService priceFeedService,
|
PriceFeedService priceFeedService,
|
||||||
AccountAgeWitnessService accountAgeWitnessService,
|
AccountAgeWitnessService accountAgeWitnessService,
|
||||||
Navigation navigation,
|
Navigation navigation,
|
||||||
@ -153,7 +148,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||||||
this.mempoolService = mempoolService;
|
this.mempoolService = mempoolService;
|
||||||
this.filterManager = filterManager;
|
this.filterManager = filterManager;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.txFeeEstimationService = txFeeEstimationService;
|
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
@ -277,7 +271,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||||||
|
|
||||||
// We don't want that the fee gets updated anymore after we show the funding screen.
|
// We don't want that the fee gets updated anymore after we show the funding screen.
|
||||||
void onShowPayFundsScreen() {
|
void onShowPayFundsScreen() {
|
||||||
estimateTxVsize();
|
|
||||||
freezeFee = true;
|
freezeFee = true;
|
||||||
calculateTotalToPay();
|
calculateTotalToPay();
|
||||||
}
|
}
|
||||||
@ -347,50 +340,6 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This works only if have already funds in the wallet
|
|
||||||
// TODO: There still are issues if we get funded by very small inputs which are causing higher tx fees and the
|
|
||||||
// changed total required amount is not updated. That will cause a InsufficientMoneyException and the user need to
|
|
||||||
// start over again. To reproduce keep adding 0.002 BTC amounts while in the funding screen.
|
|
||||||
// It would require a listener on changed balance and a new fee estimation with a correct recalculation of the required funds.
|
|
||||||
// Another edge case not handled correctly is: If there are many small inputs and user add a large input later the
|
|
||||||
// fee estimation is based on the large tx with many inputs but the actual tx will get created with the large input, thus
|
|
||||||
// leading to a smaller tx and too high fees. Simply updating the fee estimation would lead to changed required funds
|
|
||||||
// and if funds get higher (if tx get larger) the user would get confused (adding small inputs would increase total required funds).
|
|
||||||
// So that would require more thoughts how to deal with all those cases.
|
|
||||||
public void estimateTxVsize() {
|
|
||||||
int txVsize = 0;
|
|
||||||
if (xmrWalletService.getWallet().getUnlockedBalance(0).compareTo(new BigInteger("0")) > 0) {
|
|
||||||
Coin fundsNeededForTrade = getFundsNeededForTrade();
|
|
||||||
if (isBuyOffer())
|
|
||||||
fundsNeededForTrade = fundsNeededForTrade.add(amount.get());
|
|
||||||
|
|
||||||
// As taker we pay 3 times the fee and currently the fee is the same for all 3 txs (trade fee tx, deposit
|
|
||||||
// tx and payout tx).
|
|
||||||
// We should try to change that in future to have the deposit and payout tx with a fixed fee as the vsize is
|
|
||||||
// there more deterministic.
|
|
||||||
// The trade fee tx can be in the worst case very large if there are many inputs so if we take that tx alone
|
|
||||||
// for the fee estimation we would overpay a lot.
|
|
||||||
// On the other side if we have the best case of a 1 input tx fee tx then it is only 175 vbytes but the
|
|
||||||
// other 2 txs are different (233 and 169 vbytes) and may get a lower fee/vbyte as intended.
|
|
||||||
// We apply following model to not overpay too much but be on the safe side as well.
|
|
||||||
// We sum the taker fee tx and the deposit tx together as it can be assumed that both be in the same block and
|
|
||||||
// as they are dependent txs the miner will pick both if the fee in total is good enough.
|
|
||||||
// We make sure that the fee is sufficient to meet our intended fee/vbyte for the larger deposit tx with 233 vbytes.
|
|
||||||
Tuple2<Coin, Integer> estimatedFeeAndTxVsize = txFeeEstimationService.getEstimatedFeeAndTxVsizeForTaker(fundsNeededForTrade,
|
|
||||||
getTakerFee());
|
|
||||||
txFeeFromFeeService = estimatedFeeAndTxVsize.first;
|
|
||||||
feeTxVsize = estimatedFeeAndTxVsize.second;
|
|
||||||
} else {
|
|
||||||
feeTxVsize = 233;
|
|
||||||
txFeeFromFeeService = txFeePerVbyteFromFeeService.multiply(feeTxVsize);
|
|
||||||
log.info("We cannot do the fee estimation because there are no funds in the wallet.\nThis is expected " +
|
|
||||||
"if the user has not funded their wallet yet.\n" +
|
|
||||||
"In that case we use an estimated tx vsize of 233 vbytes.\n" +
|
|
||||||
"txFee based on estimated vsize of {} vbytes. feeTxVsize = {} vbytes. Actual tx vsize = {} vbytes. TxFee is {} ({} sat/vbyte)",
|
|
||||||
feeTxVsize, feeTxVsize, txVsize, txFeeFromFeeService.toFriendlyString(), feeService.getTxFeePerVbyte());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
public void onPaymentAccountSelected(PaymentAccount paymentAccount) {
|
||||||
if (paymentAccount != null) {
|
if (paymentAccount != null) {
|
||||||
this.paymentAccount = paymentAccount;
|
this.paymentAccount = paymentAccount;
|
||||||
|
@ -20,22 +20,16 @@ package bisq.desktop.main.overlays.windows;
|
|||||||
import bisq.desktop.Navigation;
|
import bisq.desktop.Navigation;
|
||||||
import bisq.desktop.components.AutoTooltipButton;
|
import bisq.desktop.components.AutoTooltipButton;
|
||||||
import bisq.desktop.components.BusyAnimation;
|
import bisq.desktop.components.BusyAnimation;
|
||||||
import bisq.desktop.components.TitledGroupBg;
|
|
||||||
import bisq.desktop.components.TxIdTextField;
|
|
||||||
import bisq.desktop.main.overlays.Overlay;
|
import bisq.desktop.main.overlays.Overlay;
|
||||||
import bisq.desktop.util.DisplayUtils;
|
import bisq.desktop.util.DisplayUtils;
|
||||||
import bisq.desktop.util.GUIUtil;
|
import bisq.desktop.util.GUIUtil;
|
||||||
import bisq.desktop.util.Layout;
|
import bisq.desktop.util.Layout;
|
||||||
|
|
||||||
import bisq.core.btc.wallet.BtcWalletService;
|
|
||||||
import bisq.core.locale.BankUtil;
|
|
||||||
import bisq.core.locale.CountryUtil;
|
import bisq.core.locale.CountryUtil;
|
||||||
import bisq.core.locale.Res;
|
import bisq.core.locale.Res;
|
||||||
import bisq.core.monetary.Price;
|
import bisq.core.monetary.Price;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferDirection;
|
import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
import bisq.core.offer.OfferUtil;
|
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentMethod;
|
import bisq.core.payment.payload.PaymentMethod;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
@ -80,7 +74,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
|||||||
private final User user;
|
private final User user;
|
||||||
private final KeyRing keyRing;
|
private final KeyRing keyRing;
|
||||||
private final Navigation navigation;
|
private final Navigation navigation;
|
||||||
private final BtcWalletService btcWalletService;
|
|
||||||
private Offer offer;
|
private Offer offer;
|
||||||
private Coin tradeAmount;
|
private Coin tradeAmount;
|
||||||
private Price tradePrice;
|
private Price tradePrice;
|
||||||
@ -97,13 +90,11 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
|||||||
public OfferDetailsWindow(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
public OfferDetailsWindow(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
|
||||||
User user,
|
User user,
|
||||||
KeyRing keyRing,
|
KeyRing keyRing,
|
||||||
Navigation navigation,
|
Navigation navigation) {
|
||||||
BtcWalletService btcWalletService) {
|
|
||||||
this.formatter = formatter;
|
this.formatter = formatter;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
this.navigation = navigation;
|
this.navigation = navigation;
|
||||||
this.btcWalletService = btcWalletService;
|
|
||||||
type = Type.Confirmation;
|
type = Type.Confirmation;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +312,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
|||||||
rows++;
|
rows++;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE);
|
|
||||||
addConfirmationLabelTextFieldWithCopyIcon(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(),
|
addConfirmationLabelTextFieldWithCopyIcon(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(),
|
||||||
Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE);
|
Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE);
|
||||||
addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.makersOnion"),
|
addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.makersOnion"),
|
||||||
@ -337,21 +327,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
|||||||
formatter.formatCoinWithCode(offer.getSellerSecurityDeposit());
|
formatter.formatCoinWithCode(offer.getSellerSecurityDeposit());
|
||||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), value);
|
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.securityDeposit"), value);
|
||||||
|
|
||||||
// At create offer we do not show the makerFeeTxId
|
|
||||||
if (!placeOfferHandlerOptional.isPresent()) {
|
|
||||||
TxIdTextField makerFeeTxIdTextField = addLabelTxIdTextField(gridPane, ++rowIndex,
|
|
||||||
Res.get("shared.makerFeeTxId"), offer.getOfferFeePaymentTxId()).second;
|
|
||||||
|
|
||||||
int finalRows = rows;
|
|
||||||
OfferUtil.getInvalidMakerFeeTxErrorMessage(offer, btcWalletService)
|
|
||||||
.ifPresent(errorMsg -> {
|
|
||||||
makerFeeTxIdTextField.getTextField().setId("address-text-field-error");
|
|
||||||
GridPane.setRowSpan(titledGroupBg, finalRows + 1);
|
|
||||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.errorMessage"),
|
|
||||||
errorMsg.replace("\n\n", "\n"));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (countryCode != null && !isF2F)
|
if (countryCode != null && !isF2F)
|
||||||
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.countryBank"),
|
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.countryBank"),
|
||||||
CountryUtil.getNameAndCode(countryCode));
|
CountryUtil.getNameAndCode(countryCode));
|
||||||
|
@ -184,29 +184,31 @@ public class MarketPricePresentation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setMarketPriceInItems() {
|
private void setMarketPriceInItems() {
|
||||||
priceFeedComboBoxItems.forEach(item -> {
|
UserThread.execute(() -> {
|
||||||
String currencyCode = item.currencyCode;
|
priceFeedComboBoxItems.forEach(item -> {
|
||||||
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
|
String currencyCode = item.currencyCode;
|
||||||
String priceString;
|
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
|
||||||
if (marketPrice != null && marketPrice.isPriceAvailable()) {
|
String priceString;
|
||||||
priceString = FormattingUtils.formatMarketPrice(marketPrice.getPrice(), currencyCode);
|
if (marketPrice != null && marketPrice.isPriceAvailable()) {
|
||||||
item.setPriceAvailable(true);
|
priceString = FormattingUtils.formatMarketPrice(marketPrice.getPrice(), currencyCode);
|
||||||
item.setExternallyProvidedPrice(marketPrice.isExternallyProvidedPrice());
|
item.setPriceAvailable(true);
|
||||||
} else {
|
item.setExternallyProvidedPrice(marketPrice.isExternallyProvidedPrice());
|
||||||
priceString = Res.get("shared.na");
|
} else {
|
||||||
item.setPriceAvailable(false);
|
priceString = Res.get("shared.na");
|
||||||
}
|
item.setPriceAvailable(false);
|
||||||
item.setDisplayString(CurrencyUtil.getCurrencyPair(currencyCode) + ": " + priceString);
|
}
|
||||||
|
item.setDisplayString(CurrencyUtil.getCurrencyPair(currencyCode) + ": " + priceString);
|
||||||
|
|
||||||
final String code = item.currencyCode;
|
final String code = item.currencyCode;
|
||||||
if (selectedPriceFeedComboBoxItemProperty.get() != null &&
|
if (selectedPriceFeedComboBoxItemProperty.get() != null &&
|
||||||
selectedPriceFeedComboBoxItemProperty.get().currencyCode.equals(code)) {
|
selectedPriceFeedComboBoxItemProperty.get().currencyCode.equals(code)) {
|
||||||
isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice());
|
isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice());
|
||||||
isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice());
|
isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice());
|
||||||
isExternallyProvidedPrice.set(item.isExternallyProvidedPrice());
|
isExternallyProvidedPrice.set(item.isExternallyProvidedPrice());
|
||||||
isPriceAvailable.set(item.isPriceAvailable());
|
isPriceAvailable.set(item.isPriceAvailable());
|
||||||
marketPriceUpdated.set(marketPriceUpdated.get() + 1);
|
marketPriceUpdated.set(marketPriceUpdated.get() + 1);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Running a local Haveno test network
|
# Running a local Haveno test network
|
||||||
|
|
||||||
These are the steps needed to set up a local Haveno test network. You'll have the possibility to either connect to our shared Monero stagenet node or to create your own private stagenet.
|
These are the steps needed to set up Haveno test instances.
|
||||||
|
|
||||||
## 1. Install dependencies
|
## 1. Install dependencies
|
||||||
|
|
||||||
@ -11,42 +11,33 @@ On Ubuntu: `sudo apt install make wget git git-lfs openjdk-11-jdk`. The Bitcoin
|
|||||||
1. Download this repository: `git clone https://github.com/haveno-dex/haveno.git`
|
1. Download this repository: `git clone https://github.com/haveno-dex/haveno.git`
|
||||||
2. Navigate to the root of the repository (`cd haveno`) and build the repository: run `make` in the terminal and wait until the process is completed (this will also download and verify the Monero and Bitcoin binaries).
|
2. Navigate to the root of the repository (`cd haveno`) and build the repository: run `make` in the terminal and wait until the process is completed (this will also download and verify the Monero and Bitcoin binaries).
|
||||||
|
|
||||||
## 3. Connect to Monero stagenet
|
## 3. Run a local XMR testnet
|
||||||
|
|
||||||
The easiest way to get a Monero stagenet running is by connecting to our own shared instance (3a) so you won't have to do anything except mine coins for testing (step 5). If you are testing or developing locally, we suggest you create your own local stagenet (3b).
|
1. In a new terminal window run `make monerod-local1`
|
||||||
|
1. In a new terminal window run `make monerod-local2`
|
||||||
### 3a. Join our shared stagenet
|
|
||||||
|
|
||||||
Run `make monero-shared`
|
|
||||||
|
|
||||||
### 3b. Run your own private stagenet
|
|
||||||
|
|
||||||
1. In a new terminal window run `make monero-private1`;
|
|
||||||
1. In a new terminal window run `make monero-private2`;
|
|
||||||
3. Now mine the first 130 blocks to a random address before using, so wallets only use the latest output type. Run in one of the terminal windows opened above:
|
3. Now mine the first 130 blocks to a random address before using, so wallets only use the latest output type. Run in one of the terminal windows opened above:
|
||||||
|
|
||||||
`start_mining 56k9Yra1pxwcTYzqKcnLip8mymSQdEfA6V7476W9XhSiHPp1hAboo1F6na7kxTxwvXU6JjDQtu8VJdGj9FEcjkxGJfsyyah 1`
|
`start_mining 9tsUiG9bwcU7oTbAdBwBk2PzxFtysge5qcEsHEpetmEKgerHQa1fDqH7a4FiquZmms7yM22jdifVAD7jAb2e63GSJMuhY75 1`
|
||||||
|
|
||||||
## 4. Deploy
|
## 4. Deploy
|
||||||
|
|
||||||
If you are a *screen* user, simply run `make deploy`. This command will open all needed Haveno instances (seednode, Alice, Bob, arbitrator) using *screen*. If this is the first time launching the arbitrator desktop application, register the arbitrator and mediator as explained in steps `5.3.1` and `5.3.2`.
|
If you are a *screen* user, simply run `make deploy`. This command will open all needed Haveno instances (seednode, Alice, Bob, arbitrator) using *screen*. If this is the first time launching the arbitrator desktop application, register the arbitrator as explained in step 3 below.
|
||||||
|
|
||||||
If you don't use *screen*, open 4 terminal windows and run in each one of them:
|
If you don't use *screen*, open 4 terminal windows and run in each one of them:
|
||||||
|
|
||||||
1. `make seednode`
|
1. `make seednode-local`
|
||||||
2. `make arbitrator-desktop`
|
2. `make arbitrator-desktop-local`
|
||||||
3. If this is the first time launching the arbitrator desktop application, register the arbitrator after the interface opens:
|
3. If this is the first time launching the arbitrator desktop application, register the arbitrator after the interface opens. Go to the *Account* tab and press `cmd+r`. Confirm the registration of the arbitrator.
|
||||||
1. Go to the *Account* tab and press `cmd+r`. Confirm the registration of the arbitrator.
|
4. `make alice-desktop-local` or if you want to run Alice as a daemon: `make alice-daemon-local`
|
||||||
4. `make alice-desktop` or if you want to run Alice as a daemon: `make alice-daemon`
|
5. `make bob-desktop-local` or if you want to run Bob as a daemon: `make bob-daemon-local`
|
||||||
5. `make bob-desktop` or if you want to run Bob as a daemon: `make bob-daemon`
|
|
||||||
|
|
||||||
## 5. Fund your wallets
|
## 5. Fund your wallets
|
||||||
|
|
||||||
When running Alice and Bob, you'll see a Monero address prompted in the terminal. Send stagenet XMR to the addresses of both Alice and Bob to be able to initiate a trade.
|
When running Alice and Bob, you'll see a Monero address prompted in the terminal. Send local testnet XMR to the addresses of both Alice and Bob to be able to initiate a trade.
|
||||||
|
|
||||||
You can fund the two wallets by mining some stagenet XMR coins to those addresses. To do so, open a terminal where you ran monerod and run: `start_mining ADDRESS 1`.
|
You can fund the two wallets by mining some testnet XMR coins to those addresses. To do so, open a terminal where you ran monerod and run: `start_mining ADDRESS 1`.
|
||||||
|
|
||||||
monerod will start mining stagenet coins on your device using one thread. Replace `ADDRESS` with the address of Alice first, and then Bob's. Run `stop_mining` to stop mining.
|
monerod will start mining local testnet coins on your device using one thread. Replace `ADDRESS` with the address of Alice first, and then Bob's. Run `stop_mining` to stop mining.
|
||||||
|
|
||||||
## 6. Start testing
|
## 6. Start testing
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ A sample configuration file looks like follows:
|
|||||||
# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false)
|
# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false)
|
||||||
System.useConsoleReporter=true
|
System.useConsoleReporter=true
|
||||||
|
|
||||||
# 0 -> XMR_MAINNET, 1 -> XMR_TESTNET (default)
|
# 0 -> XMR_MAINNET, 1 -> XMR_LOCAL (default)
|
||||||
System.baseCurrencyNetwork=0
|
System.baseCurrencyNetwork=0
|
||||||
|
|
||||||
## Each Metric is configured via a set of properties.
|
## Each Metric is configured via a set of properties.
|
||||||
|
@ -91,7 +91,7 @@ public abstract class Metric extends Configurable implements Runnable {
|
|||||||
super.configure(properties);
|
super.configure(properties);
|
||||||
reporter.configure(properties);
|
reporter.configure(properties);
|
||||||
|
|
||||||
Version.setBaseCryptoNetworkId(Integer.parseInt(properties.getProperty("System." + BASE_CURRENCY_NETWORK, "1"))); // defaults to XMR_TESTNET
|
Version.setBaseCryptoNetworkId(Integer.parseInt(properties.getProperty("System." + BASE_CURRENCY_NETWORK, "1"))); // defaults to XMR_LOCAL
|
||||||
|
|
||||||
// decide whether to enable or disable the task
|
// decide whether to enable or disable the task
|
||||||
if (configuration.isEmpty() || !configuration.getProperty("enabled", "false").equals("true")
|
if (configuration.isEmpty() || !configuration.getProperty("enabled", "false").equals("true")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false)
|
# true overwrites the reporters picked by the developers (for debugging for example) (defaults to false)
|
||||||
System.useConsoleReporter=true
|
System.useConsoleReporter=true
|
||||||
|
|
||||||
# 0 -> XMR_MAINNET, 1 -> XMR_TESTNET (default)
|
# 0 -> XMR_MAINNET, 1 -> XMR_LOCAL (default)
|
||||||
System.baseCurrencyNetwork=0
|
System.baseCurrencyNetwork=0
|
||||||
|
|
||||||
## Each Metric is configured via a set of properties.
|
## Each Metric is configured via a set of properties.
|
||||||
|
@ -703,11 +703,10 @@ message Arbitrator {
|
|||||||
string registration_signature = 4;
|
string registration_signature = 4;
|
||||||
bytes registration_pub_key = 5;
|
bytes registration_pub_key = 5;
|
||||||
PubKeyRing pub_key_ring = 6;
|
PubKeyRing pub_key_ring = 6;
|
||||||
bytes btc_pub_key = 7;
|
string xmr_address = 7;
|
||||||
string btc_address = 8;
|
string email_address = 8;
|
||||||
string email_address = 9;
|
string info = 9;
|
||||||
string info = 10;
|
map<string, string> extra_data = 10;
|
||||||
map<string, string> extra_data = 11;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Mediator {
|
message Mediator {
|
||||||
|
@ -171,9 +171,9 @@ public class SeedNodeMain extends ExecutableForAppWithP2p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setupConnectionLossCheck() {
|
private void setupConnectionLossCheck() {
|
||||||
// For dev testing (usually on XMR_STAGENET) we don't want to get the seed shut
|
// For dev testing (usually on XMR_LOCAL) we don't want to get the seed shut
|
||||||
// down as it is normal that the seed is the only actively running node.
|
// down as it is normal that the seed is the only actively running node.
|
||||||
if (Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_STAGENET) {
|
if (Config.baseCurrencyNetwork() != BaseCurrencyNetwork.XMR_MAINNET) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user