Merge branch 'haveno-dex:master' into federation-test

This commit is contained in:
preland 2024-06-24 13:38:59 -05:00 committed by GitHub
commit 327ce45a4a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 3744 additions and 125 deletions

View File

@ -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,

View File

@ -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.

View File

@ -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);
}
}
}

View File

@ -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);
}

View File

@ -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 {
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
// 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(); // caught internally if still fails
}
// connected to daemon

View File

@ -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());
}
}

View File

@ -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");

View File

@ -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

View File

@ -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,22 +129,25 @@ public class OfferBookService {
}
}
});
});
}
@Override
public void onRemoved(Collection<ProtectedStorageEntry> protectedStorageEntries) {
protectedStorageEntries.forEach(protectedStorageEntry -> {
if (protectedStorageEntry.getProtectedStoragePayload() instanceof OfferPayload) {
OfferPayload offerPayload = (OfferPayload) protectedStorageEntry.getProtectedStoragePayload();
maybeInitializeKeyImagePoller();
keyImagePoller.removeKeyImages(offerPayload.getReserveTxKeyImages());
Offer offer = new Offer(offerPayload);
offer.setPriceFeedService(priceFeedService);
setReservedFundsSpent(offer);
synchronized (offerBookChangedListeners) {
offerBookChangedListeners.forEach(listener -> listener.onRemoved(offer));
UserThread.execute(() -> {
protectedStorageEntries.forEach(protectedStorageEntry -> {
if (protectedStorageEntry.getProtectedStoragePayload() instanceof OfferPayload) {
OfferPayload offerPayload = (OfferPayload) protectedStorageEntry.getProtectedStoragePayload();
maybeInitializeKeyImagePoller();
keyImagePoller.removeKeyImages(offerPayload.getReserveTxKeyImages());
Offer offer = new Offer(offerPayload);
offer.setPriceFeedService(priceFeedService);
setReservedFundsSpent(offer);
synchronized (offerBookChangedListeners) {
offerBookChangedListeners.forEach(listener -> listener.onRemoved(offer));
}
}
}
});
});
}
});
@ -278,9 +282,11 @@ public class OfferBookService {
keyImagePoller.addListener(new XmrKeyImageListener() {
@Override
public void onSpentStatusChanged(Map<String, MoneroKeyImageSpentStatus> spentStatuses) {
for (String keyImage : spentStatuses.keySet()) {
updateAffectedOffers(keyImage);
}
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();
listener.onRemoved(offer);
listener.onAdded(offer);
});
}
}

View File

@ -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() {

View File

@ -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()) {

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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()) {
wallet.importMultisigHex(multisigHexes.toArray(new String[0]));
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
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);
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);
}

View File

@ -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);

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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>

View File

@ -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 *

View File

@ -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()) {
offerBook.fillOfferBookListItems();
updateChartData();
var self = this;
priceFeedService.updateCounterProperty().removeListener(self.currenciesUpdatedListener);
UserThread.execute(() -> {
offerBook.fillOfferBookListItems();
updateChartData();
var self = this;
priceFeedService.updateCounterProperty().removeListener(self.currenciesUpdatedListener);
});
}
};

View File

@ -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));

View File

@ -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,

View File

@ -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);

View File

@ -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"),

View File

@ -102,7 +102,6 @@ class EditOfferViewModel extends MutableOfferViewModel<EditOfferDataModel> {
}
public void onInvalidateMarketPriceMarginPct() {
marketPriceMargin.set("0.00%");
marketPriceMargin.set(FormattingUtils.formatToPercent(dataModel.getMarketPriceMarginPct()));
}

View File

@ -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
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -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).

View File

@ -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">

View 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

View 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"

View File

@ -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;