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
*.jpeg 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
# deploy each node in its own named screen window
for target in \
seednode \
alice-desktop \
bob-desktop \
arbitrator-desktop; do \
seednode-local \
alice-desktop-local \
bob-desktop-local \
arbitrator-desktop-local; do \
screen -S localnet -X screen -t $$target; \
screen -S localnet -p $$target -X stuff "make $$target\n"; \
done;
# give bitcoind rpc server time to start
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 \
--baseCurrencyNetwork=XMR_STAGENET \
--baseCurrencyNetwork=XMR_LOCAL \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--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 \
arbitrator-desktop:
# Arbitrator and mediator need to be registerd in the UI after launching it.
arbitrator-desktop-local:
# 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 \
--baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--useDevPrivilegeKeys=false \
--nodePort=4444 \
--appName=haveno-XMR_STAGENET_arbitrator \
--apiPassword=apitest \
--apiPort=9998
arbitrator-desktop2:
# Arbitrator and mediator need to be registerd in the UI after launching it.
arbitrator-desktop2-local:
# Arbitrator needs to be registered before making trades
./haveno-desktop \
--baseCurrencyNetwork=XMR_STAGENET \
--baseCurrencyNetwork=XMR_LOCAL \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--nodePort=7777 \
--appName=haveno-XMR_STAGENET_arbitrator2 \
--appName=haveno-XMR_LOCAL_arbitrator2 \
--apiPassword=apitest \
--apiPort=10001
arbitrator-daemon:
# Arbitrator and mediator need to be registerd in the UI before launching the daemon!
arbitrator-daemon-local:
# 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 \
--baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--useDevPrivilegeKeys=false \
--nodePort=4444 \
--appName=haveno-XMR_STAGENET_arbitrator \
--apiPassword=apitest \
--apiPort=9998 \
--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 \
--baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--useDevPrivilegeKeys=false \
--nodePort=5555 \
--appName=haveno-XMR_STAGENET_Alice \
--apiPassword=apitest \
--apiPort=9999 \
--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 \
--baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--useDevPrivilegeKeys=false \
--nodePort=5555 \
--appName=haveno-XMR_STAGENET_Alice \
--apiPassword=apitest \
@ -102,22 +208,45 @@ alice-daemon:
--walletRpcBindPort=38091 \
--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 \
--baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--useDevPrivilegeKeys=false \
--nodePort=6666 \
--appName=haveno-XMR_STAGENET_Bob \
--apiPassword=apitest \
--apiPort=10000 \
--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 \
--baseCurrencyNetwork=XMR_STAGENET \
--useLocalhostForP2P=true \
--useDevPrivilegeKeys=true \
--useDevPrivilegeKeys=false \
--nodePort=6666 \
--appName=haveno-XMR_STAGENET_Bob \
--apiPassword=apitest \
@ -125,55 +254,6 @@ bob-daemon:
--walletRpcBindPort=38092 \
--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:
./.localnet/bitcoind \
-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
// containing a sha256 hash of the remote object, indicating we should stop the
// 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. " +
"To fix this, ensure you have Git LFS installed and run `git lfs pull`. " +
"See docs/build.md for more information.")

View file

@ -106,7 +106,7 @@ public class Version {
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;
public static void setBaseCryptoNetworkId(int baseCryptoNetworkId) {

View file

@ -20,15 +20,14 @@ package bisq.common.config;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.utils.MonetaryFormat;
import lombok.Getter;
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_TESTNET(new XmrTestNet3Params(), "XMR", "TESTNET", "Monero"),
XMR_STAGENET(new XmrRegTestParams(), "XMR", "STAGENET", "Monero");
XMR_STAGENET(new XmrStageNetParams(), "XMR", "STAGENET", "Monero"),
XMR_LOCAL(new XmrTestNetParams(), "XMR", "TESTNET", "Monero");
@Getter
private final NetworkParameters parameters;
@ -51,7 +50,7 @@ public enum BaseCurrencyNetwork {
}
public boolean isTestnet() {
return "XMR_TESTNET".equals(name());
return "XMR_LOCAL".equals(name());
}
public boolean isStagenet() {
@ -71,14 +70,14 @@ public enum BaseCurrencyNetwork {
}
}
private static class XmrTestNet3Params extends TestNet3Params {
private static class XmrTestNetParams extends RegTestParams {
@Override
public MonetaryFormat getMonetaryFormat() {
return XMR_MONETARY_FORMAT;
}
}
private static class XmrRegTestParams extends RegTestParams {
private static class XmrStageNetParams extends RegTestParams {
@Override
public MonetaryFormat getMonetaryFormat() {
return XMR_MONETARY_FORMAT;

View file

@ -76,7 +76,7 @@ public class FileUtil {
public static void deleteRollingBackup(File dir, String fileName) {
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;
if (dirName.contains(".")) dirName = dirName.replace(".", "_");
File backupFileDir = new File(Paths.get(backupDir.getAbsolutePath(), dirName).toString());

View file

@ -548,15 +548,11 @@ public class CoreApi {
String paymentAccountId,
Consumer<Trade> resultHandler,
ErrorMessageHandler errorMessageHandler) {
try {
Offer offer = coreOffersService.getOffer(offerId);
coreTradesService.takeOffer(offer,
paymentAccountId,
resultHandler,
errorMessageHandler);
} catch (Exception e) {
errorMessageHandler.handleErrorMessage(e.getMessage());
}
}
public void confirmPaymentStarted(String tradeId) {

View file

@ -17,8 +17,7 @@
package bisq.core.api;
import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.support.SupportType;
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
@ -45,7 +44,6 @@ import java.util.Optional;
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.MEDIATION;
import static bisq.core.support.SupportType.REFUND;
@ -61,7 +59,7 @@ class CoreDisputeAgentsService {
private final User user;
private final Config config;
private final KeyRing keyRing;
private final BtcWalletService btcWalletService;
private final XmrWalletService xmrWalletService;
private final ArbitratorManager arbitratorManager;
private final MediatorManager mediatorManager;
private final RefundAgentManager refundAgentManager;
@ -73,7 +71,7 @@ class CoreDisputeAgentsService {
public CoreDisputeAgentsService(User user,
Config config,
KeyRing keyRing,
BtcWalletService btcWalletService,
XmrWalletService xmrWalletService,
ArbitratorManager arbitratorManager,
MediatorManager mediatorManager,
RefundAgentManager refundAgentManager,
@ -81,7 +79,7 @@ class CoreDisputeAgentsService {
this.user = user;
this.config = config;
this.keyRing = keyRing;
this.btcWalletService = btcWalletService;
this.xmrWalletService = xmrWalletService;
this.arbitratorManager = arbitratorManager;
this.mediatorManager = mediatorManager;
this.refundAgentManager = refundAgentManager;
@ -98,9 +96,6 @@ class CoreDisputeAgentsService {
|| !config.useLocalhostForP2P)
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);
if (supportType.isPresent()) {
ECKey ecKey;
@ -112,6 +107,7 @@ class CoreDisputeAgentsService {
return;
}
ecKey = arbitratorManager.getRegistrationKey(registrationKey);
if (ecKey == null) throw new IllegalStateException("invalid registration key");
signature = arbitratorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
registerArbitrator(nodeAddress, languageCodes, ecKey, signature);
return;
@ -121,6 +117,7 @@ class CoreDisputeAgentsService {
return;
}
ecKey = mediatorManager.getRegistrationKey(registrationKey);
if (ecKey == null) throw new IllegalStateException("invalid registration key");
signature = mediatorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
registerMediator(nodeAddress, languageCodes, ecKey, signature);
return;
@ -130,6 +127,7 @@ class CoreDisputeAgentsService {
return;
}
ecKey = refundAgentManager.getRegistrationKey(registrationKey);
if (ecKey == null) throw new IllegalStateException("invalid registration key");
signature = refundAgentManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
registerRefundAgent(nodeAddress, languageCodes, ecKey, signature);
return;
@ -145,11 +143,9 @@ class CoreDisputeAgentsService {
List<String> languageCodes,
ECKey ecKey,
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(
p2PService.getAddress(),
arbitratorAddressEntry.getPubKey(),
arbitratorAddressEntry.getAddressString(),
xmrWalletService.getWallet().getPrimaryAddress(), // TODO: how is this used?
keyRing.getPubKeyRing(),
new ArrayList<>(languageCodes),
new Date().getTime(),

View file

@ -1,11 +1,15 @@
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.setup.DownloadListener;
import bisq.core.btc.setup.WalletsSetup;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty;
@ -18,13 +22,15 @@ import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleObjectProperty;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroConnectionManager;
import monero.common.MoneroConnectionManagerListener;
import monero.common.MoneroError;
import monero.common.MoneroRpcConnection;
import monero.daemon.MoneroDaemon;
import monero.common.TaskLooper;
import monero.daemon.MoneroDaemonRpc;
import monero.daemon.model.MoneroDaemonInfo;
import monero.daemon.model.MoneroPeer;
@Slf4j
@ -32,16 +38,37 @@ import monero.daemon.model.MoneroPeer;
public final class CoreMoneroConnectionsService {
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 DAEMON_INFO_POLL_PERIOD_MS = 20000L; // collect daemon info periodically in ms
private static final long REFRESH_PERIOD_LOCAL_MS = 5000; // refresh period when connected to local node
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
private static final List<MoneroRpcConnection> DEFAULT_CONNECTIONS = Arrays.asList(
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
new MoneroRpcConnection("http://haveno.exchange:38081", "", "").setPriority(2)
);
// default Monero nodes
private static final Map<BaseCurrencyNetwork, List<MoneroRpcConnection>> DEFAULT_CONNECTIONS;
static {
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 CoreContext coreContext;
private final CoreAccountService accountService;
private final CoreMoneroNodeService nodeService;
private final MoneroConnectionManager connectionManager;
@ -51,15 +78,20 @@ public final class CoreMoneroConnectionsService {
private final LongProperty chainHeight = new SimpleLongProperty(0);
private final DownloadListener downloadListener = new DownloadListener();
private MoneroDaemon daemon;
private MoneroDaemonRpc daemon;
@Getter
private MoneroDaemonInfo lastInfo;
private boolean isInitialized = false;
private TaskLooper updateDaemonLooper;;
@Inject
public CoreMoneroConnectionsService(WalletsSetup walletsSetup,
public CoreMoneroConnectionsService(CoreContext coreContext,
WalletsSetup walletsSetup,
CoreAccountService accountService,
CoreMoneroNodeService nodeService,
MoneroConnectionManager connectionManager,
EncryptedConnectionList connectionList) {
this.coreContext = coreContext;
this.accountService = accountService;
this.nodeService = nodeService;
this.connectionManager = connectionManager;
@ -96,7 +128,7 @@ public final class CoreMoneroConnectionsService {
// ------------------------ CONNECTION MANAGEMENT -------------------------
public MoneroDaemon getDaemon() {
public MoneroDaemonRpc getDaemon() {
accountService.checkAccountOpen();
return this.daemon;
}
@ -171,7 +203,7 @@ public final class CoreMoneroConnectionsService {
public void startCheckingConnection(Long refreshPeriod) {
synchronized (lock) {
accountService.checkAccountOpen();
connectionManager.startCheckingConnection(refreshPeriod == null ? DAEMON_REFRESH_PERIOD_MS : refreshPeriod);
connectionManager.startCheckingConnection(refreshPeriod == null ? getDefaultRefreshPeriodMs() : 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 ------------------------------
public boolean isChainHeightSyncedWithinTolerance() {
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
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;
}
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");
// add default connections
for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS) {
for (MoneroRpcConnection connection : DEFAULT_CONNECTIONS.get(Config.baseCurrencyNetwork())) {
if (connectionList.hasConnection(connection.getUri())) continue;
addConnection(connection);
}
@ -276,7 +322,7 @@ public final class CoreMoneroConnectionsService {
connectionManager.setAutoSwitch(connectionList.getAutoSwitch());
long refreshPeriod = connectionList.getRefreshPeriod();
if (refreshPeriod > 0) connectionManager.startCheckingConnection(refreshPeriod);
else if (refreshPeriod == 0) connectionManager.startCheckingConnection(DAEMON_REFRESH_PERIOD_MS);
else if (refreshPeriod == 0) connectionManager.startCheckingConnection();
else checkConnection();
// 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 -> {
try {
if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) {
@ -312,22 +361,23 @@ public final class CoreMoneroConnectionsService {
}
});
// poll daemon periodically
startPollingDaemon();
isInitialized = true;
}
// if offline, connect to local node if available
if (!connectionManager.isConnected() && nodeService.isMoneroNodeRunning()) {
// connect to local node if available
if (nodeService.isMoneroNodeRunning() && (connectionManager.getAutoSwitch() || !connectionManager.isConnected())) {
MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri());
if (connection == null) connection = nodeService.getDaemon().getRpcConnection();
if (connection != null) {
connection.checkConnection(connectionManager.getTimeout());
setConnection(connection);
}
}
// set the daemon based on the connection
if (getConnection() != null) daemon = new MoneroDaemonRpc(connectionManager.getConnection());
updateDaemonInfo();
// if using legacy desktop app, connect to best available connection
if (!coreContext.isApiUser()) {
connectionManager.setAutoSwitch(true);
connectionManager.setConnection(connectionManager.getBestAvailableConnection());
}
// update connection
onConnectionChanged(connectionManager.getConnection());
}
}
@ -342,23 +392,35 @@ public final class CoreMoneroConnectionsService {
connectionList.addConnection(currentConnection);
connectionList.setCurrentConnectionUri(currentConnection.getUri());
}
startPollingDaemon();
}
}
private void startPollingDaemon() {
UserThread.runPeriodically(() -> {
if (updateDaemonLooper != null) updateDaemonLooper.stop();
updateDaemonLooper = new TaskLooper(() -> {
updateDaemonInfo();
}, DAEMON_INFO_POLL_PERIOD_MS / 1000l);
});
updateDaemonLooper.start(getDefaultRefreshPeriodMs());
}
private void updateDaemonInfo() {
try {
log.trace("Updating daemon info");
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());
} catch (MoneroError err) {
peers.set(new ArrayList<MoneroPeer>()); // TODO: peers unknown due to restricted RPC call
}
numPeers.set(peers.get().size());
chainHeight.set(daemon.getHeight());
} catch (Exception e) {
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.xmr.MoneroNodeSettings;
import bisq.common.config.BaseCurrencyNetwork;
import bisq.common.config.Config;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.net.URI;
import java.net.URISyntaxException;
import java.io.File;
import java.io.IOException;
@ -35,7 +34,7 @@ import java.util.Arrays;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroError;
import monero.daemon.MoneroDaemonRpc;
/**
@ -49,7 +48,7 @@ public class CoreMoneroNodeService {
private static final String LOCALHOST = "localhost";
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_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 List<MoneroNodeServiceListener> listeners = new ArrayList<>();
@ -59,8 +58,7 @@ public class CoreMoneroNodeService {
MONEROD_PATH,
"--" + MONERO_NETWORK_TYPE,
"--no-igd",
"--hide-my-port",
"--rpc-login", "superuser:abctesting123" // TODO: remove authentication
"--hide-my-port"
);
// client to the local Monero node
@ -69,21 +67,24 @@ public class CoreMoneroNodeService {
@Inject
public CoreMoneroNodeService(Preferences preferences) {
this.preferences = preferences;
int rpcPort = 18081; // mainnet
if (Config.baseCurrencyNetwork().isTestnet()) {
rpcPort = 28081;
} else if (Config.baseCurrencyNetwork().isStagenet()) {
rpcPort = 38081;
}
this.daemon = new MoneroDaemonRpc("http://" + LOOPBACK_HOST + ":" + rpcPort, "superuser", "abctesting123"); // TODO: remove authentication
Integer rpcPort = null;
if (Config.baseCurrencyNetwork().isMainnet()) rpcPort = 18081;
else if (Config.baseCurrencyNetwork().isTestnet()) rpcPort = 28081;
else if (Config.baseCurrencyNetwork().isStagenet()) rpcPort = 38081;
else throw new RuntimeException("Base network is not local testnet, stagenet, or mainnet");
this.daemon = new MoneroDaemonRpc("http://" + LOOPBACK_HOST + ":" + rpcPort);
}
/**
* 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();
return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void addListener(MoneroNodeServiceListener listener) {
@ -105,7 +106,13 @@ public class CoreMoneroNodeService {
* Returns whether a local monero node is running.
*/
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() {
@ -135,7 +142,7 @@ public class CoreMoneroNodeService {
if (dataDir == null || dataDir.isEmpty()) {
dataDir = MONEROD_DATADIR;
}
args.add("--data-dir=" + dataDir);
if (dataDir != null) args.add("--data-dir=" + dataDir);
var bootstrapUrl = settings.getBootstrapUrl();
if (bootstrapUrl != null && !bootstrapUrl.isEmpty()) {

View file

@ -500,7 +500,7 @@ class CoreWalletsService {
// Throws a RuntimeException if wallets are encrypted and locked.
void verifyEncryptedWalletIsUnlocked() {
if (walletsManager.areWalletsEncrypted() && tempAesKey == null)
if (walletsManager.areWalletsEncrypted() && !accountService.isAccountOpen())
throw new IllegalStateException("wallet is locked");
}

View file

@ -174,7 +174,7 @@ public class WalletAppSetup {
return result;
});
btcInfoBinding.subscribe((observable, oldValue, newValue) -> getBtcInfo().set(newValue));
btcInfoBinding.subscribe((observable, oldValue, newValue) -> UserThread.execute(() -> btcInfo.set(newValue)));
walletsSetup.initialize(null,
() -> {

View file

@ -224,7 +224,7 @@ public abstract class ExecutableForAppWithP2p extends HavenoExecutable {
log.warn("\n\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n" +
"We are over our memory limit ({}) and trigger a shutdown. usedMemory: {} MB. freeMemory: {} MB" +
"\n%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n\n",
(int) maxMemory, usedMemory, Profiler.getFreeMemoryInMB());
maxMemory, usedMemory, Profiler.getFreeMemoryInMB());
shutDown(gracefulShutDownHandler);
}
}, 5);

View file

@ -18,44 +18,29 @@
package bisq.core.btc.setup;
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.file.FileUtil;
import org.bitcoinj.core.BlockChain;
import org.bitcoinj.core.CheckpointManager;
import org.bitcoinj.core.Context;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.PeerAddress;
import org.bitcoinj.core.PeerGroup;
import org.bitcoinj.core.listeners.DownloadProgressTracker;
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.script.Script;
import org.bitcoinj.store.BlockStore;
import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.store.SPVBlockStore;
import org.bitcoinj.wallet.DeterministicKeyChain;
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.WalletExtension;
import org.bitcoinj.wallet.WalletProtobufSerializer;
import com.runjva.sourceforge.jsocks.protocol.Socks5Proxy;
import com.google.common.io.Closeables;
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.SimpleBooleanProperty;
@ -63,11 +48,8 @@ import javafx.beans.property.SimpleBooleanProperty;
import org.bouncycastle.crypto.params.KeyParameter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@ -107,9 +89,6 @@ import static com.google.common.base.Preconditions.checkState;
*/
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 final NetworkParameters params;
@ -264,233 +243,16 @@ public class WalletConfig extends AbstractIdleService {
@Override
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();
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) {
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
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() {

View file

@ -196,31 +196,11 @@ public class WalletsSetup {
//We are here in the btcj thread Thread[ STARTING,5,main]
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
setupTaskHandlers.forEach(Runnable::run);
// Map to user thread
UserThread.execute(() -> {
addressEntryList.onWalletReady(walletConfig.btcWallet());
timeoutTimer.stop();
setupCompletedHandlers.forEach(Runnable::run);
});

View file

@ -45,7 +45,6 @@ import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.KeyCrypterScrypt;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.script.ScriptPattern;
import org.bitcoinj.wallet.SendRequest;
import org.bitcoinj.wallet.Wallet;
@ -97,12 +96,13 @@ public class BtcWalletService extends WalletService {
this.addressEntryList = addressEntryList;
// TODO: set and use chainHeightProperty in XmrWalletService
walletsSetup.addSetupCompletedHandler(() -> {
wallet = walletsSetup.getBtcWallet();
addListenersToWallet();
walletsSetup.getChain().addNewBestBlockListener(block -> chainHeightProperty.set(block.getHeight()));
chainHeightProperty.set(walletsSetup.getChain().getBestChainHeight());
// wallet = walletsSetup.getBtcWallet();
// addListenersToWallet();
//
// walletsSetup.getChain().addNewBestBlockListener(block -> chainHeightProperty.set(block.getHeight()));
// chainHeightProperty.set(walletsSetup.getChain().getBestChainHeight());
});
}

View file

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

View file

@ -44,6 +44,7 @@ public class WalletsManager {
private static final Logger log = LoggerFactory.getLogger(WalletsManager.class);
private final BtcWalletService btcWalletService;
private final XmrWalletService xmrWalletService;
private final TradeWalletService tradeWalletService;
private final WalletsSetup walletsSetup;
@ -53,9 +54,11 @@ public class WalletsManager {
@Inject
public WalletsManager(BtcWalletService btcWalletService,
XmrWalletService xmrWalletService,
TradeWalletService tradeWalletService,
WalletsSetup walletsSetup) {
this.btcWalletService = btcWalletService;
this.xmrWalletService = xmrWalletService;
this.tradeWalletService = tradeWalletService;
this.walletsSetup = walletsSetup;
}
@ -96,12 +99,11 @@ public class WalletsManager {
}
public boolean areWalletsEncrypted() {
return areWalletsAvailable() &&
btcWalletService.isEncrypted();
return xmrWalletService.isWalletEncrypted();
}
public boolean areWalletsAvailable() {
return btcWalletService.isWalletReady();
return xmrWalletService.isWalletReady();
}
public KeyCrypterScrypt getKeyCrypterScrypt() {

View file

@ -42,8 +42,9 @@ import java.util.stream.Stream;
import javax.inject.Inject;
import monero.common.MoneroError;
import monero.common.MoneroRpcConnection;
import monero.common.MoneroRpcError;
import monero.common.MoneroUtils;
import monero.daemon.MoneroDaemon;
import monero.daemon.MoneroDaemonRpc;
import monero.daemon.model.MoneroNetworkType;
import monero.daemon.model.MoneroOutput;
import monero.daemon.model.MoneroSubmitTxResult;
@ -69,7 +70,7 @@ public class XmrWalletService {
// Monero configuration
// 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 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";
@ -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_NAME = "haveno_XMR";
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 CoreMoneroConnectionsService connectionsService;
@ -156,7 +156,15 @@ public class XmrWalletService {
return wallet;
}
public MoneroDaemon getDaemon() {
public boolean isWalletReady() {
return getWallet() != null;
}
public boolean isWalletEncrypted() {
return accountService.getPassword() != null;
}
public MoneroDaemonRpc getDaemon() {
return connectionsService.getDaemon();
}
@ -253,14 +261,14 @@ public class XmrWalletService {
// get expected mining fee
MoneroTxWallet miningFeeTx = wallet.createTx(new MoneroTxConfig()
.setAccountIndex(0)
.addDestination(TradeUtils.FEE_ADDRESS, tradeFee)
.addDestination(TradeUtils.getTradeFeeAddress(), tradeFee)
.addDestination(returnAddress, depositAmount));
BigInteger miningFee = miningFeeTx.getFee();
// create reserve tx
MoneroTxWallet reserveTx = wallet.createTx(new MoneroTxConfig()
.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?
// freeze inputs
@ -288,7 +296,7 @@ public class XmrWalletService {
// create deposit tx
MoneroTxWallet depositTx = wallet.createTx(new MoneroTxConfig()
.setAccountIndex(0)
.addDestination(TradeUtils.FEE_ADDRESS, tradeFee)
.addDestination(TradeUtils.getTradeFeeAddress(), tradeFee)
.addDestination(multisigAddress, depositAmount));
// freeze deposit inputs
@ -315,23 +323,18 @@ public class XmrWalletService {
* @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) {
boolean submittedToPool = false;
MoneroDaemon daemon = getDaemon();
MoneroDaemonRpc daemon = getDaemon();
MoneroWallet wallet = getWallet();
try {
// get tx from daemon
// verify tx not submitted to pool
MoneroTx tx = daemon.getTx(txHash);
if (tx != null) throw new RuntimeException("Tx is already submitted");
// if tx is not submitted, submit but do not relay
if (tx == null) {
// submit tx to pool
MoneroSubmitTxResult result = daemon.submitTxHex(txHex, true); // TODO (woodser): invert doNotRelay flag to relay for library consistency?
if (!result.isGood()) throw new RuntimeException("Failed to submit tx to daemon: " + JsonUtils.serialize(result));
submittedToPool = true;
tx = daemon.getTx(txHash);
} else if (tx.isRelayed()) {
throw new RuntimeException("Trade tx must not be relayed");
}
// verify reserved key images
if (keyImages != null) {
@ -344,7 +347,7 @@ public class XmrWalletService {
if (tx.getUnlockHeight() != 0) throw new RuntimeException("Unlock height must be 0");
// verify trade fee
String feeAddress = TradeUtils.FEE_ADDRESS;
String feeAddress = TradeUtils.getTradeFeeAddress();
MoneroCheckTx check = wallet.checkTxKey(txHash, txKey, feeAddress);
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());
@ -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 (check.getReceivedAmount().compareTo(depositThreshold) < 0) throw new RuntimeException("Deposit amount is not enough, needed " + depositThreshold + " but was " + check.getReceivedAmount());
} finally {
// flush tx from pool if we added it
if (submittedToPool) daemon.flushTxPool(txHash);
try {
daemon.flushTxPool(txHash); // flush tx from pool
} 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() {
// open or create wallet
MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword());
if (MoneroUtils.walletExists(xmrWalletFile.getPath())) {
wallet = openWallet(walletConfig, rpcBindPort);
} 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
@ -410,9 +418,15 @@ public class XmrWalletService {
e.printStackTrace();
}
System.out.println("Monero wallet path: " + wallet.getPath());
System.out.println("Monero wallet address: " + wallet.getPrimaryAddress());
if (connectionsService.getDaemon() == null) System.out.println("Daemon: null");
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 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 balance: " + wallet.getBalance(0));
System.out.println("Monero wallet unlocked balance: " + wallet.getUnlockedBalance(0));
@ -433,8 +447,10 @@ public class XmrWalletService {
// create wallet
try {
log.info("Creating wallet " + config.getPath());
walletRpc.createWallet(config);
walletRpc.startSyncing(MONERO_WALLET_SYNC_PERIOD);
log.info("Syncing wallet " + config.getPath());
walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
@ -450,8 +466,10 @@ public class XmrWalletService {
// open wallet
try {
log.info("Opening wallet " + config.getPath());
walletRpc.openWallet(config);
walletRpc.startSyncing(MONERO_WALLET_SYNC_PERIOD);
log.info("Syncing wallet " + config.getPath());
walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
return walletRpc;
} catch (Exception e) {
e.printStackTrace();
@ -489,10 +507,16 @@ public class XmrWalletService {
}
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) wallet.setDaemonConnection(connection);
for (MoneroWallet multisigWallet : multisigWallets.values()) multisigWallet.setDaemonConnection(connection);
if (wallet != null) {
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() {
@ -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) {
StringBuilder sb = new StringBuilder();
for (MoneroTxWallet tx : txs) sb.append('\n' + tx.toString());

View file

@ -18,8 +18,8 @@
package bisq.core.offer;
import bisq.core.btc.TxFeeEstimationService;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Price;
@ -69,7 +69,7 @@ public class CreateOfferService {
private final P2PService p2PService;
private final PubKeyRingProvider pubKeyRingProvider;
private final User user;
private final BtcWalletService btcWalletService;
private final XmrWalletService xmrWalletService;
private final TradeStatisticsManager tradeStatisticsManager;
private final ArbitratorManager arbitratorManager;
@ -85,7 +85,7 @@ public class CreateOfferService {
P2PService p2PService,
PubKeyRingProvider pubKeyRingProvider,
User user,
BtcWalletService btcWalletService,
XmrWalletService xmrWalletService,
TradeStatisticsManager tradeStatisticsManager,
ArbitratorManager arbitratorManager) {
this.offerUtil = offerUtil;
@ -94,7 +94,7 @@ public class CreateOfferService {
this.p2PService = p2PService;
this.pubKeyRingProvider = pubKeyRingProvider;
this.user = user;
this.btcWalletService = btcWalletService;
this.xmrWalletService = xmrWalletService;
this.tradeStatisticsManager = tradeStatisticsManager;
this.arbitratorManager = arbitratorManager;
}
@ -167,8 +167,6 @@ public class CreateOfferService {
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
List<String> acceptedBanks = PaymentAccountUtil.getAcceptedBanks(paymentAccount);
double sellerSecurityDeposit = getSellerSecurityDepositAsDouble(buyerSecurityDepositAsDouble);
Coin txFeeFromFeeService = getEstimatedFeeAndTxVsize(amount, direction, buyerSecurityDepositAsDouble, sellerSecurityDeposit).first;
Coin txFeeToUse = txFee.isPositive() ? txFee : txFeeFromFeeService;
Coin makerFeeAsCoin = offerUtil.getMakerFee(amount);
Coin buyerSecurityDepositAsCoin = getBuyerSecurityDeposit(amount, buyerSecurityDepositAsDouble);
Coin sellerSecurityDepositAsCoin = getSellerSecurityDeposit(amount, sellerSecurityDeposit);
@ -195,6 +193,7 @@ public class CreateOfferService {
// select signing arbitrator
Arbitrator arbitrator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, arbitratorManager);
if (arbitrator == null) throw new RuntimeException("No arbitrators available");
OfferPayload offerPayload = new OfferPayload(offerId,
creationTime,
@ -216,8 +215,8 @@ public class CreateOfferService {
bankId,
acceptedBanks,
Version.VERSION,
btcWalletService.getLastBlockSeenHeight(), // TODO (woodser): switch to XMR
txFeeToUse.value,
xmrWalletService.getWallet().getHeight(),
0, // todo: remove txFeeToUse from data model
makerFeeAsCoin.value,
buyerSecurityDepositAsCoin.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);
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");

View file

@ -33,7 +33,6 @@ import bisq.core.provider.price.PriceFeedService;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.user.AutoConfirmSettings;
import bisq.core.user.Preferences;
import bisq.core.util.AveragePriceUtil;
import bisq.core.util.coin.CoinFormatter;
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 com.google.common.base.Preconditions.checkArgument;
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.

View file

@ -24,7 +24,6 @@ import bisq.core.btc.wallet.TradeWalletService;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.exceptions.TradePriceOutOfToleranceException;
import bisq.core.filter.FilterManager;
import bisq.core.locale.Res;
import bisq.core.offer.availability.DisputeAgentSelection;
import bisq.core.offer.messages.OfferAvailabilityRequest;
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.persistable.PersistedDataHost;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
import javax.inject.Inject;
@ -1004,12 +1002,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
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,
availabilityResult,
makerSignature,

View file

@ -46,7 +46,7 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
checkCoinNotNullOrZero(offer.getMakerFee(), "MakerFee");
checkCoinNotNullOrZero(offer.getBuyerSecurityDeposit(), "buyerSecurityDeposit");
checkCoinNotNullOrZero(offer.getSellerSecurityDeposit(), "sellerSecurityDeposit");
checkCoinNotNullOrZero(offer.getTxFee(), "txFee");
//checkCoinNotNullOrZero(offer.getTxFee(), "txFee"); // TODO: remove from data model
checkCoinNotNullOrZero(offer.getMaxTradeLimit(), "MaxTradeLimit");
// We remove those checks to be more flexible with future changes.

View file

@ -18,8 +18,8 @@
package bisq.core.offer.takeoffer;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.locale.CurrencyUtil;
import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
@ -45,7 +45,7 @@ import lombok.extern.slf4j.Slf4j;
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.util.VolumeUtil.getAdjustedVolumeForHalCash;
import static bisq.core.util.VolumeUtil.getRoundedFiatVolume;
@ -59,14 +59,14 @@ import static org.bitcoinj.core.Coin.valueOf;
public class TakeOfferModel implements Model {
// Immutable
private final AccountAgeWitnessService accountAgeWitnessService;
private final BtcWalletService btcWalletService;
private final XmrWalletService xmrWalletService;
private final FeeService feeService;
private final OfferUtil offerUtil;
private final PriceFeedService priceFeedService;
// Mutable
@Getter
private AddressEntry addressEntry;
private XmrAddressEntry addressEntry;
@Getter
private Coin amount;
private Offer offer;
@ -91,18 +91,18 @@ public class TakeOfferModel implements Model {
@Getter
private Coin balance;
@Getter
private boolean isBtcWalletFunded;
private boolean isXmrWalletFunded;
@Getter
private Volume volume;
@Inject
public TakeOfferModel(AccountAgeWitnessService accountAgeWitnessService,
BtcWalletService btcWalletService,
XmrWalletService xmrWalletService,
FeeService feeService,
OfferUtil offerUtil,
PriceFeedService priceFeedService) {
this.accountAgeWitnessService = accountAgeWitnessService;
this.btcWalletService = btcWalletService;
this.xmrWalletService = xmrWalletService;
this.feeService = feeService;
this.offerUtil = offerUtil;
this.priceFeedService = priceFeedService;
@ -114,7 +114,7 @@ public class TakeOfferModel implements Model {
this.clearModel();
this.offer = offer;
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();
this.useSavingsWallet = useSavingsWallet;
@ -200,18 +200,10 @@ public class TakeOfferModel implements Model {
}
private void updateBalance() {
Coin tradeWalletBalance = btcWalletService.getBalanceForAddress(addressEntry.getAddress());
if (useSavingsWallet) {
Coin savingWalletBalance = btcWalletService.getSavingWalletBalance();
totalAvailableBalance = savingWalletBalance.add(tradeWalletBalance);
if (totalToPayAsCoin != null)
balance = minCoin(totalToPayAsCoin, totalAvailableBalance);
} else {
balance = tradeWalletBalance;
}
totalAvailableBalance = xmrWalletService.getSavingWalletBalance();
if (totalToPayAsCoin != null) balance = minCoin(totalToPayAsCoin, totalAvailableBalance);
missingCoin = offerUtil.getBalanceShortage(totalToPayAsCoin, balance);
isBtcWalletFunded = offerUtil.isBalanceSufficient(totalToPayAsCoin, balance);
isXmrWalletFunded = offerUtil.isBalanceSufficient(totalToPayAsCoin, balance);
}
private long getMaxTradeLimit() {
@ -264,7 +256,7 @@ public class TakeOfferModel implements Model {
this.addressEntry = null;
this.amount = null;
this.balance = null;
this.isBtcWalletFunded = false;
this.isXmrWalletFunded = false;
this.missingCoin = ZERO;
this.offer = null;
this.paymentAccount = null;
@ -299,7 +291,7 @@ public class TakeOfferModel implements Model {
", balance=" + balance + "\n" +
", volume=" + volume + "\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");
case CITY:
field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel(Res.get("Contact"));
field.setLabel("City");
case CONTACT:
field.setComponent(PaymentAccountFormField.Component.TEXT);
field.setLabel("City");
field.setLabel("Contact info");
case COUNTRY:
field.setComponent(PaymentAccountFormField.Component.SELECT_ONE);
field.setLabel("Country");

View file

@ -86,7 +86,6 @@ public final class StrikeAccount extends CountryBasedPaymentAccount {
@Override
@Nullable
public @NotNull List<Country> getSupportedCountries() {
System.out.println("STIKE RETURNING SUPPORTED COUNTRIES: " + SUPPORTED_COUNTRIES);
return SUPPORTED_COUNTRIES;
}

View file

@ -416,11 +416,6 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
boolean senderIsCosigner = senderIsWinner || disputeResult.isLoserPublisher();
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) {
log.warn("Received ArbitratorPayoutTxRequest but sender is not co-signer for trade id " + tradeId);
return;

View file

@ -24,7 +24,6 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.crypto.PubKeyRing;
import bisq.common.proto.ProtoUtil;
import bisq.common.util.CollectionUtils;
import bisq.common.util.Utilities;
import com.google.protobuf.ByteString;
@ -43,12 +42,10 @@ import javax.annotation.Nullable;
@Slf4j
@Getter
public final class Arbitrator extends DisputeAgent {
private final byte[] btcPubKey;
private final String btcAddress;
private final String xmrAddress;
public Arbitrator(NodeAddress nodeAddress,
byte[] btcPubKey,
String btcAddress,
String xmrAddress,
PubKeyRing pubKeyRing,
List<String> languageCodes,
long registrationDate,
@ -68,8 +65,7 @@ public final class Arbitrator extends DisputeAgent {
info,
extraDataMap);
this.btcPubKey = btcPubKey;
this.btcAddress = btcAddress;
this.xmrAddress = xmrAddress;
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -80,8 +76,7 @@ public final class Arbitrator extends DisputeAgent {
public protobuf.StoragePayload toProtoMessage() {
protobuf.Arbitrator.Builder builder = protobuf.Arbitrator.newBuilder()
.setNodeAddress(nodeAddress.toProtoMessage())
.setBtcPubKey(ByteString.copyFrom(btcPubKey))
.setBtcAddress(btcAddress)
.setXmrAddress(xmrAddress)
.setPubKeyRing(pubKeyRing.toProtoMessage())
.addAllLanguageCodes(languageCodes)
.setRegistrationDate(registrationDate)
@ -95,8 +90,7 @@ public final class Arbitrator extends DisputeAgent {
public static Arbitrator fromProto(protobuf.Arbitrator proto) {
return new Arbitrator(NodeAddress.fromProto(proto.getNodeAddress()),
proto.getBtcPubKey().toByteArray(),
proto.getBtcAddress(),
proto.getXmrAddress(),
PubKeyRing.fromProto(proto.getPubKeyRing()),
new ArrayList<>(proto.getLanguageCodesList()),
proto.getRegistrationDate(),
@ -115,8 +109,7 @@ public final class Arbitrator extends DisputeAgent {
@Override
public String toString() {
return "Arbitrator{" +
"\n btcPubKey=" + Utilities.bytesAsHexString(btcPubKey) +
",\n btcAddress='" + btcAddress + '\'' +
",\n xmrAddress='" + xmrAddress + '\'' +
"\n} " + super.toString();
}
}

View file

@ -22,14 +22,13 @@ import bisq.core.support.dispute.agent.DisputeAgentManager;
import bisq.core.user.User;
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
import bisq.common.config.Config;
import bisq.common.crypto.KeyRing;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.inject.Named;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
@ -49,21 +48,26 @@ public class ArbitratorManager extends DisputeAgentManager<Arbitrator> {
@Override
protected List<String> getPubKeyList() {
return List.of("0365c6af94681dbee69de1851f98d4684063bf5c2d64b1c73ed5d90434f375a054",
"031c502a60f9dbdb5ae5e438a79819e4e1f417211dd537ac12c9bc23246534c4bd",
"02c1e5a242387b6d5319ce27246cea6edaaf51c3550591b528d2578a4753c56c2c",
"025c319faf7067d9299590dd6c97fe7e56cd4dac61205ccee1cd1fc390142390a2",
"038f6e24c2bfe5d51d0a290f20a9a657c270b94ef2b9c12cd15ca3725fa798fc55",
"0255256ff7fb615278c4544a9bbd3f5298b903b8a011cd7889be19b6b1c45cbefe",
"024a3a37289f08c910fbd925ebc72b946f33feaeff451a4738ee82037b4cda2e95",
"02a88b75e9f0f8afba1467ab26799dcc38fd7a6468fb2795444b425eb43e2c10bd",
"02349a51512c1c04c67118386f4d27d768c5195a83247c150a4b722d161722ba81",
"03f718a2e0dc672c7cdec0113e72c3322efc70412bb95870750d25c32cd98de17d",
"028ff47ee2c56e66313928975c58fa4f1b19a0f81f3a96c4e9c9c3c6768075509e",
"02b517c0cbc3a49548f448ddf004ed695c5a1c52ec110be1bfd65fa0ca0761c94b",
"03df837a3a0f3d858e82f3356b71d1285327f101f7c10b404abed2abc1c94e7169",
"0203a90fb2ab698e524a5286f317a183a84327b8f8c3f7fa4a98fec9e1cefd6b72",
"023c99cc073b851c892d8c43329ca3beb5d2213ee87111af49884e3ce66cbd5ba5");
switch (Config.baseCurrencyNetwork()) {
case XMR_LOCAL:
throw new RuntimeException("No arbitrator pub key list for local XMR testnet. Set useDevPrivilegeKeys=true");
case XMR_STAGENET:
return List.of(
"03bb559ce207a4deb51d4c705076c95b85ad8581d35936b2a422dcb504eaf7cdb0",
"026c581ad773d987e6bd10785ac7f7e0e64864aedeb8bce5af37046de812a37854",
"025b058c9f2c60d839669dbfa5578cf5a8117d60e6b70e2f0946f8a691273c6a36",
"036c7d3f4bf05ef39b9d1b0a5d453a18210de36220c3d83cd16e59bd6132b037ad",
"030f7122a10ff73cd73808bddace95be77a94189c8a0eb24586265e125ce5ce6b9",
"03aa23e062afa0dda465f46986f8aa8d0374ad3e3f256141b05681dcb1e39c3859",
"02d3beb1293ca2ca14e6d42ca8bd18089a62aac62fd6bb23923ee6ead46ac60fba",
"03fa0f38f27bdd324db6f933f7e57851dadf3b911e4db6b19dd0950492c4525a31",
"02a1a458df5acf4ab08fdca748e28f33a955a30854c8c1a831ee733dca7f0d2fcd",
"0374dd70f3fa6e47ec5ab97932e1cec6233e98e6ae3129036b17118650c44fd3de");
case XMR_MAINNET:
return new ArrayList<String>();
default:
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
}
}
@Override

View file

@ -362,9 +362,6 @@ public abstract class Trade implements Tradable, Model {
@Setter
private NodeAddress arbitratorNodeAddress;
@Nullable
@Setter
private byte[] arbitratorBtcPubKey;
@Nullable
@Getter
@Setter
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(arbitratorNodeAddress).ifPresent(e -> builder.setArbitratorNodeAddress(arbitratorNodeAddress.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(errorMessage).ifPresent(builder::setErrorMessage);
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.setArbitratorNodeAddress(proto.hasArbitratorNodeAddress() ? NodeAddress.fromProto(proto.getArbitratorNodeAddress()) : null);
trade.setRefundAgentNodeAddress(proto.hasRefundAgentNodeAddress() ? NodeAddress.fromProto(proto.getRefundAgentNodeAddress()) : null);
trade.setArbitratorBtcPubKey(ProtoUtil.byteArrayOrNullFromProto(proto.getArbitratorBtcPubKey()));
trade.setTakerPaymentAccountId(ProtoUtil.stringOrNullFromProto(proto.getTakerPaymentAccountId()));
trade.setErrorMessage(ProtoUtil.stringOrNullFromProto(proto.getErrorMessage()));
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) {
serviceProvider.getArbitratorManager().getDisputeAgentByNodeAddress(arbitratorNodeAddress).ifPresent(arbitrator -> {
arbitratorBtcPubKey = arbitrator.getBtcPubKey();
arbitratorPubKeyRing = arbitrator.getPubKeyRing();
});
@ -1439,19 +1433,6 @@ public abstract class Trade implements Tradable, Model {
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
@ -1526,7 +1507,6 @@ public abstract class Trade implements Tradable, Model {
",\n contract=" + contract +
",\n contractAsJson='" + contractAsJson + '\'' +
",\n contractHash=" + Utilities.bytesAsHexString(contractHash) +
",\n arbitratorBtcPubKey=" + Utilities.bytesAsHexString(arbitratorBtcPubKey) +
",\n takerPaymentAccountId='" + takerPaymentAccountId + '\'' +
",\n errorMessage='" + errorMessage + '\'' +
",\n counterCurrencyTxId='" + counterCurrencyTxId + '\'' +

View file

@ -17,11 +17,11 @@
package bisq.core.trade;
import bisq.common.config.Config;
import bisq.common.crypto.KeyRing;
import bisq.common.crypto.PubKeyRing;
import bisq.common.crypto.Sig;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.offer.OfferPayload;
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
@ -32,15 +32,28 @@ import java.util.concurrent.CountDownLatch;
/**
* Collection of utilities for trading.
*
* TODO (woodser): combine with TradeUtil.java ?
*/
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.

View file

@ -39,7 +39,7 @@ public class MakerSetsLockTime extends TradeTask {
// 10 days for altcoins, 20 days for other payment methods
// For regtest dev environment we use 5 blocks
int delay = Config.baseCurrencyNetwork().isStagenet() ?
int delay = Config.baseCurrencyNetwork().isTestnet() ?
5 :
Restrictions.getLockTime(processModel.getOffer().getPaymentMethod().isBlockchain());

View file

@ -778,9 +778,10 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
switch (baseCurrencyNetwork) {
case XMR_MAINNET:
return prefPayload.getBlockChainExplorerMainNet();
case XMR_TESTNET:
case XMR_STAGENET:
return prefPayload.getBlockChainExplorerTestNet();
case XMR_LOCAL:
return prefPayload.getBlockChainExplorerTestNet(); // TODO: no testnet explorer for private testnet
default:
throw new RuntimeException("BaseCurrencyNetwork not defined. BaseCurrencyNetwork=" + baseCurrencyNetwork);
}
@ -791,9 +792,10 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
switch (baseCurrencyNetwork) {
case XMR_MAINNET:
return BTC_MAIN_NET_EXPLORERS;
case XMR_TESTNET:
case XMR_STAGENET:
return BTC_TEST_NET_EXPLORERS;
case XMR_LOCAL:
return BTC_TEST_NET_EXPLORERS; // TODO: no testnet explorer for private testnet
default:
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"
XMR_MAINNET=Monero Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Monero Testnet
XMR_LOCAL=Monero Local Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Monero Stagenet

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} jako příjemce
# suppress inspection "UnusedProperty"
XMR_MAINNET=Monero Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Monero Testnet
XMR_LOCAL=Monero Local Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Monero Stagenet

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} als Abnehmer
# suppress inspection "UnusedProperty"
XMR_MAINNET=Bitcoin-Hauptnetzwerk
# suppress inspection "UnusedProperty"
XMR_TESTNET=Bitcoin-Testnetzwerk
XMR_LOCAL=Bitcoin-Testnetzwerk
# suppress inspection "UnusedProperty"
XMR_STAGENET=Bitcoin-Regtest

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} como tomador
# suppress inspection "UnusedProperty"
XMR_MAINNET=Red principal de Monero
# suppress inspection "UnusedProperty"
XMR_TESTNET=Red de prueba de Monero
XMR_LOCAL=Red de prueba de Monero
# suppress inspection "UnusedProperty"
XMR_STAGENET=Stagenet Monero

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} به عنوان پذیرنده
# suppress inspection "UnusedProperty"
XMR_MAINNET=Monero Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Monero Testnet
XMR_LOCAL=Monero Local Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Monero Stagenet

View file

@ -1824,7 +1824,7 @@ formatter.asTaker={0} {1} en tant que taker
# suppress inspection "UnusedProperty"
XMR_MAINNET=Monero Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Monero Testnet
XMR_LOCAL=Monero Local Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Monero Stagenet

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} come taker
# suppress inspection "UnusedProperty"
XMR_MAINNET=Mainnet Bitcoin
# suppress inspection "UnusedProperty"
XMR_TESTNET=Testnet Bitcoin
XMR_LOCAL=Testnet Bitcoin
# suppress inspection "UnusedProperty"
XMR_STAGENET=Regtest Bitcoin

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1}のテイカー
# suppress inspection "UnusedProperty"
XMR_MAINNET=Monero Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Monero Testnet
XMR_LOCAL=Monero Local Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Monero Stagenet

View file

@ -1831,7 +1831,7 @@ formatter.asTaker={0} {1} como aceitador
# suppress inspection "UnusedProperty"
XMR_MAINNET=Mainnet do Monero
# suppress inspection "UnusedProperty"
XMR_TESTNET=Testnet do Monero
XMR_LOCAL=Testnet do Monero
# suppress inspection "UnusedProperty"
XMR_STAGENET=Stagenet do Monero

View file

@ -1821,7 +1821,7 @@ formatter.asTaker={0} {1} como aceitador
# suppress inspection "UnusedProperty"
XMR_MAINNET=Mainnet de Monero
# suppress inspection "UnusedProperty"
XMR_TESTNET=Testnet de Monero
XMR_LOCAL=Testnet de Monero
# suppress inspection "UnusedProperty"
XMR_STAGENET=Stagenet Monero

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} как тейкер
# suppress inspection "UnusedProperty"
XMR_MAINNET=XMR Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=XMR Testnet
XMR_LOCAL=XMR Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=XMR Stagenet

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} ในฐานะคนรับ
# suppress inspection "UnusedProperty"
XMR_MAINNET=Monero Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Monero Testnet
XMR_LOCAL=Monero Local Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Monero Stagenet

View file

@ -1825,7 +1825,7 @@ formatter.asTaker={0} {1} như người nhận
# suppress inspection "UnusedProperty"
XMR_MAINNET=Bitcoin Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=Bitcoin Testnet
XMR_LOCAL=Bitcoin Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=Bitcoin Regtest

View file

@ -1827,7 +1827,7 @@ formatter.asTaker={0} {1} 是买家
# suppress inspection "UnusedProperty"
XMR_MAINNET=XMR Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=XMR Testnet
XMR_LOCAL=XMR Testnet
# suppress inspection "UnusedProperty"
XMR_STAGENET=XMR Stagenet

View file

@ -1823,7 +1823,7 @@ formatter.asTaker={0} {1} 是買家
# suppress inspection "UnusedProperty"
XMR_MAINNET=XMR Mainnet
# suppress inspection "UnusedProperty"
XMR_TESTNET=XMR Testnet
XMR_LOCAL=XMR Testnet
# suppress inspection "UnusedProperty"
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)]
localhost:2002 (@devtest1)
# nodeaddress.onion:port [(@owner)]
localhost:2002 (@devtest1) # TODO: replace with hosted stagenet seednodes
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");
}};
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,
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,
null, null);
@ -92,11 +92,11 @@ public class ArbitratorManagerTest {
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,
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,
null, null);

View file

@ -44,8 +44,7 @@ public class ArbitratorTest {
public static Arbitrator getArbitratorMock() {
return new Arbitrator(new NodeAddress("host", 1000),
getBytes(100),
"btcaddress",
"xmraddress",
new PubKeyRing(getBytes(100), getBytes(100)),
Lists.newArrayList(),
new Date().getTime(),

View file

@ -79,7 +79,7 @@ public class CurrencyUtilTest {
// For testnet its ok
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());
// 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
Coin ether = new Ether();
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
BaseCurrencyNetwork.XMR_TESTNET).get().getTickerSymbol(), "ETH");
BaseCurrencyNetwork.XMR_LOCAL).get().getTickerSymbol(), "ETH");
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
BaseCurrencyNetwork.XMR_STAGENET).get().getTickerSymbol(), "ETH");
assertEquals(Coin.Network.MAINNET, ether.getNetwork());

View file

@ -136,6 +136,7 @@ class GrpcTradesService extends TradesImplBase {
errorMessageHandler.handleErrorMessage(errorMessage);
});
} catch (Throwable cause) {
cause.printStackTrace();
exceptionHandler.handleException(log, cause, responseObserver);
}
}

View file

@ -357,6 +357,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
settingsButtonWithBadge.getStyleClass().add("new");
navigation.addListener((viewPath, data) -> {
UserThread.execute(() -> {
if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0)
return;
@ -375,6 +376,7 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
navigation.navigateTo(MainView.class, MarketView.class, OfferBookChartView.class);
}
});
});
VBox splashScreen = createSplashScreen();
@ -431,13 +433,14 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
return new ListCell<>() {
@Override
protected void updateItem(PriceFeedComboBoxItem item, boolean empty) {
UserThread.execute(() -> {
super.updateItem(item, empty);
if (!empty && item != null) {
textProperty().bind(item.displayStringProperty);
} else {
textProperty().unbind();
}
});
}
};
}
@ -763,9 +766,11 @@ public class MainView extends InitializableView<StackPane, MainViewModel> {
buttonWithBadge.textProperty().bind(badgeNumber);
buttonWithBadge.setEnabled(badgeEnabled.get());
badgeEnabled.addListener((observable, oldValue, newValue) -> {
UserThread.execute(() -> {
buttonWithBadge.setEnabled(newValue);
buttonWithBadge.refreshBadge();
});
});
buttonWithBadge.setPosition(Pos.TOP_RIGHT);
buttonWithBadge.setMinHeight(34);

View file

@ -20,7 +20,7 @@ package bisq.desktop.main.account.register;
import bisq.desktop.common.model.ActivatableViewModel;
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.support.dispute.agent.DisputeAgent;
import bisq.core.support.dispute.agent.DisputeAgentManager;
@ -51,7 +51,7 @@ public abstract class AgentRegistrationViewModel<R extends DisputeAgent, T exten
private final T disputeAgentManager;
protected final User user;
protected final P2PService p2PService;
protected final BtcWalletService walletService;
protected final XmrWalletService xmrWalletService;
protected final KeyRing keyRing;
final BooleanProperty registrationEditDisabled = new SimpleBooleanProperty(true);
@ -73,12 +73,12 @@ public abstract class AgentRegistrationViewModel<R extends DisputeAgent, T exten
public AgentRegistrationViewModel(T disputeAgentManager,
User user,
P2PService p2PService,
BtcWalletService walletService,
XmrWalletService xmrWalletService,
KeyRing keyRing) {
this.disputeAgentManager = disputeAgentManager;
this.user = user;
this.p2PService = p2PService;
this.walletService = walletService;
this.xmrWalletService = xmrWalletService;
this.keyRing = keyRing;
mapChangeListener = change -> {

View file

@ -19,8 +19,7 @@ package bisq.desktop.main.account.register.arbitrator;
import bisq.desktop.main.account.register.AgentRegistrationViewModel;
import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.XmrWalletService;
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
import bisq.core.user.User;
@ -30,7 +29,6 @@ import bisq.network.p2p.P2PService;
import bisq.common.crypto.KeyRing;
import com.google.inject.Inject;
import java.util.ArrayList;
import java.util.Date;
@ -40,19 +38,17 @@ public class ArbitratorRegistrationViewModel extends AgentRegistrationViewModel<
public ArbitratorRegistrationViewModel(ArbitratorManager arbitratorManager,
User user,
P2PService p2PService,
BtcWalletService walletService,
XmrWalletService xmrWalletService,
KeyRing keyRing) {
super(arbitratorManager, user, p2PService, walletService, keyRing);
super(arbitratorManager, user, p2PService, xmrWalletService, keyRing);
}
@Override
protected Arbitrator getDisputeAgent(String registrationSignature,
String emailAddress) {
AddressEntry arbitratorAddressEntry = walletService.getArbitratorAddressEntry();
return new Arbitrator(
p2PService.getAddress(),
arbitratorAddressEntry.getPubKey(),
arbitratorAddressEntry.getAddressString(),
xmrWalletService.getWallet().getPrimaryAddress(), // TODO: how is arbitrator address used?
keyRing.getPubKeyRing(),
new ArrayList<>(languageCodes),
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.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.MediatorManager;
import bisq.core.user.User;
@ -39,9 +39,9 @@ class MediatorRegistrationViewModel extends AgentRegistrationViewModel<Mediator,
public MediatorRegistrationViewModel(MediatorManager mediatorManager,
User user,
P2PService p2PService,
BtcWalletService walletService,
XmrWalletService xmrWalletService,
KeyRing keyRing) {
super(mediatorManager, user, p2PService, walletService, keyRing);
super(mediatorManager, user, p2PService, xmrWalletService, keyRing);
}
@Override

View file

@ -20,7 +20,7 @@ package bisq.desktop.main.account.register.refundagent;
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.RefundAgentManager;
import bisq.core.user.User;
@ -40,9 +40,9 @@ public class RefundAgentRegistrationViewModel extends AgentRegistrationViewModel
public RefundAgentRegistrationViewModel(RefundAgentManager arbitratorManager,
User user,
P2PService p2PService,
BtcWalletService walletService,
XmrWalletService xmrWalletService,
KeyRing keyRing) {
super(arbitratorManager, user, p2PService, walletService, keyRing);
super(arbitratorManager, user, p2PService, xmrWalletService, keyRing);
}
@Override

View file

@ -52,7 +52,6 @@ import bisq.core.util.coin.CoinUtil;
import bisq.network.p2p.P2PService;
import bisq.common.util.MathUtils;
import bisq.common.util.Tuple2;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Coin;
@ -263,9 +262,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
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)
// But offer creation happens usually after that so we should have already the value from the estimation service.
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
@ -317,16 +313,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
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) {
openOfferManager.placeOffer(offer,
useSavingsWallet,
@ -439,15 +425,6 @@ public abstract class MutableOfferDataModel extends OfferDataModel {
this.marketPriceMargin = marketPriceMargin;
}
void requestTxFee(@Nullable Runnable actionHandler) {
feeService.requestFees(() -> {
txFeeFromFeeService = feeService.getTxFee(feeTxVsize);
calculateTotalToPay();
if (actionHandler != null)
actionHandler.run();
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -37,7 +37,6 @@ import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.offer.OfferPayload;
import bisq.core.offer.OfferRestrictions;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
@ -674,8 +673,6 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
}
void onShowPayFundsScreen(Runnable actionHandler) {
dataModel.updateEstimatedFeeAndTxVsize();
dataModel.requestTxFee(actionHandler);
showPayFundsScreenDisplayed.set(true);
updateSpinnerInfo();
}

View file

@ -24,7 +24,6 @@ import bisq.desktop.main.overlays.popups.Popup;
import bisq.desktop.util.GUIUtil;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.TxFeeEstimationService;
import bisq.core.btc.listeners.XmrBalanceListener;
import bisq.core.btc.model.XmrAddressEntry;
import bisq.core.btc.wallet.Restrictions;
@ -36,7 +35,6 @@ import bisq.core.monetary.Price;
import bisq.core.monetary.Volume;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.offer.OfferPayload;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.PaymentAccountUtil;
@ -52,7 +50,6 @@ import bisq.core.util.VolumeUtil;
import bisq.core.util.coin.CoinUtil;
import bisq.network.p2p.P2PService;
import bisq.common.util.Tuple2;
import org.bitcoinj.core.Coin;
@ -93,7 +90,6 @@ class TakeOfferDataModel extends OfferDataModel {
private final MempoolService mempoolService;
private final FilterManager filterManager;
final Preferences preferences;
private final TxFeeEstimationService txFeeEstimationService;
private final PriceFeedService priceFeedService;
private final AccountAgeWitnessService accountAgeWitnessService;
private final Navigation navigation;
@ -138,7 +134,6 @@ class TakeOfferDataModel extends OfferDataModel {
MempoolService mempoolService,
FilterManager filterManager,
Preferences preferences,
TxFeeEstimationService txFeeEstimationService,
PriceFeedService priceFeedService,
AccountAgeWitnessService accountAgeWitnessService,
Navigation navigation,
@ -153,7 +148,6 @@ class TakeOfferDataModel extends OfferDataModel {
this.mempoolService = mempoolService;
this.filterManager = filterManager;
this.preferences = preferences;
this.txFeeEstimationService = txFeeEstimationService;
this.priceFeedService = priceFeedService;
this.accountAgeWitnessService = accountAgeWitnessService;
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.
void onShowPayFundsScreen() {
estimateTxVsize();
freezeFee = true;
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) {
if (paymentAccount != null) {
this.paymentAccount = paymentAccount;

View file

@ -20,22 +20,16 @@ package bisq.desktop.main.overlays.windows;
import bisq.desktop.Navigation;
import bisq.desktop.components.AutoTooltipButton;
import bisq.desktop.components.BusyAnimation;
import bisq.desktop.components.TitledGroupBg;
import bisq.desktop.components.TxIdTextField;
import bisq.desktop.main.overlays.Overlay;
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;
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.Res;
import bisq.core.monetary.Price;
import bisq.core.offer.Offer;
import bisq.core.offer.OfferDirection;
import bisq.core.offer.OfferPayload;
import bisq.core.offer.OfferUtil;
import bisq.core.payment.PaymentAccount;
import bisq.core.payment.payload.PaymentMethod;
import bisq.core.user.User;
@ -80,7 +74,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
private final User user;
private final KeyRing keyRing;
private final Navigation navigation;
private final BtcWalletService btcWalletService;
private Offer offer;
private Coin tradeAmount;
private Price tradePrice;
@ -97,13 +90,11 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
public OfferDetailsWindow(@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter formatter,
User user,
KeyRing keyRing,
Navigation navigation,
BtcWalletService btcWalletService) {
Navigation navigation) {
this.formatter = formatter;
this.user = user;
this.keyRing = keyRing;
this.navigation = navigation;
this.btcWalletService = btcWalletService;
type = Type.Confirmation;
}
@ -321,7 +312,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
rows++;
}
TitledGroupBg titledGroupBg = addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE);
addConfirmationLabelTextFieldWithCopyIcon(gridPane, rowIndex, Res.get("shared.offerId"), offer.getId(),
Layout.TWICE_FIRST_ROW_AND_GROUP_DISTANCE);
addConfirmationLabelTextFieldWithCopyIcon(gridPane, ++rowIndex, Res.get("offerDetailsWindow.makersOnion"),
@ -337,21 +327,6 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
formatter.formatCoinWithCode(offer.getSellerSecurityDeposit());
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)
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("offerDetailsWindow.countryBank"),
CountryUtil.getNameAndCode(countryCode));

View file

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

View file

@ -1,6 +1,6 @@
# 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
@ -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`
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).
### 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`;
1. In a new terminal window run `make monerod-local1`
1. In a new terminal window run `make monerod-local2`
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
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:
1. `make seednode`
2. `make arbitrator-desktop`
3. If this is the first time launching the arbitrator desktop application, register the arbitrator after the interface opens:
1. Go to the *Account* tab and press `cmd+r`. Confirm the registration of the arbitrator.
4. `make alice-desktop` or if you want to run Alice as a daemon: `make alice-daemon`
5. `make bob-desktop` or if you want to run Bob as a daemon: `make bob-daemon`
1. `make seednode-local`
2. `make arbitrator-desktop-local`
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.
4. `make alice-desktop-local` or if you want to run Alice as a daemon: `make alice-daemon-local`
5. `make bob-desktop-local` or if you want to run Bob as a daemon: `make bob-daemon-local`
## 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

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)
System.useConsoleReporter=true
# 0 -> XMR_MAINNET, 1 -> XMR_TESTNET (default)
# 0 -> XMR_MAINNET, 1 -> XMR_LOCAL (default)
System.baseCurrencyNetwork=0
## 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);
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
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)
System.useConsoleReporter=true
# 0 -> XMR_MAINNET, 1 -> XMR_TESTNET (default)
# 0 -> XMR_MAINNET, 1 -> XMR_LOCAL (default)
System.baseCurrencyNetwork=0
## Each Metric is configured via a set of properties.

View file

@ -703,11 +703,10 @@ message Arbitrator {
string registration_signature = 4;
bytes registration_pub_key = 5;
PubKeyRing pub_key_ring = 6;
bytes btc_pub_key = 7;
string btc_address = 8;
string email_address = 9;
string info = 10;
map<string, string> extra_data = 11;
string xmr_address = 7;
string email_address = 8;
string info = 9;
map<string, string> extra_data = 10;
}
message Mediator {

View file

@ -171,9 +171,9 @@ public class SeedNodeMain extends ExecutableForAppWithP2p {
}
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.
if (Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_STAGENET) {
if (Config.baseCurrencyNetwork() != BaseCurrencyNetwork.XMR_MAINNET) {
return;
}