mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-06-17 11:29:37 -04:00
deposit view shows base address and externally used addresses
update deposit view on new block avoid extra request to tx pool on deposit view init
This commit is contained in:
parent
c64131ced2
commit
9fffd74ddb
5 changed files with 111 additions and 57 deletions
|
@ -43,7 +43,8 @@ public final class XmrAddressEntry implements PersistablePayload {
|
||||||
OFFER_FUNDING,
|
OFFER_FUNDING,
|
||||||
RESERVED_FOR_TRADE,
|
RESERVED_FOR_TRADE,
|
||||||
MULTI_SIG,
|
MULTI_SIG,
|
||||||
TRADE_PAYOUT
|
TRADE_PAYOUT,
|
||||||
|
BASE_ADDRESS
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyPair can be null in case the object is created from deserialization as it is transient.
|
// keyPair can be null in case the object is created from deserialization as it is transient.
|
||||||
|
|
|
@ -39,6 +39,7 @@ import monero.wallet.MoneroWallet;
|
||||||
import monero.wallet.MoneroWalletRpc;
|
import monero.wallet.MoneroWalletRpc;
|
||||||
import monero.wallet.model.MoneroCheckTx;
|
import monero.wallet.model.MoneroCheckTx;
|
||||||
import monero.wallet.model.MoneroDestination;
|
import monero.wallet.model.MoneroDestination;
|
||||||
|
import monero.wallet.model.MoneroIncomingTransfer;
|
||||||
import monero.wallet.model.MoneroOutputQuery;
|
import monero.wallet.model.MoneroOutputQuery;
|
||||||
import monero.wallet.model.MoneroOutputWallet;
|
import monero.wallet.model.MoneroOutputWallet;
|
||||||
import monero.wallet.model.MoneroSubaddress;
|
import monero.wallet.model.MoneroSubaddress;
|
||||||
|
@ -488,7 +489,7 @@ public class XmrWalletService {
|
||||||
daemon.flushTxPool(txHash); // flush tx from pool
|
daemon.flushTxPool(txHash); // flush tx from pool
|
||||||
} catch (MoneroRpcError err) {
|
} catch (MoneroRpcError err) {
|
||||||
System.out.println(daemon.getRpcConnection());
|
System.out.println(daemon.getRpcConnection());
|
||||||
throw err.getCode() == -32601 ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err;
|
throw err.getCode().equals(-32601) ? new RuntimeException("Failed to flush tx from pool. Arbitrator must use trusted, unrestricted daemon") : err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new Tuple2<>(tx, actualSecurityDeposit);
|
return new Tuple2<>(tx, actualSecurityDeposit);
|
||||||
|
@ -838,24 +839,29 @@ public class XmrWalletService {
|
||||||
// ----------------------------- LEGACY APP -------------------------------
|
// ----------------------------- LEGACY APP -------------------------------
|
||||||
|
|
||||||
public synchronized XmrAddressEntry getNewAddressEntry() {
|
public synchronized XmrAddressEntry getNewAddressEntry() {
|
||||||
return getNewAddressEntry(XmrAddressEntry.Context.AVAILABLE);
|
return getNewAddressEntryAux(null, XmrAddressEntry.Context.AVAILABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized XmrAddressEntry getNewAddressEntry(String offerId, XmrAddressEntry.Context context) {
|
public synchronized XmrAddressEntry getNewAddressEntry(String offerId, XmrAddressEntry.Context context) {
|
||||||
|
|
||||||
// try to use available and not yet used entries
|
// try to use available and not yet used entries
|
||||||
try {
|
try {
|
||||||
List<MoneroTxWallet> incomingTxs = getIncomingTxs(); // prefetch all incoming txs to avoid query per subaddress
|
List<MoneroTxWallet> incomingTxs = getTxsWithIncomingOutputs(); // prefetch all incoming txs to avoid query per subaddress
|
||||||
Optional<XmrAddressEntry> emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream().filter(e -> XmrAddressEntry.Context.AVAILABLE == e.getContext()).filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs)).findAny();
|
Optional<XmrAddressEntry> emptyAvailableAddressEntry = getAddressEntryListAsImmutableList().stream().filter(e -> XmrAddressEntry.Context.AVAILABLE == e.getContext()).filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs)).findAny();
|
||||||
if (emptyAvailableAddressEntry.isPresent()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId);
|
if (emptyAvailableAddressEntry.isPresent()) return xmrAddressEntryList.swapAvailableToAddressEntryWithOfferId(emptyAvailableAddressEntry.get(), context, offerId);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("Error getting new address entriy based on incoming transactions");
|
log.warn("Error getting new address entry based on incoming transactions");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
// create new subaddress and entry
|
// create new entry
|
||||||
|
return getNewAddressEntryAux(offerId, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private XmrAddressEntry getNewAddressEntryAux(String offerId, XmrAddressEntry.Context context) {
|
||||||
MoneroSubaddress subaddress = wallet.createSubaddress(0);
|
MoneroSubaddress subaddress = wallet.createSubaddress(0);
|
||||||
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);
|
||||||
xmrAddressEntryList.addAddressEntry(entry);
|
xmrAddressEntryList.addAddressEntry(entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
@ -866,6 +872,12 @@ public class XmrWalletService {
|
||||||
else return unusedAddressEntries.get(0);
|
else return unusedAddressEntries.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized XmrAddressEntry getFreshAddressEntry(List<MoneroTxWallet> cachedTxs) {
|
||||||
|
List<XmrAddressEntry> unusedAddressEntries = getUnusedAddressEntries(cachedTxs);
|
||||||
|
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;
|
||||||
|
@ -883,7 +895,7 @@ public class XmrWalletService {
|
||||||
Optional<XmrAddressEntry> addressEntry = getAddressEntryListAsImmutableList().stream()
|
Optional<XmrAddressEntry> addressEntry = getAddressEntryListAsImmutableList().stream()
|
||||||
.filter(e -> context == e.getContext())
|
.filter(e -> context == e.getContext())
|
||||||
.findAny();
|
.findAny();
|
||||||
return addressEntry.isPresent() ? addressEntry.get() : getNewAddressEntry(context);
|
return addressEntry.isPresent() ? addressEntry.get() : getNewAddressEntryAux(null, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Optional<XmrAddressEntry> getAddressEntry(String offerId, XmrAddressEntry.Context context) {
|
public synchronized Optional<XmrAddressEntry> getAddressEntry(String offerId, XmrAddressEntry.Context context) {
|
||||||
|
@ -923,14 +935,6 @@ public class XmrWalletService {
|
||||||
swapTradeEntryToAvailableEntry(offerId, XmrAddressEntry.Context.TRADE_PAYOUT);
|
swapTradeEntryToAvailableEntry(offerId, XmrAddressEntry.Context.TRADE_PAYOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private XmrAddressEntry getNewAddressEntry(XmrAddressEntry.Context context) {
|
|
||||||
MoneroSubaddress subaddress = wallet.createSubaddress(0);
|
|
||||||
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), context, null, null);
|
|
||||||
log.info("getOrCreateAddressEntry: add new XmrAddressEntry {}", entry);
|
|
||||||
xmrAddressEntryList.addAddressEntry(entry);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Optional<XmrAddressEntry> findAddressEntry(String address, XmrAddressEntry.Context context) {
|
private Optional<XmrAddressEntry> findAddressEntry(String address, XmrAddressEntry.Context context) {
|
||||||
return getAddressEntryListAsImmutableList().stream().filter(e -> address.equals(e.getAddressString())).filter(e -> context == e.getContext()).findAny();
|
return getAddressEntryListAsImmutableList().stream().filter(e -> address.equals(e.getAddressString())).filter(e -> context == e.getContext()).findAny();
|
||||||
}
|
}
|
||||||
|
@ -961,13 +965,25 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<XmrAddressEntry> getAddressEntryListAsImmutableList() {
|
public List<XmrAddressEntry> getAddressEntryListAsImmutableList() {
|
||||||
|
List<MoneroSubaddress> subaddresses = wallet.getSubaddresses(0);
|
||||||
|
for (MoneroSubaddress subaddress : subaddresses) {
|
||||||
|
boolean exists = xmrAddressEntryList.getAddressEntriesAsListImmutable().stream().filter(addressEntry -> addressEntry.getAddressString().equals(subaddress.getAddress())).findAny().isPresent();
|
||||||
|
if (!exists) {
|
||||||
|
XmrAddressEntry entry = new XmrAddressEntry(subaddress.getIndex(), subaddress.getAddress(), subaddress.getIndex() == 0 ? XmrAddressEntry.Context.BASE_ADDRESS : XmrAddressEntry.Context.AVAILABLE, null, null);
|
||||||
|
log.info("Add XmrAddressEntry from existing subaddress: {}", entry);
|
||||||
|
xmrAddressEntryList.addAddressEntry(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
return xmrAddressEntryList.getAddressEntriesAsListImmutable();
|
return xmrAddressEntryList.getAddressEntriesAsListImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<XmrAddressEntry> getUnusedAddressEntries() {
|
public List<XmrAddressEntry> getUnusedAddressEntries() {
|
||||||
List<MoneroTxWallet> incomingTxs = getIncomingTxs(); // prefetch all incoming txs to avoid query per subaddress
|
return getUnusedAddressEntries(getTxsWithIncomingOutputs());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<XmrAddressEntry> getUnusedAddressEntries(List<MoneroTxWallet> cachedTxs) {
|
||||||
return getAvailableAddressEntries().stream()
|
return getAvailableAddressEntries().stream()
|
||||||
.filter(e -> isSubaddressUnused(e.getSubaddressIndex(), incomingTxs))
|
.filter(e -> isSubaddressUnused(e.getSubaddressIndex(), cachedTxs))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -976,34 +992,55 @@ public class XmrWalletService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSubaddressUnused(int subaddressIndex, List<MoneroTxWallet> incomingTxs) {
|
private boolean isSubaddressUnused(int subaddressIndex, List<MoneroTxWallet> incomingTxs) {
|
||||||
return getNumTxOutputsForSubaddress(subaddressIndex, incomingTxs) == 0;
|
return getNumOutputsForSubaddress(subaddressIndex, incomingTxs) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumTxOutputsForSubaddress(int subaddressIndex) {
|
public int getNumOutputsForSubaddress(int subaddressIndex) {
|
||||||
return getNumTxOutputsForSubaddress(subaddressIndex, null);
|
return getNumOutputsForSubaddress(subaddressIndex, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumTxOutputsForSubaddress(int subaddressIndex, List<MoneroTxWallet> incomingTxs) {
|
public int getNumOutputsForSubaddress(int subaddressIndex, List<MoneroTxWallet> incomingTxs) {
|
||||||
if (incomingTxs == null) incomingTxs = getIncomingTxs(subaddressIndex);
|
if (incomingTxs == null) incomingTxs = getTxsWithIncomingOutputs(subaddressIndex);
|
||||||
int numUnspentOutputs = 0;
|
int numUnspentOutputs = 0;
|
||||||
for (MoneroTxWallet tx : incomingTxs) {
|
for (MoneroTxWallet tx : incomingTxs) {
|
||||||
if (tx.getTransfers(new MoneroTransferQuery().setSubaddressIndex(subaddressIndex)).isEmpty()) continue;
|
if (tx.getTransfers(new MoneroTransferQuery().setSubaddressIndex(subaddressIndex)).isEmpty()) continue;
|
||||||
numUnspentOutputs += tx.isConfirmed() ? tx.getOutputsWallet(new MoneroOutputQuery().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
|
||||||
}
|
}
|
||||||
return numUnspentOutputs;
|
return numUnspentOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MoneroTxWallet> getIncomingTxs() {
|
public List<MoneroTxWallet> getTxsWithIncomingOutputs() {
|
||||||
return getIncomingTxs(null);
|
return getTxsWithIncomingOutputs(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<MoneroTxWallet> getIncomingTxs(Integer subaddressIndex) {
|
public List<MoneroTxWallet> getTxsWithIncomingOutputs(Integer subaddressIndex) {
|
||||||
return wallet.getTxs(new MoneroTxQuery()
|
List<MoneroTxWallet> txs = wallet.getTxs(new MoneroTxQuery().setIncludeOutputs(true));
|
||||||
.setTransferQuery((new MoneroTransferQuery()
|
return getTxsWithIncomingOutputs(txs, subaddressIndex);
|
||||||
.setAccountIndex(0)
|
}
|
||||||
.setSubaddressIndex(subaddressIndex)
|
|
||||||
.setIsIncoming(true)))
|
public static List<MoneroTxWallet> getTxsWithIncomingOutputs(List<MoneroTxWallet> txs, Integer subaddressIndex) {
|
||||||
.setIncludeOutputs(true));
|
List<MoneroTxWallet> incomingTxs = new ArrayList<>();
|
||||||
|
for (MoneroTxWallet tx : txs) {
|
||||||
|
boolean isIncoming = false;
|
||||||
|
if (tx.getIncomingTransfers() != null) {
|
||||||
|
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
|
||||||
|
if (transfer.getAccountIndex().equals(0) && (subaddressIndex == null || transfer.getSubaddressIndex().equals(subaddressIndex))) {
|
||||||
|
isIncoming = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tx.getOutputs() != null && !isIncoming) {
|
||||||
|
for (MoneroOutputWallet output : tx.getOutputsWallet()) {
|
||||||
|
if (output.getAccountIndex().equals(0) && (subaddressIndex == null || output.getSubaddressIndex().equals(subaddressIndex))) {
|
||||||
|
isIncoming = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isIncoming) incomingTxs.add(tx);
|
||||||
|
}
|
||||||
|
return incomingTxs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BigInteger getBalanceForAddress(String address) {
|
public BigInteger getBalanceForAddress(String address) {
|
||||||
|
|
|
@ -20,7 +20,6 @@ package haveno.desktop.main.funds.deposit;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.base.Suppliers;
|
import com.google.common.base.Suppliers;
|
||||||
|
|
||||||
import common.types.Filter;
|
|
||||||
import haveno.core.locale.Res;
|
import haveno.core.locale.Res;
|
||||||
import haveno.core.trade.HavenoUtils;
|
import haveno.core.trade.HavenoUtils;
|
||||||
import haveno.core.util.coin.CoinFormatter;
|
import haveno.core.util.coin.CoinFormatter;
|
||||||
|
@ -34,8 +33,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.MoneroTransferQuery;
|
|
||||||
import monero.wallet.model.MoneroTxQuery;
|
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
@ -49,7 +46,7 @@ class DepositListItem {
|
||||||
private BigInteger balanceAsBI;
|
private BigInteger balanceAsBI;
|
||||||
private String usage = "-";
|
private String usage = "-";
|
||||||
private XmrBalanceListener balanceListener;
|
private XmrBalanceListener balanceListener;
|
||||||
private int numTxOutputs = 0;
|
private int numTxsWithOutputs = 0;
|
||||||
private final Supplier<LazyFields> lazyFieldsSupplier;
|
private final Supplier<LazyFields> lazyFieldsSupplier;
|
||||||
|
|
||||||
private static class LazyFields {
|
private static class LazyFields {
|
||||||
|
@ -98,8 +95,8 @@ class DepositListItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUsage(int subaddressIndex, List<MoneroTxWallet> cachedTxs) {
|
private void updateUsage(int subaddressIndex, List<MoneroTxWallet> cachedTxs) {
|
||||||
numTxOutputs = xmrWalletService.getNumTxOutputsForSubaddress(addressEntry.getSubaddressIndex(), cachedTxs);
|
numTxsWithOutputs = XmrWalletService.getTxsWithIncomingOutputs(cachedTxs, addressEntry.getSubaddressIndex()).size();
|
||||||
usage = numTxOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxOutputs);
|
usage = subaddressIndex == 0 ? "Base address" : numTxsWithOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxsWithOutputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
|
@ -114,6 +111,10 @@ class DepositListItem {
|
||||||
return addressEntry.getAddressString();
|
return addressEntry.getAddressString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSubaddressIndex() {
|
||||||
|
return addressEntry.getSubaddressIndex();
|
||||||
|
}
|
||||||
|
|
||||||
public String getUsage() {
|
public String getUsage() {
|
||||||
return usage;
|
return usage;
|
||||||
}
|
}
|
||||||
|
@ -130,8 +131,8 @@ class DepositListItem {
|
||||||
return balanceAsBI;
|
return balanceAsBI;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumTxOutputs() {
|
public int getNumTxsWithOutputs() {
|
||||||
return numTxOutputs;
|
return numTxsWithOutputs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getNumConfirmationsSinceFirstUsed(List<MoneroTxWallet> incomingTxs) {
|
public long getNumConfirmationsSinceFirstUsed(List<MoneroTxWallet> incomingTxs) {
|
||||||
|
@ -139,14 +140,10 @@ class DepositListItem {
|
||||||
return tx == null ? 0 : tx.getNumConfirmations();
|
return tx == null ? 0 : tx.getNumConfirmations();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MoneroTxWallet getTxWithFewestConfirmations(List<MoneroTxWallet> incomingTxs) {
|
private MoneroTxWallet getTxWithFewestConfirmations(List<MoneroTxWallet> allIncomingTxs) {
|
||||||
|
|
||||||
// get txs with incoming transfers to subaddress
|
// get txs with incoming outputs to subaddress index
|
||||||
MoneroTxQuery query = new MoneroTxQuery()
|
List<MoneroTxWallet> txs = XmrWalletService.getTxsWithIncomingOutputs(allIncomingTxs, addressEntry.getSubaddressIndex());
|
||||||
.setTransferQuery(new MoneroTransferQuery()
|
|
||||||
.setIsIncoming(true)
|
|
||||||
.setSubaddressIndex(addressEntry.getSubaddressIndex()));
|
|
||||||
List<MoneroTxWallet> txs = incomingTxs == null ? xmrWalletService.getWallet().getTxs(query) : Filter.apply(query, incomingTxs);
|
|
||||||
|
|
||||||
// get tx with fewest confirmations
|
// get tx with fewest confirmations
|
||||||
MoneroTxWallet highestTx = null;
|
MoneroTxWallet highestTx = null;
|
||||||
|
|
|
@ -62,6 +62,7 @@ import javafx.scene.layout.VBox;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
import monero.wallet.model.MoneroTxConfig;
|
import monero.wallet.model.MoneroTxConfig;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
|
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;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -104,9 +105,11 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
|
private final ObservableList<DepositListItem> observableList = FXCollections.observableArrayList();
|
||||||
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
|
private final SortedList<DepositListItem> sortedList = new SortedList<>(observableList);
|
||||||
private XmrBalanceListener balanceListener;
|
private XmrBalanceListener balanceListener;
|
||||||
|
private MoneroWalletListener walletListener;
|
||||||
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
|
||||||
|
@ -130,8 +133,11 @@ 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")));
|
||||||
|
|
||||||
// trigger creation of at least 1 savings address
|
// prefetch all incoming txs to avoid query per subaddress
|
||||||
xmrWalletService.getFreshAddressEntry();
|
txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
|
||||||
|
|
||||||
|
// trigger creation of at least 1 address
|
||||||
|
xmrWalletService.getFreshAddressEntry(txsWithIncomingOutputs);
|
||||||
|
|
||||||
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||||
tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses")));
|
tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses")));
|
||||||
|
@ -147,13 +153,10 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
setUsageColumnCellFactory();
|
setUsageColumnCellFactory();
|
||||||
setConfidenceColumnCellFactory();
|
setConfidenceColumnCellFactory();
|
||||||
|
|
||||||
// prefetch all incoming txs to avoid query per subaddress
|
|
||||||
List<MoneroTxWallet> incomingTxs = xmrWalletService.getIncomingTxs();
|
|
||||||
|
|
||||||
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(incomingTxs)));
|
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed(txsWithIncomingOutputs)));
|
||||||
usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxOutputs));
|
usageColumn.setComparator(Comparator.comparingInt(DepositListItem::getNumTxsWithOutputs));
|
||||||
tableView.getSortOrder().add(usageColumn);
|
tableView.getSortOrder().add(usageColumn);
|
||||||
tableView.setItems(sortedList);
|
tableView.setItems(sortedList);
|
||||||
|
|
||||||
|
@ -199,7 +202,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
generateNewAddressButton = buttonCheckBoxHBox.first;
|
generateNewAddressButton = buttonCheckBoxHBox.first;
|
||||||
|
|
||||||
generateNewAddressButton.setOnAction(event -> {
|
generateNewAddressButton.setOnAction(event -> {
|
||||||
boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getNumTxOutputs() == 0);
|
boolean hasUnUsedAddress = observableList.stream().anyMatch(e -> e.getSubaddressIndex() != 0 && xmrWalletService.getTxsWithIncomingOutputs(e.getSubaddressIndex()).isEmpty());
|
||||||
if (hasUnUsedAddress) {
|
if (hasUnUsedAddress) {
|
||||||
new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
|
new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,6 +222,13 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
walletListener = new MoneroWalletListener() {
|
||||||
|
@Override
|
||||||
|
public void onNewBlock(long height) {
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
GUIUtil.focusWhenAddedToScene(amountTextField);
|
GUIUtil.focusWhenAddedToScene(amountTextField);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,6 +240,8 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
updateList();
|
updateList();
|
||||||
|
|
||||||
xmrWalletService.addBalanceListener(balanceListener);
|
xmrWalletService.addBalanceListener(balanceListener);
|
||||||
|
xmrWalletService.addWalletListener(walletListener);
|
||||||
|
|
||||||
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
|
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
|
||||||
addressTextField.setAmount(HavenoUtils.parseXmr(t));
|
addressTextField.setAmount(HavenoUtils.parseXmr(t));
|
||||||
updateQRCode();
|
updateQRCode();
|
||||||
|
@ -245,6 +257,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
sortedList.comparatorProperty().unbind();
|
sortedList.comparatorProperty().unbind();
|
||||||
observableList.forEach(DepositListItem::cleanup);
|
observableList.forEach(DepositListItem::cleanup);
|
||||||
xmrWalletService.removeBalanceListener(balanceListener);
|
xmrWalletService.removeBalanceListener(balanceListener);
|
||||||
|
xmrWalletService.removeWalletListener(walletListener);
|
||||||
amountTextFieldSubscription.unsubscribe();
|
amountTextFieldSubscription.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,9 +308,14 @@ public class DepositView extends ActivatableView<VBox, Void> {
|
||||||
observableList.forEach(DepositListItem::cleanup);
|
observableList.forEach(DepositListItem::cleanup);
|
||||||
observableList.clear();
|
observableList.clear();
|
||||||
|
|
||||||
List<MoneroTxWallet> incomingTxs = xmrWalletService.getIncomingTxs(); // cache incoming txs for performance
|
// cache incoming txs
|
||||||
|
txsWithIncomingOutputs = xmrWalletService.getTxsWithIncomingOutputs();
|
||||||
|
|
||||||
|
// add available address entries and base address
|
||||||
xmrWalletService.getAvailableAddressEntries()
|
xmrWalletService.getAvailableAddressEntries()
|
||||||
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, incomingTxs)));
|
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, txsWithIncomingOutputs)));
|
||||||
|
xmrWalletService.getAddressEntries(XmrAddressEntry.Context.BASE_ADDRESS)
|
||||||
|
.forEach(e -> observableList.add(new DepositListItem(e, xmrWalletService, formatter, txsWithIncomingOutputs)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Coin getAmount() {
|
private Coin getAmount() {
|
||||||
|
|
|
@ -1308,6 +1308,7 @@ message XmrAddressEntry {
|
||||||
RESERVED_FOR_TRADE = 4;
|
RESERVED_FOR_TRADE = 4;
|
||||||
MULTI_SIG = 5;
|
MULTI_SIG = 5;
|
||||||
TRADE_PAYOUT = 6;
|
TRADE_PAYOUT = 6;
|
||||||
|
BASE_ADDRESS = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 subaddress_index = 7;
|
int32 subaddress_index = 7;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue