mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-10-01 01:35:48 -04:00
Merge branch 'haveno-dex:master' into federation-test
This commit is contained in:
commit
327ce45a4a
26
build.gradle
26
build.gradle
@ -49,7 +49,7 @@ configure(subprojects) {
|
||||
gsonVersion = '2.8.5'
|
||||
guavaVersion = '32.1.1-jre'
|
||||
guiceVersion = '7.0.0'
|
||||
moneroJavaVersion = '0.8.29'
|
||||
moneroJavaVersion = '0.8.31'
|
||||
httpclient5Version = '5.0'
|
||||
hamcrestVersion = '2.2'
|
||||
httpclientVersion = '4.5.12'
|
||||
@ -452,14 +452,14 @@ configure(project(':core')) {
|
||||
doLast {
|
||||
// get monero binaries download url
|
||||
Map moneroBinaries = [
|
||||
'linux-x86_64' : 'https://github.com/haveno-dex/monero/releases/download/release2/monero-bins-haveno-linux.tar.gz',
|
||||
'linux-x86_64-sha256' : '3537fe2006997a1065748d27e9513ac3e0c942ab56a97a6e43065ddfd1820394',
|
||||
'linux-aarch64' : 'https://github.com/haveno-dex/monero/releases/download/release2/monero-bins-haveno-linux-aarch64.tar.gz',
|
||||
'linux-aarch64-sha256' : '6ff81c61780fe08defbd6576bd93c6711cf5ad3e79be0e3bc2184ff11cc6a472',
|
||||
'mac' : 'https://github.com/haveno-dex/monero/releases/download/release2/monero-bins-haveno-mac.tar.gz',
|
||||
'mac-sha256' : 'c7cafe1000a5839f02d02ed2edce5b1df3a06b5c77f4d91eaba106d948347730',
|
||||
'windows' : 'https://github.com/haveno-dex/monero/releases/download/release2/monero-bins-haveno-windows.zip',
|
||||
'windows-sha256' : '9b900faefa75f354870646989484978d1fb11add392ffd05eb5abe7e514e395a'
|
||||
'linux-x86_64' : 'https://github.com/haveno-dex/monero/releases/download/release3/monero-bins-haveno-linux-x86_64.tar.gz',
|
||||
'linux-x86_64-sha256' : '591e63c1e3249e0cfbba74f0302022160f64f049d06abff95417ad3ecb588581',
|
||||
'linux-aarch64' : 'https://github.com/haveno-dex/monero/releases/download/release3/monero-bins-haveno-linux-aarch64.tar.gz',
|
||||
'linux-aarch64-sha256' : 'fb0a91d07dbbc30646af8007205dbd11c59fb1d124a3b2d703511d8ee2739acc',
|
||||
'mac' : 'https://github.com/haveno-dex/monero/releases/download/release3/monero-bins-haveno-mac.tar.gz',
|
||||
'mac-sha256' : '9eb01951976767372a3d10180c092af937afe6494928ea73e311476be5c0eba3',
|
||||
'windows' : 'https://github.com/haveno-dex/monero/releases/download/release3/monero-bins-haveno-windows.zip',
|
||||
'windows-sha256' : '49b84fab3a1f69564068fecff105b6079b843d99792409dffca4a66eb279288f'
|
||||
]
|
||||
|
||||
String osKey
|
||||
@ -532,6 +532,7 @@ configure(project(':core')) {
|
||||
|
||||
ext.downloadAndVerifyDependencies = { String archiveURL, String archiveSHA256, File destinationArchiveFile ->
|
||||
ext.dependencyDownloadedAndVerified = false
|
||||
|
||||
// if archive exists, check to see if its already up to date
|
||||
if (destinationArchiveFile.exists()) {
|
||||
println "Verifying existing archive ${destinationArchiveFile}"
|
||||
@ -545,14 +546,15 @@ configure(project(':core')) {
|
||||
}
|
||||
}
|
||||
|
||||
// download archives
|
||||
println "Downloading ${archiveURL}"
|
||||
ant.get(src: archiveURL, dest: destinationArchiveFile)
|
||||
println 'Download saved to ' + destinationArchiveFile
|
||||
|
||||
// verify checksum
|
||||
println 'Verifying checksum for downloaded binary ...'
|
||||
ant.archiveHash = archiveSHA256
|
||||
// use a different verifyProperty name from existing verification or it will always fail
|
||||
ant.checksum(file: destinationArchiveFile, algorithm: 'SHA-256', property: '${archiveHash}', verifyProperty: 'downloadedHashMatches')
|
||||
ant.checksum(file: destinationArchiveFile, algorithm: 'SHA-256', property: '${archiveHash}', verifyProperty: 'downloadedHashMatches') // use a different verifyProperty name from existing verification or it will always fail
|
||||
if (ant.properties['downloadedHashMatches'] != 'true') {
|
||||
ant.fail('Checksum mismatch: Downloaded archive has a different checksum than expected')
|
||||
}
|
||||
@ -603,7 +605,7 @@ configure(project(':desktop')) {
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply from: 'package/package.gradle'
|
||||
|
||||
version = '1.0.7-SNAPSHOT'
|
||||
version = '1.0.8-SNAPSHOT'
|
||||
|
||||
jar.manifest.attributes(
|
||||
"Implementation-Title": project.name,
|
||||
|
@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
public class Version {
|
||||
// The application versions
|
||||
// We use semantic versioning with major, minor and patch
|
||||
public static final String VERSION = "1.0.7";
|
||||
public static final String VERSION = "1.0.8";
|
||||
|
||||
/**
|
||||
* Holds a list of the tagged resource files for optimizing the getData requests.
|
||||
|
@ -24,6 +24,7 @@ import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
@ -38,13 +39,14 @@ public class ZipUtils {
|
||||
* @param dir The directory to create the zip from.
|
||||
* @param out The stream to write to.
|
||||
*/
|
||||
public static void zipDirToStream(File dir, OutputStream out, int bufferSize) throws Exception {
|
||||
public static void zipDirToStream(File dir, OutputStream out, int bufferSize, Collection<File> excludedFiles) throws Exception {
|
||||
|
||||
// Get all files in directory and subdirectories.
|
||||
ArrayList<String> fileList = new ArrayList<>();
|
||||
getFilesRecursive(dir, fileList);
|
||||
List<File> fileList = new ArrayList<>();
|
||||
getFilesRecursive(dir, fileList, excludedFiles);
|
||||
try (ZipOutputStream zos = new ZipOutputStream(out)) {
|
||||
for (String filePath : fileList) {
|
||||
for (File file : fileList) {
|
||||
String filePath = file.getAbsolutePath();
|
||||
log.info("Compressing: " + filePath);
|
||||
|
||||
// Creates a zip entry.
|
||||
@ -73,14 +75,15 @@ public class ZipUtils {
|
||||
/**
|
||||
* Get files list from the directory recursive to the subdirectory.
|
||||
*/
|
||||
public static void getFilesRecursive(File directory, List<String> fileList) {
|
||||
public static void getFilesRecursive(File directory, List<File> fileList, Collection<File> excludedFiles) {
|
||||
File[] files = directory.listFiles();
|
||||
if (files != null && files.length > 0) {
|
||||
for (File file : files) {
|
||||
if (excludedFiles != null && excludedFiles.contains(file)) continue;
|
||||
if (file.isFile()) {
|
||||
fileList.add(file.getAbsolutePath());
|
||||
fileList.add(file);
|
||||
} else {
|
||||
getFilesRecursive(file, fileList);
|
||||
getFilesRecursive(file, fileList, excludedFiles);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,13 @@ import haveno.common.crypto.KeyStorage;
|
||||
import haveno.common.file.FileUtil;
|
||||
import haveno.common.persistence.PersistenceManager;
|
||||
import haveno.common.util.ZipUtils;
|
||||
import haveno.core.xmr.wallet.XmrWalletService;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.PipedInputStream;
|
||||
import java.io.PipedOutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.Getter;
|
||||
@ -139,6 +141,7 @@ public class CoreAccountService {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: share common code with BackupView to backup
|
||||
public void backupAccount(int bufferSize, Consumer<InputStream> consume, Consumer<Exception> error) {
|
||||
if (!accountExists()) throw new IllegalStateException("Cannot backup non existing account");
|
||||
|
||||
@ -149,9 +152,16 @@ public class CoreAccountService {
|
||||
PipedInputStream in = new PipedInputStream(bufferSize); // pipe the serialized account object to stream which will be read by the consumer
|
||||
PipedOutputStream out = new PipedOutputStream(in);
|
||||
log.info("Zipping directory " + dataDir);
|
||||
|
||||
// exclude monero binaries from backup so they're reinstalled with permissions
|
||||
List<File> excludedFiles = Arrays.asList(
|
||||
new File(XmrWalletService.MONERO_WALLET_RPC_PATH),
|
||||
new File(XmrLocalNode.MONEROD_PATH)
|
||||
);
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
ZipUtils.zipDirToStream(dataDir, out, bufferSize);
|
||||
ZipUtils.zipDirToStream(dataDir, out, bufferSize, excludedFiles);
|
||||
} catch (Exception ex) {
|
||||
error.accept(ex);
|
||||
}
|
||||
|
@ -253,12 +253,12 @@ public final class XmrConnectionService {
|
||||
connectionList.setAutoSwitch(autoSwitch);
|
||||
}
|
||||
|
||||
public boolean isConnectionLocal() {
|
||||
return isConnectionLocal(getConnection());
|
||||
public boolean isConnectionLocalHost() {
|
||||
return isConnectionLocalHost(getConnection());
|
||||
}
|
||||
|
||||
public boolean isConnectionTor() {
|
||||
return useTorProxy(getConnection());
|
||||
public boolean isProxyApplied() {
|
||||
return isProxyApplied(getConnection());
|
||||
}
|
||||
|
||||
public long getRefreshPeriodMs() {
|
||||
@ -328,26 +328,26 @@ public final class XmrConnectionService {
|
||||
downloadListener.doneDownload();
|
||||
}
|
||||
|
||||
private boolean isConnectionLocal(MoneroRpcConnection connection) {
|
||||
private boolean isConnectionLocalHost(MoneroRpcConnection connection) {
|
||||
return connection != null && HavenoUtils.isLocalHost(connection.getUri());
|
||||
}
|
||||
|
||||
private long getDefaultRefreshPeriodMs() {
|
||||
MoneroRpcConnection connection = getConnection();
|
||||
if (connection == null) return XmrLocalNode.REFRESH_PERIOD_LOCAL_MS;
|
||||
if (isConnectionLocal(connection)) {
|
||||
if (isConnectionLocalHost(connection)) {
|
||||
if (lastInfo != null && (lastInfo.isBusySyncing() || (lastInfo.getHeightWithoutBootstrap() != null && lastInfo.getHeightWithoutBootstrap() > 0 && lastInfo.getHeightWithoutBootstrap() < lastInfo.getHeight()))) return REFRESH_PERIOD_HTTP_MS; // refresh slower if syncing or bootstrapped
|
||||
else return XmrLocalNode.REFRESH_PERIOD_LOCAL_MS; // TODO: announce faster refresh after done syncing
|
||||
} else if (useTorProxy(connection)) {
|
||||
} else if (isProxyApplied(connection)) {
|
||||
return REFRESH_PERIOD_ONION_MS;
|
||||
} else {
|
||||
return REFRESH_PERIOD_HTTP_MS;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean useTorProxy(MoneroRpcConnection connection) {
|
||||
private boolean isProxyApplied(MoneroRpcConnection connection) {
|
||||
if (connection == null) return false;
|
||||
return connection.isOnion() || (preferences.getUseTorForXmr().isUseTorForXmr() && !HavenoUtils.isLocalHost(connection.getUri()));
|
||||
return connection.isOnion() || (preferences.getUseTorForXmr().isUseTorForXmr() && !HavenoUtils.isPrivateIp(connection.getUri()));
|
||||
}
|
||||
|
||||
private void initialize() {
|
||||
@ -475,7 +475,7 @@ public final class XmrConnectionService {
|
||||
// set connection proxies
|
||||
log.info("TOR proxy URI: " + getProxyUri());
|
||||
for (MoneroRpcConnection connection : connectionManager.getConnections()) {
|
||||
if (useTorProxy(connection)) connection.setProxyUri(getProxyUri());
|
||||
if (isProxyApplied(connection)) connection.setProxyUri(getProxyUri());
|
||||
}
|
||||
|
||||
// restore auto switch
|
||||
@ -495,7 +495,7 @@ public final class XmrConnectionService {
|
||||
// set connection from startup argument if given
|
||||
connectionManager.setAutoSwitch(false);
|
||||
MoneroRpcConnection connection = new MoneroRpcConnection(config.xmrNode, config.xmrNodeUsername, config.xmrNodePassword).setPriority(1);
|
||||
if (useTorProxy(connection)) connection.setProxyUri(getProxyUri());
|
||||
if (isProxyApplied(connection)) connection.setProxyUri(getProxyUri());
|
||||
connectionManager.setConnection(connection);
|
||||
|
||||
// start local node if applicable
|
||||
@ -530,7 +530,7 @@ public final class XmrConnectionService {
|
||||
}
|
||||
|
||||
private void onConnectionChanged(MoneroRpcConnection currentConnection) {
|
||||
if (isShutDownStarted) return;
|
||||
if (isShutDownStarted || !accountService.isAccountOpen()) return;
|
||||
if (currentConnection == null) {
|
||||
log.warn("Setting daemon connection to null");
|
||||
Thread.dumpStack();
|
||||
@ -557,6 +557,7 @@ public final class XmrConnectionService {
|
||||
|
||||
// update polling
|
||||
doPollDaemon();
|
||||
if (currentConnection != getConnection()) return; // polling can change connection
|
||||
UserThread.runAfter(() -> updatePolling(), getRefreshPeriodMs() / 1000);
|
||||
|
||||
// notify listeners in parallel
|
||||
@ -607,13 +608,22 @@ public final class XmrConnectionService {
|
||||
try {
|
||||
lastInfo = daemon.getInfo();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
|
||||
// skip handling if shutting down
|
||||
if (isShutDownStarted) return;
|
||||
|
||||
// fallback to provided nodes if custom connection fails on startup
|
||||
if (lastInfo == null && "".equals(config.xmrNode) && preferences.getMoneroNodesOption() == XmrNodes.MoneroNodesOption.CUSTOM) {
|
||||
log.warn("Failed to fetch daemon info from custom node on startup, falling back to provided nodes: " + e.getMessage());
|
||||
preferences.setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.PROVIDED.ordinal());
|
||||
initializeConnections();
|
||||
return;
|
||||
}
|
||||
|
||||
// switch to best connection
|
||||
log.warn("Failed to fetch daemon info, trying to switch to best connection: " + e.getMessage());
|
||||
switchToBestConnection();
|
||||
lastInfo = daemon.getInfo();
|
||||
} catch (Exception e2) {
|
||||
throw e2; // caught internally
|
||||
}
|
||||
lastInfo = daemon.getInfo(); // caught internally if still fails
|
||||
}
|
||||
|
||||
// connected to daemon
|
||||
|
@ -394,7 +394,7 @@ public class HavenoSetup {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
log.warn("Failed to install Monero binaries: " + e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,9 +268,9 @@ public class WalletAppSetup {
|
||||
|
||||
private String getXmrDaemonNetworkAsString() {
|
||||
String postFix;
|
||||
if (xmrConnectionService.isConnectionLocal())
|
||||
if (xmrConnectionService.isConnectionLocalHost())
|
||||
postFix = " " + Res.get("mainView.footer.localhostMoneroNode");
|
||||
else if (xmrConnectionService.isConnectionTor())
|
||||
else if (xmrConnectionService.isProxyApplied())
|
||||
postFix = " " + Res.get("mainView.footer.usingTor");
|
||||
else
|
||||
postFix = "";
|
||||
@ -279,7 +279,7 @@ public class WalletAppSetup {
|
||||
|
||||
private String getXmrWalletNetworkAsString() {
|
||||
String postFix;
|
||||
if (xmrConnectionService.isConnectionLocal())
|
||||
if (xmrConnectionService.isConnectionLocalHost())
|
||||
postFix = " " + Res.get("mainView.footer.localhostMoneroNode");
|
||||
else if (xmrWalletService.isProxyApplied())
|
||||
postFix = " " + Res.get("mainView.footer.usingTor");
|
||||
|
@ -44,14 +44,14 @@ public class LanguageUtil {
|
||||
"fa", // Persian
|
||||
"it", // Italian
|
||||
"cs", // Czech
|
||||
"pl" // Polish
|
||||
"pl", // Polish
|
||||
"tr" // Turkish
|
||||
/*
|
||||
// not translated yet
|
||||
"el", // Greek
|
||||
"sr-Latn-RS", // Serbian [Latin] (Serbia)
|
||||
"hu", // Hungarian
|
||||
"ro", // Romanian
|
||||
"tr" // Turkish
|
||||
"iw", // Hebrew
|
||||
"hi", // Hindi
|
||||
"ko", // Korean
|
||||
|
@ -115,6 +115,7 @@ public class OfferBookService {
|
||||
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
|
||||
@Override
|
||||
public void onAdded(Collection<ProtectedStorageEntry> protectedStorageEntries) {
|
||||
UserThread.execute(() -> {
|
||||
protectedStorageEntries.forEach(protectedStorageEntry -> {
|
||||
if (protectedStorageEntry.getProtectedStoragePayload() instanceof OfferPayload) {
|
||||
OfferPayload offerPayload = (OfferPayload) protectedStorageEntry.getProtectedStoragePayload();
|
||||
@ -128,10 +129,12 @@ public class OfferBookService {
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(Collection<ProtectedStorageEntry> protectedStorageEntries) {
|
||||
UserThread.execute(() -> {
|
||||
protectedStorageEntries.forEach(protectedStorageEntry -> {
|
||||
if (protectedStorageEntry.getProtectedStoragePayload() instanceof OfferPayload) {
|
||||
OfferPayload offerPayload = (OfferPayload) protectedStorageEntry.getProtectedStoragePayload();
|
||||
@ -145,6 +148,7 @@ public class OfferBookService {
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -278,9 +282,11 @@ public class OfferBookService {
|
||||
keyImagePoller.addListener(new XmrKeyImageListener() {
|
||||
@Override
|
||||
public void onSpentStatusChanged(Map<String, MoneroKeyImageSpentStatus> spentStatuses) {
|
||||
UserThread.execute(() -> {
|
||||
for (String keyImage : spentStatuses.keySet()) {
|
||||
updateAffectedOffers(keyImage);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -293,7 +299,7 @@ public class OfferBookService {
|
||||
}
|
||||
|
||||
private long getKeyImageRefreshPeriodMs() {
|
||||
return xmrConnectionService.isConnectionLocal() ? KEY_IMAGE_REFRESH_PERIOD_MS_LOCAL : KEY_IMAGE_REFRESH_PERIOD_MS_REMOTE;
|
||||
return xmrConnectionService.isConnectionLocalHost() ? KEY_IMAGE_REFRESH_PERIOD_MS_LOCAL : KEY_IMAGE_REFRESH_PERIOD_MS_REMOTE;
|
||||
}
|
||||
|
||||
private void updateAffectedOffers(String keyImage) {
|
||||
@ -301,12 +307,8 @@ public class OfferBookService {
|
||||
if (offer.getOfferPayload().getReserveTxKeyImages().contains(keyImage)) {
|
||||
synchronized (offerBookChangedListeners) {
|
||||
offerBookChangedListeners.forEach(listener -> {
|
||||
|
||||
// notify off thread to avoid deadlocking
|
||||
new Thread(() -> {
|
||||
listener.onRemoved(offer);
|
||||
listener.onAdded(offer);
|
||||
}).start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +288,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
}
|
||||
|
||||
private long getKeyImageRefreshPeriodMs() {
|
||||
return xmrConnectionService.isConnectionLocal() ? KEY_IMAGE_REFRESH_PERIOD_MS_LOCAL : KEY_IMAGE_REFRESH_PERIOD_MS_REMOTE;
|
||||
return xmrConnectionService.isConnectionLocalHost() ? KEY_IMAGE_REFRESH_PERIOD_MS_LOCAL : KEY_IMAGE_REFRESH_PERIOD_MS_REMOTE;
|
||||
}
|
||||
|
||||
public void onAllServicesInitialized() {
|
||||
|
@ -274,15 +274,15 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
||||
}
|
||||
dispute.setDisputeResult(disputeResult);
|
||||
|
||||
// sync and save wallet
|
||||
if (!trade.isPayoutPublished()) trade.syncAndPollWallet();
|
||||
|
||||
// update multisig hex
|
||||
if (disputeClosedMessage.getUpdatedMultisigHex() != null) trade.getArbitrator().setUpdatedMultisigHex(disputeClosedMessage.getUpdatedMultisigHex());
|
||||
if (trade.walletExists()) trade.importMultisigHex();
|
||||
|
||||
// sync and save wallet
|
||||
if (!trade.isPayoutPublished()) trade.syncAndPollWallet();
|
||||
|
||||
// attempt to sign and publish dispute payout tx if given and not already published
|
||||
if (disputeClosedMessage.getUnsignedPayoutTxHex() != null && !trade.isPayoutPublished()) {
|
||||
if (!trade.isPayoutPublished() && disputeClosedMessage.getUnsignedPayoutTxHex() != null) {
|
||||
|
||||
// wait to sign and publish payout tx if defer flag set
|
||||
if (disputeClosedMessage.isDeferPublishPayout()) {
|
||||
|
@ -41,6 +41,14 @@ public class ClosedTradableUtil {
|
||||
public static Map<String, Long> getTotalVolumeByCurrency(List<Tradable> tradableList) {
|
||||
Map<String, Long> map = new HashMap<>();
|
||||
tradableList.stream()
|
||||
.filter(tradable -> {
|
||||
if (tradable instanceof Trade) {
|
||||
Trade trade = castToTrade(tradable);
|
||||
return trade.isCompleted(); // TODO: does not consider if trade was reverted by arbitrator
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.flatMap(tradable -> tradable.getOptionalVolume().stream())
|
||||
.forEach(volume -> {
|
||||
String currencyCode = volume.getCurrencyCode();
|
||||
|
@ -40,6 +40,7 @@ import haveno.core.xmr.wallet.XmrWalletService;
|
||||
import haveno.network.p2p.NodeAddress;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetAddress;
|
||||
import java.net.URI;
|
||||
import java.security.PrivateKey;
|
||||
import java.text.DecimalFormat;
|
||||
@ -418,12 +419,32 @@ public class HavenoUtils {
|
||||
/**
|
||||
* Check if the given URI is on local host.
|
||||
*/
|
||||
public static boolean isLocalHost(String uri) {
|
||||
public static boolean isLocalHost(String uriString) {
|
||||
try {
|
||||
String host = new URI(uri).getHost();
|
||||
String host = new URI(uriString).getHost();
|
||||
return LOOPBACK_HOST.equals(host) || LOCALHOST.equals(host);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given URI is local or a private IP address.
|
||||
*/
|
||||
public static boolean isPrivateIp(String uriString) {
|
||||
if (isLocalHost(uriString)) return true;
|
||||
try {
|
||||
|
||||
// get the host
|
||||
URI uri = new URI(uriString);
|
||||
String host = uri.getHost();
|
||||
|
||||
// check if private IP address
|
||||
if (host == null) return false;
|
||||
InetAddress inetAddress = InetAddress.getByName(host);
|
||||
return inetAddress.isAnyLocalAddress() || inetAddress.isLoopbackAddress() || inetAddress.isSiteLocalAddress();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,7 @@ import javafx.collections.ObservableList;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import monero.common.MoneroError;
|
||||
import monero.common.MoneroRpcConnection;
|
||||
import monero.common.TaskLooper;
|
||||
import monero.daemon.MoneroDaemon;
|
||||
@ -886,6 +887,8 @@ public abstract class Trade implements Tradable, Model {
|
||||
try {
|
||||
doImportMultisigHex();
|
||||
break;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to import multisig hex, attempt={}/{}, tradeId={}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, getShortId(), e.getMessage());
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
@ -909,12 +912,44 @@ public abstract class Trade implements Tradable, Model {
|
||||
log.info("Importing multisig hexes for {} {}, count={}", getClass().getSimpleName(), getShortId(), multisigHexes.size());
|
||||
long startTime = System.currentTimeMillis();
|
||||
if (!multisigHexes.isEmpty()) {
|
||||
try {
|
||||
wallet.importMultisigHex(multisigHexes.toArray(new String[0]));
|
||||
} catch (MoneroError e) {
|
||||
|
||||
// import multisig hex individually if one is invalid
|
||||
if (isInvalidImportError(e.getMessage())) {
|
||||
log.warn("Peer has invalid multisig hex for {} {}, importing individually", getClass().getSimpleName(), getShortId());
|
||||
boolean imported = false;
|
||||
Exception lastError = null;
|
||||
for (TradePeer peer : getOtherPeers()) {
|
||||
if (peer.getUpdatedMultisigHex() == null) continue;
|
||||
try {
|
||||
wallet.importMultisigHex(peer.getUpdatedMultisigHex());
|
||||
imported = true;
|
||||
} catch (MoneroError e2) {
|
||||
lastError = e2;
|
||||
if (isInvalidImportError(e2.getMessage())) {
|
||||
log.warn("{} has invalid multisig hex for {} {}, error={}, multisigHex={}", getPeerRole(peer), getClass().getSimpleName(), getShortId(), e2.getMessage(), peer.getUpdatedMultisigHex());
|
||||
} else {
|
||||
throw e2;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!imported) throw new IllegalArgumentException("Could not import any multisig hexes for " + getClass().getSimpleName() + " " + getShortId(), lastError);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
requestSaveWallet();
|
||||
}
|
||||
log.info("Done importing multisig hexes for {} {} in {} ms, count={}", getClass().getSimpleName(), getShortId(), System.currentTimeMillis() - startTime, multisigHexes.size());
|
||||
}
|
||||
|
||||
// TODO: checking error strings isn't robust, but the library doesn't provide a way to check if multisig hex is invalid. throw IllegalArgumentException from library on invalid multisig hex?
|
||||
private boolean isInvalidImportError(String errMsg) {
|
||||
return errMsg.contains("Failed to parse hex") || errMsg.contains("Multisig info is for a different account");
|
||||
}
|
||||
|
||||
public void changeWalletPassword(String oldPassword, String newPassword) {
|
||||
synchronized (walletLock) {
|
||||
getWallet().changePassword(oldPassword, newPassword);
|
||||
@ -1228,10 +1263,14 @@ public abstract class Trade implements Tradable, Model {
|
||||
if (sign) {
|
||||
|
||||
// sign tx
|
||||
try {
|
||||
MoneroMultisigSignResult result = wallet.signMultisigTxHex(payoutTxHex);
|
||||
if (result.getSignedMultisigTxHex() == null) throw new IllegalArgumentException("Error signing payout tx, signed multisig hex is null");
|
||||
payoutTxHex = result.getSignedMultisigTxHex();
|
||||
setPayoutTxHex(payoutTxHex);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
// describe result
|
||||
describedTxSet = wallet.describeMultisigTxSet(payoutTxHex);
|
||||
@ -1239,6 +1278,7 @@ public abstract class Trade implements Tradable, Model {
|
||||
|
||||
// verify fee is within tolerance by recreating payout tx
|
||||
// TODO (monero-project): creating tx will require exchanging updated multisig hex if message needs reprocessed. provide weight with describe_transfer so fee can be estimated?
|
||||
log.info("Creating fee estimate tx for {} {}", getClass().getSimpleName(), getId());
|
||||
MoneroTxWallet feeEstimateTx = createPayoutTx();
|
||||
BigInteger feeEstimate = feeEstimateTx.getFee();
|
||||
double feeDiff = payoutTx.getFee().subtract(feeEstimate).abs().doubleValue() / feeEstimate.doubleValue(); // TODO: use BigDecimal?
|
||||
@ -2175,7 +2215,7 @@ public abstract class Trade implements Tradable, Model {
|
||||
return tradeAmountTransferred();
|
||||
}
|
||||
|
||||
private boolean tradeAmountTransferred() {
|
||||
public boolean tradeAmountTransferred() {
|
||||
return isPaymentReceived() || (getDisputeResult() != null && getDisputeResult().getWinner() == DisputeResult.Winner.SELLER);
|
||||
}
|
||||
|
||||
|
@ -265,11 +265,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
cssThemeProperty.set(prefPayload.getCssTheme());
|
||||
|
||||
|
||||
// if no valid Bitcoin block explorer is set, select the 1st valid Bitcoin block explorer
|
||||
ArrayList<BlockChainExplorer> btcExplorers = getBlockChainExplorers();
|
||||
// if no valid Monero block explorer is set, select the 1st valid Monero block explorer
|
||||
ArrayList<BlockChainExplorer> xmrExplorers = getBlockChainExplorers();
|
||||
if (getBlockChainExplorer() == null ||
|
||||
getBlockChainExplorer().name.length() == 0) {
|
||||
setBlockChainExplorer(btcExplorers.get(0));
|
||||
setBlockChainExplorer(xmrExplorers.get(0));
|
||||
}
|
||||
tradeCurrenciesAsObservable.addAll(prefPayload.getTraditionalCurrencies());
|
||||
tradeCurrenciesAsObservable.addAll(prefPayload.getCryptoCurrencies());
|
||||
@ -281,8 +281,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
|
||||
if (xmrNodesFromOptions != null && !xmrNodesFromOptions.isEmpty()) {
|
||||
if (getMoneroNodes() != null && !getMoneroNodes().equals(xmrNodesFromOptions)) {
|
||||
log.warn("The Bitcoin node(s) from the program argument and the one(s) persisted in the UI are different. " +
|
||||
"The Bitcoin node(s) {} from the program argument will be used.", xmrNodesFromOptions);
|
||||
log.warn("The Monero node(s) from the program argument and the one(s) persisted in the UI are different. " +
|
||||
"The Monero node(s) {} from the program argument will be used.", xmrNodesFromOptions);
|
||||
}
|
||||
setMoneroNodes(xmrNodesFromOptions);
|
||||
setMoneroNodesOptionOrdinal(XmrNodes.MoneroNodesOption.CUSTOM.ordinal());
|
||||
@ -574,8 +574,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
public void setMoneroNodes(String bitcoinNodes) {
|
||||
prefPayload.setMoneroNodes(bitcoinNodes);
|
||||
public void setMoneroNodes(String moneroNodes) {
|
||||
prefPayload.setMoneroNodes(moneroNodes);
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
@ -665,8 +665,8 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
public void setMoneroNodesOptionOrdinal(int bitcoinNodesOptionOrdinal) {
|
||||
prefPayload.setMoneroNodesOptionOrdinal(bitcoinNodesOptionOrdinal);
|
||||
public void setMoneroNodesOptionOrdinal(int moneroNodesOptionOrdinal) {
|
||||
prefPayload.setMoneroNodesOptionOrdinal(moneroNodesOptionOrdinal);
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
@ -904,7 +904,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
|
||||
void setSortMarketCurrenciesNumerically(boolean sortMarketCurrenciesNumerically);
|
||||
|
||||
void setMoneroNodes(String bitcoinNodes);
|
||||
void setMoneroNodes(String moneroNodes);
|
||||
|
||||
void setUseCustomWithdrawalTxFee(boolean useCustomWithdrawalTxFee);
|
||||
|
||||
@ -938,7 +938,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
|
||||
void setUseTorForXmrOrdinal(int useTorForXmrOrdinal);
|
||||
|
||||
void setMoneroNodesOptionOrdinal(int bitcoinNodesOption);
|
||||
void setMoneroNodesOptionOrdinal(int moneroNodesOption);
|
||||
|
||||
void setReferralId(String referralId);
|
||||
|
||||
|
@ -113,7 +113,7 @@ public class XmrWalletService {
|
||||
|
||||
// monero configuration
|
||||
public static final int NUM_BLOCKS_UNLOCK = 10;
|
||||
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_BINS_DIR = Config.appDataDir().getAbsolutePath();
|
||||
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_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
|
||||
@ -322,7 +322,7 @@ public class XmrWalletService {
|
||||
}
|
||||
|
||||
public boolean isProxyApplied(boolean wasWalletSynced) {
|
||||
return preferences.isProxyApplied(wasWalletSynced);
|
||||
return preferences.isProxyApplied(wasWalletSynced) && xmrConnectionService.isProxyApplied();
|
||||
}
|
||||
|
||||
public String getWalletPassword() {
|
||||
@ -1545,6 +1545,7 @@ public class XmrWalletService {
|
||||
// open wallet
|
||||
config.setNetworkType(getMoneroNetworkType());
|
||||
config.setServer(connection);
|
||||
log.info("Opening full wallet " + config.getPath() + " with monerod=" + connection.getUri());
|
||||
walletFull = MoneroWalletFull.openWallet(config);
|
||||
if (walletFull.getDaemonConnection() != null) walletFull.getDaemonConnection().setPrintStackTrace(PRINT_RPC_STACK_TRACE);
|
||||
log.info("Done opening full wallet " + config.getPath());
|
||||
@ -1604,7 +1605,7 @@ public class XmrWalletService {
|
||||
if (!applyProxyUri) connection.setProxyUri(null);
|
||||
|
||||
// open wallet
|
||||
log.info("Opening RPC wallet " + config.getPath() + " connected to daemon " + connection.getUri());
|
||||
log.info("Opening RPC wallet " + config.getPath() + " with monerod=" + connection.getUri());
|
||||
config.setServer(connection);
|
||||
walletRpc.openWallet(config);
|
||||
if (walletRpc.getDaemonConnection() != null) walletRpc.getDaemonConnection().setPrintStackTrace(PRINT_RPC_STACK_TRACE);
|
||||
|
3345
core/src/main/resources/i18n/displayStrings_tr.properties
Normal file
3345
core/src/main/resources/i18n/displayStrings_tr.properties
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,10 +5,10 @@
|
||||
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.7</string>
|
||||
<string>1.0.8</string>
|
||||
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.7</string>
|
||||
<string>1.0.8</string>
|
||||
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Haveno</string>
|
||||
|
@ -1200,6 +1200,19 @@ textfield */
|
||||
-fx-border-color: -bs-background-gray;
|
||||
}
|
||||
|
||||
.text-area-no-border {
|
||||
-fx-border-color: -fx-control-inner-background;
|
||||
}
|
||||
|
||||
.text-area-no-border .content {
|
||||
-fx-background-color: -fx-control-inner-background;
|
||||
}
|
||||
|
||||
.text-area-no-border:focused {
|
||||
-fx-focus-color: -fx-control-inner-background;
|
||||
-fx-faint-focus-color: -fx-control-inner-background;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Tab pane *
|
||||
|
@ -19,6 +19,8 @@ package haveno.desktop.main.market.offerbook;
|
||||
|
||||
import com.google.common.math.LongMath;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import haveno.common.UserThread;
|
||||
import haveno.core.account.witness.AccountAgeWitnessService;
|
||||
import haveno.core.locale.CurrencyUtil;
|
||||
import haveno.core.locale.GlobalSettings;
|
||||
@ -135,10 +137,12 @@ class OfferBookChartViewModel extends ActivatableViewModel {
|
||||
|
||||
currenciesUpdatedListener = (observable, oldValue, newValue) -> {
|
||||
if (!isAnyPriceAbsent()) {
|
||||
UserThread.execute(() -> {
|
||||
offerBook.fillOfferBookListItems();
|
||||
updateChartData();
|
||||
var self = this;
|
||||
priceFeedService.updateCounterProperty().removeListener(self.currenciesUpdatedListener);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -33,6 +33,7 @@ import haveno.desktop.components.AutoTooltipCheckBox;
|
||||
import haveno.desktop.components.AutoTooltipLabel;
|
||||
import haveno.desktop.components.BusyAnimation;
|
||||
import haveno.desktop.main.MainView;
|
||||
import haveno.desktop.util.CssTheme;
|
||||
import haveno.desktop.util.FormBuilder;
|
||||
import haveno.desktop.util.GUIUtil;
|
||||
import haveno.desktop.util.Layout;
|
||||
@ -41,6 +42,8 @@ import javafx.animation.Interpolator;
|
||||
import javafx.animation.KeyFrame;
|
||||
import javafx.animation.KeyValue;
|
||||
import javafx.animation.Timeline;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
@ -56,6 +59,7 @@ import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
import javafx.scene.control.Hyperlink;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextArea;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.ScrollPane.ScrollBarPolicy;
|
||||
@ -163,7 +167,8 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
protected boolean useAnimation = true;
|
||||
protected boolean showScrollPane = false;
|
||||
|
||||
protected Label headlineIcon, copyIcon, headLineLabel, messageLabel;
|
||||
protected TextArea messageTextArea;
|
||||
protected Label headlineIcon, copyIcon, headLineLabel;
|
||||
protected String headLine, message, closeButtonText, actionButtonText,
|
||||
secondaryActionButtonText, dontShowAgainId, dontShowAgainText,
|
||||
truncatedMessage;
|
||||
@ -847,20 +852,37 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
|
||||
protected void addMessage() {
|
||||
if (message != null) {
|
||||
messageLabel = new AutoTooltipLabel(truncatedMessage);
|
||||
messageLabel.setMouseTransparent(true);
|
||||
messageLabel.setWrapText(true);
|
||||
messageTextArea = new TextArea(truncatedMessage);
|
||||
messageTextArea.setEditable(false);
|
||||
messageTextArea.getStyleClass().add("text-area-no-border");
|
||||
messageTextArea.sceneProperty().addListener((o, oldScene, newScene) -> {
|
||||
if (newScene != null) {
|
||||
// avoid javafx css warning
|
||||
CssTheme.loadSceneStyles(newScene, CssTheme.CSS_THEME_LIGHT, false);
|
||||
messageTextArea.applyCss();
|
||||
var text = messageTextArea.lookup(".text");
|
||||
|
||||
messageTextArea.prefHeightProperty().bind(Bindings.createDoubleBinding(() -> {
|
||||
return messageTextArea.getFont().getSize() + text.getBoundsInLocal().getHeight();
|
||||
}, text.boundsInLocalProperty()));
|
||||
|
||||
text.boundsInLocalProperty().addListener((observableBoundsAfter, boundsBefore, boundsAfter) -> {
|
||||
Platform.runLater(() -> messageTextArea.requestLayout());
|
||||
});
|
||||
}
|
||||
});
|
||||
messageTextArea.setWrapText(true);
|
||||
|
||||
Region messageRegion;
|
||||
if (showScrollPane) {
|
||||
scrollPane = new ScrollPane(messageLabel);
|
||||
scrollPane = new ScrollPane(messageTextArea);
|
||||
scrollPane.setHbarPolicy(ScrollBarPolicy.NEVER);
|
||||
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
|
||||
scrollPane.setFitToWidth(true);
|
||||
|
||||
messageRegion = scrollPane;
|
||||
} else
|
||||
messageRegion = messageLabel;
|
||||
messageRegion = messageTextArea;
|
||||
|
||||
GridPane.setHalignment(messageRegion, HPos.LEFT);
|
||||
GridPane.setHgrow(messageRegion, Priority.ALWAYS);
|
||||
@ -892,7 +914,7 @@ public abstract class Overlay<T extends Overlay<T>> {
|
||||
}
|
||||
|
||||
private void addReportErrorButtons() {
|
||||
messageLabel.setText(Res.get("popup.reportError", truncatedMessage));
|
||||
messageTextArea.setText(Res.get("popup.reportError", truncatedMessage));
|
||||
|
||||
Button logButton = new AutoTooltipButton(Res.get("popup.reportError.log"));
|
||||
GridPane.setMargin(logButton, new Insets(20, 0, 0, 0));
|
||||
|
@ -26,7 +26,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static haveno.desktop.util.FormBuilder.addMultilineLabel;
|
||||
|
||||
public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow> {
|
||||
private static final Logger log = LoggerFactory.getLogger(DisplayAlertMessageWindow.class);
|
||||
@ -41,6 +40,7 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
|
||||
type = Type.Attention;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
width = 768;
|
||||
// need to set headLine, otherwise the fields will not be created in addHeadLine
|
||||
@ -75,7 +75,8 @@ public class DisplayAlertMessageWindow extends Overlay<DisplayAlertMessageWindow
|
||||
|
||||
private void addContent() {
|
||||
checkNotNull(alert, "alertMessage must not be null");
|
||||
addMultilineLabel(gridPane, ++rowIndex, alert.getMessage(), 10);
|
||||
message(alert.getMessage());
|
||||
addMessage();
|
||||
if (alert.isSoftwareUpdateNotification()) {
|
||||
String url = "https://haveno.exchange/downloads";
|
||||
HyperlinkWithIcon hyperlinkWithIcon = FormBuilder.addLabelHyperlinkWithIcon(gridPane, ++rowIndex,
|
||||
|
@ -301,7 +301,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
||||
tooltip = new Tooltip(CountryUtil.getNamesByCodesString(acceptedCountryCodes));
|
||||
}
|
||||
}
|
||||
Label acceptedCountries = addConfirmationLabelLabel(gridPane, ++rowIndex,
|
||||
Label acceptedCountries = addConfirmationLabelLabel(gridPane, true, ++rowIndex,
|
||||
Res.get("shared.acceptedTakerCountries"), countries).second;
|
||||
if (tooltip != null) {
|
||||
acceptedCountries.setMouseTransparent(false);
|
||||
|
@ -89,7 +89,7 @@ public class TacWindow extends Overlay<TacWindow> {
|
||||
protected void addMessage() {
|
||||
super.addMessage();
|
||||
String fontStyleClass = smallScreen ? "small-text" : "normal-text";
|
||||
messageLabel.getStyleClass().add(fontStyleClass);
|
||||
messageTextArea.getStyleClass().add(fontStyleClass);
|
||||
|
||||
// TODO: link to the wiki
|
||||
// HyperlinkWithIcon hyperlinkWithIcon = addHyperlinkWithIcon(gridPane, ++rowIndex, Res.get("tacWindow.arbitrationSystem"),
|
||||
|
@ -102,7 +102,6 @@ class EditOfferViewModel extends MutableOfferViewModel<EditOfferDataModel> {
|
||||
}
|
||||
|
||||
public void onInvalidateMarketPriceMarginPct() {
|
||||
marketPriceMargin.set("0.00%");
|
||||
marketPriceMargin.set(FormattingUtils.formatToPercent(dataModel.getMarketPriceMarginPct()));
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,7 @@ import haveno.desktop.components.TxIdTextField;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.VPos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.CheckBox;
|
||||
@ -379,15 +380,30 @@ public class FormBuilder {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Confirmation Fields
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static Tuple2<Label, Label> addConfirmationLabelLabel(GridPane gridPane,
|
||||
int rowIndex,
|
||||
String title1,
|
||||
String title2,
|
||||
double top) {
|
||||
return addConfirmationLabelLabel(gridPane, false, rowIndex, title1, title2, top);
|
||||
}
|
||||
public static Tuple2<Label, Label> addConfirmationLabelLabel(GridPane gridPane,
|
||||
int rowIndex,
|
||||
String title1,
|
||||
String title2) {
|
||||
return addConfirmationLabelLabel(gridPane, rowIndex, title1, title2, 0);
|
||||
return addConfirmationLabelLabel(gridPane, false, rowIndex, title1, title2, 0);
|
||||
}
|
||||
|
||||
public static Tuple2<Label, Label> addConfirmationLabelLabel(GridPane gridPane,
|
||||
boolean isWrapped,
|
||||
int rowIndex,
|
||||
String title1,
|
||||
String title2) {
|
||||
return addConfirmationLabelLabel(gridPane, isWrapped, rowIndex, title1, title2, 0);
|
||||
}
|
||||
|
||||
public static Tuple2<Label, Label> addConfirmationLabelLabel(GridPane gridPane,
|
||||
boolean isWrapped,
|
||||
int rowIndex,
|
||||
String title1,
|
||||
String title2,
|
||||
@ -396,10 +412,14 @@ public class FormBuilder {
|
||||
label1.getStyleClass().add("confirmation-label");
|
||||
Label label2 = addLabel(gridPane, rowIndex, title2);
|
||||
label2.getStyleClass().add("confirmation-value");
|
||||
label2.setWrapText(isWrapped);
|
||||
GridPane.setColumnIndex(label2, 1);
|
||||
GridPane.setMargin(label1, new Insets(top, 0, 0, 0));
|
||||
GridPane.setHalignment(label1, HPos.LEFT);
|
||||
GridPane.setValignment(label1, VPos.TOP);
|
||||
GridPane.setMargin(label2, new Insets(top, 0, 0, 0));
|
||||
GridPane.setHalignment(label2, HPos.LEFT);
|
||||
GridPane.setValignment(label2, VPos.TOP);
|
||||
|
||||
return new Tuple2<>(label1, label2);
|
||||
}
|
||||
@ -451,12 +471,21 @@ public class FormBuilder {
|
||||
String title1,
|
||||
String title2,
|
||||
double top) {
|
||||
return addConfirmationLabelTextArea(gridPane, false, rowIndex, title1, title2, top);
|
||||
}
|
||||
|
||||
public static Tuple2<Label, TextArea> addConfirmationLabelTextArea(GridPane gridPane,
|
||||
boolean isWrapped,
|
||||
int rowIndex,
|
||||
String title1,
|
||||
String title2,
|
||||
double top) {
|
||||
Label label = addLabel(gridPane, rowIndex, title1);
|
||||
label.getStyleClass().add("confirmation-label");
|
||||
|
||||
TextArea textArea = addTextArea(gridPane, rowIndex, title2);
|
||||
((JFXTextArea) textArea).setLabelFloat(false);
|
||||
|
||||
textArea.setWrapText(isWrapped);
|
||||
GridPane.setColumnIndex(textArea, 1);
|
||||
GridPane.setMargin(label, new Insets(top, 0, 0, 0));
|
||||
GridPane.setHalignment(label, HPos.LEFT);
|
||||
@ -466,6 +495,7 @@ public class FormBuilder {
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Label + TextFieldWithIcon
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -48,6 +48,11 @@ Follow [instructions](https://github.com/haveno-dex/haveno-ts#run-tests) to run
|
||||
|
||||
Based on these instructions: https://github.com/monero-project/monero#cross-compiling
|
||||
|
||||
> Note:
|
||||
> If during building you get the prompt "Reversed (or previously applied) patch detected! Assume -R? [n]" then confirm 'y'.
|
||||
|
||||
**Prepare Linux x86_64**
|
||||
|
||||
1. Install Ubuntu 20.04 on x86_64.
|
||||
2. `sudo apt-get update && sudo apt-get upgrade`
|
||||
3. Install monero dependencies: `sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache`
|
||||
@ -57,15 +62,12 @@ Based on these instructions: https://github.com/monero-project/monero#cross-comp
|
||||
7. `git fetch origin && git reset --hard origin/release-v0.18`
|
||||
8. `git submodule update --init --force`
|
||||
|
||||
> Note:
|
||||
> If you get the prompt "Reversed (or previously applied) patch detected! Assume -R? [n]" then confirm 'y'.
|
||||
|
||||
**Build for Linux**
|
||||
**Build for Linux x86_64**
|
||||
|
||||
1. `make depends target=x86_64-linux-gnu -j<num cores>`
|
||||
2. `cd build/x86_64-linux-gnu/release/bin/`
|
||||
3. `tar -zcvf monero-bins-haveno-linux.tar.gz monerod monero-wallet-rpc`
|
||||
4. Save monero-bins-haveno-linux.tar.gz for release.
|
||||
3. `tar -zcvf monero-bins-haveno-linux-x86_64.tar.gz monerod monero-wallet-rpc`
|
||||
4. Save monero-bins-haveno-linux-x86_64.tar.gz for release.
|
||||
|
||||
**Build for Mac**
|
||||
|
||||
@ -83,6 +85,24 @@ Based on these instructions: https://github.com/monero-project/monero#cross-comp
|
||||
5. `zip monero-bins-haveno-windows.zip monerod.exe monero-wallet-rpc.exe`
|
||||
6. Save monero-bins-haveno-windows.zip for release.
|
||||
|
||||
**Prepare Linux aarch64**
|
||||
|
||||
1. Install Ubuntu 20.04 on aarch64.
|
||||
2. `sudo apt-get update && sudo apt-get upgrade`
|
||||
3. Install monero dependencies: `sudo apt update && sudo apt install build-essential cmake pkg-config libssl-dev libzmq3-dev libsodium-dev libunwind8-dev liblzma-dev libreadline6-dev libpgm-dev qttools5-dev-tools libhidapi-dev libusb-1.0-0-dev libprotobuf-dev protobuf-compiler libudev-dev libboost-chrono-dev libboost-date-time-dev libboost-filesystem-dev libboost-locale-dev libboost-program-options-dev libboost-regex-dev libboost-serialization-dev libboost-system-dev libboost-thread-dev python3 ccache`
|
||||
4. `sudo apt install cmake imagemagick libcap-dev librsvg2-bin libz-dev libbz2-dev libtiff-tools python-dev libtinfo5 autoconf libtool libtool-bin gperf git curl`
|
||||
5. `git clone https://github.com/haveno-dex/monero.git`
|
||||
6. `cd ./monero` (or rename to haveno-monero: `mv monero/ haveno-monero && cd ./haveno-monero`)
|
||||
7. `git fetch origin && git reset --hard origin/release-v0.18`
|
||||
8. `git submodule update --init --force`
|
||||
|
||||
**Build for Linux aarch64**
|
||||
|
||||
1. `make depends target=aarch64-linux-gnu -j<num cores>`
|
||||
2. `cd build/aarch64-linux-gnu/release/bin/`
|
||||
3. `tar -zcvf monero-bins-haveno-linux-aarch64.tar.gz monerod monero-wallet-rpc`
|
||||
4. Save monero-bins-haveno-linux-aarch64.tar.gz for release.
|
||||
|
||||
## Build executable installers for each platform
|
||||
|
||||
See [instructions](/desktop/package/README.md).
|
||||
|
@ -886,9 +886,9 @@
|
||||
<sha256 value="c92e2ca40a3f2474d61e56831aeb379cf8ae3dddeea61b4a828cee2d99f71f38" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.github.woodser" name="monero-java" version="0.8.29">
|
||||
<artifact name="monero-java-0.8.29.jar">
|
||||
<sha256 value="41eba8c84a1b1c99f209628fdc8302b493b629cedadc790774cb215da2f08b4d" origin="Generated by Gradle"/>
|
||||
<component group="io.github.woodser" name="monero-java" version="0.8.31">
|
||||
<artifact name="monero-java-0.8.31.jar">
|
||||
<sha256 value="46b81b98bc76f60a965bc7de429ff72cf6c443858987cbd51b9cacd2f8a8d28b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.grpc" name="grpc-api" version="1.42.1">
|
||||
|
11
scripts/install_tails/README.md
Normal file
11
scripts/install_tails/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
# Steps to use (This has serious security concerns to tails threat model only run when you need to access haveno)
|
||||
|
||||
## 1. Enable persistent storage and admin password before starting tails
|
||||
|
||||
## 2. Get your haveno deb file in persistent storage, currently most people use haveno-reto (amd64 version for tails)
|
||||
|
||||
## 3. Edit the path to the haveno deb file if necessary then run ```sudo ./haveno-install.sh```
|
||||
## 4. As amnesia run ```source ~/.bashrc```
|
||||
## 5. Start haveno using ```haveno-tails```
|
||||
|
||||
## You will need to run this script after each reset, but your data will be saved persistently in /home/amnesia/Persistence/Haveno-reto
|
77
scripts/install_tails/haveno-install.sh
Normal file
77
scripts/install_tails/haveno-install.sh
Normal file
@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
|
||||
#############################################################################
|
||||
# Written by BrandyJson, with heavy inspiration from bisq.wiki tails script #
|
||||
#############################################################################
|
||||
echo "Installing dpkg from persistent, (1.07-1, if this is out of date change the deb path in the script or manually install after running"
|
||||
dpkg -i "/home/amnesia/Persistent/haveno_1.0.7-1_amd64.deb"
|
||||
echo -e "Allowing amnesia to read tor control port cookie, only run this script when you actually want to use haveno\n\n!!! not secure !!!\n"
|
||||
chmod o+r /var/run/tor/control.authcookie
|
||||
echo "Updating apparmor-profile"
|
||||
echo "---
|
||||
- apparmor-profiles:
|
||||
- '/opt/haveno/bin/Haveno'
|
||||
users:
|
||||
- 'amnesia'
|
||||
commands:
|
||||
AUTHCHALLENGE:
|
||||
- 'SAFECOOKIE .*'
|
||||
SETEVENTS:
|
||||
- 'CIRC ORCONN INFO NOTICE WARN ERR HS_DESC HS_DESC_CONTENT'
|
||||
GETINFO:
|
||||
- pattern: 'status/bootstrap-phase'
|
||||
response:
|
||||
- pattern: '250-status/bootstrap-phase=*'
|
||||
replacement: '250-status/bootstrap-phase=NOTICE BOOTSTRAP PROGRESS=100 TAG=done SUMMARY="Done"'
|
||||
- 'net/listeners/socks'
|
||||
ADD_ONION:
|
||||
- pattern: 'NEW:(\S+) Port=9999,(\S+)'
|
||||
replacement: 'NEW:{} Port=9999,{client-address}:{}'
|
||||
- pattern: '(\S+):(\S+) Port=9999,(\S+)'
|
||||
replacement: '{}:{} Port=9999,{client-address}:{}'
|
||||
DEL_ONION:
|
||||
- '.+'
|
||||
HSFETCH:
|
||||
- '.+'
|
||||
events:
|
||||
CIRC:
|
||||
suppress: true
|
||||
ORCONN:
|
||||
suppress: true
|
||||
INFO:
|
||||
suppress: true
|
||||
NOTICE:
|
||||
suppress: true
|
||||
WARN:
|
||||
suppress: true
|
||||
ERR:
|
||||
suppress: true
|
||||
HS_DESC:
|
||||
response:
|
||||
- pattern: '650 HS_DESC CREATED (\S+) (\S+) (\S+) \S+ (.+)'
|
||||
replacement: '650 HS_DESC CREATED {} {} {} redacted {}'
|
||||
- pattern: '650 HS_DESC UPLOAD (\S+) (\S+) .*'
|
||||
replacement: '650 HS_DESC UPLOAD {} {} redacted redacted'
|
||||
- pattern: '650 HS_DESC UPLOADED (\S+) (\S+) .+'
|
||||
replacement: '650 HS_DESC UPLOADED {} {} redacted'
|
||||
- pattern: '650 HS_DESC REQUESTED (\S+) NO_AUTH'
|
||||
replacement: '650 HS_DESC REQUESTED {} NO_AUTH'
|
||||
- pattern: '650 HS_DESC REQUESTED (\S+) NO_AUTH \S+ \S+'
|
||||
replacement: '650 HS_DESC REQUESTED {} NO_AUTH redacted redacted'
|
||||
- pattern: '650 HS_DESC RECEIVED (\S+) NO_AUTH \S+ \S+'
|
||||
replacement: '650 HS_DESC RECEIVED {} NO_AUTH redacted redacted'
|
||||
- pattern: '.*'
|
||||
replacement: ''
|
||||
HS_DESC_CONTENT:
|
||||
suppress: true" > /etc/onion-grater.d/haveno.yml
|
||||
echo "Adding rule to iptables to allow for monero-wallet-rpc to work"
|
||||
iptables -I OUTPUT 2 -p tcp -d 127.0.0.1 -m tcp --dport 18081 -m owner --uid-owner 1855 -j ACCEPT
|
||||
echo "Updating torsocks to allow for inbound connection"
|
||||
sed -i 's/#AllowInbound/AllowInbound/g' /etc/tor/torsocks.conf
|
||||
|
||||
echo "Restarting onion-grater service"
|
||||
|
||||
systemctl restart onion-grater.service
|
||||
|
||||
echo "alias haveno-tails='torsocks /opt/haveno/bin/Haveno --torControlPort 951 --torControlCookieFile=/var/run/tor/control.authcookie --torControlUseSafeCookieAuth --useTorForXmr=ON --userDataDir=/home/amnesia/Persistent/'" >> /home/amnesia/.bashrc
|
||||
echo -e "Everything is set up just run\n\nsource ~/.bashrc\n\nThen you can start haveno using haveno-tails"
|
@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
@Slf4j
|
||||
public class SeedNodeMain extends ExecutableForAppWithP2p {
|
||||
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
|
||||
private static final String VERSION = "1.0.7";
|
||||
private static final String VERSION = "1.0.8";
|
||||
private SeedNode seedNode;
|
||||
private Timer checkConnectionLossTime;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user