mirror of
https://github.com/boldsuck/haveno.git
synced 2025-01-10 18:14:30 +00:00
cache wallet state to avoid requests on main thread
This commit is contained in:
parent
d8ef7e82d4
commit
eaf096adeb
4 changed files with 76 additions and 100 deletions
|
@ -155,6 +155,10 @@ public class XmrWalletService {
|
||||||
private ExecutorService syncWalletThreadPool = Executors.newFixedThreadPool(10); // TODO: adjust based on connection type
|
private ExecutorService syncWalletThreadPool = Executors.newFixedThreadPool(10); // TODO: adjust based on connection type
|
||||||
private Long syncStartHeight = null;
|
private Long syncStartHeight = null;
|
||||||
private TaskLooper syncLooper = null;
|
private TaskLooper syncLooper = null;
|
||||||
|
private BigInteger cachedBalance = null;
|
||||||
|
private BigInteger cachedAvailableBalance = null;
|
||||||
|
private List<MoneroSubaddress> cachedSubaddresses;
|
||||||
|
private List<MoneroTxWallet> cachedTxs;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
XmrWalletService(User user,
|
XmrWalletService(User user,
|
||||||
|
@ -439,7 +443,6 @@ public class XmrWalletService {
|
||||||
if (!unreservedFrozenKeyImages.isEmpty()) {
|
if (!unreservedFrozenKeyImages.isEmpty()) {
|
||||||
log.warn("Thawing outputs which are not reserved for offer or trade: " + unreservedFrozenKeyImages);
|
log.warn("Thawing outputs which are not reserved for offer or trade: " + unreservedFrozenKeyImages);
|
||||||
thawOutputs(unreservedFrozenKeyImages);
|
thawOutputs(unreservedFrozenKeyImages);
|
||||||
saveMainWallet();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -452,6 +455,8 @@ public class XmrWalletService {
|
||||||
public void freezeOutputs(Collection<String> keyImages) {
|
public void freezeOutputs(Collection<String> keyImages) {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
for (String keyImage : keyImages) wallet.freezeOutput(keyImage);
|
for (String keyImage : keyImages) wallet.freezeOutput(keyImage);
|
||||||
|
saveMainWallet();
|
||||||
|
cacheWalletState();
|
||||||
}
|
}
|
||||||
updateBalanceListeners(); // TODO (monero-java): balance listeners not notified on freeze/thaw output
|
updateBalanceListeners(); // TODO (monero-java): balance listeners not notified on freeze/thaw output
|
||||||
}
|
}
|
||||||
|
@ -464,6 +469,8 @@ public class XmrWalletService {
|
||||||
public void thawOutputs(Collection<String> keyImages) {
|
public void thawOutputs(Collection<String> keyImages) {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
for (String keyImage : keyImages) wallet.thawOutput(keyImage);
|
for (String keyImage : keyImages) wallet.thawOutput(keyImage);
|
||||||
|
saveMainWallet();
|
||||||
|
cacheWalletState();
|
||||||
}
|
}
|
||||||
updateBalanceListeners(); // TODO (monero-java): balance listeners not notified on freeze/thaw output
|
updateBalanceListeners(); // TODO (monero-java): balance listeners not notified on freeze/thaw output
|
||||||
}
|
}
|
||||||
|
@ -845,11 +852,12 @@ public class XmrWalletService {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
syncWalletWithProgress(); // blocking
|
syncWalletWithProgress(); // blocking
|
||||||
log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms");
|
log.info("Done syncing main wallet in " + (System.currentTimeMillis() - time) + " ms");
|
||||||
|
cacheWalletState();
|
||||||
|
|
||||||
// log wallet balances
|
// log wallet balances
|
||||||
if (getMoneroNetworkType() != MoneroNetworkType.MAINNET) {
|
if (getMoneroNetworkType() != MoneroNetworkType.MAINNET) {
|
||||||
BigInteger balance = wallet.getBalance();
|
BigInteger balance = getBalance();
|
||||||
BigInteger unlockedBalance = wallet.getUnlockedBalance();
|
BigInteger unlockedBalance = getAvailableBalance();
|
||||||
log.info("Monero wallet unlocked balance={}, pending balance={}, total balance={}", unlockedBalance, balance.subtract(unlockedBalance), balance);
|
log.info("Monero wallet unlocked balance={}, pending balance={}, total balance={}", unlockedBalance, balance.subtract(unlockedBalance), balance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1118,7 +1126,7 @@ public class XmrWalletService {
|
||||||
|
|
||||||
// try to use available and not yet used entries
|
// try to use available and not yet used entries
|
||||||
try {
|
try {
|
||||||
List<XmrAddressEntry> unusedAddressEntries = getUnusedAddressEntries(getTxsWithIncomingOutputs(), wallet.getSubaddresses(0));
|
List<XmrAddressEntry> unusedAddressEntries = getUnusedAddressEntries();
|
||||||
if (!unusedAddressEntries.isEmpty()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(unusedAddressEntries.get(0), context, offerId);
|
if (!unusedAddressEntries.isEmpty()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(unusedAddressEntries.get(0), context, offerId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Error getting new address entry based on incoming transactions");
|
log.warn("Error getting new address entry based on incoming transactions");
|
||||||
|
@ -1131,6 +1139,7 @@ public class XmrWalletService {
|
||||||
|
|
||||||
private XmrAddressEntry getNewAddressEntryAux(String offerId, XmrAddressEntry.Context context) {
|
private XmrAddressEntry getNewAddressEntryAux(String offerId, XmrAddressEntry.Context context) {
|
||||||
MoneroSubaddress subaddress = wallet.createSubaddress(0);
|
MoneroSubaddress subaddress = wallet.createSubaddress(0);
|
||||||
|
cacheWalletState();
|
||||||
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, offerId, null);
|
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, offerId, null);
|
||||||
log.info("Add new XmrAddressEntry {}", entry);
|
log.info("Add new XmrAddressEntry {}", entry);
|
||||||
xmrAddressEntryList.addAddressEntry(entry);
|
xmrAddressEntryList.addAddressEntry(entry);
|
||||||
|
@ -1143,12 +1152,6 @@ public class XmrWalletService {
|
||||||
else return unusedAddressEntries.get(0);
|
else return unusedAddressEntries.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized XmrAddressEntry getFreshAddressEntry(List<MoneroTxWallet> cachedTxs, List<MoneroSubaddress> cachedSubaddresses) {
|
|
||||||
List<XmrAddressEntry> unusedAddressEntries = getUnusedAddressEntries(cachedTxs, cachedSubaddresses);
|
|
||||||
if (unusedAddressEntries.isEmpty()) return getNewAddressEntry();
|
|
||||||
else return unusedAddressEntries.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
public synchronized XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
||||||
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
|
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
|
||||||
if (!available.isPresent()) return null;
|
if (!available.isPresent()) return null;
|
||||||
|
@ -1228,15 +1231,13 @@ public class XmrWalletService {
|
||||||
|
|
||||||
public List<XmrAddressEntry> getFundedAvailableAddressEntries() {
|
public List<XmrAddressEntry> getFundedAvailableAddressEntries() {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
List<MoneroSubaddress> subaddresses = wallet.getSubaddresses(0);
|
return getAvailableAddressEntries().stream().filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).compareTo(BigInteger.ZERO) > 0).collect(Collectors.toList());
|
||||||
return getAvailableAddressEntries().stream().filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex(), subaddresses).compareTo(BigInteger.ZERO) > 0).collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<XmrAddressEntry> getAddressEntryListAsImmutableList() {
|
public List<XmrAddressEntry> getAddressEntryListAsImmutableList() {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
List<MoneroSubaddress> subaddresses = wallet.getSubaddresses(0);
|
for (MoneroSubaddress subaddress : cachedSubaddresses) {
|
||||||
for (MoneroSubaddress subaddress : subaddresses) {
|
|
||||||
boolean exists = xmrAddressEntryList.getAddressEntriesAsListImmutable().stream().filter(addressEntry -> addressEntry.getAddressString().equals(subaddress.getAddress())).findAny().isPresent();
|
boolean exists = xmrAddressEntryList.getAddressEntriesAsListImmutable().stream().filter(addressEntry -> addressEntry.getAddressString().equals(subaddress.getAddress())).findAny().isPresent();
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), subaddress.getIndex() == 0 ? XmrAddressEntry.Context.BASE_ADDRESS : XmrAddressEntry.Context.AVAILABLE, null, null);
|
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), subaddress.getIndex() == 0 ? XmrAddressEntry.Context.BASE_ADDRESS : XmrAddressEntry.Context.AVAILABLE, null, null);
|
||||||
|
@ -1249,46 +1250,37 @@ public class XmrWalletService {
|
||||||
|
|
||||||
public List<XmrAddressEntry> getUnusedAddressEntries() {
|
public List<XmrAddressEntry> getUnusedAddressEntries() {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
return getUnusedAddressEntries(getTxsWithIncomingOutputs(), wallet.getSubaddresses(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<XmrAddressEntry> getUnusedAddressEntries(List<MoneroTxWallet> cachedTxs, List<MoneroSubaddress> cachedSubaddresses) {
|
|
||||||
return getAvailableAddressEntries().stream()
|
return getAvailableAddressEntries().stream()
|
||||||
.filter(e -> e.getContext() == XmrAddressEntry.Context.AVAILABLE && !subaddressHasIncomingTransfers(e.getSubaddressIndex(), cachedTxs, cachedSubaddresses))
|
.filter(e -> e.getContext() == XmrAddressEntry.Context.AVAILABLE && !subaddressHasIncomingTransfers(e.getSubaddressIndex()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean subaddressHasIncomingTransfers(int subaddressIndex) {
|
public boolean subaddressHasIncomingTransfers(int subaddressIndex) {
|
||||||
return subaddressHasIncomingTransfers(subaddressIndex, null, null);
|
return getNumOutputsForSubaddress(subaddressIndex) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean subaddressHasIncomingTransfers(int subaddressIndex, List<MoneroTxWallet> incomingTxs, List<MoneroSubaddress> subaddresses) {
|
public int getNumOutputsForSubaddress(int subaddressIndex) {
|
||||||
return getNumOutputsForSubaddress(subaddressIndex, incomingTxs, subaddresses) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumOutputsForSubaddress(int subaddressIndex, List<MoneroTxWallet> incomingTxs, List<MoneroSubaddress> subaddresses) {
|
|
||||||
incomingTxs = getTxsWithIncomingOutputs(subaddressIndex, incomingTxs);
|
|
||||||
int numUnspentOutputs = 0;
|
int numUnspentOutputs = 0;
|
||||||
for (MoneroTxWallet tx : incomingTxs) {
|
for (MoneroTxWallet tx : cachedTxs) {
|
||||||
//if (tx.getTransfers(new MoneroTransferQuery().setSubaddressIndex(subaddressIndex)).isEmpty()) continue; // TODO monero-project: transfers are occluded by transfers from/to same account, so this will return unused when used
|
//if (tx.getTransfers(new MoneroTransferQuery().setSubaddressIndex(subaddressIndex)).isEmpty()) continue; // TODO monero-project: transfers are occluded by transfers from/to same account, so this will return unused when used
|
||||||
numUnspentOutputs += tx.isConfirmed() ? tx.getOutputsWallet(new MoneroOutputQuery().setAccountIndex(0).setSubaddressIndex(subaddressIndex)).size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs
|
numUnspentOutputs += tx.isConfirmed() ? tx.getOutputsWallet(new MoneroOutputQuery().setAccountIndex(0).setSubaddressIndex(subaddressIndex)).size() : 1; // TODO: monero-project does not provide outputs for unconfirmed txs
|
||||||
}
|
}
|
||||||
boolean positiveBalance = getSubaddress(subaddresses, subaddressIndex).getBalance().compareTo(BigInteger.ZERO) > 0;
|
boolean positiveBalance = getSubaddress(subaddressIndex).getBalance().compareTo(BigInteger.ZERO) > 0;
|
||||||
if (positiveBalance && numUnspentOutputs == 0) return 1; // outputs do not appear until confirmed and internal transfers are occluded, so report 1 if positive balance
|
if (positiveBalance && numUnspentOutputs == 0) return 1; // outputs do not appear until confirmed and internal transfers are occluded, so report 1 if positive balance
|
||||||
return numUnspentOutputs;
|
return numUnspentOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MoneroSubaddress getSubaddress(Collection<MoneroSubaddress> subaddresses, int subaddressIndex) {
|
private MoneroSubaddress getSubaddress(int subaddressIndex) {
|
||||||
for (MoneroSubaddress subaddress : subaddresses) {
|
for (MoneroSubaddress subaddress : cachedSubaddresses) {
|
||||||
if (subaddress.getIndex() == subaddressIndex) return subaddress;
|
if (subaddress.getIndex() == subaddressIndex) return subaddress;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumTxsWithIncomingOutputs(int subaddressIndex, List<MoneroTxWallet> txs, List<MoneroSubaddress> subaddresses) {
|
public int getNumTxsWithIncomingOutputs(int subaddressIndex) {
|
||||||
List<MoneroTxWallet> txsWithIncomingOutputs = getTxsWithIncomingOutputs(subaddressIndex, txs);
|
List<MoneroTxWallet> txsWithIncomingOutputs = getTxsWithIncomingOutputs(subaddressIndex);
|
||||||
if (txsWithIncomingOutputs.isEmpty() && subaddressHasIncomingTransfers(subaddressIndex, txsWithIncomingOutputs, subaddresses)) return 1; // outputs do not appear until confirmed and internal transfers are occluded, so report 1 if positive balance
|
if (txsWithIncomingOutputs.isEmpty() && subaddressHasIncomingTransfers(subaddressIndex)) return 1; // outputs do not appear until confirmed and internal transfers are occluded, so report 1 if positive balance
|
||||||
return txsWithIncomingOutputs.size();
|
return txsWithIncomingOutputs.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1297,13 +1289,8 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex) {
|
public List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex) {
|
||||||
return getTxsWithIncomingOutputs(subaddressIndex, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex, List<MoneroTxWallet> txs) {
|
|
||||||
if (txs == null) txs = wallet.getTxs(new MoneroTxQuery().setIncludeOutputs(true));
|
|
||||||
List<MoneroTxWallet> incomingTxs = new ArrayList<>();
|
List<MoneroTxWallet> incomingTxs = new ArrayList<>();
|
||||||
for (MoneroTxWallet tx : txs) {
|
for (MoneroTxWallet tx : cachedTxs) {
|
||||||
boolean isIncoming = false;
|
boolean isIncoming = false;
|
||||||
if (tx.getIncomingTransfers() != null) {
|
if (tx.getIncomingTransfers() != null) {
|
||||||
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
|
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
|
||||||
|
@ -1331,35 +1318,23 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getBalanceForSubaddress(int subaddressIndex) {
|
public BigInteger getBalanceForSubaddress(int subaddressIndex) {
|
||||||
synchronized (walletLock) {
|
return getSubaddress(subaddressIndex).getBalance();
|
||||||
return wallet.getBalance(0, subaddressIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BigInteger getBalanceForSubaddress(int subaddressIndex, Collection<MoneroSubaddress> subaddresses) {
|
|
||||||
if (subaddresses == null) return getBalanceForSubaddress(subaddressIndex);
|
|
||||||
return getSubaddress(subaddresses, subaddressIndex).getBalance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getAvailableBalanceForSubaddress(int subaddressIndex) {
|
public BigInteger getAvailableBalanceForSubaddress(int subaddressIndex) {
|
||||||
synchronized (walletLock) {
|
return getSubaddress(subaddressIndex).getUnlockedBalance();
|
||||||
if (wallet == null) throw new IllegalStateException("Cannot get available balance for subaddress because main wallet is null");
|
|
||||||
return wallet.getUnlockedBalance(0, subaddressIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getBalance() {
|
public BigInteger getBalance() {
|
||||||
synchronized (walletLock) {
|
return cachedBalance;
|
||||||
if (wallet == null) throw new IllegalStateException("Cannot get balance because main wallet is null");
|
|
||||||
return wallet.getBalance(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getAvailableBalance() {
|
public BigInteger getAvailableBalance() {
|
||||||
synchronized (walletLock) {
|
return cachedAvailableBalance;
|
||||||
if (wallet == null) throw new IllegalStateException("Cannot get available balance because main wallet is null");
|
|
||||||
return wallet.getUnlockedBalance(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MoneroSubaddress> getSubaddresses() {
|
||||||
|
return cachedSubaddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<XmrAddressEntry> getAddressEntriesForAvailableBalanceStream() {
|
public Stream<XmrAddressEntry> getAddressEntriesForAvailableBalanceStream() {
|
||||||
|
@ -1368,8 +1343,7 @@ public class XmrWalletService {
|
||||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.ARBITRATOR).stream());
|
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.ARBITRATOR).stream());
|
||||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.OFFER_FUNDING).stream().filter(entry -> !tradeManager.getOpenOfferManager().getOpenOfferById(entry.getOfferId()).isPresent()));
|
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.OFFER_FUNDING).stream().filter(entry -> !tradeManager.getOpenOfferManager().getOpenOfferById(entry.getOfferId()).isPresent()));
|
||||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.TRADE_PAYOUT).stream().filter(entry -> tradeManager.getTrade(entry.getOfferId()) == null || tradeManager.getTrade(entry.getOfferId()).isPayoutUnlocked()));
|
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.TRADE_PAYOUT).stream().filter(entry -> tradeManager.getTrade(entry.getOfferId()) == null || tradeManager.getTrade(entry.getOfferId()).isPayoutUnlocked()));
|
||||||
List<MoneroSubaddress> subaddresses = wallet.getSubaddresses(0);
|
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).compareTo(BigInteger.ZERO) > 0);
|
||||||
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex(), subaddresses).compareTo(BigInteger.ZERO) > 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1417,7 +1391,8 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MoneroTxWallet> getTransactions(boolean includeDead) {
|
public List<MoneroTxWallet> getTransactions(boolean includeDead) {
|
||||||
return wallet.getTxs(new MoneroTxQuery().setIsFailed(includeDead ? null : false));
|
if (includeDead) return cachedTxs;
|
||||||
|
return cachedTxs.stream().filter(tx -> !tx.isFailed()).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1445,6 +1420,13 @@ public class XmrWalletService {
|
||||||
|
|
||||||
// -------------------------------- HELPERS -------------------------------
|
// -------------------------------- HELPERS -------------------------------
|
||||||
|
|
||||||
|
private void cacheWalletState() {
|
||||||
|
cachedBalance = wallet.getBalance(0);
|
||||||
|
cachedAvailableBalance = wallet.getUnlockedBalance(0);
|
||||||
|
cachedSubaddresses = wallet.getSubaddresses(0);
|
||||||
|
cachedTxs = wallet.getTxs(new MoneroTxQuery().setIncludeOutputs(true));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relays wallet notifications to external listeners.
|
* Relays wallet notifications to external listeners.
|
||||||
*/
|
*/
|
||||||
|
@ -1457,6 +1439,7 @@ public class XmrWalletService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onNewBlock(long height) {
|
public void onNewBlock(long height) {
|
||||||
|
cacheWalletState();
|
||||||
UserThread.execute(() -> {
|
UserThread.execute(() -> {
|
||||||
walletHeight.set(height);
|
walletHeight.set(height);
|
||||||
for (MoneroWalletListenerI listener : walletListeners) ThreadUtils.submitToPool(() -> listener.onNewBlock(height));
|
for (MoneroWalletListenerI listener : walletListeners) ThreadUtils.submitToPool(() -> listener.onNewBlock(height));
|
||||||
|
@ -1465,6 +1448,7 @@ public class XmrWalletService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
|
public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) {
|
||||||
|
cacheWalletState();
|
||||||
updateBalanceListeners();
|
updateBalanceListeners();
|
||||||
for (MoneroWalletListenerI listener : walletListeners) ThreadUtils.submitToPool(() -> listener.onBalancesChanged(newBalance, newUnlockedBalance));
|
for (MoneroWalletListenerI listener : walletListeners) ThreadUtils.submitToPool(() -> listener.onBalancesChanged(newBalance, newUnlockedBalance));
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ import javafx.beans.property.StringProperty;
|
||||||
import javafx.scene.control.Tooltip;
|
import javafx.scene.control.Tooltip;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.daemon.model.MoneroTx;
|
import monero.daemon.model.MoneroTx;
|
||||||
import monero.wallet.model.MoneroSubaddress;
|
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -57,14 +56,14 @@ class DepositListItem {
|
||||||
return lazyFieldsSupplier.get();
|
return lazyFieldsSupplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
DepositListItem(XmrAddressEntry addressEntry, XmrWalletService xmrWalletService, CoinFormatter formatter, List<MoneroTxWallet> cachedTxs, List<MoneroSubaddress> cachedSubaddresses) {
|
DepositListItem(XmrAddressEntry addressEntry, XmrWalletService xmrWalletService, CoinFormatter formatter) {
|
||||||
this.xmrWalletService = xmrWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.addressEntry = addressEntry;
|
this.addressEntry = addressEntry;
|
||||||
|
|
||||||
balanceAsBI = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex(), cachedSubaddresses);
|
balanceAsBI = xmrWalletService.getBalanceForSubaddress(addressEntry.getSubaddressIndex());
|
||||||
balance.set(HavenoUtils.formatXmr(balanceAsBI));
|
balance.set(HavenoUtils.formatXmr(balanceAsBI));
|
||||||
|
|
||||||
updateUsage(addressEntry.getSubaddressIndex(), cachedTxs, cachedSubaddresses);
|
updateUsage(addressEntry.getSubaddressIndex());
|
||||||
|
|
||||||
// confidence
|
// confidence
|
||||||
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
|
lazyFieldsSupplier = Suppliers.memoize(() -> new LazyFields() {{
|
||||||
|
@ -73,7 +72,7 @@ class DepositListItem {
|
||||||
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
tooltip = new Tooltip(Res.get("shared.notUsedYet"));
|
||||||
txConfidenceIndicator.setProgress(0);
|
txConfidenceIndicator.setProgress(0);
|
||||||
txConfidenceIndicator.setTooltip(tooltip);
|
txConfidenceIndicator.setTooltip(tooltip);
|
||||||
MoneroTx tx = getTxWithFewestConfirmations(cachedTxs);
|
MoneroTx tx = getTxWithFewestConfirmations();
|
||||||
if (tx == null) {
|
if (tx == null) {
|
||||||
txConfidenceIndicator.setVisible(false);
|
txConfidenceIndicator.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
|
@ -83,8 +82,8 @@ class DepositListItem {
|
||||||
}});
|
}});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUsage(int subaddressIndex, List<MoneroTxWallet> cachedTxs, List<MoneroSubaddress> cachedSubaddresses) {
|
private void updateUsage(int subaddressIndex) {
|
||||||
numTxsWithOutputs = xmrWalletService.getNumTxsWithIncomingOutputs(addressEntry.getSubaddressIndex(), cachedTxs, cachedSubaddresses);
|
numTxsWithOutputs = xmrWalletService.getNumTxsWithIncomingOutputs(addressEntry.getSubaddressIndex());
|
||||||
switch (addressEntry.getContext()) {
|
switch (addressEntry.getContext()) {
|
||||||
case BASE_ADDRESS:
|
case BASE_ADDRESS:
|
||||||
usage = Res.get("funds.deposit.baseAddress");
|
usage = Res.get("funds.deposit.baseAddress");
|
||||||
|
@ -138,15 +137,15 @@ class DepositListItem {
|
||||||
return numTxsWithOutputs;
|
return numTxsWithOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNumConfirmationsSinceFirstUsed(List<MoneroTxWallet> incomingTxs) {
|
public long getNumConfirmationsSinceFirstUsed() {
|
||||||
MoneroTx tx = getTxWithFewestConfirmations(incomingTxs);
|
MoneroTx tx = getTxWithFewestConfirmations();
|
||||||
return tx == null ? 0 : tx.getNumConfirmations();
|
return tx == null ? 0 : tx.getNumConfirmations();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MoneroTxWallet getTxWithFewestConfirmations(List<MoneroTxWallet> allIncomingTxs) {
|
private MoneroTxWallet getTxWithFewestConfirmations() {
|
||||||
|
|
||||||
// get txs with incoming outputs to subaddress index
|
// get txs with incoming outputs to subaddress index
|
||||||
List<MoneroTxWallet> txs = xmrWalletService.getTxsWithIncomingOutputs(addressEntry.getSubaddressIndex(), allIncomingTxs);
|
List<MoneroTxWallet> txs = xmrWalletService.getTxsWithIncomingOutputs(addressEntry.getSubaddressIndex());
|
||||||
|
|
||||||
// get tx with fewest confirmations
|
// get tx with fewest confirmations
|
||||||
MoneroTxWallet highestTx = null;
|
MoneroTxWallet highestTx = null;
|
||||||
|
|
|
@ -76,9 +76,7 @@ import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
import monero.wallet.model.MoneroSubaddress;
|
|
||||||
import monero.wallet.model.MoneroTxConfig;
|
import monero.wallet.model.MoneroTxConfig;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
|
||||||
import monero.wallet.model.MoneroWalletListener;
|
import monero.wallet.model.MoneroWalletListener;
|
||||||
import net.glxn.qrgen.QRCode;
|
import net.glxn.qrgen.QRCode;
|
||||||
import net.glxn.qrgen.image.ImageType;
|
import net.glxn.qrgen.image.ImageType;
|
||||||
|
@ -127,7 +125,6 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
private Subscription amountTextFieldSubscription;
|
private Subscription amountTextFieldSubscription;
|
||||||
private ChangeListener<DepositListItem> tableViewSelectionListener;
|
private ChangeListener<DepositListItem> tableViewSelectionListener;
|
||||||
private int gridRow = 0;
|
private int gridRow = 0;
|
||||||
List<MoneroTxWallet> txsWithIncomingOutputs;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor, lifecycle
|
// Constructor, lifecycle
|
||||||
|
@ -151,15 +148,9 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
confirmationsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations")));
|
confirmationsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations")));
|
||||||
usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage")));
|
usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage")));
|
||||||
|
|
||||||
// try to initialize with wallet txs
|
|
||||||
try {
|
|
||||||
|
|
||||||
// prefetch to avoid query per subaddress
|
|
||||||
txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
|
|
||||||
List<MoneroSubaddress> subaddresses = xmrWalletService.getWallet().getSubaddresses(0);
|
|
||||||
|
|
||||||
// trigger creation of at least 1 address
|
// trigger creation of at least 1 address
|
||||||
xmrWalletService.getFreshAddressEntry(txsWithIncomingOutputs, subaddresses);
|
try {
|
||||||
|
xmrWalletService.getFreshAddressEntry();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Failed to get wallet txs to initialize DepositView");
|
log.warn("Failed to get wallet txs to initialize DepositView");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -181,7 +172,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
|
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
|
||||||
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI));
|
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI));
|
||||||
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed(txsWithIncomingOutputs)));
|
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed()));
|
||||||
usageColumn.setComparator(Comparator.comparing(DepositListItem::getUsage));
|
usageColumn.setComparator(Comparator.comparing(DepositListItem::getUsage));
|
||||||
tableView.getSortOrder().add(usageColumn);
|
tableView.getSortOrder().add(usageColumn);
|
||||||
tableView.setItems(sortedList);
|
tableView.setItems(sortedList);
|
||||||
|
@ -335,16 +326,12 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
|
|
||||||
private void updateList() {
|
private void updateList() {
|
||||||
|
|
||||||
// cache incoming txs
|
|
||||||
txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
|
|
||||||
List<MoneroSubaddress> subaddresses = xmrWalletService.getWallet().getSubaddresses(0);
|
|
||||||
|
|
||||||
// create deposit list items
|
// create deposit list items
|
||||||
List<XmrAddressEntry> addressEntries = xmrWalletService.getAddressEntries();
|
List<XmrAddressEntry> addressEntries = xmrWalletService.getAddressEntries();
|
||||||
List<DepositListItem> items = new ArrayList<>();
|
List<DepositListItem> items = new ArrayList<>();
|
||||||
for (XmrAddressEntry addressEntry : addressEntries) {
|
for (XmrAddressEntry addressEntry : addressEntries) {
|
||||||
if (addressEntry.isTrade()) continue; // skip reserved for trade
|
if (addressEntry.isTrade()) continue; // skip reserved for trade
|
||||||
items.add(new DepositListItem(addressEntry, xmrWalletService, formatter, txsWithIncomingOutputs, subaddresses));
|
items.add(new DepositListItem(addressEntry, xmrWalletService, formatter));
|
||||||
}
|
}
|
||||||
|
|
||||||
// update list
|
// update list
|
||||||
|
|
|
@ -375,6 +375,8 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
||||||
advancedOptionsBox.setVisible(false);
|
advancedOptionsBox.setVisible(false);
|
||||||
advancedOptionsBox.setManaged(false);
|
advancedOptionsBox.setManaged(false);
|
||||||
|
|
||||||
|
updateQrCode();
|
||||||
|
|
||||||
model.onShowPayFundsScreen(() -> {
|
model.onShowPayFundsScreen(() -> {
|
||||||
if (!DevEnv.isDevMode()) {
|
if (!DevEnv.isDevMode()) {
|
||||||
String key = "createOfferFundWalletInfo";
|
String key = "createOfferFundWalletInfo";
|
||||||
|
@ -755,14 +757,7 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
||||||
|
|
||||||
missingCoinListener = (observable, oldValue, newValue) -> {
|
missingCoinListener = (observable, oldValue, newValue) -> {
|
||||||
if (!newValue.toString().equals("")) {
|
if (!newValue.toString().equals("")) {
|
||||||
final byte[] imageBytes = QRCode
|
//updateQrCode(); // disabled to avoid wallet requests on key strokes
|
||||||
.from(getMoneroURI())
|
|
||||||
.withSize(300, 300)
|
|
||||||
.to(ImageType.PNG)
|
|
||||||
.stream()
|
|
||||||
.toByteArray();
|
|
||||||
Image qrImage = new Image(new ByteArrayInputStream(imageBytes));
|
|
||||||
qrCodeImageView.setImage(qrImage);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -810,6 +805,17 @@ public abstract class MutableOfferView<M extends MutableOfferViewModel<?>> exten
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateQrCode() {
|
||||||
|
final byte[] imageBytes = QRCode
|
||||||
|
.from(getMoneroURI())
|
||||||
|
.withSize(300, 300)
|
||||||
|
.to(ImageType.PNG)
|
||||||
|
.stream()
|
||||||
|
.toByteArray();
|
||||||
|
Image qrImage = new Image(new ByteArrayInputStream(imageBytes));
|
||||||
|
qrCodeImageView.setImage(qrImage);
|
||||||
|
}
|
||||||
|
|
||||||
private void closeAndGoToOpenOffers() {
|
private void closeAndGoToOpenOffers() {
|
||||||
//go to open offers
|
//go to open offers
|
||||||
UserThread.runAfter(() ->
|
UserThread.runAfter(() ->
|
||||||
|
|
Loading…
Reference in a new issue