From e2208355b11b14a35d20bf7da5fd496f731061e1 Mon Sep 17 00:00:00 2001 From: woodser Date: Thu, 7 Jul 2022 09:10:59 -0400 Subject: [PATCH] support local, stagenet, and mainnet xmr network configuration (#335) remove btc wallet disable local zmq --- .gitattributes | 2 +- Makefile | 226 ++++++++++------ build.gradle | 2 +- .../main/java/bisq/common/app/Version.java | 2 +- .../common/config/BaseCurrencyNetwork.java | 11 +- .../main/java/bisq/common/file/FileUtil.java | 2 +- core/src/main/java/bisq/core/api/CoreApi.java | 14 +- .../core/api/CoreDisputeAgentsService.java | 20 +- .../api/CoreMoneroConnectionsService.java | 148 +++++++---- .../bisq/core/api/CoreMoneroNodeService.java | 43 ++-- .../bisq/core/api/CoreWalletsService.java | 2 +- .../java/bisq/core/app/WalletAppSetup.java | 2 +- .../app/misc/ExecutableForAppWithP2p.java | 2 +- .../bisq/core/btc/setup/WalletConfig.java | 242 +----------------- .../bisq/core/btc/setup/WalletsSetup.java | 20 -- .../core/btc/wallet/BtcWalletService.java | 12 +- .../bisq/core/btc/wallet/WalletService.java | 12 +- .../bisq/core/btc/wallet/WalletsManager.java | 8 +- .../core/btc/wallet/XmrWalletService.java | 107 +++++--- .../bisq/core/offer/CreateOfferService.java | 15 +- .../bisq/core/offer/OfferRestrictions.java | 2 +- .../main/java/bisq/core/offer/OfferUtil.java | 2 - .../bisq/core/offer/OpenOfferManager.java | 8 - .../offer/placeoffer/tasks/ValidateOffer.java | 2 +- .../core/offer/takeoffer/TakeOfferModel.java | 36 +-- .../bisq/core/payment/PaymentAccount.java | 4 +- .../java/bisq/core/payment/StrikeAccount.java | 1 - .../arbitration/ArbitrationManager.java | 5 - .../arbitration/arbitrator/Arbitrator.java | 19 +- .../arbitrator/ArbitratorManager.java | 38 +-- core/src/main/java/bisq/core/trade/Trade.java | 20 -- .../main/java/bisq/core/trade/TradeUtils.java | 23 +- .../tasks/maker/MakerSetsLockTime.java | 2 +- .../main/java/bisq/core/user/Preferences.java | 6 +- .../bisq/core/util/FeeReceiverSelector.java | 86 ------- .../resources/i18n/displayStrings.properties | 2 +- .../i18n/displayStrings_cs.properties | 2 +- .../i18n/displayStrings_de.properties | 2 +- .../i18n/displayStrings_es.properties | 2 +- .../i18n/displayStrings_fa.properties | 2 +- .../i18n/displayStrings_fr.properties | 2 +- .../i18n/displayStrings_it.properties | 2 +- .../i18n/displayStrings_ja.properties | 2 +- .../i18n/displayStrings_pt-br.properties | 2 +- .../i18n/displayStrings_pt.properties | 2 +- .../i18n/displayStrings_ru.properties | 2 +- .../i18n/displayStrings_th.properties | 2 +- .../i18n/displayStrings_vi.properties | 2 +- .../i18n/displayStrings_zh-hans.properties | 2 +- .../i18n/displayStrings_zh-hant.properties | 2 +- core/src/main/resources/xmr_local.seednodes | 3 + .../src/main/resources/xmr_stagenet.seednodes | 6 +- core/src/main/resources/xmr_testnet.seednodes | 2 - .../arbitration/ArbitratorManagerTest.java | 8 +- .../bisq/core/arbitration/ArbitratorTest.java | 3 +- .../bisq/core/locale/CurrencyUtilTest.java | 4 +- .../bisq/daemon/grpc/GrpcTradesService.java | 1 + .../main/java/bisq/desktop/main/MainView.java | 53 ++-- .../register/AgentRegistrationViewModel.java | 8 +- .../ArbitratorRegistrationViewModel.java | 12 +- .../MediatorRegistrationViewModel.java | 6 +- .../RefundAgentRegistrationViewModel.java | 6 +- .../main/offer/MutableOfferDataModel.java | 23 -- .../main/offer/MutableOfferViewModel.java | 3 - .../offer/takeoffer/TakeOfferDataModel.java | 51 ---- .../overlays/windows/OfferDetailsWindow.java | 27 +- .../presentation/MarketPricePresentation.java | 46 ++-- docs/installing.md | 37 +-- monitor/README.md | 2 +- .../src/main/java/bisq/monitor/Metric.java | 2 +- monitor/src/main/resources/metrics.properties | 2 +- ...ountAgeWitnessStore_XMR_LOCAL_placeholder} | 0 proto/src/main/proto/pb.proto | 9 +- .../main/java/bisq/seednode/SeedNodeMain.java | 4 +- 74 files changed, 595 insertions(+), 899 deletions(-) delete mode 100644 core/src/main/java/bisq/core/util/FeeReceiverSelector.java create mode 100644 core/src/main/resources/xmr_local.seednodes delete mode 100644 core/src/main/resources/xmr_testnet.seednodes rename p2p/src/main/resources/{AccountAgeWitnessStore_XMR_TESTNET_placeholder => AccountAgeWitnessStore_XMR_LOCAL_placeholder} (100%) diff --git a/.gitattributes b/.gitattributes index a47754d06b..446a2a5c8f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/Makefile b/Makefile index ab6ac7b01d..226bfe938c 100644 --- a/Makefile +++ b/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 \ diff --git a/build.gradle b/build.gradle index 2cb17d430c..fec6399521 100644 --- a/build.gradle +++ b/build.gradle @@ -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.") diff --git a/common/src/main/java/bisq/common/app/Version.java b/common/src/main/java/bisq/common/app/Version.java index 4431c6ea70..b59eec2349 100644 --- a/common/src/main/java/bisq/common/app/Version.java +++ b/common/src/main/java/bisq/common/app/Version.java @@ -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) { diff --git a/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java b/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java index 288bd3d2c9..0cd6995ced 100644 --- a/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java +++ b/common/src/main/java/bisq/common/config/BaseCurrencyNetwork.java @@ -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; diff --git a/common/src/main/java/bisq/common/file/FileUtil.java b/common/src/main/java/bisq/common/file/FileUtil.java index 12bb1adb22..d754c70d85 100644 --- a/common/src/main/java/bisq/common/file/FileUtil.java +++ b/common/src/main/java/bisq/common/file/FileUtil.java @@ -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()); diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 3f84a620d2..c4ab13a7a8 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -548,15 +548,11 @@ public class CoreApi { String paymentAccountId, Consumer resultHandler, ErrorMessageHandler errorMessageHandler) { - try { - Offer offer = coreOffersService.getOffer(offerId); - coreTradesService.takeOffer(offer, - paymentAccountId, - resultHandler, - errorMessageHandler); - } catch (Exception e) { - errorMessageHandler.handleErrorMessage(e.getMessage()); - } + Offer offer = coreOffersService.getOffer(offerId); + coreTradesService.takeOffer(offer, + paymentAccountId, + resultHandler, + errorMessageHandler); } public void confirmPaymentStarted(String tradeId) { diff --git a/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java b/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java index a77a5d6267..8c5d6cfe3c 100644 --- a/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java +++ b/core/src/main/java/bisq/core/api/CoreDisputeAgentsService.java @@ -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 = 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 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(), diff --git a/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java b/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java index 6ff628b91c..35a06c6120 100644 --- a/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java +++ b/core/src/main/java/bisq/core/api/CoreMoneroConnectionsService.java @@ -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 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> DEFAULT_CONNECTIONS; + static { + DEFAULT_CONNECTIONS = new HashMap>(); + 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,33 +347,37 @@ public final class CoreMoneroConnectionsService { } }); - // start local node if last connection is local and offline - currentConnectionUri.ifPresent(uri -> { - try { - if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) { - nodeService.startMoneroNode(); - } - } catch (Exception e) { - log.warn("Unable to start local monero node: " + e.getMessage()); - } - }); - - // poll daemon periodically - startPollingDaemon(); isInitialized = true; } - // if offline, connect to local node if available - if (!connectionManager.isConnected() && nodeService.isMoneroNodeRunning()) { + // start local node if offline and last connection is local + currentConnectionUri.ifPresent(uri -> { + try { + if (CoreMoneroNodeService.isLocalHost(uri) && !nodeService.isMoneroNodeRunning()) { + nodeService.startMoneroNode(); + } + } catch (Exception e) { + log.warn("Unable to start local monero node: " + e.getMessage()); + } + }); + + // connect to local node if available + if (nodeService.isMoneroNodeRunning() && (connectionManager.getAutoSwitch() || !connectionManager.isConnected())) { MoneroRpcConnection connection = connectionManager.getConnectionByUri(nodeService.getDaemon().getRpcConnection().getUri()); - if (connection == null) connection = nodeService.getDaemon().getRpcConnection(); - connection.checkConnection(connectionManager.getTimeout()); - setConnection(connection); + 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"); - peers.set(getOnlinePeers()); + lastInfo = daemon.getInfo(); + //System.out.println(JsonUtils.serialize(lastInfo)); + //System.out.println(JsonUtils.serialize(daemon.getSyncInfo())); + chainHeight.set(lastInfo.getTargetHeight() == 0 ? lastInfo.getHeight() : lastInfo.getTargetHeight()); + try { + peers.set(getOnlinePeers()); + } catch (MoneroError err) { + peers.set(new ArrayList()); // 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()); } } diff --git a/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java b/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java index 521d2b1c64..c465f20d2b 100644 --- a/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java +++ b/core/src/main/java/bisq/core/api/CoreMoneroNodeService.java @@ -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 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 { - String host = new URI(uri).getHost(); - return host.equals(CoreMoneroNodeService.LOOPBACK_HOST) || host.equals(CoreMoneroNodeService.LOCALHOST); + 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()) { diff --git a/core/src/main/java/bisq/core/api/CoreWalletsService.java b/core/src/main/java/bisq/core/api/CoreWalletsService.java index 9a29bab410..5d89667083 100644 --- a/core/src/main/java/bisq/core/api/CoreWalletsService.java +++ b/core/src/main/java/bisq/core/api/CoreWalletsService.java @@ -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"); } diff --git a/core/src/main/java/bisq/core/app/WalletAppSetup.java b/core/src/main/java/bisq/core/app/WalletAppSetup.java index c1962005c8..7b9d517f8c 100644 --- a/core/src/main/java/bisq/core/app/WalletAppSetup.java +++ b/core/src/main/java/bisq/core/app/WalletAppSetup.java @@ -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, () -> { diff --git a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java index f1577fba65..a9fbab9b03 100644 --- a/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java +++ b/core/src/main/java/bisq/core/app/misc/ExecutableForAppWithP2p.java @@ -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); diff --git a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java index 5995d19752..f6f193fc9e 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletConfig.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletConfig.java @@ -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() { - @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; + onSetupCompleted(); } 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() { diff --git a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java index 6c9461041c..fdbf3b98f5 100644 --- a/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java +++ b/core/src/main/java/bisq/core/btc/setup/WalletsSetup.java @@ -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); }); diff --git a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java index f39bf1dd76..00130705c6 100644 --- a/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/BtcWalletService.java @@ -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()); }); } diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletService.java b/core/src/main/java/bisq/core/btc/wallet/WalletService.java index 6e6ea209d1..81a6bd9ca0 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletService.java @@ -160,11 +160,13 @@ public abstract class WalletService { /////////////////////////////////////////////////////////////////////////////////////////// protected void addListenersToWallet() { - wallet.addCoinsReceivedEventListener(walletEventListener); - wallet.addCoinsSentEventListener(walletEventListener); - wallet.addReorganizeEventListener(walletEventListener); - wallet.addTransactionConfidenceEventListener(walletEventListener); - wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener); + if (wallet != null) { + wallet.addCoinsReceivedEventListener(walletEventListener); + wallet.addCoinsSentEventListener(walletEventListener); + wallet.addReorganizeEventListener(walletEventListener); + wallet.addTransactionConfidenceEventListener(walletEventListener); + wallet.addChangeEventListener(Threading.SAME_THREAD, cacheInvalidationListener); + } } public void shutDown() { diff --git a/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java b/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java index f6d73e1c55..6bb8e44591 100644 --- a/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java +++ b/core/src/main/java/bisq/core/btc/wallet/WalletsManager.java @@ -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() { diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 3d90343244..07a57abb55 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -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; @@ -155,8 +155,16 @@ public class XmrWalletService { checkState(state == State.STARTING || state == State.RUNNING, "Cannot call until startup is complete"); return wallet; } + + public boolean isWalletReady() { + return getWallet() != null; + } + + public boolean isWalletEncrypted() { + return accountService.getPassword() != null; + } - public MoneroDaemon getDaemon() { + 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,36 +323,31 @@ 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 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 is not submitted, submit but do not relay - if (tx == null) { - 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"); - } - + if (tx != null) throw new RuntimeException("Tx is already submitted"); + + // 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)); + tx = daemon.getTx(txHash); + // verify reserved key images if (keyImages != null) { Set txKeyImages = new HashSet(); for (MoneroOutput input : tx.getInputs()) txKeyImages.add(input.getKeyImage().getHex()); if (!txKeyImages.equals(new HashSet(keyImages))) throw new Error("Reserve tx's inputs do not match claimed key images"); } - + // verify the unlock height 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()); diff --git a/core/src/main/java/bisq/core/offer/CreateOfferService.java b/core/src/main/java/bisq/core/offer/CreateOfferService.java index 2283cb3641..2e8f1c9750 100644 --- a/core/src/main/java/bisq/core/offer/CreateOfferService.java +++ b/core/src/main/java/bisq/core/offer/CreateOfferService.java @@ -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 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, diff --git a/core/src/main/java/bisq/core/offer/OfferRestrictions.java b/core/src/main/java/bisq/core/offer/OfferRestrictions.java index 108b80587a..826e0d8267 100644 --- a/core/src/main/java/bisq/core/offer/OfferRestrictions.java +++ b/core/src/main/java/bisq/core/offer/OfferRestrictions.java @@ -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"); diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java index e93861eb0f..be3749b31f 100644 --- a/core/src/main/java/bisq/core/offer/OfferUtil.java +++ b/core/src/main/java/bisq/core/offer/OfferUtil.java @@ -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. diff --git a/core/src/main/java/bisq/core/offer/OpenOfferManager.java b/core/src/main/java/bisq/core/offer/OpenOfferManager.java index 806200454f..4021d8fe48 100644 --- a/core/src/main/java/bisq/core/offer/OpenOfferManager.java +++ b/core/src/main/java/bisq/core/offer/OpenOfferManager.java @@ -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, diff --git a/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java b/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java index 8e2f904d1e..f5e3d321e2 100644 --- a/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java +++ b/core/src/main/java/bisq/core/offer/placeoffer/tasks/ValidateOffer.java @@ -46,7 +46,7 @@ public class ValidateOffer extends Task { 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. diff --git a/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java b/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java index ad5e78819e..4fe4269fa2 100644 --- a/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java +++ b/core/src/main/java/bisq/core/offer/takeoffer/TakeOfferModel.java @@ -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" + '}'; } } diff --git a/core/src/main/java/bisq/core/payment/PaymentAccount.java b/core/src/main/java/bisq/core/payment/PaymentAccount.java index 791c32e1c2..e905aea06a 100644 --- a/core/src/main/java/bisq/core/payment/PaymentAccount.java +++ b/core/src/main/java/bisq/core/payment/PaymentAccount.java @@ -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"); diff --git a/core/src/main/java/bisq/core/payment/StrikeAccount.java b/core/src/main/java/bisq/core/payment/StrikeAccount.java index 00e7116a12..31625fba73 100644 --- a/core/src/main/java/bisq/core/payment/StrikeAccount.java +++ b/core/src/main/java/bisq/core/payment/StrikeAccount.java @@ -86,7 +86,6 @@ public final class StrikeAccount extends CountryBasedPaymentAccount { @Override @Nullable public @NotNull List getSupportedCountries() { - System.out.println("STIKE RETURNING SUPPORTED COUNTRIES: " + SUPPORTED_COUNTRIES); return SUPPORTED_COUNTRIES; } diff --git a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java index 08e48564af..321c20b634 100644 --- a/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java +++ b/core/src/main/java/bisq/core/support/dispute/arbitration/ArbitrationManager.java @@ -416,11 +416,6 @@ public final class ArbitrationManager extends DisputeManager 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(); } } diff --git a/core/src/main/java/bisq/core/support/dispute/arbitration/arbitrator/ArbitratorManager.java b/core/src/main/java/bisq/core/support/dispute/arbitration/arbitrator/ArbitratorManager.java index 677e7f62fc..f79a94a7e9 100644 --- a/core/src/main/java/bisq/core/support/dispute/arbitration/arbitrator/ArbitratorManager.java +++ b/core/src/main/java/bisq/core/support/dispute/arbitration/arbitrator/ArbitratorManager.java @@ -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 { @Override protected List 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(); + default: + throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork()); + } } @Override diff --git a/core/src/main/java/bisq/core/trade/Trade.java b/core/src/main/java/bisq/core/trade/Trade.java index 3ed4a20e1c..10c19a7a84 100644 --- a/core/src/main/java/bisq/core/trade/Trade.java +++ b/core/src/main/java/bisq/core/trade/Trade.java @@ -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 + '\'' + diff --git a/core/src/main/java/bisq/core/trade/TradeUtils.java b/core/src/main/java/bisq/core/trade/TradeUtils.java index 14773562e4..5e49f103d1 100644 --- a/core/src/main/java/bisq/core/trade/TradeUtils.java +++ b/core/src/main/java/bisq/core/trade/TradeUtils.java @@ -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. diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetsLockTime.java b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetsLockTime.java index 973e9c2a24..96471970df 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetsLockTime.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/maker/MakerSetsLockTime.java @@ -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()); diff --git a/core/src/main/java/bisq/core/user/Preferences.java b/core/src/main/java/bisq/core/user/Preferences.java index a94b3243fa..18ddaae06a 100644 --- a/core/src/main/java/bisq/core/user/Preferences.java +++ b/core/src/main/java/bisq/core/user/Preferences.java @@ -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); } diff --git a/core/src/main/java/bisq/core/util/FeeReceiverSelector.java b/core/src/main/java/bisq/core/util/FeeReceiverSelector.java deleted file mode 100644 index c052d218c5..0000000000 --- a/core/src/main/java/bisq/core/util/FeeReceiverSelector.java +++ /dev/null @@ -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 . - */ - -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 feeReceivers = Optional.ofNullable(filterManager.getFilter()) - .flatMap(f -> Optional.ofNullable(f.getBtcFeeReceiverAddresses())) - .orElse(List.of()); - - List amountList = new ArrayList<>(); - List 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 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; - } -} diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 41d6293a32..2f64e3a863 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_cs.properties b/core/src/main/resources/i18n/displayStrings_cs.properties index 72480e7599..8738833b31 100644 --- a/core/src/main/resources/i18n/displayStrings_cs.properties +++ b/core/src/main/resources/i18n/displayStrings_cs.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_de.properties b/core/src/main/resources/i18n/displayStrings_de.properties index eccaf11ec6..6680d7483f 100644 --- a/core/src/main/resources/i18n/displayStrings_de.properties +++ b/core/src/main/resources/i18n/displayStrings_de.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_es.properties b/core/src/main/resources/i18n/displayStrings_es.properties index bcae3b8d94..735d7f78e5 100644 --- a/core/src/main/resources/i18n/displayStrings_es.properties +++ b/core/src/main/resources/i18n/displayStrings_es.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_fa.properties b/core/src/main/resources/i18n/displayStrings_fa.properties index 707ec0b4ca..8cfbf21279 100644 --- a/core/src/main/resources/i18n/displayStrings_fa.properties +++ b/core/src/main/resources/i18n/displayStrings_fa.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_fr.properties b/core/src/main/resources/i18n/displayStrings_fr.properties index 13be1b20ee..98b2dc894d 100644 --- a/core/src/main/resources/i18n/displayStrings_fr.properties +++ b/core/src/main/resources/i18n/displayStrings_fr.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_it.properties b/core/src/main/resources/i18n/displayStrings_it.properties index 02b4beddd2..2a1cb6076a 100644 --- a/core/src/main/resources/i18n/displayStrings_it.properties +++ b/core/src/main/resources/i18n/displayStrings_it.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_ja.properties b/core/src/main/resources/i18n/displayStrings_ja.properties index 6bb916d005..21e81260f0 100644 --- a/core/src/main/resources/i18n/displayStrings_ja.properties +++ b/core/src/main/resources/i18n/displayStrings_ja.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_pt-br.properties b/core/src/main/resources/i18n/displayStrings_pt-br.properties index 1eedd02208..8995c757bc 100644 --- a/core/src/main/resources/i18n/displayStrings_pt-br.properties +++ b/core/src/main/resources/i18n/displayStrings_pt-br.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_pt.properties b/core/src/main/resources/i18n/displayStrings_pt.properties index ade9cb8857..ab4067cb92 100644 --- a/core/src/main/resources/i18n/displayStrings_pt.properties +++ b/core/src/main/resources/i18n/displayStrings_pt.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_ru.properties b/core/src/main/resources/i18n/displayStrings_ru.properties index 77a573c991..bd4bcd7bf9 100644 --- a/core/src/main/resources/i18n/displayStrings_ru.properties +++ b/core/src/main/resources/i18n/displayStrings_ru.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_th.properties b/core/src/main/resources/i18n/displayStrings_th.properties index 467c367ce8..0fce0e0ad2 100644 --- a/core/src/main/resources/i18n/displayStrings_th.properties +++ b/core/src/main/resources/i18n/displayStrings_th.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_vi.properties b/core/src/main/resources/i18n/displayStrings_vi.properties index 10dc18a5f9..b7b90f3a28 100644 --- a/core/src/main/resources/i18n/displayStrings_vi.properties +++ b/core/src/main/resources/i18n/displayStrings_vi.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hans.properties b/core/src/main/resources/i18n/displayStrings_zh-hans.properties index 76abb22fcf..da0e34d504 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hans.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hans.properties @@ -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 diff --git a/core/src/main/resources/i18n/displayStrings_zh-hant.properties b/core/src/main/resources/i18n/displayStrings_zh-hant.properties index 06acc9f3ae..b558a3fff7 100644 --- a/core/src/main/resources/i18n/displayStrings_zh-hant.properties +++ b/core/src/main/resources/i18n/displayStrings_zh-hant.properties @@ -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 diff --git a/core/src/main/resources/xmr_local.seednodes b/core/src/main/resources/xmr_local.seednodes new file mode 100644 index 0000000000..44cd4ec296 --- /dev/null +++ b/core/src/main/resources/xmr_local.seednodes @@ -0,0 +1,3 @@ +# nodeaddress.onion:port [(@owner,@backup)] +localhost:2002 (@devtest1) +localhost:3002 (@devtest2) diff --git a/core/src/main/resources/xmr_stagenet.seednodes b/core/src/main/resources/xmr_stagenet.seednodes index 44cd4ec296..dc1ff1ec6b 100644 --- a/core/src/main/resources/xmr_stagenet.seednodes +++ b/core/src/main/resources/xmr_stagenet.seednodes @@ -1,3 +1,3 @@ -# nodeaddress.onion:port [(@owner,@backup)] -localhost:2002 (@devtest1) -localhost:3002 (@devtest2) +# nodeaddress.onion:port [(@owner)] +localhost:2002 (@devtest1) # TODO: replace with hosted stagenet seednodes +localhost:3002 (@devtest2) \ No newline at end of file diff --git a/core/src/main/resources/xmr_testnet.seednodes b/core/src/main/resources/xmr_testnet.seednodes deleted file mode 100644 index 93f8c13fb1..0000000000 --- a/core/src/main/resources/xmr_testnet.seednodes +++ /dev/null @@ -1,2 +0,0 @@ -# nodeaddress.onion:port [(@owner)] -placeholder.onion:8001 diff --git a/core/src/test/java/bisq/core/arbitration/ArbitratorManagerTest.java b/core/src/test/java/bisq/core/arbitration/ArbitratorManagerTest.java index 2d5e8792c2..c4b45fee6f 100644 --- a/core/src/test/java/bisq/core/arbitration/ArbitratorManagerTest.java +++ b/core/src/test/java/bisq/core/arbitration/ArbitratorManagerTest.java @@ -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); diff --git a/core/src/test/java/bisq/core/arbitration/ArbitratorTest.java b/core/src/test/java/bisq/core/arbitration/ArbitratorTest.java index 8625ac1fa2..e5b8de08c0 100644 --- a/core/src/test/java/bisq/core/arbitration/ArbitratorTest.java +++ b/core/src/test/java/bisq/core/arbitration/ArbitratorTest.java @@ -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(), diff --git a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java index 913ab9629c..dc86b2967f 100644 --- a/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java +++ b/core/src/test/java/bisq/core/locale/CurrencyUtilTest.java @@ -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()); diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 72eae92e93..fdbb6bd4c1 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -136,6 +136,7 @@ class GrpcTradesService extends TradesImplBase { errorMessageHandler.handleErrorMessage(errorMessage); }); } catch (Throwable cause) { + cause.printStackTrace(); exceptionHandler.handleException(log, cause, responseObserver); } } diff --git a/desktop/src/main/java/bisq/desktop/main/MainView.java b/desktop/src/main/java/bisq/desktop/main/MainView.java index 431eaf1627..b3866a392d 100644 --- a/desktop/src/main/java/bisq/desktop/main/MainView.java +++ b/desktop/src/main/java/bisq/desktop/main/MainView.java @@ -357,23 +357,25 @@ public class MainView extends InitializableView { settingsButtonWithBadge.getStyleClass().add("new"); navigation.addListener((viewPath, data) -> { - if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0) - return; + UserThread.execute(() -> { + if (viewPath.size() != 2 || viewPath.indexOf(MainView.class) != 0) + return; - Class viewClass = viewPath.tip(); - View view = viewLoader.load(viewClass); - contentContainer.getChildren().setAll(view.getRoot()); + Class viewClass = viewPath.tip(); + View view = viewLoader.load(viewClass); + contentContainer.getChildren().setAll(view.getRoot()); - try { - navButtons.getToggles().stream() - .filter(toggle -> toggle instanceof NavButton) - .filter(button -> viewClass == ((NavButton) button).viewClass) - .findFirst() - .orElseThrow(() -> new HavenoException("No button matching %s found", viewClass)) - .setSelected(true); - } catch (HavenoException e) { - navigation.navigateTo(MainView.class, MarketView.class, OfferBookChartView.class); - } + try { + navButtons.getToggles().stream() + .filter(toggle -> toggle instanceof NavButton) + .filter(button -> viewClass == ((NavButton) button).viewClass) + .findFirst() + .orElseThrow(() -> new HavenoException("No button matching %s found", viewClass)) + .setSelected(true); + } catch (HavenoException e) { + navigation.navigateTo(MainView.class, MarketView.class, OfferBookChartView.class); + } + }); }); VBox splashScreen = createSplashScreen(); @@ -431,13 +433,14 @@ public class MainView extends InitializableView { return new ListCell<>() { @Override protected void updateItem(PriceFeedComboBoxItem item, boolean empty) { - super.updateItem(item, empty); - - if (!empty && item != null) { - textProperty().bind(item.displayStringProperty); - } else { - textProperty().unbind(); - } + UserThread.execute(() -> { + super.updateItem(item, empty); + if (!empty && item != null) { + textProperty().bind(item.displayStringProperty); + } else { + textProperty().unbind(); + } + }); } }; } @@ -763,8 +766,10 @@ public class MainView extends InitializableView { buttonWithBadge.textProperty().bind(badgeNumber); buttonWithBadge.setEnabled(badgeEnabled.get()); badgeEnabled.addListener((observable, oldValue, newValue) -> { - buttonWithBadge.setEnabled(newValue); - buttonWithBadge.refreshBadge(); + UserThread.execute(() -> { + buttonWithBadge.setEnabled(newValue); + buttonWithBadge.refreshBadge(); + }); }); buttonWithBadge.setPosition(Pos.TOP_RIGHT); diff --git a/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationViewModel.java b/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationViewModel.java index 30824109f4..e68e85a914 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/register/AgentRegistrationViewModel.java @@ -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 { diff --git a/desktop/src/main/java/bisq/desktop/main/account/register/arbitrator/ArbitratorRegistrationViewModel.java b/desktop/src/main/java/bisq/desktop/main/account/register/arbitrator/ArbitratorRegistrationViewModel.java index 9d14f9ad31..f0cef0c438 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/register/arbitrator/ArbitratorRegistrationViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/register/arbitrator/ArbitratorRegistrationViewModel.java @@ -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(), diff --git a/desktop/src/main/java/bisq/desktop/main/account/register/mediator/MediatorRegistrationViewModel.java b/desktop/src/main/java/bisq/desktop/main/account/register/mediator/MediatorRegistrationViewModel.java index a7d01871b2..e361b1d8e0 100644 --- a/desktop/src/main/java/bisq/desktop/main/account/register/mediator/MediatorRegistrationViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/account/register/mediator/MediatorRegistrationViewModel.java @@ -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 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 /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java index b1bd166f0b..81b284a534 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/MutableOfferViewModel.java @@ -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 ext } void onShowPayFundsScreen(Runnable actionHandler) { - dataModel.updateEstimatedFeeAndTxVsize(); - dataModel.requestTxFee(actionHandler); showPayFundsScreenDisplayed.set(true); updateSpinnerInfo(); } diff --git a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java index 89a09d5dc4..aa854a57e2 100644 --- a/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/offer/takeoffer/TakeOfferDataModel.java @@ -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 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; diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java index ae57d3f457..4a010901f0 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/OfferDetailsWindow.java @@ -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 { 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 { 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 { 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 { 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)); diff --git a/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java b/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java index 615857539b..5b6df5af66 100644 --- a/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java +++ b/desktop/src/main/java/bisq/desktop/main/presentation/MarketPricePresentation.java @@ -184,29 +184,31 @@ public class MarketPricePresentation { } private void setMarketPriceInItems() { - priceFeedComboBoxItems.forEach(item -> { - String currencyCode = item.currencyCode; - MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); - String priceString; - if (marketPrice != null && marketPrice.isPriceAvailable()) { - priceString = FormattingUtils.formatMarketPrice(marketPrice.getPrice(), currencyCode); - item.setPriceAvailable(true); - item.setExternallyProvidedPrice(marketPrice.isExternallyProvidedPrice()); - } else { - priceString = Res.get("shared.na"); - item.setPriceAvailable(false); - } - item.setDisplayString(CurrencyUtil.getCurrencyPair(currencyCode) + ": " + priceString); + UserThread.execute(() -> { + priceFeedComboBoxItems.forEach(item -> { + String currencyCode = item.currencyCode; + MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode); + String priceString; + if (marketPrice != null && marketPrice.isPriceAvailable()) { + priceString = FormattingUtils.formatMarketPrice(marketPrice.getPrice(), currencyCode); + item.setPriceAvailable(true); + item.setExternallyProvidedPrice(marketPrice.isExternallyProvidedPrice()); + } else { + priceString = Res.get("shared.na"); + item.setPriceAvailable(false); + } + item.setDisplayString(CurrencyUtil.getCurrencyPair(currencyCode) + ": " + priceString); - final String code = item.currencyCode; - if (selectedPriceFeedComboBoxItemProperty.get() != null && - selectedPriceFeedComboBoxItemProperty.get().currencyCode.equals(code)) { - isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); - isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); - isExternallyProvidedPrice.set(item.isExternallyProvidedPrice()); - isPriceAvailable.set(item.isPriceAvailable()); - marketPriceUpdated.set(marketPriceUpdated.get() + 1); - } + final String code = item.currencyCode; + if (selectedPriceFeedComboBoxItemProperty.get() != null && + selectedPriceFeedComboBoxItemProperty.get().currencyCode.equals(code)) { + isFiatCurrencyPriceFeedSelected.set(CurrencyUtil.isFiatCurrency(code) && CurrencyUtil.getFiatCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); + isCryptoCurrencyPriceFeedSelected.set(CurrencyUtil.isCryptoCurrency(code) && CurrencyUtil.getCryptoCurrency(code).isPresent() && item.isPriceAvailable() && item.isExternallyProvidedPrice()); + isExternallyProvidedPrice.set(item.isExternallyProvidedPrice()); + isPriceAvailable.set(item.isPriceAvailable()); + marketPriceUpdated.set(marketPriceUpdated.get() + 1); + } + }); }); } diff --git a/docs/installing.md b/docs/installing.md index 1582613db1..2da5daf2c8 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -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 diff --git a/monitor/README.md b/monitor/README.md index ccd54f6287..b8125e6d4b 100644 --- a/monitor/README.md +++ b/monitor/README.md @@ -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. diff --git a/monitor/src/main/java/bisq/monitor/Metric.java b/monitor/src/main/java/bisq/monitor/Metric.java index 0c21917491..c60c28df50 100644 --- a/monitor/src/main/java/bisq/monitor/Metric.java +++ b/monitor/src/main/java/bisq/monitor/Metric.java @@ -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") diff --git a/monitor/src/main/resources/metrics.properties b/monitor/src/main/resources/metrics.properties index 89bd9c2b24..c77087a23c 100644 --- a/monitor/src/main/resources/metrics.properties +++ b/monitor/src/main/resources/metrics.properties @@ -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. diff --git a/p2p/src/main/resources/AccountAgeWitnessStore_XMR_TESTNET_placeholder b/p2p/src/main/resources/AccountAgeWitnessStore_XMR_LOCAL_placeholder similarity index 100% rename from p2p/src/main/resources/AccountAgeWitnessStore_XMR_TESTNET_placeholder rename to p2p/src/main/resources/AccountAgeWitnessStore_XMR_LOCAL_placeholder diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 84e58b8ba1..a702234a54 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -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 extra_data = 11; + string xmr_address = 7; + string email_address = 8; + string info = 9; + map extra_data = 10; } message Mediator { diff --git a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java index 04e4948b6e..24189fb5a2 100644 --- a/seednode/src/main/java/bisq/seednode/SeedNodeMain.java +++ b/seednode/src/main/java/bisq/seednode/SeedNodeMain.java @@ -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; }