Compare commits

..

9 Commits

Author SHA1 Message Date
woodser
5c36b00d07 instruct to checkout v0.0.19 2024-03-16 09:44:12 -04:00
woodser
9c0af85ed8 update monero binaries to v0.18.3.3 2024-03-15 17:08:49 -04:00
woodser
216260da85 cancel offer waits for result 2024-03-15 11:17:29 -04:00
woodser
4a57b26469 get txs after sync to update from pool 2024-03-15 11:17:29 -04:00
woodser
8d7bb250c5 get updated balances from Balances.java 2024-03-15 11:17:29 -04:00
napoly
82eb081089
Create task to generate key pairs (#823) 2024-03-14 12:12:42 -04:00
woodser
697828d773 update to monero-java v0.8.11 2024-03-13 10:29:55 -04:00
woodser
5ad099a33c
update litecoin address validation (#815)
Co-authored-by: OrginalS <36825453+OrginalS@users.noreply.github.com>
2024-03-12 21:12:39 -04:00
napoly
77d3879013
Update script to install Haveno seed node as a system service (#758)
Co-authored-by: alkum <98610826+alkum@users.noreply.github.com>
Co-authored-by: Christoph Atteneder <christoph.atteneder@gmail.com>
Co-authored-by: Stephan Oeste <emzy@emzy.de>
Co-authored-by: Alva Swanson <alvasw@protonmail.com>
2024-03-12 10:46:32 -04:00
28 changed files with 217 additions and 208 deletions

View File

@ -17,19 +17,20 @@
package haveno.asset.coins;
import haveno.asset.Base58AddressValidator;
import haveno.asset.BitcoinAddressValidator;
import haveno.asset.Coin;
import haveno.asset.NetworkParametersAdapter;
public class Litecoin extends Coin {
public Litecoin() {
super("Litecoin", "LTC", new Base58AddressValidator(new LitecoinMainNetParams()), Network.MAINNET);
super("Litecoin", "LTC", new BitcoinAddressValidator(new LitecoinMainNetParams()), Network.MAINNET);
}
public static class LitecoinMainNetParams extends NetworkParametersAdapter {
public LitecoinMainNetParams() {
this.addressHeader = 48;
this.p2shHeader = 5;
this.p2shHeader = 50;
this.segwitAddressHrp = "ltc";
}
}
}
}

View File

@ -31,6 +31,17 @@ public class LitecoinTest extends AbstractAssetTest {
assertValidAddress("Lg3PX8wRWmApFCoCMAsPF5P9dPHYQHEWKW");
assertValidAddress("LTuoeY6RBHV3n3cfhXVVTbJbxzxnXs9ofm");
assertValidAddress("LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW");
assertValidAddress("M8T1B2Z97gVdvmfkQcAtYbEepune1tzGua");
assertValidAddress("ltc1qr07zu594qf63xm7l7x6pu3a2v39m2z6hh5pp4t");
assertValidAddress("ltc1qzvcgmntglcuv4smv3lzj6k8szcvsrmvk0phrr9wfq8w493r096ssm2fgsw");
assertValidAddress("MESruSiB2uC9i7tMU6VMUVom91ohM7Rnbd");
assertValidAddress("ltc1q2a0laq2jg2gntzhfs43qptajd325kkx7hrq9cs");
assertValidAddress("ltc1qd6d54mt8xxcg0xg3l0vh6fymdfvd2tv0vnwyrv");
assertValidAddress("ltc1gmay6ht028aurcm680f8e8wxdup07y2tq46f6z2d4v8rutewqmmcqk29jtm");
assertValidAddress("MTf4tP1TCNBn8dNkyxeBVoPrFCcVzxJvvh");
assertValidAddress("LaRoRBC6utQtY3U2FbHwhmhhDPyxodDeKA");
assertValidAddress("MDMFP9Dx84tyaxiYksjvkG1jymBdqCuHGA");
//assertValidAddress("3MSvaVbVFFLML86rt5eqgA9SvW23upaXdY"); // deprecated
}
@Test
@ -38,5 +49,14 @@ public class LitecoinTest extends AbstractAssetTest {
assertInvalidAddress("1LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW");
assertInvalidAddress("LgfapHEPhZbdRF9pMd5WPT35hFXcZS1USrW");
assertInvalidAddress("LgfapHEPhZbRF9pMd5WPT35hFXcZS1USrW#");
assertInvalidAddress("3MSvaVbVFFLML86rt5eqgl9SvW23upaXdY"); // contains lowercase l
assertInvalidAddress("LURw7hYhREXjWHyiXhQNsKInWtPezwNe98"); // contains uppercase I
assertInvalidAddress("LM4ch8ZtAowdiGLSnf92MrMOC9dVmve2hr"); // contains uppercase O
assertInvalidAddress("MArsfeyS7P0HzsqLpAFGC9pFdhuqHgdL2R"); // contains number 0
assertInvalidAddress("ltc1qr6quwn3v2gxpadd0cu040r9385gayk5vdcyl5"); // too short
assertInvalidAddress("ltc1q5det08ke2gpet06wczcdfs2v3hgfqllxw28uln8vxxx82qlue6uswceljma"); // too long
assertInvalidAddress("MADpfTtabZ6pDjms4pMd3ZmnrgyhTCo4N8?time=1708476729&exp=86400"); // additional information
assertInvalidAddress("ltc1q8tk47lvgqu55h4pfast39r3t9360gmll5z9m6z?time=1708476604&exp=600"); // additional information
assertInvalidAddress("ltc1q026xyextkwhmveh7rpf6v6mp5p88vwc25aynxr?time=1708476626"); // additional information
}
}

View File

@ -49,7 +49,7 @@ configure(subprojects) {
gsonVersion = '2.8.5'
guavaVersion = '32.1.1-jre'
guiceVersion = '7.0.0'
moneroJavaVersion = '0.8.10'
moneroJavaVersion = '0.8.11'
httpclient5Version = '5.0'
hamcrestVersion = '2.2'
httpclientVersion = '4.5.12'
@ -436,16 +436,21 @@ configure(project(':core')) {
systemProperty 'jdk.attach.allowAttachSelf', true
}
task generateKeypairs(type: JavaExec) {
mainClass = 'haveno.core.util.GenerateKeyPairs'
classpath = sourceSets.main.runtimeClasspath
}
task havenoDeps {
doLast {
// get monero binaries download url
Map moneroBinaries = [
'linux' : 'https://github.com/haveno-dex/monero/releases/download/release1/monero-bins-haveno-linux.tar.gz',
'linux-sha256' : '4020274ef546410f218c3c3a3c2a8c2c2cda3f653f8cc6fe8e3cd74334500489',
'mac' : 'https://github.com/haveno-dex/monero/releases/download/release1/monero-bins-haveno-mac.tar.gz',
'mac-sha256' : '72514caac499c4900c5cb6e957e5e3aaabba48968ae798c419f4559a51e5fc79',
'windows' : 'https://github.com/haveno-dex/monero/releases/download/release1/monero-bins-haveno-windows.zip',
'windows-sha256': '27d8315d5da876e57fd12ed1c1d60a622763de99f0cc2521ce7f31c7fa4ab0ee'
'linux' : 'https://github.com/haveno-dex/monero/releases/download/release2/monero-bins-haveno-linux.tar.gz',
'linux-sha256' : '3537fe2006997a1065748d27e9513ac3e0c942ab56a97a6e43065ddfd1820394',
'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'
]
String osKey

View File

@ -22,7 +22,6 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@ -41,10 +40,10 @@ public class ThreadUtils {
* @param command the command to execute
* @param threadId the thread id
*/
public static void execute(Runnable command, String threadId) {
public static Future<?> execute(Runnable command, String threadId) {
synchronized (EXECUTORS) {
if (!EXECUTORS.containsKey(threadId)) EXECUTORS.put(threadId, Executors.newFixedThreadPool(1));
EXECUTORS.get(threadId).execute(() -> {
return EXECUTORS.get(threadId).submit(() -> {
synchronized (THREADS) {
THREADS.put(threadId, Thread.currentThread());
}
@ -60,24 +59,10 @@ public class ThreadUtils {
* @param threadId the thread id
*/
public static void await(Runnable command, String threadId) {
if (isCurrentThread(Thread.currentThread(), threadId)) {
command.run();
} else {
CountDownLatch latch = new CountDownLatch(1);
execute(() -> {
try {
command.run();
} catch (Exception e) {
throw e;
} finally {
latch.countDown();
}
}, threadId);
try {
latch.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
execute(command, threadId).get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

View File

@ -19,16 +19,6 @@ package haveno.common.crypto;
import haveno.common.util.Hex;
import haveno.common.util.Utilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
@ -43,10 +33,17 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Encryption {
private static final Logger log = LoggerFactory.getLogger(Encryption.class);
public static final String ASYM_KEY_ALGO = "RSA";
private static final String ASYM_CIPHER = "RSA/ECB/OAEPWithSHA-256AndMGF1PADDING";

View File

@ -95,7 +95,7 @@ public class AlertManager {
}
pubKeyAsHex = useDevPrivilegeKeys ?
DevEnv.DEV_PRIVILEGE_PUB_KEY :
"036d8a1dfcb406886037d2381da006358722823e1940acc2598c844bbc0fd1026f"; // TODO: replace with mainnet pub key for alerts
"036d8a1dfcb406886037d2381da006358722823e1940acc2598c844bbc0fd1026f";
}

View File

@ -95,7 +95,7 @@ public class PrivateNotificationManager implements MessageListener {
}
pubKeyAsHex = useDevPrivilegeKeys ?
DevEnv.DEV_PRIVILEGE_PUB_KEY :
"02ba7c5de295adfe57b60029f3637a2c6b1d0e969a8aaefb9e0ddc3a7963f26925"; // TODO: replace with mainnet dev pub key for private messages
"02ba7c5de295adfe57b60029f3637a2c6b1d0e969a8aaefb9e0ddc3a7963f26925";
}
private void handleMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, NodeAddress senderNodeAddress) {

View File

@ -458,8 +458,8 @@ public class CoreApi {
paymentAccount);
}
public void cancelOffer(String id) {
coreOffersService.cancelOffer(id);
public void cancelOffer(String id, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
coreOffersService.cancelOffer(id, resultHandler, errorMessageHandler);
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -38,6 +38,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
import haveno.common.crypto.KeyRing;
import haveno.common.handlers.ErrorMessageHandler;
import haveno.common.handlers.ResultHandler;
import static haveno.common.util.MathUtils.exactMultiply;
import static haveno.common.util.MathUtils.roundDoubleToLong;
import static haveno.common.util.MathUtils.scaleUpByPowerOf10;
@ -236,14 +237,9 @@ public class CoreOffersService {
paymentAccount);
}
void cancelOffer(String id) {
void cancelOffer(String id, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
Offer offer = getMyOffer(id).getOffer();
openOfferManager.removeOffer(offer,
() -> {
},
errorMessage -> {
throw new IllegalStateException(errorMessage);
});
openOfferManager.removeOffer(offer, resultHandler, errorMessageHandler);
}
// -------------------------- PRIVATE HELPERS -----------------------------

View File

@ -133,7 +133,6 @@ class CoreWalletsService {
verifyWalletCurrencyCodeIsValid(currencyCode);
verifyWalletsAreAvailable();
verifyEncryptedWalletIsUnlocked();
if (balances.getAvailableBalance().get() == null) throw new IllegalStateException("balance is not yet available");
switch (currencyCode.trim().toUpperCase()) {
case "":
@ -418,28 +417,8 @@ class CoreWalletsService {
private XmrBalanceInfo getXmrBalances() {
verifyWalletsAreAvailable();
verifyEncryptedWalletIsUnlocked();
var availableBalance = balances.getAvailableBalance().get();
if (availableBalance == null)
throw new IllegalStateException("available balance is not yet available");
var pendingBalance = balances.getPendingBalance().get();
if (pendingBalance == null)
throw new IllegalStateException("locked balance is not yet available");
var reservedOfferBalance = balances.getReservedOfferBalance().get();
if (reservedOfferBalance == null)
throw new IllegalStateException("reserved offer balance is not yet available");
var reservedTradeBalance = balances.getReservedTradeBalance().get();
if (reservedTradeBalance == null)
throw new IllegalStateException("reserved trade balance is not yet available");
return new XmrBalanceInfo(availableBalance.longValue() + pendingBalance.longValue(),
availableBalance.longValue(),
pendingBalance.longValue(),
reservedOfferBalance.longValue(),
reservedTradeBalance.longValue());
if (balances.getAvailableBalance() == null) throw new IllegalStateException("Balances are not yet available");
return balances.getBalances();
}
// Returns a Coin for the transfer amount string, or a RuntimeException if invalid.

View File

@ -1,10 +1,10 @@
package haveno.core.api.model;
import java.math.BigInteger;
import com.google.common.annotations.VisibleForTesting;
import haveno.common.Payload;
import lombok.Getter;
@Getter
public class XmrBalanceInfo implements Payload {
public static final XmrBalanceInfo EMPTY = new XmrBalanceInfo(-1,
@ -19,17 +19,19 @@ public class XmrBalanceInfo implements Payload {
private final long pendingBalance;
private final long reservedOfferBalance;
private final long reservedTradeBalance;
private final long reservedBalance;
public XmrBalanceInfo(long balance,
long unlockedBalance,
long lockedBalance,
long pendingBalance,
long reservedOfferBalance,
long reservedTradeBalance) {
this.balance = balance;
this.availableBalance = unlockedBalance;
this.pendingBalance = lockedBalance;
this.pendingBalance = pendingBalance;
this.reservedOfferBalance = reservedOfferBalance;
this.reservedTradeBalance = reservedTradeBalance;
this.reservedBalance = reservedOfferBalance + reservedTradeBalance;
}
@VisibleForTesting
@ -45,6 +47,30 @@ public class XmrBalanceInfo implements Payload {
reservedTradeBalance);
}
public BigInteger getBalance() {
return BigInteger.valueOf(balance);
}
public BigInteger getAvailableBalance() {
return BigInteger.valueOf(availableBalance);
}
public BigInteger getPendingBalance() {
return BigInteger.valueOf(pendingBalance);
}
public BigInteger getReservedOfferBalance() {
return BigInteger.valueOf(reservedOfferBalance);
}
public BigInteger getReservedTradeBalance() {
return BigInteger.valueOf(reservedTradeBalance);
}
public BigInteger getReservedBalance() {
return BigInteger.valueOf(reservedBalance);
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -116,7 +116,7 @@ public class FilterManager {
publicKeys = useDevPrivilegeKeys ?
Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) :
List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27", // TODO: replace with mainnet dev pub keys
List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27",
"029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f",
"034dc7530bf66ffd9580aa98031ea9a18ac2d269f7c56c0e71eca06105b9ed69f9");

View File

@ -620,9 +620,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
if (!offersToBeEdited.containsKey(openOffer.getId())) {
if (openOffer.isDeactivated()) {
onCancelled(openOffer);
resultHandler.handleResult();
} else {
offerBookService.removeOffer(openOffer.getOffer().getOfferPayload(),
() -> onCancelled(openOffer),
() -> {
onCancelled(openOffer);
resultHandler.handleResult();
},
errorMessageHandler);
}
} else {

View File

@ -19,6 +19,7 @@ package haveno.core.presentation;
import com.google.inject.Inject;
import haveno.common.UserThread;
import haveno.core.api.model.XmrBalanceInfo;
import haveno.core.trade.HavenoUtils;
import haveno.core.xmr.Balances;
import javafx.beans.property.SimpleStringProperty;
@ -38,14 +39,13 @@ public class BalancePresentation {
@Inject
public BalancePresentation(Balances balances) {
balances.getAvailableBalance().addListener((observable, oldValue, newValue) -> {
UserThread.execute(() -> availableBalance.set(HavenoUtils.formatXmr(newValue, true)));
});
balances.getPendingBalance().addListener((observable, oldValue, newValue) -> {
UserThread.execute(() -> pendingBalance.set(HavenoUtils.formatXmr(newValue, true)));
});
balances.getReservedBalance().addListener((observable, oldValue, newValue) -> {
UserThread.execute(() -> reservedBalance.set(HavenoUtils.formatXmr(newValue, true)));
balances.getUpdateCounter().addListener((observable, oldValue, newValue) -> {
XmrBalanceInfo info = balances.getBalances();
UserThread.execute(() -> {
availableBalance.set(HavenoUtils.formatXmr(info.getAvailableBalance(), true));
pendingBalance.set(HavenoUtils.formatXmr(info.getPendingBalance(), true));
reservedBalance.set(HavenoUtils.formatXmr(info.getReservedBalance(), true));
});
});
}
}

View File

@ -79,7 +79,7 @@ public class ArbitratorManager extends DisputeAgentManager<Arbitrator> {
"02a1a458df5acf4ab08fdca748e28f33a955a30854c8c1a831ee733dca7f0d2fcd",
"0374dd70f3fa6e47ec5ab97932e1cec6233e98e6ae3129036b17118650c44fd3de");
case XMR_MAINNET:
return new ArrayList<String>(); // TODO: add mainnet arbitrator pubkeys
return new ArrayList<String>();
default:
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
}

View File

@ -63,7 +63,6 @@ import org.bitcoinj.core.Coin;
public class HavenoUtils {
// configurable
// TODO: adjust for mainnet release
private static final String RELEASE_DATE = "01-03-2024 00:00:00"; // optionally set to release date of the network in format dd-mm-yyyy to impose temporary limits, etc. e.g. "01-03-2024 00:00:00"
public static final int RELEASE_LIMIT_DAYS = 60; // number of days to limit sell offers to max buy limit for new accounts
public static final int WARN_ON_OFFER_EXCEEDS_UNSIGNED_BUY_LIMIT_DAYS = 182; // number of days to warn if sell offer exceeds unsigned buy limit
@ -454,7 +453,7 @@ public class HavenoUtils {
case XMR_STAGENET:
return "5B11hTJdG2XDNwjdKGLRxwSLwDhkbGg7C7UEAZBxjE6FbCeRMjudrpNACmDNtWPiSnNfjDQf39QRjdtdgoL69txv81qc2Mc";
case XMR_MAINNET:
throw new RuntimeException("Mainnet fee address not implemented"); // TODO: replace with mainnet trade fee address
throw new RuntimeException("Mainnet fee address not implemented");
default:
throw new RuntimeException("Unhandled base currency network: " + Config.baseCurrencyNetwork());
}

View File

@ -12,29 +12,29 @@ import org.bitcoinj.core.Utils;
import haveno.common.crypto.Encryption;
/**
* This utility generates and prints public/private keypairs
* This utility generates and prints public/private key-pairs
* which can be used to register arbitrators on the network.
*/
public class GenerateKeypairs {
public class GenerateKeyPairs {
public static void main(String[] args) {
// generate public/private keypairs
List<SecretKey> secretKeys = new ArrayList<SecretKey>();
// generate public/private key-pairs
List<SecretKey> secretKeys = new ArrayList<>();
for (int i = 0; i < 20; i++) {
secretKeys.add(Encryption.generateSecretKey(256));
}
// print keypairs
// print key-pairs
System.out.println("Private keys:");
for (SecretKey sk : secretKeys) {
String privKey = Utils.HEX.encode(sk.getEncoded());
System.out.println(privKey);
String privateKey = Utils.HEX.encode(sk.getEncoded());
System.out.println(privateKey);
}
System.out.println("Corresponding public keys:");
for (SecretKey sk : secretKeys) {
String privKey = Utils.HEX.encode(sk.getEncoded());
ECKey ecKey = ECKey.fromPrivate(new BigInteger(1, Utils.HEX.decode(privKey)));
String privateKey = Utils.HEX.encode(sk.getEncoded());
ECKey ecKey = ECKey.fromPrivate(new BigInteger(1, Utils.HEX.decode(privateKey)));
String pubKey = Utils.HEX.encode(ecKey.getPubKey());
System.out.println(pubKey);
}

View File

@ -37,6 +37,7 @@ package haveno.core.xmr;
import com.google.inject.Inject;
import haveno.common.ThreadUtils;
import haveno.common.UserThread;
import haveno.core.api.model.XmrBalanceInfo;
import haveno.core.offer.OpenOffer;
import haveno.core.offer.OpenOfferManager;
import haveno.core.support.dispute.Dispute;
@ -51,8 +52,8 @@ import haveno.core.xmr.wallet.XmrWalletService;
import java.math.BigInteger;
import java.util.List;
import java.util.stream.Collectors;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.ListChangeListener;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@ -67,15 +68,18 @@ public class Balances {
private final RefundManager refundManager;
@Getter
private final ObjectProperty<BigInteger> availableBalance = new SimpleObjectProperty<>();
private BigInteger availableBalance;
@Getter
private final ObjectProperty<BigInteger> pendingBalance = new SimpleObjectProperty<>();
private BigInteger pendingBalance;
@Getter
private final ObjectProperty<BigInteger> reservedOfferBalance = new SimpleObjectProperty<>();
private BigInteger reservedOfferBalance;
@Getter
private final ObjectProperty<BigInteger> reservedTradeBalance = new SimpleObjectProperty<>();
private BigInteger reservedTradeBalance;
@Getter
private final ObjectProperty<BigInteger> reservedBalance = new SimpleObjectProperty<>(); // TODO (woodser): this balance is sum of reserved funds for offers and trade multisigs; remove?
private BigInteger reservedBalance; // TODO (woodser): this balance is sum of reserved funds for offers and trade multisigs; remove?
@Getter
private final IntegerProperty updateCounter = new SimpleIntegerProperty(0);
@Inject
public Balances(TradeManager tradeManager,
@ -103,52 +107,57 @@ public class Balances {
updateBalances();
}
public XmrBalanceInfo getBalances() {
synchronized (this) {
return new XmrBalanceInfo(availableBalance.longValue() + pendingBalance.longValue(),
availableBalance.longValue(),
pendingBalance.longValue(),
reservedOfferBalance.longValue(),
reservedTradeBalance.longValue());
}
}
private void updateBalances() {
ThreadUtils.submitToPool(() -> doUpdateBalances());
}
private void doUpdateBalances() {
synchronized (this) {
// get wallet balances
BigInteger balance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getBalance();
availableBalance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getAvailableBalance();
// get wallet balances
BigInteger balance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getWallet().getBalance(0);
BigInteger unlockedBalance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getWallet().getUnlockedBalance(0);
// calculate pending balance by adding frozen trade balances - reserved amounts
pendingBalance = balance.subtract(availableBalance);
List<Trade> trades = tradeManager.getTradesStreamWithFundsLockedIn().collect(Collectors.toList());
for (Trade trade : trades) {
if (trade.getFrozenAmount().equals(new BigInteger("0"))) continue;
BigInteger tradeFee = trade instanceof MakerTrade ? trade.getMakerFee() : trade.getTakerFee();
pendingBalance = pendingBalance.add(trade.getFrozenAmount()).subtract(trade.getReservedAmount()).subtract(tradeFee).subtract(trade.getSelf().getDepositTxFee());
}
// calculate pending balance by adding frozen trade balances - reserved amounts
BigInteger pendingBalance = balance.subtract(unlockedBalance);
List<Trade> trades = tradeManager.getTradesStreamWithFundsLockedIn().collect(Collectors.toList());
for (Trade trade : trades) {
if (trade.getFrozenAmount().equals(new BigInteger("0"))) continue;
BigInteger tradeFee = trade instanceof MakerTrade ? trade.getMakerFee() : trade.getTakerFee();
pendingBalance = pendingBalance.add(trade.getFrozenAmount()).subtract(trade.getReservedAmount()).subtract(tradeFee).subtract(trade.getSelf().getDepositTxFee());
// calculate reserved offer balance
reservedOfferBalance = BigInteger.ZERO;
if (xmrWalletService.getWallet() != null) {
List<MoneroOutputWallet> frozenOutputs = xmrWalletService.getWallet().getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false));
for (MoneroOutputWallet frozenOutput : frozenOutputs) reservedOfferBalance = reservedOfferBalance.add(frozenOutput.getAmount());
}
for (Trade trade : trades) {
reservedOfferBalance = reservedOfferBalance.subtract(trade.getFrozenAmount()); // subtract frozen trade balances
}
// calculate reserved trade balance
reservedTradeBalance = BigInteger.ZERO;
for (Trade trade : trades) {
reservedTradeBalance = reservedTradeBalance.add(trade.getReservedAmount());
}
// calculate reserved balance
reservedBalance = reservedOfferBalance.add(reservedTradeBalance);
// notify balance update
UserThread.execute(() -> updateCounter.set(updateCounter.get() + 1));
}
// calculate reserved offer balance
BigInteger reservedOfferBalance = BigInteger.ZERO;
if (xmrWalletService.getWallet() != null) {
List<MoneroOutputWallet> frozenOutputs = xmrWalletService.getWallet().getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false));
for (MoneroOutputWallet frozenOutput : frozenOutputs) reservedOfferBalance = reservedOfferBalance.add(frozenOutput.getAmount());
}
for (Trade trade : trades) {
reservedOfferBalance = reservedOfferBalance.subtract(trade.getFrozenAmount()); // subtract frozen trade balances
}
// calculate reserved trade balance
BigInteger reservedTradeBalance = BigInteger.ZERO;
for (Trade trade : trades) {
reservedTradeBalance = reservedTradeBalance.add(trade.getReservedAmount());
}
// set balances
setBalances(balance, unlockedBalance, pendingBalance, reservedOfferBalance, reservedTradeBalance);
}
private void setBalances(BigInteger balance, BigInteger unlockedBalance, BigInteger pendingBalance, BigInteger reservedOfferBalance, BigInteger reservedTradeBalance) {
UserThread.execute(() -> {
this.availableBalance.set(unlockedBalance);
this.pendingBalance.set(pendingBalance);
this.reservedOfferBalance.set(reservedOfferBalance);
this.reservedTradeBalance.set(reservedTradeBalance);
this.reservedBalance.set(reservedOfferBalance.add(reservedTradeBalance));
});
}
}

View File

@ -333,7 +333,9 @@ public class XmrWalletService {
Callable<MoneroSyncResult> task = () -> wallet.sync();
Future<MoneroSyncResult> future = syncWalletThreadPool.submit(task);
try {
return future.get();
MoneroSyncResult result = future.get();
wallet.getTxs(); // TODO: this is necessary to sync from pool, otherwise balance can be incorrect
return result;
} catch (Exception e) {
throw new MoneroError(e.getMessage());
}
@ -893,7 +895,7 @@ public class XmrWalletService {
}
// register internal listener to notify external listeners
wallet.addListener(new XmrWalletListener());
wallet.addListener(new XmrWalletListener()); // TODO: initial snapshot calls getTxs() which updates balance after returning but will not announce change
}
}
}

View File

@ -1,2 +1,2 @@
# nodeaddress.onion:port [(@owner,@backup)] (add mainnet seed addresses)
# nodeaddress.onion:port [(@owner,@backup)]
placeholder.onion:8000 (@placeholder)

View File

@ -140,11 +140,7 @@ class GrpcOffersService extends OffersImplBase {
@Override
public void postOffer(PostOfferRequest req,
StreamObserver<PostOfferReply> responseObserver) {
GrpcErrorMessageHandler errorMessageHandler =
new GrpcErrorMessageHandler(getPostOfferMethod().getFullMethodName(),
responseObserver,
exceptionHandler,
log);
GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getPostOfferMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
try {
coreApi.postOffer(
req.getCurrencyCode(),
@ -170,8 +166,7 @@ class GrpcOffersService extends OffersImplBase {
responseObserver.onCompleted();
},
errorMessage -> {
if (!errorMessageHandler.isErrorHandled())
errorMessageHandler.handleErrorMessage(errorMessage);
if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
});
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
@ -181,11 +176,15 @@ class GrpcOffersService extends OffersImplBase {
@Override
public void cancelOffer(CancelOfferRequest req,
StreamObserver<CancelOfferReply> responseObserver) {
GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getCancelOfferMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
try {
coreApi.cancelOffer(req.getId());
var reply = CancelOfferReply.newBuilder().build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
coreApi.cancelOffer(req.getId(), () -> {
var reply = CancelOfferReply.newBuilder().build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}, errorMessage -> {
if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
});
} catch (Throwable cause) {
exceptionHandler.handleException(log, cause, responseObserver);
}

View File

@ -48,7 +48,7 @@ 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
1. Install Ubuntu 20.04.
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`
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`

View File

@ -31,7 +31,7 @@ If it's the first time you are building Haveno, run the following commands to do
```
git clone https://github.com/haveno-dex/haveno.git
cd haveno
git checkout v0.0.18
git checkout v0.0.19
make
```
@ -40,7 +40,7 @@ make
If you are updating from a previous version, run from the root of the repository:
```
git checkout v0.0.18
git checkout v0.0.19
git pull
make clean && make
```

View File

@ -846,9 +846,9 @@
<sha256 value="c92e2ca40a3f2474d61e56831aeb379cf8ae3dddeea61b4a828cee2d99f71f38" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.github.woodser" name="monero-java" version="0.8.10">
<artifact name="monero-java-0.8.10.jar">
<sha256 value="3ea921cb8122e9be4401479e7040055063b2132e8210f7129117b8b10472f773" origin="Generated by Gradle"/>
<component group="io.github.woodser" name="monero-java" version="0.8.11">
<artifact name="monero-java-0.8.11.jar">
<sha256 value="37c125a31463c44e43452bc3e18e74a05b65d1fbebb11adc5131521b29b48a6b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="io.grpc" name="grpc-api" version="1.42.1">

View File

@ -1,7 +1,7 @@
# docker run -it -p 9050 -p 2002 --restart-policy unless-stopped --name haveno-seednode haveno-seednode
# TODO: image very heavy, but it's hard to significantly reduce the size without bins
FROM openjdk:11
FROM openjdk:21-jdk-bullseye
RUN set -ex && \
apt update && \

View File

@ -1,11 +1,11 @@
# configuration for haveno service
# install in /etc/default/haveno.env
# java home, set to openjdk 10
JAVA_HOME=/usr/lib/jvm/openjdk-10.0.2
# java home, set to latest openjdk 21 from os repository
JAVA_HOME=/usr/lib/jvm/openjdk-21
# java memory and remote management options
JAVA_OPTS="-Xms4096M -Xmx4096M"
JAVA_OPTS="-Xms4096M -Xmx4096M" -XX:+ExitOnOutOfMemoryError"
# use external tor (change to -1 for internal tor binary)
HAVENO_EXTERNAL_TOR_PORT=9051
@ -28,7 +28,7 @@ BITCOIN_RPC_BLOCKNOTIFY_PORT=5120
HAVENO_HOME=__HAVENO_HOME__
HAVENO_APP_NAME=haveno-seednode
HAVENO_ENTRYPOINT=haveno-seednode
HAVENO_BASE_CURRENCY=btc_mainnet
HAVENO_BASE_CURRENCY=xmr_mainnet
# haveno node settings
HAVENO_NODE_PORT=8000

View File

@ -7,22 +7,21 @@ echo "[*] Haveno Seednode installation script"
ROOT_USER=root
ROOT_GROUP=root
ROOT_PKG="build-essential libtool autotools-dev automake pkg-config bsdmainutils python3 git vim screen ufw"
ROOT_PKG="build-essential libtool autotools-dev automake pkg-config bsdmainutils python3 git vim screen ufw openjdk-21-jdk"
ROOT_HOME=/root
SYSTEMD_SERVICE_HOME=/etc/systemd/system
SYSTEMD_ENV_HOME=/etc/default
HAVENO_REPO_URL=https://github.com/bisq-network/bisq
HAVENO_REPO_URL=https://github.com/haveno-dex/haveno
HAVENO_REPO_NAME=haveno
HAVENO_REPO_TAG=master
HAVENO_LATEST_RELEASE=$(curl -s https://api.github.com/repos/bisq-network/bisq/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
HAVENO_LATEST_RELEASE=$(curl -s https://api.github.com/repos/haveno-dex/haveno/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
HAVENO_HOME=/haveno
HAVENO_USER=haveno
# by default, this script will build and setup bitcoin fullnode
# if you want to use an existing bitcoin fullnode, see next section
BITCOIN_INSTALL=true
# by default, this script will not build and setup bitcoin full-node
BITCOIN_INSTALL=false
BITCOIN_REPO_URL=https://github.com/bitcoin/bitcoin
BITCOIN_REPO_NAME=bitcoin
BITCOIN_REPO_TAG=$(curl -s https://api.github.com/repos/bitcoin/bitcoin/releases/latest|grep tag_name|head -1|cut -d '"' -f4)
@ -60,19 +59,13 @@ sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get upgrade -qq
echo "[*] Installing base packages"
sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ${ROOT_PKG}
echo "[*] Installing Git LFS"
sudo -H -i -u "${ROOT_USER}" apt-get install git-lfs
sudo -H -i -u "${ROOT_USER}" git lfs install
echo "[*] Cloning Haveno repo"
sudo -H -i -u "${ROOT_USER}" git config --global advice.detachedHead false
sudo -H -i -u "${ROOT_USER}" git clone --branch "${HAVENO_REPO_TAG}" "${HAVENO_REPO_URL}" "${ROOT_HOME}/${HAVENO_REPO_NAME}"
echo "[*] Installing Tor"
sudo -H -i -u "${ROOT_USER}" wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | gp
g --dearmor | tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null
sudo -H -i -u "${ROOT_USER}" echo "deb [arch=amd64 signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.o
rg/torproject.org focal main" > /etc/apt/sources.list.d/tor.list
sudo -H -i -u "${ROOT_USER}" wget -qO- https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc | sudo gpg --dearmor | sudo tee /usr/share/keyrings/tor-archive-keyring.gpg >/dev/null
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/tor-archive-keyring.gpg] https://deb.torproject.org/torproject.org focal main" | sudo -H -i -u "${ROOT_USER}" tee /etc/apt/sources.list.d/tor.list
sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get update -q
sudo -H -i -u "${ROOT_USER}" DEBIAN_FRONTEND=noninteractive apt-get install -qq -y ${TOR_PKG}
@ -128,17 +121,14 @@ echo "[*] Moving Haveno repo"
sudo -H -i -u "${ROOT_USER}" mv "${ROOT_HOME}/${HAVENO_REPO_NAME}" "${HAVENO_HOME}/${HAVENO_REPO_NAME}"
sudo -H -i -u "${ROOT_USER}" chown -R "${HAVENO_USER}:${HAVENO_GROUP}" "${HAVENO_HOME}/${HAVENO_REPO_NAME}"
echo "[*] Installing OpenJDK 10.0.2 from Haveno repo"
sudo -H -i -u "${ROOT_USER}" "${HAVENO_HOME}/${HAVENO_REPO_NAME}/scripts/install_java.sh"
echo "[*] Installing Haveno init script"
sudo -H -i -u "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${HAVENO_HOME}/${HAVENO_REPO_NAME}/seednode/haveno.service" "${SYSTEMD_SERVICE_HOME}/haveno.service"
sudo -H -i -u "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${HAVENO_HOME}/${HAVENO_REPO_NAME}/seednode/haveno-seednode.service" "${SYSTEMD_SERVICE_HOME}/haveno-seednode.service"
if [ "${BITCOIN_INSTALL}" = true ];then
sudo sed -i -e "s/#Requires=bitcoin.service/Requires=bitcoin.service/" "${SYSTEMD_SERVICE_HOME}/haveno.service"
sudo sed -i -e "s/#BindsTo=bitcoin.service/BindsTo=bitcoin.service/" "${SYSTEMD_SERVICE_HOME}/haveno.service"
sudo sed -i -e "s/#Requires=bitcoin.service/Requires=bitcoin.service/" "${SYSTEMD_SERVICE_HOME}/haveno-seednode.service"
sudo sed -i -e "s/#BindsTo=bitcoin.service/BindsTo=bitcoin.service/" "${SYSTEMD_SERVICE_HOME}/haveno-seednode.service"
fi
sudo sed -i -e "s/__HAVENO_REPO_NAME__/${HAVENO_REPO_NAME}/" "${SYSTEMD_SERVICE_HOME}/haveno.service"
sudo sed -i -e "s!__HAVENO_HOME__!${HAVENO_HOME}!" "${SYSTEMD_SERVICE_HOME}/haveno.service"
sudo sed -i -e "s/__HAVENO_REPO_NAME__/${HAVENO_REPO_NAME}/" "${SYSTEMD_SERVICE_HOME}/haveno-seednode.service"
sudo sed -i -e "s!__HAVENO_HOME__!${HAVENO_HOME}!" "${SYSTEMD_SERVICE_HOME}/haveno-seednode.service"
echo "[*] Installing Haveno environment file with Bitcoin RPC credentials"
sudo -H -i -u "${ROOT_USER}" install -c -o "${ROOT_USER}" -g "${ROOT_GROUP}" -m 644 "${HAVENO_HOME}/${HAVENO_REPO_NAME}/seednode/haveno.env" "${SYSTEMD_ENV_HOME}/haveno.env"
@ -154,16 +144,13 @@ sudo sed -i -e "s!__HAVENO_HOME__!${HAVENO_HOME}!" "${SYSTEMD_ENV_HOME}/haveno.e
echo "[*] Checking out Haveno ${HAVENO_LATEST_RELEASE}"
sudo -H -i -u "${HAVENO_USER}" sh -c "cd ${HAVENO_HOME}/${HAVENO_REPO_NAME} && git checkout ${HAVENO_LATEST_RELEASE}"
echo "[*] Performing Git LFS pull"
sudo -H -i -u "${HAVENO_USER}" sh -c "cd ${HAVENO_HOME}/${HAVENO_REPO_NAME} && git lfs pull"
echo "[*] Building Haveno from source"
sudo -H -i -u "${HAVENO_USER}" sh -c "cd ${HAVENO_HOME}/${HAVENO_REPO_NAME} && ./gradlew build -x test < /dev/null" # redirect from /dev/null is necessary to workaround gradlew non-interactive shell hanging issue
echo "[*] Updating systemd daemon configuration"
sudo -H -i -u "${ROOT_USER}" systemctl daemon-reload
sudo -H -i -u "${ROOT_USER}" systemctl enable tor.service
sudo -H -i -u "${ROOT_USER}" systemctl enable haveno.service
sudo -H -i -u "${ROOT_USER}" systemctl enable haveno-seednode.service
if [ "${BITCOIN_INSTALL}" = true ];then
sudo -H -i -u "${ROOT_USER}" systemctl enable bitcoin.service
fi
@ -185,13 +172,13 @@ fi
echo "[*] Adding notes to motd"
sudo -H -i -u "${ROOT_USER}" sh -c 'echo " " >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "Haveno Seednode instructions:" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "https://github.com/bisq-network/bisq/tree/master/seednode" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "https://github.com/haveno-dex/haveno/tree/master/seednode" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo " " >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "How to check logs for Haveno-Seednode service:" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "sudo journalctl --no-pager --unit haveno" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "sudo journalctl --no-pager --unit haveno-seednode" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo " " >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "How to restart Haveno-Seednode service:" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "sudo service haveno restart" >> /etc/motd'
sudo -H -i -u "${ROOT_USER}" sh -c 'echo "sudo service haveno-seednode restart" >> /etc/motd'
echo '[*] Done!'

View File

@ -3,9 +3,9 @@ echo "[*] Uninstalling Bitcoin and Haveno, will delete all data!!"
sleep 10
sudo rm -rf /root/haveno
sudo systemctl stop bitcoin
sudo systemctl stop haveno
sudo systemctl stop haveno-seednode
sudo systemctl disable bitcoin
sudo systemctl disable haveno
sudo systemctl disable haveno-seednode
sudo userdel -f -r haveno
sudo userdel -f -r bitcoin
echo "[*] Done!"