update state from wallet notifications on UserThread

This commit is contained in:
woodser 2024-01-08 07:07:38 -05:00
parent a6f6f5c00a
commit c28ffb70ff
7 changed files with 93 additions and 100 deletions

View file

@ -17,6 +17,7 @@
package haveno.core.xmr;
import haveno.common.UserThread;
import haveno.core.offer.OpenOffer;
import haveno.core.offer.OpenOfferManager;
import haveno.core.support.dispute.Dispute;
@ -86,67 +87,47 @@ public class Balances {
}
private void updateBalances() {
if (!xmrWalletService.isWalletAvailable()) return;
try {
updateAvailableBalance();
updatePendingBalance();
updateReservedOfferBalance();
updateReservedTradeBalance();
updateReservedBalance();
} catch (Exception e) {
if (xmrWalletService.isWalletAvailable()) throw e; // ignore exception if wallet isn't ready
}
}
// TODO (woodser): converting to long should generally be avoided since can lose precision, but in practice these amounts are below max value
private void updateAvailableBalance() {
availableBalance.set(xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getWallet().getUnlockedBalance(0));
}
private void updatePendingBalance() {
// 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);
BigInteger pendingBalanceSum = balance.subtract(unlockedBalance);
// add frozen trade balances - reserved amounts
// 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();
pendingBalanceSum = pendingBalanceSum.add(trade.getFrozenAmount()).subtract(trade.getReservedAmount()).subtract(tradeFee).subtract(trade.getSelf().getDepositTxFee());
pendingBalance = pendingBalance.add(trade.getFrozenAmount()).subtract(trade.getReservedAmount()).subtract(tradeFee).subtract(trade.getSelf().getDepositTxFee());
}
// add frozen offer balances
pendingBalance.set(pendingBalanceSum);
}
private void updateReservedOfferBalance() {
BigInteger sum = BigInteger.ZERO;
// 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) sum = sum.add(frozenOutput.getAmount());
for (MoneroOutputWallet frozenOutput : frozenOutputs) reservedOfferBalance = reservedOfferBalance.add(frozenOutput.getAmount());
}
// subtract frozen trade balances
List<Trade> trades = tradeManager.getTradesStreamWithFundsLockedIn().collect(Collectors.toList());
for (Trade trade : trades) {
sum = sum.subtract(trade.getFrozenAmount());
reservedOfferBalance = reservedOfferBalance.subtract(trade.getFrozenAmount()); // subtract frozen trade balances
}
reservedOfferBalance.set(sum);
// 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 updateReservedTradeBalance() {
BigInteger sum = BigInteger.ZERO;
List<Trade> trades = tradeManager.getTradesStreamWithFundsLockedIn().collect(Collectors.toList());
for (Trade trade : trades) {
sum = sum.add(trade.getReservedAmount());
}
reservedTradeBalance.set(sum);
}
private void updateReservedBalance() {
reservedBalance.set(reservedOfferBalance.get().add(reservedTradeBalance.get()));
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

@ -1297,12 +1297,16 @@ public class XmrWalletService {
}
public void addWalletListener(MoneroWalletListenerI listener) {
walletListeners.add(listener);
synchronized (walletListeners) {
walletListeners.add(listener);
}
}
public void removeWalletListener(MoneroWalletListenerI listener) {
if (!walletListeners.contains(listener)) throw new RuntimeException("Listener is not registered with wallet");
walletListeners.remove(listener);
synchronized (walletListeners) {
if (!walletListeners.contains(listener)) throw new RuntimeException("Listener is not registered with wallet");
walletListeners.remove(listener);
}
}
// TODO (woodser): update balance and other listening

View file

@ -20,11 +20,9 @@ package haveno.desktop.main.funds.deposit;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import haveno.common.UserThread;
import haveno.core.locale.Res;
import haveno.core.trade.HavenoUtils;
import haveno.core.util.coin.CoinFormatter;
import haveno.core.xmr.listeners.XmrBalanceListener;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletService;
import haveno.desktop.components.indicator.TxConfidenceIndicator;
@ -46,7 +44,6 @@ class DepositListItem {
private final XmrWalletService xmrWalletService;
private BigInteger balanceAsBI;
private String usage = "-";
private XmrBalanceListener balanceListener;
private int numTxsWithOutputs = 0;
private final Supplier<LazyFields> lazyFieldsSupplier;
@ -63,18 +60,6 @@ class DepositListItem {
this.xmrWalletService = xmrWalletService;
this.addressEntry = addressEntry;
balanceListener = new XmrBalanceListener(addressEntry.getSubaddressIndex()) {
@Override
public void onBalanceChanged(BigInteger balance) {
UserThread.execute(() -> {
DepositListItem.this.balanceAsBI = balance;
DepositListItem.this.balance.set(HavenoUtils.formatXmr(balanceAsBI));
updateUsage(addressEntry.getSubaddressIndex(), null);
});
}
};
xmrWalletService.addBalanceListener(balanceListener);
balanceAsBI = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
balance.set(HavenoUtils.formatXmr(balanceAsBI));
@ -118,7 +103,6 @@ class DepositListItem {
}
public void cleanup() {
xmrWalletService.removeBalanceListener(balanceListener);
}
public TxConfidenceIndicator getTxConfidenceIndicator() {

View file

@ -73,6 +73,7 @@ import javax.inject.Inject;
import javax.inject.Named;
import java.io.ByteArrayInputStream;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -214,10 +215,12 @@ public class DepositView extends ActivatableView<VBox, Void> {
} else {
XmrAddressEntry newSavingsAddressEntry = xmrWalletService.getNewAddressEntry();
updateList();
observableList.stream()
.filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString()))
.findAny()
.ifPresent(depositListItem -> tableView.getSelectionModel().select(depositListItem));
UserThread.execute(() -> {
observableList.stream()
.filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString()))
.findAny()
.ifPresent(depositListItem -> tableView.getSelectionModel().select(depositListItem));
});
}
});
@ -315,16 +318,23 @@ public class DepositView extends ActivatableView<VBox, Void> {
// cache incoming txs
txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
// clear existing items
observableList.forEach(DepositListItem::cleanup);
observableList.clear();
// add non-reserved address entries
for (XmrAddressEntry addressEntry : xmrWalletService.getAddressEntries()) {
// create deposit list items
List<XmrAddressEntry> addressEntries = xmrWalletService.getAddressEntries();
List<DepositListItem> items = new ArrayList<>();
for (XmrAddressEntry addressEntry : addressEntries) {
if (addressEntry.getContext().isReserved()) continue;
observableList.add(new DepositListItem(addressEntry, xmrWalletService, formatter, txsWithIncomingOutputs));
items.add(new DepositListItem(addressEntry, xmrWalletService, formatter, txsWithIncomingOutputs));
}
// update list
UserThread.execute(() -> {
observableList.forEach(DepositListItem::cleanup);
observableList.clear();
for (DepositListItem item : items) {
observableList.add(item);
}
});
}
private Coin getAmount() {

View file

@ -17,6 +17,7 @@
package haveno.desktop.main.funds.transactions;
import haveno.common.UserThread;
import haveno.core.trade.Tradable;
import haveno.core.xmr.wallet.XmrWalletService;
import monero.wallet.model.MoneroTxWallet;
@ -42,9 +43,10 @@ class DisplayedTransactions extends ObservableListDecorator<TransactionsListItem
void update() {
List<TransactionsListItem> transactionsListItems = getTransactionListItems();
// are sorted by getRecentTransactions
forEach(TransactionsListItem::cleanup);
setAll(transactionsListItems);
UserThread.execute(() -> {
forEach(TransactionsListItem::cleanup);
setAll(transactionsListItems);
});
}
private List<TransactionsListItem> getTransactionListItems() {

View file

@ -17,6 +17,7 @@
package haveno.desktop.main.funds.withdrawal;
import haveno.common.UserThread;
import haveno.core.locale.Res;
import haveno.core.trade.HavenoUtils;
import haveno.core.util.coin.CoinFormatter;
@ -68,8 +69,9 @@ class WithdrawalListItem {
private void updateBalance() {
balance = walletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
if (balance != null)
balanceLabel.setText(HavenoUtils.formatXmr(this.balance));
if (balance != null) {
UserThread.execute(() -> balanceLabel.setText(HavenoUtils.formatXmr(this.balance)));
}
}
public final String getLabel() {

View file

@ -17,6 +17,7 @@
package haveno.desktop.main.offer;
import haveno.common.UserThread;
import haveno.core.offer.OfferUtil;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletService;
@ -66,35 +67,44 @@ public abstract class OfferDataModel extends ActivatableDataModel {
protected void updateBalance() {
updateBalances();
missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), balance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), balance.get()));
if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
showWalletFundedNotification.set(true);
}
UserThread.await(() -> {
missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), balance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), balance.get()));
if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
showWalletFundedNotification.set(true);
}
});
}
protected void updateAvailableBalance() {
updateBalances();
missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), availableBalance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), availableBalance.get()));
if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
showWalletFundedNotification.set(true);
}
UserThread.await(() -> {
missingCoin.set(offerUtil.getBalanceShortage(totalToPay.get(), availableBalance.get()));
isXmrWalletFunded.set(offerUtil.isBalanceSufficient(totalToPay.get(), availableBalance.get()));
if (totalToPay.get() != null && isXmrWalletFunded.get() && !showWalletFundedNotification.get()) {
showWalletFundedNotification.set(true);
}
});
}
private void updateBalances() {
BigInteger tradeWalletBalance = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
BigInteger tradeWalletAvailableBalance = xmrWalletService.getAvailableBalanceForSubaddress(addressEntry.getSubaddressIndex());
if (useSavingsWallet) {
totalBalance = xmrWalletService.getBalance();
totalAvailableBalance = xmrWalletService.getAvailableBalance();
if (totalToPay.get() != null) {
balance.set(totalToPay.get().min(totalBalance));
availableBalance.set(totalToPay.get().min(totalAvailableBalance));
BigInteger walletBalance = xmrWalletService.getBalance();
BigInteger walletAvailableBalance = xmrWalletService.getAvailableBalance();
UserThread.await(() -> {
if (useSavingsWallet) {
totalBalance = walletBalance;
totalAvailableBalance = walletAvailableBalance;
if (totalToPay.get() != null) {
balance.set(totalToPay.get().min(totalBalance));
availableBalance.set(totalToPay.get().min(totalAvailableBalance));
}
} else {
balance.set(tradeWalletBalance);
availableBalance.set(tradeWalletAvailableBalance);
}
} else {
balance.set(tradeWalletBalance);
availableBalance.set(tradeWalletAvailableBalance);
}
});
}
}