diff --git a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java index 3b6dd54bd6..8a19701fbb 100644 --- a/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java +++ b/core/src/main/java/bisq/core/btc/wallet/XmrWalletService.java @@ -340,7 +340,6 @@ public class XmrWalletService { // freeze deposit inputs for (MoneroOutput input : depositTx.getInputs()) wallet.freezeOutput(input.getKeyImage().getHex()); wallet.save(); - return depositTx; } } @@ -441,10 +440,8 @@ public class XmrWalletService { } public MoneroTx getTx(String txHash) { - synchronized (txCache) { - List txs = getTxs(Arrays.asList(txHash)); - return txs.isEmpty() ? null : txs.get(0); - } + List txs = getTxs(Arrays.asList(txHash)); + return txs.isEmpty() ? null : txs.get(0); } public List getTxs(List txHashes) { @@ -461,16 +458,14 @@ public class XmrWalletService { synchronized (txCache) { for (MoneroTx tx : txs) txCache.remove(tx.getHash()); } - }, connectionsService.getDefaultRefreshPeriodMs()); + }, connectionsService.getDefaultRefreshPeriodMs() / 1000); return txs; } } public MoneroTx getTxWithCache(String txHash) { - synchronized (txCache) { - List cachedTxs = getTxsWithCache(Arrays.asList(txHash)); - return cachedTxs.isEmpty() ? null : cachedTxs.get(0); - } + List cachedTxs = getTxsWithCache(Arrays.asList(txHash)); + return cachedTxs.isEmpty() ? null : cachedTxs.get(0); } public List getTxsWithCache(List txHashes) { diff --git a/core/src/main/java/bisq/core/trade/TradeUtil.java b/core/src/main/java/bisq/core/trade/TradeUtil.java index bdc4105bcd..b278424e9c 100644 --- a/core/src/main/java/bisq/core/trade/TradeUtil.java +++ b/core/src/main/java/bisq/core/trade/TradeUtil.java @@ -180,19 +180,14 @@ public class TradeUtil { * @return String describing a trader's role for a given trade */ public String getRole(Trade trade) { - Contract contract = trade.getContract(); - if (contract == null) - throw new IllegalStateException(format("could not get role because no contract was found for trade '%s'", - trade.getShortId())); - Offer offer = trade.getOffer(); if (offer == null) throw new IllegalStateException(format("could not get role because no offer was found for trade '%s'", trade.getShortId())); - - return getRole(contract.isBuyerMakerAndSellerTaker(), - offer.isMyOffer(keyRing), - offer.getCurrencyCode()); + return (trade.isArbitrator() ? "Arbitrator for " : "") + // TODO: use Res.get() + getRole(trade.getBuyer() == trade.getMaker(), + trade.isArbitrator() ? true : trade.isMaker(), // arbitrator role in context of maker + offer.getCurrencyCode()); } /** diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java index 1a39a5ea0a..db4743d44b 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessDepositRequest.java @@ -103,7 +103,7 @@ public class ArbitratorProcessDepositRequest extends TradeTask { // relay txs MoneroDaemon daemon = trade.getXmrWalletService().getDaemon(); - daemon.submitTxHex(processModel.getMaker().getDepositTxHex()); // TODO (woodser): check that result is good. will need to release funds if one is submitted + daemon.submitTxHex(processModel.getMaker().getDepositTxHex()); // TODO (woodser): check that result is good. will need to release funds if one is submitted daemon.submitTxHex(processModel.getTaker().getDepositTxHex()); // update trade state diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java index 9e4894ac46..d28a56ab57 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java @@ -85,7 +85,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask { null); // send request to maker - log.info("Send {} with offerId {} and uid {} to maker {} with pub key ring", makerRequest.getClass().getSimpleName(), makerRequest.getTradeId(), makerRequest.getUid(), trade.getMaker().getNodeAddress(), trade.getMaker().getPubKeyRing()); + log.info("Send {} with offerId {} and uid {} to maker {}", makerRequest.getClass().getSimpleName(), makerRequest.getTradeId(), makerRequest.getUid(), trade.getMaker().getNodeAddress()); processModel.getP2PService().sendEncryptedDirectMessage( trade.getMaker().getNodeAddress(), // TODO (woodser): maker's address might be different from original owner address if they disconnect and reconnect, need to validate and update address when requests received trade.getMaker().getPubKeyRing(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSetupDepositTxListener.java b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSetupDepositTxListener.java deleted file mode 100644 index c896581a30..0000000000 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSetupDepositTxListener.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * This file is part of Haveno. - * - * Haveno is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Haveno is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Haveno. If not, see . - */ - -package bisq.core.trade.protocol.tasks; - -import bisq.core.btc.listeners.AddressConfidenceListener; -import bisq.core.trade.Trade; -import bisq.common.taskrunner.TaskRunner; - -import org.fxmisc.easybind.Subscription; - -import lombok.extern.slf4j.Slf4j; - -// TODO (woodser): adapt to XMR or remove -@Slf4j -public class BuyerSetupDepositTxListener extends TradeTask { - // Use instance fields to not get eaten up by the GC - private Subscription tradeStateSubscription; - private AddressConfidenceListener confidenceListener; - - public BuyerSetupDepositTxListener(TaskRunner taskHandler, Trade trade) { - super(taskHandler, trade); - } - - @Override - protected void run() { - throw new RuntimeException("BuyerSetupDepositTxListener needs updated for XMR"); -// try { -// runInterceptHook(); -// -// if (trade.getDepositTx() == null && processModel.getPreparedDepositTx() != null) { -// BtcWalletService walletService = processModel.getBtcWalletService(); -// NetworkParameters params = walletService.getParams(); -// Transaction preparedDepositTx = new Transaction(params, processModel.getPreparedDepositTx()); -// checkArgument(!preparedDepositTx.getOutputs().isEmpty(), "preparedDepositTx.getOutputs() must not be empty"); -// Address depositTxAddress = preparedDepositTx.getOutput(0).getScriptPubKey().getToAddress(params); -// -// // For buyer as maker takerFeeTxId is null -// @Nullable String takerFeeTxId = trade.getTakerFeeTxId(); -// String makerFeeTxId = trade.getOffer().getOfferFeePaymentTxId(); -// TransactionConfidence confidence = walletService.getConfidenceForAddress(depositTxAddress); -// if (isConfTxDepositTx(confidence, params, depositTxAddress, takerFeeTxId, makerFeeTxId) && -// isVisibleInNetwork(confidence)) { -// applyConfidence(confidence); -// } else { -// confidenceListener = new AddressConfidenceListener(depositTxAddress) { -// @Override -// public void onTransactionConfidenceChanged(TransactionConfidence confidence) { -// if (isConfTxDepositTx(confidence, params, depositTxAddress, -// takerFeeTxId, makerFeeTxId) && isVisibleInNetwork(confidence)) { -// applyConfidence(confidence); -// } -// } -// }; -// walletService.addAddressConfidenceListener(confidenceListener); -// -// tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> { -// if (trade.isDepositPublished()) { -// swapReservedForTradeEntry(); -// -// // hack to remove tradeStateSubscription at callback -// UserThread.execute(this::unSubscribeAndRemoveListener); -// } -// }); -// } -// } -// -// // we complete immediately, our object stays alive because the balanceListener is stored in the WalletService -// complete(); -// } catch (Throwable t) { -// failed(t); -// } - } - -// // We check if the txIds of the inputs matches our maker fee tx and taker fee tx and if the depositTxAddress we -// // use for the confidence lookup is use as an output address. -// // This prevents that past txs which have the our depositTxAddress as input or output (deposit or payout txs) could -// // be interpreted as our deposit tx. This happened because if a bug which caused re-use of the Multisig address -// // entries and if both traders use the same key for multiple trades the depositTxAddress would be the same. -// // We fix that bug as well but we also need to avoid that past already used addresses might be taken again -// // (the Multisig flag got reverted to available in the address entry). -// private boolean isConfTxDepositTx(@Nullable TransactionConfidence confidence, -// NetworkParameters params, -// Address depositTxAddress, -// @Nullable String takerFeeTxId, -// String makerFeeTxId) { -// if (confidence == null) { -// return false; -// } -// -// Transaction walletTx = processModel.getTradeWalletService().getWalletTx(confidence.getTransactionHash()); -// long numInputMatches = walletTx.getInputs().stream() -// .map(TransactionInput::getOutpoint) -// .filter(Objects::nonNull) -// .map(TransactionOutPoint::getHash) -// .map(Sha256Hash::toString) -// .filter(txId -> txId.equals(takerFeeTxId) || txId.equals(makerFeeTxId)) -// .count(); -// if (takerFeeTxId == null && numInputMatches != 1) { -// log.warn("We got a transactionConfidenceTx which does not match our inputs. " + -// "takerFeeTxId is null (valid if role is buyer as maker) and numInputMatches " + -// "is not 1 as expected (for makerFeeTxId). " + -// "numInputMatches={}, transactionConfidenceTx={}", -// numInputMatches, walletTx); -// return false; -// } else if (takerFeeTxId != null && numInputMatches != 2) { -// log.warn("We got a transactionConfidenceTx which does not match our inputs. " + -// "numInputMatches is not 2 as expected (for makerFeeTxId and takerFeeTxId). " + -// "numInputMatches={}, transactionConfidenceTx={}", -// numInputMatches, walletTx); -// return false; -// } -// -// boolean isOutputMatching = walletTx.getOutputs().stream() -// .map(transactionOutput -> transactionOutput.getScriptPubKey().getToAddress(params)) -// .anyMatch(address -> address.equals(depositTxAddress)); -// if (!isOutputMatching) { -// log.warn("We got a transactionConfidenceTx which does not has the depositTxAddress " + -// "as output (but as input). depositTxAddress={}, transactionConfidenceTx={}", -// depositTxAddress, walletTx); -// } -// return isOutputMatching; -// } -// -// private void applyConfidence(TransactionConfidence confidence) { -// if (trade.getDepositTx() == null) { -// Transaction walletTx = processModel.getTradeWalletService().getWalletTx(confidence.getTransactionHash()); -// trade.applyDepositTx(walletTx); -// BtcWalletService.printTx("depositTx received from network", walletTx); -// -// // We don't want to trigger the tradeStateSubscription when setting the state, so we unsubscribe before -// unSubscribeAndRemoveListener(); -// trade.setState(Trade.State.BUYER_SAW_DEPOSIT_TX_IN_NETWORK); -// -// processModel.getTradeManager().requestPersistence(); -// } else { -// unSubscribeAndRemoveListener(); -// } -// -// swapReservedForTradeEntry(); -// } -// -// private boolean isVisibleInNetwork(TransactionConfidence confidence) { -// return confidence != null && -// (confidence.getConfidenceType().equals(TransactionConfidence.ConfidenceType.BUILDING) || -// confidence.getConfidenceType().equals(TransactionConfidence.ConfidenceType.PENDING)); -// } -// -// private void swapReservedForTradeEntry() { -// processModel.getBtcWalletService().swapTradeEntryToAvailableEntry(trade.getId(), -// AddressEntry.Context.RESERVED_FOR_TRADE); -// } -// -// private void unSubscribeAndRemoveListener() { -// if (tradeStateSubscription != null) { -// tradeStateSubscription.unsubscribe(); -// tradeStateSubscription = null; -// } -// -// if (confidenceListener != null) { -// processModel.getBtcWalletService().removeAddressConfidenceListener(confidenceListener); -// confidenceListener = null; -// } -// } -} diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java index 834f84792c..59ddbb4e7d 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java @@ -82,7 +82,7 @@ public class MakerSendInitTradeRequest extends TradeTask { null); // send request to arbitrator - log.info("Sending {} with offerId {} and uid {} to arbitrator {} with pub key ring {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress(), trade.getArbitrator().getPubKeyRing()); + log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress()); processModel.getP2PService().sendEncryptedDirectMessage( trade.getArbitrator().getNodeAddress(), trade.getArbitrator().getPubKeyRing(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/SendDepositsConfirmedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/SendDepositsConfirmedMessage.java index cc3bce8aa9..fcee13135a 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/SendDepositsConfirmedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/SendDepositsConfirmedMessage.java @@ -18,7 +18,6 @@ package bisq.core.trade.protocol.tasks; import bisq.core.btc.wallet.XmrWalletService; -import bisq.core.trade.BuyerTrade; import bisq.core.trade.Trade; import bisq.core.trade.messages.DepositsConfirmedMessage; import bisq.core.trade.messages.TradeMailboxMessage; diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java b/core/src/main/java/bisq/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java index 037ea61006..588b38eb24 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java @@ -119,7 +119,7 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask { processModel.getMakerSignature()); // send request to arbitrator - log.info("Sending {} with offerId {} and uid {} to arbitrator {} with pub key ring {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress(), trade.getArbitrator().getPubKeyRing()); + log.info("Sending {} with offerId {} and uid {} to arbitrator {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitrator().getNodeAddress()); processModel.getP2PService().sendEncryptedDirectMessage( arbitratorNodeAddress, arbitrator.getPubKeyRing(), diff --git a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java index ba19ec3377..9aff0d09fb 100644 --- a/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java +++ b/desktop/src/main/java/bisq/desktop/components/TxIdTextField.java @@ -30,7 +30,6 @@ import bisq.common.util.Utilities; import de.jensd.fx.fontawesome.AwesomeDude; import de.jensd.fx.fontawesome.AwesomeIcon; -import java.math.BigInteger; import com.jfoenix.controls.JFXTextField; import javafx.scene.control.Label; @@ -135,17 +134,13 @@ public class TxIdTextField extends AnchorPane { // TODO: this only listens for new blocks, listen for double spend txUpdater = new MoneroWalletListener() { @Override - public void onNewBlock(long height) { - updateConfidence(txId); - } - @Override - public void onBalancesChanged(BigInteger newBalance, BigInteger newUnlockedBalance) { - updateConfidence(txId); + public void onNewBlock(long lastBlockHeight) { + updateConfidence(txId, false, lastBlockHeight + 1); } }; xmrWalletService.addWalletListener(txUpdater); - updateConfidence(txId); + updateConfidence(txId, true, null); textField.setText(txId); textField.setOnMouseClicked(mouseEvent -> openBlockExplorer(txId)); @@ -175,31 +170,22 @@ public class TxIdTextField extends AnchorPane { } } - private void updateConfidence(String txId) { + private void updateConfidence(String txId, boolean useCache, Long height) { MoneroTx tx = null; try { - tx = xmrWalletService.getTxWithCache(txId); - tx.setNumConfirmations(tx.isConfirmed() ? xmrWalletService.getConnectionsService().getLastInfo().getHeight() - tx.getHeight() : 0l); // TODO: use tx.getNumConfirmations() when MoneroDaemonRpc supports it + tx = useCache ? xmrWalletService.getTxWithCache(txId) : xmrWalletService.getTx(txId); + tx.setNumConfirmations(tx.isConfirmed() ? (height == null ? xmrWalletService.getConnectionsService().getLastInfo().getHeight() : height) - tx.getHeight(): 0l); // TODO: don't set if tx.getNumConfirmations() works reliably on non-local testnet } catch (Exception e) { // do nothing } GUIUtil.updateConfidence(tx, progressIndicatorTooltip, txConfidenceIndicator); - if (tx != null) { - if (txConfidenceIndicator.getProgress() != 0) { - txConfidenceIndicator.setVisible(true); - AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0); - } - if (txConfidenceIndicator.getProgress() >= 1.0 && txUpdater != null) { - xmrWalletService.removeWalletListener(txUpdater); // unregister listener - txUpdater = null; - } - } else { - //TODO we should show some placeholder in case of a tx which we are not aware of but which can be - // confirmed already. This is for instance the case of the other peers trade fee tx, as it is not related - // to our wallet we don't have a confidence object but we should show that it is in an unknown state instead - // of not showing anything which causes confusion that the tx was not broadcasted. Best would be to request - // it from a block explorer service but that is a bit too heavy for that use case... - // Maybe a question mark with a tooltip explaining why we don't know about the confidence might be ok... + if (txConfidenceIndicator.getProgress() != 0) { + txConfidenceIndicator.setVisible(true); + AnchorPane.setRightAnchor(txConfidenceIndicator, 0.0); + } + if (txConfidenceIndicator.getProgress() >= 1.0 && txUpdater != null) { + xmrWalletService.removeWalletListener(txUpdater); // unregister listener + txUpdater = null; } } } diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java index efcfe41885..67fa5d7ef2 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -376,18 +376,18 @@ public class PendingTradesDataModel extends ActivatableDataModel { return; } - MoneroTx makerDepositTx = selectedTrade.getMakerDepositTx(); - MoneroTx takerDepositTx = selectedTrade.getTakerDepositTx(); String tradeId = selectedTrade.getId(); tradeStateChangeListener = (observable, oldValue, newValue) -> { - if (makerDepositTx != null && takerDepositTx != null) { // TODO (woodser): this treats separate deposit ids as one unit, being both available or unavailable - makerTxId.set(makerDepositTx.getHash()); - takerTxId.set(takerDepositTx.getHash()); + String makerDepositTxHash = selectedTrade.getMaker().getDepositTxHash(); + String takerDepositTxHash = selectedTrade.getTaker().getDepositTxHash(); + if (makerDepositTxHash != null && takerDepositTxHash != null) { // TODO (woodser): this treats separate deposit ids as one unit, being both available or unavailable + makerTxId.set(makerDepositTxHash); + takerTxId.set(takerDepositTxHash); notificationCenter.setSelectedTradeId(tradeId); selectedTrade.stateProperty().removeListener(tradeStateChangeListener); } else { - makerTxId.set(""); - takerTxId.set(""); + makerTxId.set(""); + takerTxId.set(""); } }; selectedTrade.stateProperty().addListener(tradeStateChangeListener); @@ -399,9 +399,11 @@ public class PendingTradesDataModel extends ActivatableDataModel { } isMaker = tradeManager.isMyOffer(offer); - if (makerDepositTx != null && takerDepositTx != null) { - makerTxId.set(makerDepositTx.getHash()); - takerTxId.set(takerDepositTx.getHash()); + String makerDepositTxHash = selectedTrade.getMaker().getDepositTxHash(); + String takerDepositTxHash = selectedTrade.getTaker().getDepositTxHash(); + if (makerDepositTxHash != null && takerDepositTxHash != null) { + makerTxId.set(makerDepositTxHash); + takerTxId.set(takerDepositTxHash); } else { makerTxId.set(""); takerTxId.set(""); diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java index c226d5ecff..e99a6b624a 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -299,18 +299,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel { + peerTxIdSubscription = EasyBind.subscribe(model.dataModel.isMaker() ? model.dataModel.takerTxId : model.dataModel.makerTxId, id -> { if (!id.isEmpty()) peerTxIdTextField.setup(id); else diff --git a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java index 4dfc5259a5..bd510cd925 100644 --- a/desktop/src/main/java/bisq/desktop/util/GUIUtil.java +++ b/desktop/src/main/java/bisq/desktop/util/GUIUtil.java @@ -569,23 +569,21 @@ public class GUIUtil { public static void updateConfidence(MoneroTx tx, Tooltip tooltip, TxConfidenceIndicator txConfidenceIndicator) { - if (tx != null) { - if (!tx.isRelayed()) { - tooltip.setText(Res.get("confidence.unknown")); - txConfidenceIndicator.setProgress(0); - } else if (tx.isFailed()) { - tooltip.setText(Res.get("confidence.invalid")); - txConfidenceIndicator.setProgress(0); - } else if (tx.isConfirmed()) { - tooltip.setText(Res.get("confidence.confirmed", tx.getNumConfirmations())); - txConfidenceIndicator.setProgress(Math.min(1, tx.getNumConfirmations() / (double) XmrWalletService.NUM_BLOCKS_UNLOCK)); - } else { - tooltip.setText(Res.get("confidence.seen", 0)); // TODO: replace with numBroadcastPeers - txConfidenceIndicator.setProgress(-1.0); - } - - txConfidenceIndicator.setPrefSize(24, 24); + if (tx != null && !tx.isRelayed()) { + tooltip.setText(Res.get("confidence.unknown")); + txConfidenceIndicator.setProgress(0); + } else if (tx != null && tx.isFailed()) { + tooltip.setText(Res.get("confidence.invalid")); + txConfidenceIndicator.setProgress(0); + } else if (tx != null && tx.isConfirmed()) { + tooltip.setText(Res.get("confidence.confirmed", tx.getNumConfirmations())); + txConfidenceIndicator.setProgress((double) tx.getNumConfirmations() / (double) XmrWalletService.NUM_BLOCKS_UNLOCK); + } else { + tooltip.setText(Res.get("confidence.seen", 0)); // TODO: replace with numBroadcastPeers + txConfidenceIndicator.setProgress(-1.0); } + + txConfidenceIndicator.setPrefSize(24, 24); } public static void openWebPage(String target) {