support local, stagenet, and mainnet xmr network configuration (#335)
remove btc wallet disable local zmq
This commit is contained in:
parent
b4112e50e9
commit
e2208355b1
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -12,4 +12,4 @@
|
||||
*.jpg binary
|
||||
*.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
226
Makefile
@ -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 \
|
||||
|
@ -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.")
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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) {
|
||||
|
@ -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(),
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
@ -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");
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
() -> {
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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());
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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" +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 + '\'' +
|
||||
|
@ -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.
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Haveno is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.util;
|
||||
|
||||
import bisq.core.filter.FilterManager;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import org.bitcoinj.core.Coin;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class FeeReceiverSelector {
|
||||
public static final String BTC_FEE_RECEIVER_ADDRESS = "38bZBj5peYS3Husdz7AH3gEUiUbYRD951t";
|
||||
|
||||
public static String getMostRecentAddress() {
|
||||
return Config.baseCurrencyNetwork().isMainnet() ? BTC_FEE_RECEIVER_ADDRESS :
|
||||
Config.baseCurrencyNetwork().isTestnet() ? "2N4mVTpUZAnhm9phnxB7VrHB4aBhnWrcUrV" :
|
||||
"2MzBNTJDjjXgViKBGnatDU3yWkJ8pJkEg9w";
|
||||
}
|
||||
|
||||
public static String getAddress(FilterManager filterManager) {
|
||||
return getAddress(filterManager, new Random());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String getAddress(FilterManager filterManager, Random rnd) {
|
||||
List<String> feeReceivers = Optional.ofNullable(filterManager.getFilter())
|
||||
.flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses()))
|
||||
.orElse(List.of());
|
||||
|
||||
List<Long> amountList = new ArrayList<>();
|
||||
List<String> receiverAddressList = new ArrayList<>();
|
||||
|
||||
feeReceivers.forEach(e -> {
|
||||
try {
|
||||
String[] tokens = e.split("#");
|
||||
amountList.add(Coin.parseCoin(tokens[1]).longValue()); // total amount the victim should receive
|
||||
receiverAddressList.add(tokens[0]); // victim's receiver address
|
||||
} catch (RuntimeException ignore) {
|
||||
// If input format is not as expected we ignore entry
|
||||
}
|
||||
});
|
||||
|
||||
if (!amountList.isEmpty()) {
|
||||
return receiverAddressList.get(weightedSelection(amountList, rnd));
|
||||
}
|
||||
|
||||
// If no fee address receiver is defined via filter we use the hard coded recent address
|
||||
return getMostRecentAddress();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static int weightedSelection(List<Long> weights, Random rnd) {
|
||||
long sum = weights.stream().mapToLong(n -> n).sum();
|
||||
long target = rnd.longs(0, sum).findFirst().orElseThrow();
|
||||
int i;
|
||||
for (i = 0; i < weights.size() && target >= 0; i++) {
|
||||
target -= weights.get(i);
|
||||
}
|
||||
return i - 1;
|
||||
}
|
||||
}
|
@ -2391,7 +2391,7 @@ formatter.asTaker={0} {1} as taker
|
||||
# suppress inspection "UnusedProperty"
|
||||
XMR_MAINNET=Monero Mainnet
|
||||
# suppress inspection "UnusedProperty"
|
||||
XMR_TESTNET=Monero Testnet
|
||||
XMR_LOCAL=Monero Local Testnet
|
||||
# suppress inspection "UnusedProperty"
|
||||
XMR_STAGENET=Monero Stagenet
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
3
core/src/main/resources/xmr_local.seednodes
Normal file
3
core/src/main/resources/xmr_local.seednodes
Normal file
@ -0,0 +1,3 @@
|
||||
# nodeaddress.onion:port [(@owner,@backup)]
|
||||
localhost:2002 (@devtest1)
|
||||
localhost:3002 (@devtest2)
|
@ -1,3 +1,3 @@
|
||||
# nodeaddress.onion:port [(@owner,@backup)]
|
||||
localhost:2002 (@devtest1)
|
||||
# nodeaddress.onion:port [(@owner)]
|
||||
localhost:2002 (@devtest1) # TODO: replace with hosted stagenet seednodes
|
||||
localhost:3002 (@devtest2)
|
@ -1,2 +0,0 @@
|
||||
# nodeaddress.onion:port [(@owner)]
|
||||
placeholder.onion:8001
|
@ -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);
|
||||
|
||||
|
@ -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(),
|
||||
|
@ -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());
|
||||
|
@ -136,6 +136,7 @@ class GrpcTradesService extends TradesImplBase {
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
});
|
||||
} catch (Throwable cause) {
|
||||
cause.printStackTrace();
|
||||
exceptionHandler.handleException(log, cause, responseObserver);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 -> {
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user