get updated balances from Balances.java

This commit is contained in:
woodser 2024-03-14 14:35:30 -04:00
parent 82eb081089
commit 8d7bb250c5
5 changed files with 101 additions and 102 deletions

View file

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

View file

@ -133,7 +133,6 @@ class CoreWalletsService {
verifyWalletCurrencyCodeIsValid(currencyCode); verifyWalletCurrencyCodeIsValid(currencyCode);
verifyWalletsAreAvailable(); verifyWalletsAreAvailable();
verifyEncryptedWalletIsUnlocked(); verifyEncryptedWalletIsUnlocked();
if (balances.getAvailableBalance().get() == null) throw new IllegalStateException("balance is not yet available");
switch (currencyCode.trim().toUpperCase()) { switch (currencyCode.trim().toUpperCase()) {
case "": case "":
@ -418,28 +417,8 @@ class CoreWalletsService {
private XmrBalanceInfo getXmrBalances() { private XmrBalanceInfo getXmrBalances() {
verifyWalletsAreAvailable(); verifyWalletsAreAvailable();
verifyEncryptedWalletIsUnlocked(); verifyEncryptedWalletIsUnlocked();
if (balances.getAvailableBalance() == null) throw new IllegalStateException("Balances are not yet available");
var availableBalance = balances.getAvailableBalance().get(); return balances.getBalances();
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());
} }
// Returns a Coin for the transfer amount string, or a RuntimeException if invalid. // Returns a Coin for the transfer amount string, or a RuntimeException if invalid.

View file

@ -1,10 +1,10 @@
package haveno.core.api.model; package haveno.core.api.model;
import java.math.BigInteger;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import haveno.common.Payload; import haveno.common.Payload;
import lombok.Getter;
@Getter
public class XmrBalanceInfo implements Payload { public class XmrBalanceInfo implements Payload {
public static final XmrBalanceInfo EMPTY = new XmrBalanceInfo(-1, public static final XmrBalanceInfo EMPTY = new XmrBalanceInfo(-1,
@ -19,17 +19,19 @@ public class XmrBalanceInfo implements Payload {
private final long pendingBalance; private final long pendingBalance;
private final long reservedOfferBalance; private final long reservedOfferBalance;
private final long reservedTradeBalance; private final long reservedTradeBalance;
private final long reservedBalance;
public XmrBalanceInfo(long balance, public XmrBalanceInfo(long balance,
long unlockedBalance, long unlockedBalance,
long lockedBalance, long pendingBalance,
long reservedOfferBalance, long reservedOfferBalance,
long reservedTradeBalance) { long reservedTradeBalance) {
this.balance = balance; this.balance = balance;
this.availableBalance = unlockedBalance; this.availableBalance = unlockedBalance;
this.pendingBalance = lockedBalance; this.pendingBalance = pendingBalance;
this.reservedOfferBalance = reservedOfferBalance; this.reservedOfferBalance = reservedOfferBalance;
this.reservedTradeBalance = reservedTradeBalance; this.reservedTradeBalance = reservedTradeBalance;
this.reservedBalance = reservedOfferBalance + reservedTradeBalance;
} }
@VisibleForTesting @VisibleForTesting
@ -45,6 +47,30 @@ public class XmrBalanceInfo implements Payload {
reservedTradeBalance); 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 // PROTO BUFFER
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View file

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

View file

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