fixes from congestion testing

- refactor main wallet polling
- restart main wallet if connection changes before initial sync
- use cached wallet state throughout app
- avoid rescanning spent outputs until payout tx expected
- allow payment sent/received buttons to be clicked until arrived
- apply timeout to payment sent/received buttons
- load DepositView asynchronously
- remove separate timeout from OpenOffer
- tolerate error importing multisig hex until necessary
This commit is contained in:
woodser 2024-04-18 14:02:22 -04:00
parent 9cbf042da2
commit ca2d7704ab
22 changed files with 802 additions and 680 deletions

View file

@ -33,7 +33,6 @@ import haveno.core.trade.protocol.tasks.ProcessPaymentReceivedMessage;
import haveno.core.trade.protocol.tasks.ProcessPaymentSentMessage;
import haveno.core.trade.protocol.tasks.RemoveOffer;
import haveno.core.trade.protocol.tasks.SellerPreparePaymentReceivedMessage;
import haveno.core.trade.protocol.tasks.SellerPublishDepositTx;
import haveno.core.trade.protocol.tasks.SellerPublishTradeStatistics;
import haveno.core.trade.protocol.tasks.SellerSendPaymentReceivedMessageToBuyer;
import haveno.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
@ -87,7 +86,6 @@ public class DebugView extends InitializableView<GridPane, Void> {
VerifyPeersAccountAgeWitness.class,
//SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
SellerPublishDepositTx.class,
SellerPublishTradeStatistics.class,
ProcessPaymentSentMessage.class,
@ -140,7 +138,6 @@ public class DebugView extends InitializableView<GridPane, Void> {
RemoveOffer.class,
//SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
SellerPublishDepositTx.class,
SellerPublishTradeStatistics.class,
ProcessPaymentSentMessage.class,

View file

@ -36,6 +36,8 @@ package haveno.desktop.main.funds.deposit;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import haveno.common.ThreadUtils;
import haveno.common.UserThread;
import haveno.common.app.DevEnv;
import haveno.common.util.Tuple3;
@ -78,6 +80,7 @@ import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
@ -111,6 +114,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
private Button generateNewAddressButton;
private TitledGroupBg titledGroupBg;
private InputTextField amountTextField;
private static final String THREAD_ID = DepositView.class.getName();
private final XmrWalletService xmrWalletService;
private final Preferences preferences;
@ -146,142 +150,155 @@ public class DepositView extends ActivatableView<VBox, Void> {
confirmationsColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.confirmations")));
usageColumn.setGraphic(new AutoTooltipLabel(Res.get("shared.usage")));
// trigger creation of at least 1 address
try {
xmrWalletService.getFreshAddressEntry();
} catch (Exception e) {
log.warn("Failed to get wallet txs to initialize DepositView");
e.printStackTrace();
}
// set loading placeholder
Label placeholderLabel = new Label("Loading...");
tableView.setPlaceholder(placeholderLabel);
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses")));
tableViewSelectionListener = (observableValue, oldValue, newValue) -> {
if (newValue != null) {
fillForm(newValue.getAddressString());
GUIUtil.requestFocus(amountTextField);
ThreadUtils.execute(() -> {
// trigger creation of at least 1 address
try {
xmrWalletService.getFreshAddressEntry();
} catch (Exception e) {
log.warn("Failed to create fresh address entry to initialize DepositView");
e.printStackTrace();
}
};
setAddressColumnCellFactory();
setBalanceColumnCellFactory();
setUsageColumnCellFactory();
setConfidenceColumnCellFactory();
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI));
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed()));
usageColumn.setComparator(Comparator.comparing(DepositListItem::getUsage));
tableView.getSortOrder().add(usageColumn);
tableView.setItems(sortedList);
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 4, Res.get("funds.deposit.fundWallet"));
titledGroupBg.getStyleClass().add("last");
qrCodeImageView = new ImageView();
qrCodeImageView.setFitHeight(150);
qrCodeImageView.setFitWidth(150);
qrCodeImageView.getStyleClass().add("qr-code");
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter(
() -> new QRCodeWindow(getPaymentUri()).show(),
200, TimeUnit.MILLISECONDS));
GridPane.setRowIndex(qrCodeImageView, gridRow);
GridPane.setRowSpan(qrCodeImageView, 4);
GridPane.setColumnIndex(qrCodeImageView, 1);
GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10));
gridPane.getChildren().add(qrCodeImageView);
addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.address"), Layout.FIRST_ROW_DISTANCE);
addressTextField.setPaymentLabel(paymentLabelString);
amountTextField = addInputTextField(gridPane, ++gridRow, Res.get("funds.deposit.amount"));
amountTextField.setMaxWidth(380);
if (DevEnv.isDevMode())
amountTextField.setText("10");
titledGroupBg.setVisible(false);
titledGroupBg.setManaged(false);
qrCodeImageView.setVisible(false);
qrCodeImageView.setManaged(false);
addressTextField.setVisible(false);
addressTextField.setManaged(false);
amountTextField.setManaged(false);
Tuple3<Button, CheckBox, HBox> buttonCheckBoxHBox = addButtonCheckBoxWithBox(gridPane, ++gridRow,
Res.get("funds.deposit.generateAddress"),
null,
15);
buttonCheckBoxHBox.third.setSpacing(25);
generateNewAddressButton = buttonCheckBoxHBox.first;
generateNewAddressButton.setOnAction(event -> {
boolean hasUnusedAddress = !xmrWalletService.getUnusedAddressEntries().isEmpty();
if (hasUnusedAddress) {
new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
} else {
XmrAddressEntry newSavingsAddressEntry = xmrWalletService.getNewAddressEntry();
updateList();
UserThread.execute(() -> {
observableList.stream()
.filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString()))
.findAny()
.ifPresent(depositListItem -> tableView.getSelectionModel().select(depositListItem));
UserThread.execute(() -> {
tableView.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
tableView.setPlaceholder(new AutoTooltipLabel(Res.get("funds.deposit.noAddresses")));
tableViewSelectionListener = (observableValue, oldValue, newValue) -> {
if (newValue != null) {
fillForm(newValue.getAddressString());
GUIUtil.requestFocus(amountTextField);
}
};
setAddressColumnCellFactory();
setBalanceColumnCellFactory();
setUsageColumnCellFactory();
setConfidenceColumnCellFactory();
addressColumn.setComparator(Comparator.comparing(DepositListItem::getAddressString));
balanceColumn.setComparator(Comparator.comparing(DepositListItem::getBalanceAsBI));
confirmationsColumn.setComparator(Comparator.comparingLong(o -> o.getNumConfirmationsSinceFirstUsed()));
usageColumn.setComparator(Comparator.comparing(DepositListItem::getUsage));
tableView.getSortOrder().add(usageColumn);
tableView.setItems(sortedList);
titledGroupBg = addTitledGroupBg(gridPane, gridRow, 4, Res.get("funds.deposit.fundWallet"));
titledGroupBg.getStyleClass().add("last");
qrCodeImageView = new ImageView();
qrCodeImageView.setFitHeight(150);
qrCodeImageView.setFitWidth(150);
qrCodeImageView.getStyleClass().add("qr-code");
Tooltip.install(qrCodeImageView, new Tooltip(Res.get("shared.openLargeQRWindow")));
qrCodeImageView.setOnMouseClicked(e -> UserThread.runAfter(
() -> new QRCodeWindow(getPaymentUri()).show(),
200, TimeUnit.MILLISECONDS));
GridPane.setRowIndex(qrCodeImageView, gridRow);
GridPane.setRowSpan(qrCodeImageView, 4);
GridPane.setColumnIndex(qrCodeImageView, 1);
GridPane.setMargin(qrCodeImageView, new Insets(Layout.FIRST_ROW_DISTANCE, 0, 0, 10));
gridPane.getChildren().add(qrCodeImageView);
addressTextField = addAddressTextField(gridPane, ++gridRow, Res.get("shared.address"), Layout.FIRST_ROW_DISTANCE);
addressTextField.setPaymentLabel(paymentLabelString);
amountTextField = addInputTextField(gridPane, ++gridRow, Res.get("funds.deposit.amount"));
amountTextField.setMaxWidth(380);
if (DevEnv.isDevMode())
amountTextField.setText("10");
titledGroupBg.setVisible(false);
titledGroupBg.setManaged(false);
qrCodeImageView.setVisible(false);
qrCodeImageView.setManaged(false);
addressTextField.setVisible(false);
addressTextField.setManaged(false);
amountTextField.setManaged(false);
Tuple3<Button, CheckBox, HBox> buttonCheckBoxHBox = addButtonCheckBoxWithBox(gridPane, ++gridRow,
Res.get("funds.deposit.generateAddress"),
null,
15);
buttonCheckBoxHBox.third.setSpacing(25);
generateNewAddressButton = buttonCheckBoxHBox.first;
generateNewAddressButton.setOnAction(event -> {
boolean hasUnusedAddress = !xmrWalletService.getUnusedAddressEntries().isEmpty();
if (hasUnusedAddress) {
new Popup().warning(Res.get("funds.deposit.selectUnused")).show();
} else {
XmrAddressEntry newSavingsAddressEntry = xmrWalletService.getNewAddressEntry();
updateList();
UserThread.execute(() -> {
observableList.stream()
.filter(depositListItem -> depositListItem.getAddressString().equals(newSavingsAddressEntry.getAddressString()))
.findAny()
.ifPresent(depositListItem -> tableView.getSelectionModel().select(depositListItem));
});
}
});
}
});
balanceListener = new XmrBalanceListener() {
@Override
public void onBalanceChanged(BigInteger balance) {
updateList();
}
};
walletListener = new MoneroWalletListener() {
@Override
public void onNewBlock(long height) {
updateList();
}
};
GUIUtil.focusWhenAddedToScene(amountTextField);
balanceListener = new XmrBalanceListener() {
@Override
public void onBalanceChanged(BigInteger balance) {
updateList();
}
};
walletListener = new MoneroWalletListener() {
@Override
public void onNewBlock(long height) {
updateList();
}
};
GUIUtil.focusWhenAddedToScene(amountTextField);
});
}, THREAD_ID);
}
@Override
protected void activate() {
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
// try to update deposits list
try {
updateList();
} catch (Exception e) {
log.warn("Could not update deposits list");
e.printStackTrace();
}
xmrWalletService.addBalanceListener(balanceListener);
xmrWalletService.addWalletListener(walletListener);
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmount(HavenoUtils.parseXmr(t));
updateQRCode();
});
if (tableView.getSelectionModel().getSelectedItem() == null && !sortedList.isEmpty())
tableView.getSelectionModel().select(0);
ThreadUtils.execute(() -> {
UserThread.execute(() -> {
tableView.getSelectionModel().selectedItemProperty().addListener(tableViewSelectionListener);
sortedList.comparatorProperty().bind(tableView.comparatorProperty());
// try to update deposits list
try {
updateList();
} catch (Exception e) {
log.warn("Could not update deposits list");
e.printStackTrace();
}
xmrWalletService.addBalanceListener(balanceListener);
xmrWalletService.addWalletListener(walletListener);
amountTextFieldSubscription = EasyBind.subscribe(amountTextField.textProperty(), t -> {
addressTextField.setAmount(HavenoUtils.parseXmr(t));
updateQRCode();
});
if (tableView.getSelectionModel().getSelectedItem() == null && !sortedList.isEmpty())
tableView.getSelectionModel().select(0);
});
}, THREAD_ID);
}
@Override
protected void deactivate() {
tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener);
sortedList.comparatorProperty().unbind();
observableList.forEach(DepositListItem::cleanup);
xmrWalletService.removeBalanceListener(balanceListener);
xmrWalletService.removeWalletListener(walletListener);
amountTextFieldSubscription.unsubscribe();
ThreadUtils.execute(() -> {
tableView.getSelectionModel().selectedItemProperty().removeListener(tableViewSelectionListener);
sortedList.comparatorProperty().unbind();
observableList.forEach(DepositListItem::cleanup);
xmrWalletService.removeBalanceListener(balanceListener);
xmrWalletService.removeWalletListener(walletListener);
amountTextFieldSubscription.unsubscribe();
}, THREAD_ID);
}

