From 01dd6a8af96a84c841efc5e192ff54e418367174 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 1 Apr 2024 18:41:40 -0400 Subject: [PATCH] support using monero native libraries (experimental) --- Makefile | 97 +++++++----- build.gradle | 2 +- .../java/haveno/common/config/Config.java | 9 ++ .../java/haveno/core/api/XmrLocalNode.java | 7 +- .../java/haveno/core/app/HavenoSetup.java | 15 +- .../main/java/haveno/core/xmr/XmrModule.java | 1 + .../core/xmr/wallet/XmrWalletService.java | 146 ++++++++++++++---- gradle/verification-metadata.xml | 6 +- 8 files changed, 204 insertions(+), 79 deletions(-) diff --git a/Makefile b/Makefile index d71c502147..4da57e7a3a 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ bitcoind: -peerbloomfilters=1 \ -datadir=.localnet/ \ -rpcuser=haveno \ - -rpcpassword=1234 + -rpcpassword=1234 \ btc-blocks: ./.localnet/bitcoin-cli \ @@ -75,7 +75,7 @@ monerod1-local: --add-exclusive-node 127.0.0.1:58080 \ --rpc-access-control-origins http://localhost:8080 \ --fixed-difficulty 500 \ - --disable-rpc-ban + --disable-rpc-ban \ monerod2-local: ./.localnet/monerod \ @@ -93,7 +93,7 @@ monerod2-local: --add-exclusive-node 127.0.0.1:58080 \ --rpc-access-control-origins http://localhost:8080 \ --fixed-difficulty 500 \ - --disable-rpc-ban + --disable-rpc-ban \ monerod3-local: ./.localnet/monerod \ @@ -111,7 +111,7 @@ monerod3-local: --add-exclusive-node 127.0.0.1:48080 \ --rpc-access-control-origins http://localhost:8080 \ --fixed-difficulty 500 \ - --disable-rpc-ban + --disable-rpc-ban \ funding-wallet-stagenet: ./.localnet/monero-wallet-rpc \ @@ -121,7 +121,7 @@ funding-wallet-stagenet: --rpc-access-control-origins http://localhost:8080 \ --wallet-dir ./.localnet \ --daemon-ssl-allow-any-cert \ - --daemon-address http://127.0.0.1:38081 + --daemon-address http://127.0.0.1:38081 \ #--proxy 127.0.0.1:49775 \ @@ -132,7 +132,7 @@ funding-wallet-local: --rpc-bind-port 28084 \ --rpc-login rpc_user:abc123 \ --rpc-access-control-origins http://localhost:8080 \ - --wallet-dir ./.localnet + --wallet-dir ./.localnet \ # use .bat extension for windows binaries APP_EXT := @@ -168,7 +168,8 @@ arbitrator-daemon-local: --appName=haveno-XMR_LOCAL_arbitrator \ --apiPassword=apitest \ --apiPort=9998 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ arbitrator-desktop-local: # Arbitrator needs to be registered before making trades @@ -179,7 +180,8 @@ arbitrator-desktop-local: --nodePort=4444 \ --appName=haveno-XMR_LOCAL_arbitrator \ --apiPassword=apitest \ - --apiPort=9998 + --apiPort=9998 \ + --useNativeXmrWallet=false \ arbitrator2-daemon-local: # Arbitrator needs to be registered before making trades @@ -190,7 +192,8 @@ arbitrator2-daemon-local: --nodePort=7777 \ --appName=haveno-XMR_LOCAL_arbitrator2 \ --apiPassword=apitest \ - --apiPort=10001 + --apiPort=10001 \ + --useNativeXmrWallet=false \ arbitrator2-desktop-local: # Arbitrator needs to be registered before making trades @@ -201,7 +204,8 @@ arbitrator2-desktop-local: --nodePort=7777 \ --appName=haveno-XMR_LOCAL_arbitrator2 \ --apiPassword=apitest \ - --apiPort=10001 + --apiPort=10001 \ + --useNativeXmrWallet=false \ user1-daemon-local: ./haveno-daemon$(APP_EXT) \ @@ -213,7 +217,8 @@ user1-daemon-local: --apiPassword=apitest \ --apiPort=9999 \ --walletRpcBindPort=38091 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ user1-desktop-local: ./haveno-desktop$(APP_EXT) \ @@ -225,7 +230,8 @@ user1-desktop-local: --apiPassword=apitest \ --apiPort=9999 \ --walletRpcBindPort=38091 \ - --logLevel=info + --logLevel=info \ + --useNativeXmrWallet=false \ user2-desktop-local: ./haveno-desktop$(APP_EXT) \ @@ -236,7 +242,8 @@ user2-desktop-local: --appName=haveno-XMR_LOCAL_user2 \ --apiPassword=apitest \ --apiPort=10000 \ - --walletRpcBindPort=38092 + --walletRpcBindPort=38092 \ + --useNativeXmrWallet=false \ user2-daemon-local: ./haveno-daemon$(APP_EXT) \ @@ -248,7 +255,8 @@ user2-daemon-local: --apiPassword=apitest \ --apiPort=10000 \ --walletRpcBindPort=38092 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ user3-desktop-local: ./haveno-desktop$(APP_EXT) \ @@ -259,7 +267,8 @@ user3-desktop-local: --appName=haveno-XMR_LOCAL_user3 \ --apiPassword=apitest \ --apiPort=10002 \ - --walletRpcBindPort=38093 + --walletRpcBindPort=38093 \ + --useNativeXmrWallet=false \ user3-daemon-local: ./haveno-daemon$(APP_EXT) \ @@ -271,7 +280,8 @@ user3-daemon-local: --apiPassword=apitest \ --apiPort=10002 \ --walletRpcBindPort=38093 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ # Stagenet network @@ -288,7 +298,7 @@ monerod-stagenet-custom: --p2p-bind-port 39080 \ --rpc-bind-port 39081 \ --bootstrap-daemon-address auto \ - --rpc-access-control-origins http://localhost:8080 + --rpc-access-control-origins http://localhost:8080 \ seednode-stagenet: ./haveno-seednode$(APP_EXT) \ @@ -297,7 +307,7 @@ seednode-stagenet: --useDevPrivilegeKeys=false \ --nodePort=9999 \ --appName=haveno-XMR_STAGENET_Seed_3002 \ - --xmrNode=http://127.0.0.1:38081 + --xmrNode=http://127.0.0.1:38081 \ seednode2-stagenet: ./haveno-seednode$(APP_EXT) \ @@ -306,7 +316,7 @@ seednode2-stagenet: --useDevPrivilegeKeys=false \ --nodePort=9999 \ --appName=haveno-XMR_STAGENET_Seed_3003 \ - --xmrNode=http://127.0.0.1:38081 + --xmrNode=http://127.0.0.1:38081 \ arbitrator-daemon-stagenet: # Arbitrator needs to be registered before making trades @@ -319,7 +329,8 @@ arbitrator-daemon-stagenet: --apiPassword=apitest \ --apiPort=3200 \ --passwordRequired=false \ - --xmrNode=http://127.0.0.1:38081 + --xmrNode=http://127.0.0.1:38081 \ + --useNativeXmrWallet=false \ # Arbitrator needs to be registered before making trades arbitrator-desktop-stagenet: @@ -331,7 +342,8 @@ arbitrator-desktop-stagenet: --appName=haveno-XMR_STAGENET_arbitrator \ --apiPassword=apitest \ --apiPort=3200 \ - --xmrNode=http://127.0.0.1:38081 + --xmrNode=http://127.0.0.1:38081 \ + --useNativeXmrWallet=false \ user1-daemon-stagenet: ./haveno-daemon$(APP_EXT) \ @@ -342,7 +354,8 @@ user1-daemon-stagenet: --appName=haveno-XMR_STAGENET_user1 \ --apiPassword=apitest \ --apiPort=3201 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ user1-desktop-stagenet: ./haveno-desktop$(APP_EXT) \ @@ -352,7 +365,8 @@ user1-desktop-stagenet: --nodePort=9999 \ --appName=haveno-XMR_STAGENET_user1 \ --apiPassword=apitest \ - --apiPort=3201 + --apiPort=3201 \ + --useNativeXmrWallet=false \ user2-daemon-stagenet: ./haveno-daemon$(APP_EXT) \ @@ -363,7 +377,8 @@ user2-daemon-stagenet: --appName=haveno-XMR_STAGENET_user2 \ --apiPassword=apitest \ --apiPort=3202 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ user2-desktop-stagenet: ./haveno-desktop$(APP_EXT) \ @@ -373,7 +388,8 @@ user2-desktop-stagenet: --nodePort=9999 \ --appName=haveno-XMR_STAGENET_user2 \ --apiPassword=apitest \ - --apiPort=3202 + --apiPort=3202 \ + --useNativeXmrWallet=false \ user3-desktop-stagenet: ./haveno-desktop$(APP_EXT) \ @@ -383,7 +399,8 @@ user3-desktop-stagenet: --nodePort=9999 \ --appName=haveno-XMR_STAGENET_user3 \ --apiPassword=apitest \ - --apiPort=3203 + --apiPort=3203 \ + --useNativeXmrWallet=false \ haveno-desktop-stagenet: ./haveno-desktop$(APP_EXT) \ @@ -393,7 +410,8 @@ haveno-desktop-stagenet: --nodePort=9999 \ --appName=Haveno \ --apiPassword=apitest \ - --apiPort=3204 + --apiPort=3204 \ + --useNativeXmrWallet=false \ # Mainnet network @@ -409,7 +427,7 @@ seednode: --useDevPrivilegeKeys=false \ --nodePort=9999 \ --appName=haveno-XMR_MAINNET_Seed_1002 \ - --xmrNode=http://127.0.0.1:18081 + --xmrNode=http://127.0.0.1:18081 \ seednode2: ./haveno-seednode$(APP_EXT) \ @@ -418,7 +436,7 @@ seednode2: --useDevPrivilegeKeys=false \ --nodePort=9999 \ --appName=haveno-XMR_MAINNET_Seed_1003 \ - --xmrNode=http://127.0.0.1:18081 + --xmrNode=http://127.0.0.1:18081 \ arbitrator-daemon: # Arbitrator needs to be registered before making trades @@ -431,7 +449,8 @@ arbitrator-daemon: --apiPassword=apitest \ --apiPort=1200 \ --passwordRequired=false \ - --xmrNode=http://127.0.0.1:18081 + --xmrNode=http://127.0.0.1:18081 \ + --useNativeXmrWallet=false \ # Arbitrator needs to be registered before making trades arbitrator-desktop: @@ -443,7 +462,8 @@ arbitrator-desktop: --appName=haveno-XMR_MAINNET_arbitrator \ --apiPassword=apitest \ --apiPort=1200 \ - --xmrNode=http://127.0.0.1:18081 + --xmrNode=http://127.0.0.1:18081 \ + --useNativeXmrWallet=false \ user1-daemon: ./haveno-daemon$(APP_EXT) \ @@ -454,7 +474,8 @@ user1-daemon: --appName=haveno-XMR_MAINNET_user1 \ --apiPassword=apitest \ --apiPort=1201 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ user1-desktop: ./haveno-desktop$(APP_EXT) \ @@ -464,7 +485,8 @@ user1-desktop: --nodePort=9999 \ --appName=haveno-XMR_MAINNET_user1 \ --apiPassword=apitest \ - --apiPort=1201 + --apiPort=1201 \ + --useNativeXmrWallet=false \ user2-daemon: ./haveno-daemon$(APP_EXT) \ @@ -475,7 +497,8 @@ user2-daemon: --appName=haveno-XMR_MAINNET_user2 \ --apiPassword=apitest \ --apiPort=1202 \ - --passwordRequired=false + --passwordRequired=false \ + --useNativeXmrWallet=false \ user2-desktop: ./haveno-desktop$(APP_EXT) \ @@ -485,7 +508,8 @@ user2-desktop: --nodePort=9999 \ --appName=haveno-XMR_MAINNET_user2 \ --apiPassword=apitest \ - --apiPort=1202 + --apiPort=1202 \ + --useNativeXmrWallet=false \ user3-desktop: ./haveno-desktop$(APP_EXT) \ @@ -495,4 +519,5 @@ user3-desktop: --nodePort=9999 \ --appName=haveno-XMR_MAINNET_user3 \ --apiPassword=apitest \ - --apiPort=1203 + --apiPort=1203 \ + --useNativeXmrWallet=false \ diff --git a/build.gradle b/build.gradle index 8625ca2005..aaaaf2a826 100644 --- a/build.gradle +++ b/build.gradle @@ -49,7 +49,7 @@ configure(subprojects) { gsonVersion = '2.8.5' guavaVersion = '32.1.1-jre' guiceVersion = '7.0.0' - moneroJavaVersion = '0.8.11' + moneroJavaVersion = '0.8.21' httpclient5Version = '5.0' hamcrestVersion = '2.2' httpclientVersion = '4.5.12' diff --git a/common/src/main/java/haveno/common/config/Config.java b/common/src/main/java/haveno/common/config/Config.java index 4682293f18..9147d53536 100644 --- a/common/src/main/java/haveno/common/config/Config.java +++ b/common/src/main/java/haveno/common/config/Config.java @@ -100,6 +100,7 @@ public class Config { public static final String XMR_NODE_USERNAME = "xmrNodeUsername"; public static final String XMR_NODE_PASSWORD = "xmrNodePassword"; public static final String XMR_NODES = "xmrNodes"; + public static final String USE_NATIVE_XMR_WALLET = "useNativeXmrWallet"; public static final String SOCKS5_DISCOVER_MODE = "socks5DiscoverMode"; public static final String USE_ALL_PROVIDED_NODES = "useAllProvidedNodes"; public static final String USER_AGENT = "userAgent"; @@ -188,6 +189,7 @@ public class Config { public final String xmrNodeUsername; public final String xmrNodePassword; public final String xmrNodes; + public final boolean useNativeXmrWallet; public final UseTorForXmr useTorForXmr; public final boolean useTorForXmrOptionSetExplicitly; public final String socks5DiscoverMode; @@ -532,6 +534,12 @@ public class Config { .describedAs("ip[,...]") .defaultsTo(""); + ArgumentAcceptingOptionSpec useNativeXmrWalletOpt = + parser.accepts(USE_NATIVE_XMR_WALLET, "Use native wallet libraries instead of monero-wallet-rpc server") + .withRequiredArg() + .ofType(boolean.class) + .defaultsTo(false); + //noinspection rawtypes ArgumentAcceptingOptionSpec useTorForXmrOpt = parser.accepts(USE_TOR_FOR_XMR, "Configure TOR for Monero connections, one of: after_sync, off, or on.") @@ -702,6 +710,7 @@ public class Config { this.xmrNodeUsername = options.valueOf(xmrNodeUsernameOpt); this.xmrNodePassword = options.valueOf(xmrNodePasswordOpt); this.xmrNodes = options.valueOf(xmrNodesOpt); + this.useNativeXmrWallet = options.valueOf(useNativeXmrWalletOpt); this.useTorForXmr = (UseTorForXmr) options.valueOf(useTorForXmrOpt); this.useTorForXmrOptionSetExplicitly = options.has(useTorForXmrOpt); this.socks5DiscoverMode = options.valueOf(socks5DiscoverModeOpt); diff --git a/core/src/main/java/haveno/core/api/XmrLocalNode.java b/core/src/main/java/haveno/core/api/XmrLocalNode.java index 6f05cff024..3821aa5aec 100644 --- a/core/src/main/java/haveno/core/api/XmrLocalNode.java +++ b/core/src/main/java/haveno/core/api/XmrLocalNode.java @@ -25,6 +25,8 @@ import haveno.core.trade.HavenoUtils; import haveno.core.user.Preferences; import haveno.core.xmr.XmrNodeSettings; import haveno.core.xmr.nodes.XmrNodes; +import haveno.core.xmr.wallet.XmrWalletService; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -44,10 +46,9 @@ public class XmrLocalNode { // constants public static final long REFRESH_PERIOD_LOCAL_MS = 5000; // refresh period for local node - public static final String MONEROD_DIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? System.getProperty("user.dir") + File.separator + ".localnet" : Config.appDataDir().getAbsolutePath(); public static final String MONEROD_NAME = Utilities.isWindows() ? "monerod.exe" : "monerod"; - public static final String MONEROD_PATH = MONEROD_DIR + File.separator + MONEROD_NAME; - private static final String MONEROD_DATADIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? MONEROD_DIR + File.separator + Config.baseCurrencyNetwork().toString().toLowerCase() + File.separator + "node1" : null; // use default directory unless local + public static final String MONEROD_PATH = XmrWalletService.MONERO_BINS_DIR + File.separator + MONEROD_NAME; + private static final String MONEROD_DATADIR = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL ? XmrWalletService.MONERO_BINS_DIR + File.separator + Config.baseCurrencyNetwork().toString().toLowerCase() + File.separator + "node1" : null; // use default directory unless local // instance fields private MoneroDaemonRpc daemon; diff --git a/core/src/main/java/haveno/core/app/HavenoSetup.java b/core/src/main/java/haveno/core/app/HavenoSetup.java index e44520ec9b..0b408a0faf 100644 --- a/core/src/main/java/haveno/core/app/HavenoSetup.java +++ b/core/src/main/java/haveno/core/app/HavenoSetup.java @@ -357,6 +357,8 @@ public class HavenoSetup { private void maybeInstallDependencies() { try { + + // install monerod File monerodFile = new File(XmrLocalNode.MONEROD_PATH); String monerodResourcePath = "bin/" + XmrLocalNode.MONEROD_NAME; if (!monerodFile.exists() || !FileUtil.resourceEqualToFile(monerodResourcePath, monerodFile)) { @@ -366,13 +368,14 @@ public class HavenoSetup { monerodFile.setExecutable(true); } - File moneroWalletFile = new File(XmrWalletService.MONERO_WALLET_RPC_PATH); - String moneroWalletResourcePath = "bin/" + XmrWalletService.MONERO_WALLET_RPC_NAME; - if (!moneroWalletFile.exists() || !FileUtil.resourceEqualToFile(moneroWalletResourcePath, moneroWalletFile)) { + // install monero-wallet-rpc + File moneroWalletRpcFile = new File(XmrWalletService.MONERO_WALLET_RPC_PATH); + String moneroWalletRpcResourcePath = "bin/" + XmrWalletService.MONERO_WALLET_RPC_NAME; + if (!moneroWalletRpcFile.exists() || !FileUtil.resourceEqualToFile(moneroWalletRpcResourcePath, moneroWalletRpcFile)) { log.info("Installing monero-wallet-rpc"); - moneroWalletFile.getParentFile().mkdirs(); - FileUtil.resourceToFile(moneroWalletResourcePath, moneroWalletFile); - moneroWalletFile.setExecutable(true); + moneroWalletRpcFile.getParentFile().mkdirs(); + FileUtil.resourceToFile(moneroWalletRpcResourcePath, moneroWalletRpcFile); + moneroWalletRpcFile.setExecutable(true); } } catch (Exception e) { e.printStackTrace(); diff --git a/core/src/main/java/haveno/core/xmr/XmrModule.java b/core/src/main/java/haveno/core/xmr/XmrModule.java index f869b97a49..b039da557a 100644 --- a/core/src/main/java/haveno/core/xmr/XmrModule.java +++ b/core/src/main/java/haveno/core/xmr/XmrModule.java @@ -92,6 +92,7 @@ public class XmrModule extends AppModule { bindConstant().annotatedWith(named(Config.XMR_NODE_USERNAME)).to(config.xmrNodeUsername); bindConstant().annotatedWith(named(Config.XMR_NODE_PASSWORD)).to(config.xmrNodePassword); bindConstant().annotatedWith(named(Config.XMR_NODES)).to(config.xmrNodes); + bindConstant().annotatedWith(named(Config.USE_NATIVE_XMR_WALLET)).to(config.useNativeXmrWallet); bindConstant().annotatedWith(named(Config.USER_AGENT)).to(config.userAgent); bindConstant().annotatedWith(named(Config.NUM_CONNECTIONS_FOR_BTC)).to(config.numConnectionsForBtc); bindConstant().annotatedWith(named(Config.USE_ALL_PROVIDED_NODES)).to(config.useAllProvidedNodes); diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index bd1c97731f..fbb13c8c3b 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -85,6 +85,7 @@ import monero.daemon.model.MoneroOutput; import monero.daemon.model.MoneroSubmitTxResult; import monero.daemon.model.MoneroTx; import monero.wallet.MoneroWallet; +import monero.wallet.MoneroWalletFull; import monero.wallet.MoneroWalletRpc; import monero.wallet.model.MoneroCheckTx; import monero.wallet.model.MoneroDestination; @@ -108,11 +109,11 @@ import org.slf4j.LoggerFactory; public class XmrWalletService { private static final Logger log = LoggerFactory.getLogger(XmrWalletService.class); - // Monero configuration + // monero configuration public static final int NUM_BLOCKS_UNLOCK = 10; - public static final String MONERO_WALLET_RPC_DIR = Config.baseCurrencyNetwork().isTestnet() ? System.getProperty("user.dir") + File.separator + ".localnet" : Config.appDataDir().getAbsolutePath(); // .localnet contains monero-wallet-rpc and wallet files + public static final String MONERO_BINS_DIR = Config.baseCurrencyNetwork().isTestnet() ? System.getProperty("user.dir") + File.separator + ".localnet" : Config.appDataDir().getAbsolutePath(); // .localnet contains monero binaries and wallet files public static final String MONERO_WALLET_RPC_NAME = Utilities.isWindows() ? "monero-wallet-rpc.exe" : "monero-wallet-rpc"; - public static final String MONERO_WALLET_RPC_PATH = MONERO_WALLET_RPC_DIR + File.separator + MONERO_WALLET_RPC_NAME; + public static final String MONERO_WALLET_RPC_PATH = MONERO_BINS_DIR + File.separator + MONERO_WALLET_RPC_NAME; public static final double MINER_FEE_TOLERANCE = 0.25; // miner fee must be within percent of estimated fee public static final MoneroTxPriority PROTOCOL_FEE_PRIORITY = MoneroTxPriority.ELEVATED; private static final MoneroNetworkType MONERO_NETWORK_TYPE = getMoneroNetworkType(); @@ -123,7 +124,7 @@ public class XmrWalletService { private static final String KEYS_FILE_POSTFIX = ".keys"; private static final String ADDRESS_FILE_POSTFIX = ".address.txt"; private static final int NUM_MAX_WALLET_BACKUPS = 1; - private static final int MONERO_LOG_LEVEL = 0; + private static final int MONERO_LOG_LEVEL = -1; // monero library log level, -1 to disable private static final int MAX_SYNC_ATTEMPTS = 3; private static final boolean PRINT_STACK_TRACE = false; private static final String THREAD_ID = XmrWalletService.class.getSimpleName(); @@ -140,12 +141,13 @@ public class XmrWalletService { private final File walletDir; private final File xmrWalletFile; private final int rpcBindPort; + private final boolean useNativeXmrWallet; protected final CopyOnWriteArraySet balanceListeners = new CopyOnWriteArraySet<>(); protected final CopyOnWriteArraySet walletListeners = new CopyOnWriteArraySet<>(); private ChangeListener walletInitListener; private TradeManager tradeManager; - private MoneroWalletRpc wallet; + private MoneroWallet wallet; private Object walletLock = new Object(); private boolean wasWalletSynced = false; private final Map> txCache = new HashMap>(); @@ -158,6 +160,7 @@ public class XmrWalletService { private List cachedSubaddresses; private List cachedTxs; + @SuppressWarnings("unused") @Inject XmrWalletService(User user, Preferences preferences, @@ -166,7 +169,8 @@ public class XmrWalletService { WalletsSetup walletsSetup, XmrAddressEntryList xmrAddressEntryList, @Named(Config.WALLET_DIR) File walletDir, - @Named(Config.WALLET_RPC_BIND_PORT) int rpcBindPort) { + @Named(Config.WALLET_RPC_BIND_PORT) int rpcBindPort, + @Named(Config.USE_NATIVE_XMR_WALLET) boolean useNativeXmrWallet) { this.user = user; this.preferences = preferences; this.accountService = accountService; @@ -175,10 +179,11 @@ public class XmrWalletService { this.xmrAddressEntryList = xmrAddressEntryList; this.walletDir = walletDir; this.rpcBindPort = rpcBindPort; + this.useNativeXmrWallet = useNativeXmrWallet; this.xmrWalletFile = new File(walletDir, MONERO_WALLET_NAME); // set monero logging - MoneroUtils.setLogLevel(MONERO_LOG_LEVEL); + if (MONERO_LOG_LEVEL >= 0) MoneroUtils.setLogLevel(MONERO_LOG_LEVEL); // initialize after account open and basic setup walletsSetup.addSetupTaskHandler(() -> { // TODO: use something better than legacy WalletSetup for notification to initialize @@ -311,23 +316,37 @@ public class XmrWalletService { return new File(path + KEYS_FILE_POSTFIX).exists(); } - public MoneroWalletRpc createWallet(String walletName) { - log.info("{}.createWallet({})", getClass().getSimpleName(), walletName); - if (isShutDownStarted) throw new IllegalStateException("Cannot create wallet because shutting down"); - return createWalletRpc(new MoneroWalletConfig() - .setPath(walletName) - .setPassword(getWalletPassword()), - null); + public MoneroWallet createWallet(String walletName) { + return createWallet(walletName, null); } - public MoneroWalletRpc openWallet(String walletName, boolean applyProxyUri) { + public MoneroWallet createWallet(String walletName, Integer walletRpcPort) { + log.info("{}.createWallet({})", getClass().getSimpleName(), walletName); + if (isShutDownStarted) throw new IllegalStateException("Cannot create wallet because shutting down"); + MoneroWalletConfig config = getWalletConfig(walletName); + return isNativeLibraryApplied() ? createWalletFull(config) : createWalletRpc(config, walletRpcPort); + } + + public MoneroWallet openWallet(String walletName, boolean applyProxyUri) { + return openWallet(walletName, null, applyProxyUri); + } + + public MoneroWallet openWallet(String walletName, Integer walletRpcPort, boolean applyProxyUri) { log.info("{}.openWallet({})", getClass().getSimpleName(), walletName); if (isShutDownStarted) throw new IllegalStateException("Cannot open wallet because shutting down"); - return openWalletRpc(new MoneroWalletConfig() - .setPath(walletName) - .setPassword(getWalletPassword()), - null, - applyProxyUri); + MoneroWalletConfig config = getWalletConfig(walletName); + return isNativeLibraryApplied() ? openWalletFull(config, applyProxyUri) : openWalletRpc(config, walletRpcPort, applyProxyUri); + } + + private MoneroWalletConfig getWalletConfig(String walletName) { + String walletConfigPath = (isNativeLibraryApplied() ? walletDir.getPath() + File.separator : "") + walletName; + MoneroWalletConfig config = new MoneroWalletConfig().setPath(walletConfigPath).setPassword(getWalletPassword()); + if (isNativeLibraryApplied()) config.setNetworkType(getMoneroNetworkType()); + return config; + } + + private boolean isNativeLibraryApplied() { + return useNativeXmrWallet && MoneroUtils.isNativeLibraryLoaded(); } /** @@ -377,7 +396,11 @@ public class XmrWalletService { } public void stopWallet(MoneroWallet wallet, String path, boolean force) { - MONERO_WALLET_RPC_MANAGER.stopInstance((MoneroWalletRpc) wallet, path, force); + + // only wallet rpc process needs stopped + if (wallet instanceof MoneroWalletRpc) { + MONERO_WALLET_RPC_MANAGER.stopInstance((MoneroWalletRpc) wallet, path, force); + } } public void deleteWallet(String walletName) { @@ -805,6 +828,18 @@ public class XmrWalletService { walletInitListener = (obs, oldVal, newVal) -> initMainWalletIfConnected(); xmrConnectionService.downloadPercentageProperty().addListener(walletInitListener); initMainWalletIfConnected(); + + // try to load native monero library + if (useNativeXmrWallet && !MoneroUtils.isNativeLibraryLoaded()) { + try { + MoneroUtils.loadNativeLibrary(); + } catch (Exception | UnsatisfiedLinkError e) { + log.warn("Failed to load Monero native libraries: " + e.getMessage()); + } + } + String appliedMsg = "Monero native libraries applied: " + isNativeLibraryApplied(); + if (useNativeXmrWallet && !isNativeLibraryApplied()) log.warn(appliedMsg); + else log.info(appliedMsg); } private void initMainWalletIfConnected() { @@ -823,6 +858,7 @@ public class XmrWalletService { } private void maybeInitMainWallet(boolean sync, int numAttempts) { + synchronized (walletLock) { if (isShutDownStarted) return; @@ -830,11 +866,10 @@ public class XmrWalletService { if (wallet == null) { MoneroDaemonRpc daemon = xmrConnectionService.getDaemon(); log.info("Initializing main wallet with monerod=" + (daemon == null ? "null" : daemon.getRpcConnection().getUri())); - MoneroWalletConfig walletConfig = new MoneroWalletConfig().setPath(MONERO_WALLET_NAME).setPassword(getWalletPassword()); if (MoneroUtils.walletExists(xmrWalletFile.getPath())) { - wallet = openWalletRpc(walletConfig, rpcBindPort, isProxyApplied(wasWalletSynced)); + wallet = openWallet(MONERO_WALLET_NAME, rpcBindPort, isProxyApplied(wasWalletSynced)); } else if (xmrConnectionService.getConnection() != null && Boolean.TRUE.equals(xmrConnectionService.getConnection().isConnected())) { - wallet = createWalletRpc(walletConfig, rpcBindPort); + wallet = createWallet(MONERO_WALLET_NAME, rpcBindPort); // set wallet creation date to yesterday to guarantee complete restore LocalDateTime localDateTime = LocalDate.now().atStartOfDay().minusDays(1); @@ -845,7 +880,7 @@ public class XmrWalletService { // sync wallet and register listener if (wallet != null && !isShutDownStarted) { - log.info("Monero wallet uri={}, path={}", wallet.getRpcConnection().getUri(), wallet.getPath()); + log.info("Monero wallet path={}", wallet.getPath()); // sync main wallet if applicable if (sync && numAttempts > 0) { @@ -946,6 +981,53 @@ public class XmrWalletService { }); } + private MoneroWalletFull createWalletFull(MoneroWalletConfig config) { + + // must be connected to daemon + MoneroRpcConnection connection = xmrConnectionService.getConnection(); + if (connection == null || !Boolean.TRUE.equals(connection.isConnected())) throw new RuntimeException("Must be connected to daemon before creating wallet"); + + // create wallet + MoneroWalletFull walletFull = null; + try { + + // create wallet + log.info("Creating full wallet " + config.getPath() + " connected to daemon " + connection.getUri()); + long time = System.currentTimeMillis(); + config.setServer(connection); + walletFull = MoneroWalletFull.createWallet(config); + walletFull.getDaemonConnection().setPrintStackTrace(PRINT_STACK_TRACE); + log.info("Done creating full wallet " + config.getPath() + " in " + (System.currentTimeMillis() - time) + " ms"); + return walletFull; + } catch (Exception e) { + e.printStackTrace(); + if (walletFull != null) stopWallet(walletFull, config.getPath()); + throw new IllegalStateException("Could not create wallet '" + config.getPath() + "'"); + } + } + + private MoneroWalletFull openWalletFull(MoneroWalletConfig config, boolean applyProxyUri) { + MoneroWalletFull walletFull = null; + try { + + // configure connection + MoneroRpcConnection connection = new MoneroRpcConnection(xmrConnectionService.getConnection()); + if (!applyProxyUri) connection.setProxyUri(null); + + // open wallet + config.setNetworkType(getMoneroNetworkType()); + config.setServer(connection); + walletFull = MoneroWalletFull.openWallet(config); + if (walletFull.getDaemonConnection() != null) walletFull.getDaemonConnection().setPrintStackTrace(PRINT_STACK_TRACE); + log.info("Done opening full wallet " + config.getPath()); + return walletFull; + } catch (Exception e) { + e.printStackTrace(); + if (walletFull != null) stopWallet(walletFull, config.getPath()); + throw new IllegalStateException("Could not open full wallet '" + config.getPath() + "'"); + } + } + private MoneroWalletRpc createWalletRpc(MoneroWalletConfig config, Integer port) { // must be connected to daemon @@ -964,11 +1046,12 @@ public class XmrWalletService { walletRpc.stopSyncing(); // create wallet - log.info("Creating wallet " + config.getPath() + " connected to daemon " + connection.getUri()); + log.info("Creating RPC wallet " + config.getPath() + " connected to daemon " + connection.getUri()); long time = System.currentTimeMillis(); - walletRpc.createWallet(config.setServer(connection)); + config.setServer(connection); + walletRpc.createWallet(config); walletRpc.getDaemonConnection().setPrintStackTrace(PRINT_STACK_TRACE); - log.info("Done creating wallet " + config.getPath() + " in " + (System.currentTimeMillis() - time) + " ms"); + log.info("Done creating RPC wallet " + config.getPath() + " in " + (System.currentTimeMillis() - time) + " ms"); return walletRpc; } catch (Exception e) { e.printStackTrace(); @@ -993,9 +1076,11 @@ public class XmrWalletService { if (!applyProxyUri) connection.setProxyUri(null); // open wallet - walletRpc.openWallet(config.setServer(connection)); + log.info("Opening RPC wallet " + config.getPath() + " connected to daemon " + connection.getUri()); + config.setServer(connection); + walletRpc.openWallet(config); if (walletRpc.getDaemonConnection() != null) walletRpc.getDaemonConnection().setPrintStackTrace(PRINT_STACK_TRACE); - log.info("Done opening wallet " + config.getPath()); + log.info("Done opening RPC wallet " + config.getPath()); return walletRpc; } catch (Exception e) { e.printStackTrace(); @@ -1060,6 +1145,7 @@ public class XmrWalletService { maybeInitMainWallet(false); } else { wallet.setDaemonConnection(connection); + wallet.setProxyUri(connection.getProxyUri()); } // sync wallet on new thread diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 7ca2973df4..897d39b048 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -886,9 +886,9 @@ - - - + + +