From c28ffb70ffea5358271f7f196766ce0128525816 Mon Sep 17 00:00:00 2001 From: woodser Date: Mon, 8 Jan 2024 07:07:38 -0500 Subject: [PATCH] update state from wallet notifications on UserThread --- .../main/java/haveno/core/xmr/Balances.java | 69 +++++++------------ .../core/xmr/wallet/XmrWalletService.java | 10 ++- .../main/funds/deposit/DepositListItem.java | 16 ----- .../main/funds/deposit/DepositView.java | 34 +++++---- .../transactions/DisplayedTransactions.java | 8 ++- .../funds/withdrawal/WithdrawalListItem.java | 6 +- .../desktop/main/offer/OfferDataModel.java | 50 ++++++++------ 7 files changed, 93 insertions(+), 100 deletions(-) diff --git a/core/src/main/java/haveno/core/xmr/Balances.java b/core/src/main/java/haveno/core/xmr/Balances.java index e6662e479c..29bd449aad 100644 --- a/core/src/main/java/haveno/core/xmr/Balances.java +++ b/core/src/main/java/haveno/core/xmr/Balances.java @@ -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 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 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 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 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)); + }); } } diff --git a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java index a7e2288a0d..75ccb78832 100644 --- a/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java +++ b/core/src/main/java/haveno/core/xmr/wallet/XmrWalletService.java @@ -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 diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java index fd49291256..c508e6f462 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositListItem.java @@ -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 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() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java index 4ce4b0f88a..270d243555 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/deposit/DepositView.java @@ -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 { } 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 { // 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 addressEntries = xmrWalletService.getAddressEntries(); + List 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() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/transactions/DisplayedTransactions.java b/desktop/src/main/java/haveno/desktop/main/funds/transactions/DisplayedTransactions.java index bee147581c..e0bfb6c38b 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/transactions/DisplayedTransactions.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/transactions/DisplayedTransactions.java @@ -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 transactionsListItems = getTransactionListItems(); - // are sorted by getRecentTransactions - forEach(TransactionsListItem::cleanup); - setAll(transactionsListItems); + UserThread.execute(() -> { + forEach(TransactionsListItem::cleanup); + setAll(transactionsListItems); + }); } private List getTransactionListItems() { diff --git a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalListItem.java b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalListItem.java index 5cafb3304d..9c9b5aa645 100644 --- a/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalListItem.java +++ b/desktop/src/main/java/haveno/desktop/main/funds/withdrawal/WithdrawalListItem.java @@ -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() { diff --git a/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java b/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java index d530ea521c..4460a13b41 100644 --- a/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java +++ b/desktop/src/main/java/haveno/desktop/main/offer/OfferDataModel.java @@ -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); - } + }); + } }