View file

@ -156,7 +156,6 @@ public class BuyerStep2View extends TradeStepView {
statusLabel.setText(Res.get("shared.preparingConfirmation"));
break;
case BUYER_SENT_PAYMENT_SENT_MSG:
case BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG:
busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation"));
timeoutTimer = UserThread.runAfter(() -> {
@ -168,6 +167,7 @@ public class BuyerStep2View extends TradeStepView {
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
break;
case BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG:
case SELLER_RECEIVED_PAYMENT_SENT_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
@ -442,7 +442,8 @@ public class BuyerStep2View extends TradeStepView {
private boolean confirmPaymentSentPermitted() {
if (!trade.confirmPermitted()) return false;
return trade.isDepositsUnlocked() && trade.getState().ordinal() < Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal();
if (trade.getState() == Trade.State.BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG) return false;
return trade.isDepositsUnlocked() && trade.getState().ordinal() < Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG.ordinal();
}
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -123,20 +123,19 @@ public class SellerStep3View extends TradeStepView {
case SELLER_SENT_PAYMENT_RECEIVED_MSG:
busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation"));
timeoutTimer = UserThread.runAfter(() -> {
busyAnimation.stop();
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
}, 30);
break;
case SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
break;
case SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
break;
case SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
break;
case SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG:
// We get a popup and the trade closed, so we dont need to show anything here
busyAnimation.stop();
@ -290,7 +289,8 @@ public class SellerStep3View extends TradeStepView {
private boolean confirmPaymentReceivedPermitted() {
if (!trade.confirmPermitted()) return false;
return trade.getState().ordinal() >= Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal() && trade.getState().ordinal() < Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal();
if (trade.getState() == Trade.State.SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG) return false;
return trade.getState().ordinal() >= Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal() && trade.getState().ordinal() <= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal();
}
///////////////////////////////////////////////////////////////////////////////////////////