support local, stagenet, and mainnet xmr network configuration (#335)

remove btc wallet
disable local zmq
This commit is contained in:
woodser 2022-07-07 09:10:59 -04:00 committed by GitHub
parent b4112e50e9
commit e2208355b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 595 additions and 899 deletions

2
.gitattributes vendored
View File

@ -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
View File

@ -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 \

View File

@ -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.")

View File

@ -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) {

View File

@ -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;

View File

@ -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());

View File

@ -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) {

View File

@ -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(),

View File

@ -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,7 +347,10 @@ public final class CoreMoneroConnectionsService {
} }
}); });
// start local node if last connection is local and offline isInitialized = true;
}
// start local node if offline and last connection is local
currentConnectionUri.ifPresent(uri -> { currentConnectionUri.ifPresent(uri -> {
try { try {
if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) { if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) {
@ -312,22 +361,23 @@ public final class CoreMoneroConnectionsService {
} }
}); });
// poll daemon periodically // connect to local node if available
startPollingDaemon(); if (nodeService.isMoneroNodeRunning() && (connectionManager.getAutoSwitch() || !connectionManager.isConnected())) {
isInitialized = true;
}
// if offline, connect to local node if available
if (!connectionManager.isConnected() && nodeService.isMoneroNodeRunning()) {
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");
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()); 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());
} }
} }

View File

@ -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) {
try {
String host = new URI(uri).getHost(); String host = new URI(uri).getHost();
return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST); 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()) {

View File

@ -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");
} }

View File

@ -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,
() -> { () -> {

View File

@ -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);

View File

@ -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.
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(); 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() {

View File

@ -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);
}); });

View File

@ -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());
}); });
} }

View File

@ -160,12 +160,14 @@ public abstract class WalletService {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
protected void addListenersToWallet() { protected void addListenersToWallet() {
if (wallet != null) {
wallet.addCoinsReceivedEventListener(walletEventListener); wallet.addCoinsReceivedEventListener(walletEventListener);
wallet.addCoinsSentEventListener(walletEventListener); wallet.addCoinsSentEventListener(walletEventListener);
wallet.addReorganizeEventListener(walletEventListener); wallet.addReorganizeEventListener(walletEventListener);
wallet.addTransactionConfidenceEventListener(walletEventListener); wallet.addTransactionConfidenceEventListener(walletEventListener);
wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener); wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener);
} }
}
public void shutDown() { public void shutDown() {
if (wallet != null) { if (wallet != null) {

View File

@ -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() {

View File

@ -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;
@ -156,7 +156,15 @@ public class XmrWalletService {
return wallet; return wallet;
} }
public MoneroDaemon getDaemon() { public boolean isWalletReady() {
return getWallet() != null;
}
public boolean isWalletEncrypted() {
return accountService.getPassword() != null;
}
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,23 +323,18 @@ 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 // submit tx to pool
if (tx == null) {
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) {
@ -344,7 +347,7 @@ public class XmrWalletService {
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());

View File

@ -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,

View File

@ -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");

View File

@ -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.

View File

@ -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,

View File

@ -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.

View File

@ -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" +
'}'; '}';
} }
} }

View File

@ -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");

View File

@ -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;
} }

View File

@ -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;

View File

@ -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();
} }
} }

View File

@ -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

View File

@ -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 + '\'' +

View File

@ -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.

View File

@ -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());

View File

@ -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);
} }

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,3 @@
# nodeaddress.onion:port [(@owner,@backup)]
localhost:2002 (@devtest1)
localhost:3002 (@devtest2)

View File

@ -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)

View File

@ -1,2 +0,0 @@
# nodeaddress.onion:port [(@owner)]
placeholder.onion:8001

View File

@ -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);

View File

@ -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(),

View File

@ -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());

View File

@ -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);
} }
} }

View File

@ -357,6 +357,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
settingsButtonWithBadge.getStyleClass().add("new"); settingsButtonWithBadge.getStyleClass().add("new");
navigation.addListener((viewPath, data) -> { navigation.addListener((viewPath, data) -> {
UserThread.execute(() -> {
if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0) if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0)
return; return;
@ -375,6 +376,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
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) {
UserThread.execute(() -> {
super.updateItem(item, empty); 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,9 +766,11 @@ 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) -> {
UserThread.execute(() -> {
buttonWithBadge.setEnabled(newValue); buttonWithBadge.setEnabled(newValue);
buttonWithBadge.refreshBadge(); buttonWithBadge.refreshBadge();
}); });
});
buttonWithBadge.setPosition(Pos.TOP_RIGHT); buttonWithBadge.setPosition(Pos.TOP_RIGHT);
buttonWithBadge.setMinHeight(34); buttonWithBadge.setMinHeight(34);

View File

@ -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 -> {

View File

@ -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(),

View File

@ -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

View File

@ -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

View File

@ -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
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -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();
} }

View File

@ -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;

View File

@ -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));

View File

@ -184,6 +184,7 @@ public class MarketPricePresentation {
} }
private void setMarketPriceInItems() { private void setMarketPriceInItems() {
UserThread.execute(() -> {
priceFeedComboBoxItems.forEach(item -> { priceFeedComboBoxItems.forEach(item -> {
String currencyCode = item.currencyCode; String currencyCode = item.currencyCode;
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
@ -208,6 +209,7 @@ public class MarketPricePresentation {
marketPriceUpdated.set(marketPriceUpdated.get() + 1); marketPriceUpdated.set(marketPriceUpdated.get() + 1);
} }
}); });
});
} }
public ObjectProperty<PriceFeedComboBoxItem> getSelectedPriceFeedComboBoxItemProperty() { public ObjectProperty<PriceFeedComboBoxItem> getSelectedPriceFeedComboBoxItemProperty() {

View File

@ -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

View File

@ -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.

View File

@ -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")

View File

@ -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.

View File

@ -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 {

View File

@ -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;
} }