remove XmrTxProofService

This commit is contained in:
woodser 2023-04-21 09:55:34 -04:00
parent 33147e1c7c
commit 37e812dead
38 changed files with 5 additions and 3737 deletions

View file

@ -36,7 +36,6 @@ import haveno.core.user.Cookie;
import haveno.core.user.CookieKey;
import haveno.core.user.Preferences;
import haveno.core.user.User;
import haveno.core.xmr.wallet.BtcWalletService;
import haveno.core.xmr.wallet.WalletsManager;
import haveno.desktop.common.view.CachingViewLoader;
import haveno.desktop.common.view.View;
@ -44,9 +43,7 @@ import haveno.desktop.common.view.ViewLoader;
import haveno.desktop.main.MainView;
import haveno.desktop.main.debug.DebugView;
import haveno.desktop.main.overlays.popups.Popup;
import haveno.desktop.main.overlays.windows.BtcEmptyWalletWindow;
import haveno.desktop.main.overlays.windows.FilterWindow;
import haveno.desktop.main.overlays.windows.ManualPayoutTxWindow;
import haveno.desktop.main.overlays.windows.SendAlertMessageWindow;
import haveno.desktop.main.overlays.windows.ShowWalletDataWindow;
import haveno.desktop.util.CssTheme;
@ -299,9 +296,7 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
Utilities.isCtrlPressed(KeyCode.Q, keyEvent)) {
shutDownByUser();
} else {
if (Utilities.isAltOrCtrlPressed(KeyCode.E, keyEvent)) {
injector.getInstance(BtcEmptyWalletWindow.class).show();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.M, keyEvent)) {
if (Utilities.isAltOrCtrlPressed(KeyCode.M, keyEvent)) {
injector.getInstance(SendAlertMessageWindow.class).show();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.F, keyEvent)) {
injector.getInstance(FilterWindow.class).show();
@ -323,11 +318,6 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
new ShowWalletDataWindow(walletsManager).show();
else
new Popup().warning(Res.get("popup.warning.walletNotInitialized")).show();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.G, keyEvent)) {
if (injector.getInstance(BtcWalletService.class).isWalletReady())
injector.getInstance(ManualPayoutTxWindow.class).show();
else
new Popup().warning(Res.get("popup.warning.walletNotInitialized")).show();
} else if (DevEnv.isDevMode()) {
if (Utilities.isAltOrCtrlPressed(KeyCode.Z, keyEvent))
showDebugWindow(scene, injector);

View file

@ -1,170 +0,0 @@
package haveno.desktop.main.overlays.windows;
import com.google.inject.Inject;
import haveno.common.UserThread;
import haveno.core.api.CoreMoneroConnectionsService;
import haveno.core.locale.Res;
import haveno.core.offer.OpenOfferManager;
import haveno.core.util.FormattingUtils;
import haveno.core.util.coin.CoinFormatter;
import haveno.core.xmr.wallet.BtcWalletService;
import haveno.core.xmr.wallet.Restrictions;
import haveno.desktop.components.AutoTooltipButton;
import haveno.desktop.components.InputTextField;
import haveno.desktop.main.overlays.Overlay;
import haveno.desktop.main.overlays.popups.Popup;
import haveno.desktop.util.GUIUtil;
import haveno.desktop.util.Transitions;
import haveno.network.p2p.P2PService;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bouncycastle.crypto.params.KeyParameter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Named;
import java.util.concurrent.TimeUnit;
import static haveno.desktop.util.FormBuilder.addInputTextField;
import static haveno.desktop.util.FormBuilder.addMultilineLabel;
import static haveno.desktop.util.FormBuilder.addTopLabelTextField;
public final class BtcEmptyWalletWindow extends Overlay<BtcEmptyWalletWindow> {
protected static final Logger log = LoggerFactory.getLogger(BtcEmptyWalletWindow.class);
private final WalletPasswordWindow walletPasswordWindow;
private final OpenOfferManager openOfferManager;
private final P2PService p2PService;
private final CoreMoneroConnectionsService connectionService;
private final BtcWalletService btcWalletService;
private final CoinFormatter btcFormatter;
private Button emptyWalletButton;
private InputTextField addressInputTextField;
private TextField balanceTextField;
@Inject
public BtcEmptyWalletWindow(WalletPasswordWindow walletPasswordWindow,
OpenOfferManager openOfferManager,
P2PService p2PService,
CoreMoneroConnectionsService connectionService,
BtcWalletService btcWalletService,
@Named(FormattingUtils.BTC_FORMATTER_KEY) CoinFormatter btcFormatter) {
headLine(Res.get("emptyWalletWindow.headline", "BTC"));
width = 768;
type = Type.Instruction;
this.p2PService = p2PService;
this.connectionService = connectionService;
this.btcWalletService = btcWalletService;
this.btcFormatter = btcFormatter;
this.walletPasswordWindow = walletPasswordWindow;
this.openOfferManager = openOfferManager;
}
@Override
public void show() {
createGridPane();
addHeadLine();
addContent();
applyStyles();
display();
}
@Override
protected void setupKeyHandler(Scene scene) {
if (!hideCloseButton) {
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
e.consume();
doClose();
}
});
}
}
private void addContent() {
addMultilineLabel(gridPane, ++rowIndex, Res.get("emptyWalletWindow.info"), 0);
Coin totalBalance = btcWalletService.getAvailableConfirmedBalance();
balanceTextField = addTopLabelTextField(gridPane, ++rowIndex, Res.get("emptyWalletWindow.balance"),
btcFormatter.formatCoinWithCode(totalBalance), 10).second;
addressInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("emptyWalletWindow.address"));
closeButton = new AutoTooltipButton(Res.get("shared.cancel"));
closeButton.setOnAction(e -> {
hide();
closeHandlerOptional.ifPresent(Runnable::run);
});
emptyWalletButton = new AutoTooltipButton(Res.get("emptyWalletWindow.button"));
boolean isBalanceSufficient = Restrictions.isAboveDust(totalBalance);
emptyWalletButton.setDefaultButton(isBalanceSufficient);
emptyWalletButton.setDisable(!isBalanceSufficient && addressInputTextField.getText().length() > 0);
emptyWalletButton.setOnAction(e -> {
if (addressInputTextField.getText().length() > 0 && isBalanceSufficient) {
log.warn(getClass().getSimpleName() + ".addContent() needs updated for XMR");
}
});
closeButton.setDefaultButton(!isBalanceSufficient);
HBox hBox = new HBox();
hBox.setSpacing(10);
GridPane.setRowIndex(hBox, ++rowIndex);
hBox.getChildren().addAll(emptyWalletButton, closeButton);
gridPane.getChildren().add(hBox);
GridPane.setMargin(hBox, new Insets(10, 0, 0, 0));
}
private void doEmptyWallet(KeyParameter aesKey) {
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, connectionService)) {
if (!openOfferManager.getObservableList().isEmpty()) {
UserThread.runAfter(() ->
new Popup().warning(Res.get("emptyWalletWindow.openOffers.warn"))
.actionButtonText(Res.get("emptyWalletWindow.openOffers.yes"))
.onAction(() -> doEmptyWallet2(aesKey))
.show(), 300, TimeUnit.MILLISECONDS);
} else {
doEmptyWallet2(aesKey);
}
}
}
private void doEmptyWallet2(KeyParameter aesKey) {
emptyWalletButton.setDisable(true);
openOfferManager.removeAllOpenOffers(() -> {
try {
btcWalletService.emptyBtcWallet(addressInputTextField.getText(),
aesKey,
() -> {
closeButton.updateText(Res.get("shared.close"));
balanceTextField.setText(btcFormatter.formatCoinWithCode(btcWalletService.getAvailableConfirmedBalance()));
emptyWalletButton.setDisable(true);
log.debug("wallet empty successful");
onClose(() -> UserThread.runAfter(() -> new Popup()
.feedback(Res.get("emptyWalletWindow.sent.success"))
.show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS));
doClose();
},
(errorMessage) -> {
emptyWalletButton.setDisable(false);
log.error("wallet empty failed {}", errorMessage);
});
} catch (InsufficientMoneyException | AddressFormatException e1) {
e1.printStackTrace();
log.error(e1.getMessage());
emptyWalletButton.setDisable(false);
}
});
}
}

View file

@ -1,821 +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 <http://www.gnu.org/licenses/>.
*/
package haveno.desktop.main.overlays.windows;
import de.jensd.fx.fontawesome.AwesomeDude;
import de.jensd.fx.fontawesome.AwesomeIcon;
import haveno.common.UserThread;
import haveno.common.config.Config;
import haveno.common.util.Base64;
import haveno.common.util.Tuple2;
import haveno.common.util.Utilities;
import haveno.core.api.CoreMoneroConnectionsService;
import haveno.core.locale.Res;
import haveno.core.payment.validation.LengthValidator;
import haveno.core.payment.validation.PercentageNumberValidator;
import haveno.core.support.dispute.Dispute;
import haveno.core.support.dispute.mediation.MediationManager;
import haveno.core.user.BlockChainExplorer;
import haveno.core.user.Preferences;
import haveno.core.xmr.exceptions.TransactionVerificationException;
import haveno.core.xmr.exceptions.TxBroadcastException;
import haveno.core.xmr.exceptions.WalletException;
import haveno.core.xmr.wallet.TradeWalletService;
import haveno.core.xmr.wallet.TxBroadcaster;
import haveno.core.xmr.wallet.WalletsManager;
import haveno.desktop.components.AutoTooltipButton;
import haveno.desktop.components.HavenoTextArea;
import haveno.desktop.components.InputTextField;
import haveno.desktop.main.overlays.Overlay;
import haveno.desktop.main.overlays.popups.Popup;
import haveno.desktop.util.GUIUtil;
import haveno.network.p2p.P2PService;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.TextArea;
import javafx.scene.control.Tooltip;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.SignatureDecodeException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Utils;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.script.Script;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.nio.charset.Charset;
import java.security.SignatureException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import static haveno.desktop.util.FormBuilder.addCheckBox;
import static haveno.desktop.util.FormBuilder.addInputTextField;
import static haveno.desktop.util.FormBuilder.addTopLabelComboBox;
// We don't translate here as it is for dev only purpose
public class ManualPayoutTxWindow extends Overlay<ManualPayoutTxWindow> {
private static final int HEX_HASH_LENGTH = 32 * 2;
private static final int HEX_PUBKEY_LENGTH = 33 * 2;
private static final Logger log = LoggerFactory.getLogger(ManualPayoutTxWindow.class);
private final TradeWalletService tradeWalletService;
private final P2PService p2PService;
private final MediationManager mediationManager;
private final Preferences preferences;
private final CoreMoneroConnectionsService connectionService;
private final WalletsManager walletsManager;
GridPane inputsGridPane;
GridPane importTxGridPane;
GridPane exportTxGridPane;
GridPane signTxGridPane;
GridPane buildTxGridPane;
GridPane signVerifyMsgGridPane;
CheckBox depositTxLegacy, recentTickets;
ComboBox<String> mediationDropDown;
ObservableList<Dispute> disputeObservableList;
InputTextField depositTxHex;
InputTextField amountInMultisig;
InputTextField buyerPayoutAmount;
InputTextField sellerPayoutAmount;
InputTextField txFee;
InputTextField txFeePct;
InputTextField buyerAddressString;
InputTextField sellerAddressString;
InputTextField buyerPubKeyAsHex;
InputTextField sellerPubKeyAsHex;
InputTextField buyerSignatureAsHex;
InputTextField sellerSignatureAsHex;
InputTextField privateKeyHex;
InputTextField signatureHex;
TextArea importHex;
TextArea exportHex;
TextArea finalSignedTxHex;
private ChangeListener<Boolean> txFeeListener, amountInMultisigListener, buyerPayoutAmountListener, sellerPayoutAmountListener;
///////////////////////////////////////////////////////////////////////////////////////////
// Public API
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public ManualPayoutTxWindow(TradeWalletService tradeWalletService,
P2PService p2PService,
MediationManager mediationManager,
Preferences preferences,
CoreMoneroConnectionsService connectionService,
WalletsManager walletsManager) {
this.tradeWalletService = tradeWalletService;
this.p2PService = p2PService;
this.mediationManager = mediationManager;
this.preferences = preferences;
this.connectionService = connectionService;
this.walletsManager = walletsManager;
type = Type.Attention;
}
@Override
public void show() {
if (headLine == null)
headLine = "Emergency MultiSig payout tool"; // We dont translate here as it is for dev only purpose
width = 1068;
createGridPane();
addHeadLine();
addContent();
addButtons();
applyStyles();
txFeeListener = (observable, oldValue, newValue) -> {
calculateTxFee();
};
buyerPayoutAmountListener = (observable, oldValue, newValue) -> {
calculateTxFee();
};
sellerPayoutAmountListener = (observable, oldValue, newValue) -> {
calculateTxFee();
};
amountInMultisigListener = (observable, oldValue, newValue) -> {
calculateTxFee();
};
txFee.focusedProperty().addListener(txFeeListener);
buyerPayoutAmount.focusedProperty().addListener(buyerPayoutAmountListener);
sellerPayoutAmount.focusedProperty().addListener(sellerPayoutAmountListener);
amountInMultisig.focusedProperty().addListener(amountInMultisigListener);
display();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Protected
///////////////////////////////////////////////////////////////////////////////////////////
@Override
protected void setupKeyHandler(Scene scene) {
if (!hideCloseButton) {
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
e.consume();
doClose();
}
});
}
}
@Override
protected void createGridPane() {
gridPane = new GridPane();
gridPane.setHgap(15);
gridPane.setVgap(15);
gridPane.setPadding(new Insets(64, 64, 64, 64));
gridPane.setPrefWidth(width);
ColumnConstraints columnConstraints1 = new ColumnConstraints();
ColumnConstraints columnConstraints2 = new ColumnConstraints();
columnConstraints1.setPercentWidth(25);
columnConstraints2.setPercentWidth(75);
gridPane.getColumnConstraints().addAll(columnConstraints1, columnConstraints2);
}
@Override
protected void cleanup() {
txFee.focusedProperty().removeListener(txFeeListener);
buyerPayoutAmount.focusedProperty().removeListener(buyerPayoutAmountListener);
sellerPayoutAmount.focusedProperty().removeListener(sellerPayoutAmountListener);
amountInMultisig.focusedProperty().removeListener(amountInMultisigListener);
super.cleanup();
}
private void addContent() {
rowIndex = 1;
this.disableActionButton = true;
addLeftPanelButtons();
addInputsPane();
addImportPane();
addExportPane();
addSignPane();
addBuildPane();
signVerifyMsgGridPane = addSignVerifyMsgPane(new GridPane());
hideAllPanes();
inputsGridPane.setVisible(true);
// Notes:
// Open with alt+g
// Priv key is only visible if pw protection is removed (wallet details data (alt+j))
// Take missing buyerPubKeyAsHex and sellerPubKeyAsHex from contract data!
// Lookup sellerPrivateKeyAsHex associated with sellerPubKeyAsHex (or buyers) in wallet details data
// sellerPubKeys/buyerPubKeys are auto generated if used the fields below
}
private void addLeftPanelButtons() {
Button buttonInputs = new AutoTooltipButton("Inputs");
Button buttonImport = new AutoTooltipButton("Import");
Button buttonExport = new AutoTooltipButton("Export");
Button buttonSign = new AutoTooltipButton("Sign");
Button buttonBuild = new AutoTooltipButton("Build");
Button buttonSignVerifyMsg = new AutoTooltipButton("Sign/Verify Msg");
VBox vBox = new VBox(12, buttonInputs, buttonImport, buttonExport, buttonSign, buttonBuild, buttonSignVerifyMsg);
vBox.getChildren().forEach(button -> ((Button) button).setPrefWidth(500));
gridPane.add(vBox, 0, rowIndex);
buttonInputs.getStyleClass().add("action-button");
buttonInputs.setOnAction(e -> { // just show the inputs pane
hideAllPanes();
vBox.getChildren().forEach(button -> button.getStyleClass().remove("action-button"));
buttonInputs.getStyleClass().add("action-button");
inputsGridPane.setVisible(true);
});
buttonImport.setOnAction(e -> { // just show the import pane
hideAllPanes();
vBox.getChildren().forEach(button -> button.getStyleClass().remove("action-button"));
buttonImport.getStyleClass().add("action-button");
importTxGridPane.setVisible(true);
importHex.setText("");
});
buttonExport.setOnAction(e -> { // show export pane and fill in the data
hideAllPanes();
vBox.getChildren().forEach(button -> button.getStyleClass().remove("action-button"));
buttonExport.getStyleClass().add("action-button");
exportTxGridPane.setVisible(true);
exportHex.setText(generateExportText());
});
buttonSign.setOnAction(e -> { // just show the sign pane
hideAllPanes();
vBox.getChildren().forEach(button -> button.getStyleClass().remove("action-button"));
buttonSign.getStyleClass().add("action-button");
signTxGridPane.setVisible(true);
privateKeyHex.setText("");
signatureHex.setText("");
});
buttonBuild.setOnAction(e -> { // just show the build pane
hideAllPanes();
vBox.getChildren().forEach(button -> button.getStyleClass().remove("action-button"));
buttonBuild.getStyleClass().add("action-button");
buildTxGridPane.setVisible(true);
finalSignedTxHex.setText("");
});
buttonSignVerifyMsg.setOnAction(e -> { // just show the sign msg pane
hideAllPanes();
vBox.getChildren().forEach(button -> button.getStyleClass().remove("action-button"));
buttonSignVerifyMsg.getStyleClass().add("action-button");
signVerifyMsgGridPane.setVisible(true);
});
}
private void addInputsPane() {
inputsGridPane = new GridPane();
gridPane.add(inputsGridPane, 1, rowIndex);
int rowIndexA = 0;
depositTxLegacy = addCheckBox(inputsGridPane, rowIndexA, "depositTxLegacy");
Tooltip tooltip = new Tooltip(Res.get("txIdTextField.blockExplorerIcon.tooltip"));
Label blockExplorerIcon = new Label();
blockExplorerIcon.getStyleClass().addAll("icon", "highlight");
blockExplorerIcon.setTooltip(tooltip);
AwesomeDude.setIcon(blockExplorerIcon, AwesomeIcon.EXTERNAL_LINK);
blockExplorerIcon.setMinWidth(20);
blockExplorerIcon.setOnMouseClicked(mouseEvent -> openBlockExplorer(depositTxHex.getText()));
depositTxHex = addInputTextField(inputsGridPane, rowIndexA, "depositTxId");
HBox hBoxTx = new HBox(12, depositTxHex, blockExplorerIcon);
hBoxTx.setAlignment(Pos.BASELINE_LEFT);
hBoxTx.setPrefWidth(800);
inputsGridPane.add(new Label(""), 0, ++rowIndexA); // spacer
inputsGridPane.add(hBoxTx, 0, ++rowIndexA);
amountInMultisig = addInputTextField(inputsGridPane, ++rowIndexA, "amountInMultisig");
inputsGridPane.add(new Label(""), 0, ++rowIndexA); // spacer
buyerPayoutAmount = addInputTextField(inputsGridPane, rowIndexA, "buyerPayoutAmount");
sellerPayoutAmount = addInputTextField(inputsGridPane, rowIndexA, "sellerPayoutAmount");
txFee = addInputTextField(inputsGridPane, rowIndexA, "Tx fee");
txFee.setEditable(false);
txFeePct = addInputTextField(inputsGridPane, rowIndexA, "Tx fee %");
txFeePct.setEditable(false);
PercentageNumberValidator validator = new PercentageNumberValidator();
validator.setMaxValue(10D);
txFeePct.setValidator(validator);
HBox hBox = new HBox(12, buyerPayoutAmount, sellerPayoutAmount, txFee, txFeePct);
hBox.setAlignment(Pos.BASELINE_LEFT);
hBox.setPrefWidth(800);
inputsGridPane.add(hBox, 0, ++rowIndexA);
buyerAddressString = addInputTextField(inputsGridPane, ++rowIndexA, "buyerPayoutAddress");
sellerAddressString = addInputTextField(inputsGridPane, ++rowIndexA, "sellerPayoutAddress");
buyerPubKeyAsHex = addInputTextField(inputsGridPane, ++rowIndexA, "buyerPubKeyAsHex");
sellerPubKeyAsHex = addInputTextField(inputsGridPane, ++rowIndexA, "sellerPubKeyAsHex");
depositTxHex.setPrefWidth(800);
depositTxLegacy.setAllowIndeterminate(false);
depositTxLegacy.setSelected(false);
depositTxHex.setValidator(new LengthValidator(HEX_HASH_LENGTH, HEX_HASH_LENGTH));
buyerAddressString.setValidator(new LengthValidator(20, 80));
sellerAddressString.setValidator(new LengthValidator(20, 80));
buyerPubKeyAsHex.setValidator(new LengthValidator(HEX_PUBKEY_LENGTH, HEX_PUBKEY_LENGTH));
sellerPubKeyAsHex.setValidator(new LengthValidator(HEX_PUBKEY_LENGTH, HEX_PUBKEY_LENGTH));
}
private void addImportPane() {
int rowIndexB = 0;
importTxGridPane = new GridPane();
gridPane.add(importTxGridPane, 1, rowIndex);
importHex = new HavenoTextArea();
importHex.setEditable(true);
importHex.setWrapText(true);
importHex.setPrefSize(800, 150);
importTxGridPane.add(importHex, 0, ++rowIndexB);
importTxGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
Button buttonImport = new AutoTooltipButton("Import From String");
buttonImport.setOnAction(e -> {
// here we need to populate the "inputs" fields from the data contained in the TextArea
if (doImport(importHex.getText())) {
// switch back to the inputs pane
hideAllPanes();
inputsGridPane.setVisible(true);
}
});
HBox hBox = new HBox(12, buttonImport);
hBox.setAlignment(Pos.BASELINE_CENTER);
hBox.setPrefWidth(800);
importTxGridPane.add(hBox, 0, ++rowIndexB);
importTxGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
final Separator separator = new Separator(Orientation.HORIZONTAL);
separator.setPadding(new Insets(10, 10, 10, 10));
importTxGridPane.add(separator, 0, ++rowIndexB);
importTxGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
final Tuple2<Label, ComboBox<String>> xTuple = addTopLabelComboBox(importTxGridPane, rowIndexB, "Mediation Ticket", "", 0);
mediationDropDown = xTuple.second;
recentTickets = addCheckBox(importTxGridPane, rowIndexB, "Recent Tickets");
recentTickets.setSelected(true);
HBox hBox2 = new HBox(12, mediationDropDown, recentTickets);
hBox2.setAlignment(Pos.BASELINE_CENTER);
hBox2.setPrefWidth(800);
importTxGridPane.add(hBox2, 0, ++rowIndexB);
populateMediationTicketCombo(recentTickets.isSelected());
recentTickets.setOnAction(e -> {
populateMediationTicketCombo(recentTickets.isSelected());
});
importTxGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
Button buttonImportTicket = new AutoTooltipButton("Import From Mediation Ticket");
buttonImportTicket.setOnAction(e -> {
// here we need to populate the "inputs" fields from the chosen mediator ticket
importFromMediationTicket(mediationDropDown.getValue());
});
HBox hBox3 = new HBox(12, buttonImportTicket);
hBox3.setAlignment(Pos.BASELINE_CENTER);
hBox3.setPrefWidth(800);
importTxGridPane.add(hBox3, 0, ++rowIndexB);
}
private void addExportPane() {
exportTxGridPane = new GridPane();
gridPane.add(exportTxGridPane, 1, rowIndex);
exportHex = new HavenoTextArea();
exportHex.setEditable(false);
exportHex.setWrapText(true);
exportHex.setPrefSize(800, 250);
exportTxGridPane.add(exportHex, 0, 1);
}
private void addSignPane() {
int rowIndexB = 0;
signTxGridPane = new GridPane();
gridPane.add(signTxGridPane, 1, rowIndex);
privateKeyHex = addInputTextField(inputsGridPane, ++rowIndexB, "privateKeyHex");
signTxGridPane.add(privateKeyHex, 0, ++rowIndexB);
signatureHex = addInputTextField(signTxGridPane, ++rowIndexB, "signatureHex");
signatureHex.setPrefWidth(800);
signatureHex.setEditable(false);
Label copyIcon = new Label();
copyIcon.setTooltip(new Tooltip(Res.get("txIdTextField.copyIcon.tooltip")));
AwesomeDude.setIcon(copyIcon, AwesomeIcon.COPY);
copyIcon.getStyleClass().addAll("icon", "highlight");
copyIcon.setMinWidth(20);
copyIcon.setOnMouseClicked(mouseEvent -> Utilities.copyToClipboard(signatureHex.getText()));
HBox hBoxSig = new HBox(12, signatureHex, copyIcon);
hBoxSig.setAlignment(Pos.BASELINE_LEFT);
hBoxSig.setPrefWidth(800);
signTxGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
signTxGridPane.add(hBoxSig, 0, ++rowIndexB);
signTxGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
Button buttonLocate = new AutoTooltipButton("Locate key in wallet");
Button buttonSign = new AutoTooltipButton("Generate Signature");
HBox hBox = new HBox(12, buttonLocate, buttonSign);
hBox.setAlignment(Pos.BASELINE_CENTER);
hBox.setPrefWidth(800);
signTxGridPane.add(hBox, 0, ++rowIndexB);
buttonLocate.setOnAction(e -> {
if (!validateInputFields()) {
signatureHex.setText("You need to fill in the inputs tab first");
return;
}
String walletInfo = walletsManager.getWalletsAsString(true);
String privateKeyText = findPrivForPubOrAddress(walletInfo, buyerPubKeyAsHex.getText());
if (privateKeyText == null) {
privateKeyText = findPrivForPubOrAddress(walletInfo, sellerPubKeyAsHex.getText());
}
if (privateKeyText == null) {
privateKeyText = "Not found in wallet";
}
privateKeyHex.setText(privateKeyText);
});
buttonSign.setOnAction(e -> {
signatureHex.setText(generateSignature());
});
}
private void addBuildPane() {
buildTxGridPane = new GridPane();
gridPane.add(buildTxGridPane, 1, rowIndex);
int rowIndexA = 0;
buyerSignatureAsHex = addInputTextField(buildTxGridPane, ++rowIndexA, "buyerSignatureAsHex");
sellerSignatureAsHex = addInputTextField(buildTxGridPane, ++rowIndexA, "sellerSignatureAsHex");
buildTxGridPane.add(new Label(""), 0, ++rowIndexA); // spacer
finalSignedTxHex = new HavenoTextArea();
finalSignedTxHex.setEditable(false);
finalSignedTxHex.setWrapText(true);
finalSignedTxHex.setPrefSize(800, 250);
buildTxGridPane.add(finalSignedTxHex, 0, ++rowIndexA);
buildTxGridPane.add(new Label(""), 0, ++rowIndexA); // spacer
Button buttonBuild = new AutoTooltipButton("Build");
Button buttonBroadcast = new AutoTooltipButton("Broadcast");
HBox hBox = new HBox(12, buttonBuild, buttonBroadcast);
hBox.setAlignment(Pos.BASELINE_CENTER);
hBox.setPrefWidth(800);
buildTxGridPane.add(hBox, 0, ++rowIndexA);
buttonBuild.setOnAction(e -> {
finalSignedTxHex.setText(buildFinalTx(false));
});
buttonBroadcast.setOnAction(e -> {
finalSignedTxHex.setText(buildFinalTx(true));
});
}
private GridPane addSignVerifyMsgPane(GridPane myGridPane) {
int rowIndexB = 0;
gridPane.add(myGridPane, 1, rowIndex);
TextArea messageText = new HavenoTextArea();
messageText.setPromptText("Message");
messageText.setEditable(true);
messageText.setWrapText(true);
messageText.setPrefSize(800, 150);
myGridPane.add(messageText, 0, ++rowIndexB);
myGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
InputTextField address = addInputTextField(myGridPane, ++rowIndexB, "Address");
myGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
TextArea messageSig = new HavenoTextArea();
messageSig.setPromptText("Signature");
messageSig.setEditable(true);
messageSig.setWrapText(true);
messageSig.setPrefSize(800, 65);
myGridPane.add(messageSig, 0, ++rowIndexB);
myGridPane.add(new Label(""), 0, ++rowIndexB); // spacer
Button buttonSign = new AutoTooltipButton("Sign");
Button buttonVerify = new AutoTooltipButton("Verify");
HBox buttonBox = new HBox(12, buttonSign, buttonVerify);
buttonBox.setAlignment(Pos.BASELINE_CENTER);
buttonBox.setPrefWidth(800);
myGridPane.add(buttonBox, 0, ++rowIndexB);
buttonSign.setOnAction(e -> {
String walletInfo = walletsManager.getWalletsAsString(true);
String privKeyHex = findPrivForPubOrAddress(walletInfo, address.getText());
if (privKeyHex == null) {
messageSig.setText("");
new Popup().information("Key not found in wallet").show();
} else {
ECKey myPrivateKey = ECKey.fromPrivate(Utils.HEX.decode(privKeyHex));
String signatureBase64 = myPrivateKey.signMessage(messageText.getText());
messageSig.setText(signatureBase64);
}
});
buttonVerify.setOnAction(e -> {
try {
ECKey key = ECKey.signedMessageToKey(messageText.getText(), messageSig.getText());
Address address1 = Address.fromKey(Config.baseCurrencyNetworkParameters(), key, Script.ScriptType.P2PKH);
Address address2 = Address.fromKey(Config.baseCurrencyNetworkParameters(), key, Script.ScriptType.P2WPKH);
if (address.getText().equalsIgnoreCase(address1.toString()) ||
address.getText().equalsIgnoreCase(address2.toString())) {
new Popup().information("Signature verified").show();
} else {
new Popup().warning("Wrong signature").show();
}
} catch (SignatureException ex) {
log.warn(ex.toString());
new Popup().warning("Wrong signature").show();
}
});
return myGridPane;
}
private void hideAllPanes() {
inputsGridPane.setVisible(false);
importTxGridPane.setVisible(false);
exportTxGridPane.setVisible(false);
signTxGridPane.setVisible(false);
buildTxGridPane.setVisible(false);
signVerifyMsgGridPane.setVisible(false);
}
private void populateMediationTicketCombo(boolean recentTicketsOnly) {
Instant twoWeeksAgo = Instant.ofEpochSecond(Instant.now().getEpochSecond() - TimeUnit.DAYS.toSeconds(14));
disputeObservableList = mediationManager.getDisputesAsObservableList();
ObservableList<String> disputeIds = FXCollections.observableArrayList();
for (Dispute dispute :disputeObservableList) {
if (dispute.getDisputePayoutTxId() != null) // only show disputes not paid out
continue;
if (recentTicketsOnly && dispute.getOpeningDate().toInstant().isBefore(twoWeeksAgo))
continue;
if (!disputeIds.contains(dispute.getTradeId()))
disputeIds.add(dispute.getTradeId());
}
disputeIds.sort((a, b) -> a.compareTo(b));
mediationDropDown.setItems(disputeIds);
}
private void clearInputFields() {
depositTxHex.setText("");
amountInMultisig.setText("");
buyerPayoutAmount.setText("");
sellerPayoutAmount.setText("");
buyerAddressString.setText("");
sellerAddressString.setText("");
buyerPubKeyAsHex.setText("");
sellerPubKeyAsHex.setText("");
}
private boolean validateInputFields() {
return (depositTxHex.getText().length() == HEX_HASH_LENGTH &&
amountInMultisig.getText().length() > 0 &&
buyerPayoutAmount.getText().length() > 0 &&
sellerPayoutAmount.getText().length() > 0 &&
txFee.getText().length() > 0 &&
buyerAddressString.getText().length() > 0 &&
sellerAddressString.getText().length() > 0 &&
buyerPubKeyAsHex.getText().length() == HEX_PUBKEY_LENGTH &&
sellerPubKeyAsHex.getText().length() == HEX_PUBKEY_LENGTH &&
txFeePct.getValidator().validate(txFeePct.getText()).isValid);
}
private boolean validateInputFieldsAndSignatures() {
return (validateInputFields() &&
buyerSignatureAsHex.getText().length() > 0 &&
sellerSignatureAsHex.getText().length() > 0);
}
private Coin getInputFieldAsCoin(InputTextField inputTextField) {
try {
return Coin.parseCoin(inputTextField.getText().trim());
} catch (RuntimeException ignore) {
}
return Coin.ZERO;
}
private void calculateTxFee() {
if (buyerPayoutAmount.getText().length() > 0 &&
sellerPayoutAmount.getText().length() > 0 &&
amountInMultisig.getText().length() > 0) {
Coin txFeeValue = getInputFieldAsCoin(amountInMultisig)
.subtract(getInputFieldAsCoin(buyerPayoutAmount))
.subtract(getInputFieldAsCoin(sellerPayoutAmount));
txFee.setText(txFeeValue.toPlainString());
double feePercent = (double) txFeeValue.value / getInputFieldAsCoin(amountInMultisig).value;
txFeePct.setText(String.format("%.2f", feePercent * 100));
}
}
private void openBlockExplorer(String txId) {
if (txId.length() != HEX_HASH_LENGTH)
return;
if (preferences != null) {
BlockChainExplorer blockChainExplorer = preferences.getBlockChainExplorer();
GUIUtil.openWebPage(blockChainExplorer.txUrl + txId, false);
}
}
private String findPrivForPubOrAddress(String walletInfo, String searchKey) {
// split the walletInfo into lines, strip whitespace
// look for lines beginning " addr:" followed by "DeterministicKey{pub HEX=" .... ", priv HEX="
int lineIndex = 0;
while (lineIndex < walletInfo.length() && lineIndex != -1) {
lineIndex = walletInfo.indexOf(" addr:", lineIndex);
if (lineIndex == -1) {
return null;
}
int toIndex = walletInfo.indexOf("}", lineIndex);
if (toIndex == -1) {
return null;
}
String candidate1 = walletInfo.substring(lineIndex, toIndex);
lineIndex = toIndex;
// do we have the search key?
if (candidate1.indexOf(searchKey, 0) > -1) {
int startOfPriv = candidate1.indexOf("priv HEX=", 0);
if (startOfPriv > -1) {
return candidate1.substring(startOfPriv + 9, startOfPriv + 9 + HEX_HASH_LENGTH);
}
}
}
return null;
}
private String generateExportText() {
// check that all input fields have been entered, except signatures
ArrayList<String> fieldList = new ArrayList<>();
fieldList.add(depositTxLegacy.isSelected() ? "legacy" : "segwit");
fieldList.add(depositTxHex.getText());
fieldList.add(amountInMultisig.getText());
fieldList.add(buyerPayoutAmount.getText());
fieldList.add(sellerPayoutAmount.getText());
fieldList.add(buyerAddressString.getText());
fieldList.add(sellerAddressString.getText());
fieldList.add(buyerPubKeyAsHex.getText());
fieldList.add(sellerPubKeyAsHex.getText());
for (String item : fieldList) {
if (item.length() < 1) {
return "You need to fill in the inputs first";
}
}
String listString = String.join(":", fieldList);
String base64encoded = Base64.encode(listString.getBytes());
return base64encoded;
}
private boolean doImport(String importedText) {
try {
clearInputFields();
String decoded = new String(Base64.decode(importedText.replaceAll("\\s+", "")), Charset.forName("UTF-8"));
String splitArray[] = decoded.split(":");
if (splitArray.length < 9) {
importHex.setText("Import failed - data format incorrect");
return false;
}
int fieldIndex = 0;
depositTxLegacy.setSelected(splitArray[fieldIndex++].equalsIgnoreCase("legacy"));
depositTxHex.setText(splitArray[fieldIndex++]);
amountInMultisig.setText(splitArray[fieldIndex++]);
buyerPayoutAmount.setText(splitArray[fieldIndex++]);
sellerPayoutAmount.setText(splitArray[fieldIndex++]);
buyerAddressString.setText(splitArray[fieldIndex++]);
sellerAddressString.setText(splitArray[fieldIndex++]);
buyerPubKeyAsHex.setText(splitArray[fieldIndex++]);
sellerPubKeyAsHex.setText(splitArray[fieldIndex++]);
calculateTxFee();
} catch (IllegalArgumentException e) {
importHex.setText("Import failed - base64 string incorrect");
return false;
}
return true;
}
private void importFromMediationTicket(String tradeId) {
throw new RuntimeException("ManualPayoutTxWindow.importFromMediationTicket() not adapted to XMR");
// clearInputFields();
// Optional<Dispute> optionalDispute = mediationManager.findDispute(tradeId);
// if (optionalDispute.isPresent()) {
// Dispute dispute = optionalDispute.get();
// depositTxHex.setText(dispute.getDepositTxId());
// if (dispute.disputeResultProperty().get() != null) {
// buyerPayoutAmount.setText(dispute.disputeResultProperty().get().getBuyerPayoutAmount().toPlainString());
// sellerPayoutAmount.setText(dispute.disputeResultProperty().get().getSellerPayoutAmount().toPlainString());
// }
// buyerAddressString.setText(dispute.getContract().getBuyerPayoutAddressString());
// sellerAddressString.setText(dispute.getContract().getSellerPayoutAddressString());
// buyerPubKeyAsHex.setText(Utils.HEX.encode(dispute.getContract().getBuyerMultiSigPubKey()));
// sellerPubKeyAsHex.setText(Utils.HEX.encode(dispute.getContract().getSellerMultiSigPubKey()));
// // switch back to the inputs pane
// hideAllPanes();
// inputsGridPane.setVisible(true);
// UserThread.execute(() -> new Popup().warning("Ticket imported. You still need to enter the multisig amount and specify if it is a legacy Tx").show());
// }
}
private String generateSignature() {
calculateTxFee();
// check that all input fields have been entered, except signatures
if (!validateInputFields() || privateKeyHex.getText().length() < 1) {
return "You need to fill in the inputs first";
}
String retVal = "";
try {
Tuple2<String, String> combined = tradeWalletService.emergencyBuildPayoutTxFrom2of2MultiSig(depositTxHex.getText(),
getInputFieldAsCoin(buyerPayoutAmount),
getInputFieldAsCoin(sellerPayoutAmount),
getInputFieldAsCoin(txFee),
buyerAddressString.getText(),
sellerAddressString.getText(),
buyerPubKeyAsHex.getText(),
sellerPubKeyAsHex.getText(),
depositTxLegacy.isSelected());
String redeemScriptHex = combined.first;
String unsignedTxHex = combined.second;
retVal = tradeWalletService.emergencyGenerateSignature(
unsignedTxHex,
redeemScriptHex,
getInputFieldAsCoin(amountInMultisig),
privateKeyHex.getText());
} catch (IllegalArgumentException ee) {
log.error(ee.toString());
ee.printStackTrace();
UserThread.execute(() -> new Popup().warning(ee.toString()).show());
}
return retVal;
}
private String buildFinalTx(boolean broadcastIt) {
String retVal = "";
calculateTxFee();
// check that all input fields have been entered, including signatures
if (!validateInputFieldsAndSignatures()) {
retVal = "You need to fill in the inputs first";
} else {
try {
// grab data from the inputs pane, build an unsigned tx and write it to the TextArea
Tuple2<String, String> combined = tradeWalletService.emergencyBuildPayoutTxFrom2of2MultiSig(depositTxHex.getText(),
getInputFieldAsCoin(buyerPayoutAmount),
getInputFieldAsCoin(sellerPayoutAmount),
getInputFieldAsCoin(txFee),
buyerAddressString.getText(),
sellerAddressString.getText(),
buyerPubKeyAsHex.getText(),
sellerPubKeyAsHex.getText(),
depositTxLegacy.isSelected());
String redeemScriptHex = combined.first;
String unsignedTxHex = combined.second;
Tuple2<String, String> txIdAndHex = tradeWalletService.emergencyApplySignatureToPayoutTxFrom2of2MultiSig(
unsignedTxHex,
redeemScriptHex,
buyerSignatureAsHex.getText(),
sellerSignatureAsHex.getText(),
depositTxLegacy.isSelected());
retVal = "txId:{" + txIdAndHex.first + "}\r\ntxHex:{" + txIdAndHex.second + "}";
if (broadcastIt) {
TxBroadcaster.Callback callback = new TxBroadcaster.Callback() {
@Override
public void onSuccess(@Nullable Transaction result) {
log.info("onSuccess");
UserThread.execute(() -> {
String txId = result != null ? result.getTxId().toString() : "null";
new Popup().information("Transaction successfully published. Transaction ID: " + txId).show();
});
}
@Override
public void onFailure(TxBroadcastException exception) {
log.error(exception.toString());
UserThread.execute(() -> new Popup().warning(exception.toString()).show());
}
};
if (GUIUtil.isReadyForTxBroadcastOrShowPopup(p2PService, connectionService)) {
try {
tradeWalletService.emergencyPublishPayoutTxFrom2of2MultiSig(
txIdAndHex.second,
callback);
} catch (AddressFormatException | WalletException | TransactionVerificationException ee) {
log.error(ee.toString());
ee.printStackTrace();
UserThread.execute(() -> new Popup().warning(ee.toString()).show());
}
}
}
} catch (IllegalArgumentException | SignatureDecodeException | VerificationException ee) {
log.error(ee.toString());
ee.printStackTrace();
retVal = ee.toString();
}
}
return retVal;
}
}

View file

@ -1,118 +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 <http://www.gnu.org/licenses/>.
*/
package haveno.desktop.main.overlays.windows;
import haveno.core.locale.Res;
import haveno.core.trade.txproof.xmr.XmrTxProofModel;
import haveno.core.util.validation.RegexValidator;
import haveno.desktop.components.InputTextField;
import haveno.desktop.main.overlays.Overlay;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import lombok.Getter;
import javax.annotation.Nullable;
import static haveno.common.app.DevEnv.isDevMode;
import static haveno.desktop.util.FormBuilder.addInputTextField;
import static haveno.desktop.util.FormBuilder.addMultilineLabel;
import static javafx.beans.binding.Bindings.createBooleanBinding;
public class SetXmrTxKeyWindow extends Overlay<SetXmrTxKeyWindow> {
private InputTextField txHashInputTextField, txKeyInputTextField;
@Getter
private RegexValidator regexValidator;
public SetXmrTxKeyWindow() {
type = Type.Attention;
}
public void show() {
if (headLine == null)
headLine = Res.get("setXMRTxKeyWindow.headline");
width = 868;
createGridPane();
addHeadLine();
addContent();
addButtons();
regexValidator = new RegexValidator();
regexValidator.setPattern("[a-fA-F0-9]{64}|^$");
regexValidator.setErrorMessage(Res.get("portfolio.pending.step2_buyer.confirmStart.proof.invalidInput"));
txHashInputTextField.setValidator(regexValidator);
txKeyInputTextField.setValidator(regexValidator);
if (isDevMode()) {
// pre-populate the fields with test data when in dev mode
txHashInputTextField.setText(XmrTxProofModel.DEV_TX_HASH);
txKeyInputTextField.setText(XmrTxProofModel.DEV_TX_KEY);
}
actionButton.disableProperty().bind(createBooleanBinding(() -> {
String txHash = txHashInputTextField.getText();
String txKey = txKeyInputTextField.getText();
// If a field is empty we allow to continue. We do not enforce that users send the data.
if (txHash.isEmpty() || txKey.isEmpty()) {
return false;
}
// Otherwise we require that input is valid
return !txHashInputTextField.getValidator().validate(txHash).isValid ||
!txKeyInputTextField.getValidator().validate(txKey).isValid;
},
txHashInputTextField.textProperty(), txKeyInputTextField.textProperty()));
applyStyles();
display();
}
@Override
protected void createGridPane() {
gridPane = new GridPane();
gridPane.setHgap(5);
gridPane.setVgap(5);
gridPane.setPadding(new Insets(64, 64, 64, 64));
gridPane.setPrefWidth(width);
ColumnConstraints columnConstraints1 = new ColumnConstraints();
columnConstraints1.setHalignment(HPos.RIGHT);
columnConstraints1.setHgrow(Priority.SOMETIMES);
gridPane.getColumnConstraints().addAll(columnConstraints1);
}
@Nullable
public String getTxHash() {
return txHashInputTextField != null ? txHashInputTextField.getText() : null;
}
@Nullable
public String getTxKey() {
return txKeyInputTextField != null ? txKeyInputTextField.getText() : null;
}
private void addContent() {
addMultilineLabel(gridPane, ++rowIndex, Res.get("setXMRTxKeyWindow.note"), 0);
txHashInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("setXMRTxKeyWindow.txHash"), 10);
txKeyInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("setXMRTxKeyWindow.txKey"));
}
}

View file

@ -29,7 +29,6 @@ import haveno.core.trade.Contract;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade;
import haveno.core.trade.TradeManager;
import haveno.core.trade.txproof.AssetTxProofResult;
import haveno.core.util.FormattingUtils;
import haveno.core.util.VolumeUtil;
import haveno.core.util.coin.CoinFormatter;
@ -38,7 +37,6 @@ import haveno.desktop.components.HavenoTextArea;
import haveno.desktop.main.MainView;
import haveno.desktop.main.overlays.Overlay;
import haveno.desktop.util.DisplayUtils;
import haveno.desktop.util.GUIUtil;
import haveno.desktop.util.Layout;
import haveno.network.p2p.NodeAddress;
import javafx.beans.property.IntegerProperty;
@ -64,7 +62,6 @@ import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
import static com.google.common.base.Preconditions.checkNotNull;
import static haveno.desktop.util.DisplayUtils.getAccountWitnessDescription;
import static haveno.desktop.util.FormBuilder.add2ButtonsWithBox;
import static haveno.desktop.util.FormBuilder.addConfirmationLabelTextArea;
@ -188,10 +185,6 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
rows++;
}
boolean showXmrProofResult = checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR") &&
trade.getAssetTxProofResult() != null &&
trade.getAssetTxProofResult() != AssetTxProofResult.UNDEFINED;
if (trade.getPayoutTxId() != null)
rows++;
boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() &&
@ -202,8 +195,6 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
rows += 2;
if (trade.getTradePeerNodeAddress() != null)
rows++;
if (showXmrProofResult)
rows++;
addTitledGroupBg(gridPane, ++rowIndex, rows, Res.get("shared.details"), Layout.GROUP_DISTANCE);
addConfirmationLabelTextField(gridPane, rowIndex, Res.get("shared.tradeId"),
@ -230,14 +221,6 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
addConfirmationLabelTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.tradePeersOnion"),
trade.getTradePeerNodeAddress().getFullAddress());
if (showXmrProofResult) {
// As the window is already overloaded we replace the tradePeersPubKeyHash field with the auto-conf state
// if XMR is the currency
addConfirmationLabelTextField(gridPane, ++rowIndex,
Res.get("portfolio.pending.step3_seller.autoConf.status.label"),
GUIUtil.getProofResultAsString(trade.getAssetTxProofResult()));
}
if (contract != null) {
buyersAccountAge = getAccountWitnessDescription(accountAgeWitnessService, offer.getPaymentMethod(), buyerPaymentAccountPayload, contract.getBuyerPubKeyRing());
sellersAccountAge = getAccountWitnessDescription(accountAgeWitnessService, offer.getPaymentMethod(), sellerPaymentAccountPayload, contract.getSellerPubKeyRing());

View file

@ -96,11 +96,9 @@ import haveno.desktop.components.paymentmethods.VerseForm;
import haveno.desktop.components.paymentmethods.WeChatPayForm;
import haveno.desktop.components.paymentmethods.WesternUnionForm;
import haveno.desktop.main.overlays.popups.Popup;
import haveno.desktop.main.overlays.windows.SetXmrTxKeyWindow;
import haveno.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
import haveno.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
import haveno.desktop.util.Layout;
import haveno.desktop.util.Transitions;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
@ -110,7 +108,6 @@ import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.google.common.base.Preconditions.checkNotNull;
import static haveno.desktop.util.FormBuilder.addButtonBusyAnimationLabel;
@ -543,44 +540,11 @@ public class BuyerStep2View extends TradeStepView {
} else {
showConfirmPaymentSentPopup();
}
} else if (sellersPaymentAccountPayload instanceof AssetAccountPayload && isXmrTrade()) {
SetXmrTxKeyWindow setXmrTxKeyWindow = new SetXmrTxKeyWindow();
setXmrTxKeyWindow
.actionButtonText(Res.get("portfolio.pending.step2_buyer.confirmStart.headline"))
.onAction(() -> {
String txKey = setXmrTxKeyWindow.getTxKey();
String txHash = setXmrTxKeyWindow.getTxHash();
if (txKey == null || txHash == null || txKey.isEmpty() || txHash.isEmpty()) {
UserThread.runAfter(this::showProofWarningPopup, Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
return;
}
trade.setCounterCurrencyExtraData(txKey);
trade.setCounterCurrencyTxId(txHash);
model.dataModel.getTradeManager().requestPersistence();
showConfirmPaymentSentPopup();
})
.closeButtonText(Res.get("shared.cancel"))
.onClose(setXmrTxKeyWindow::hide)
.show();
} else {
showConfirmPaymentSentPopup();
}
}
private void showProofWarningPopup() {
Popup popup = new Popup();
popup.headLine(Res.get("portfolio.pending.step2_buyer.confirmStart.proof.warningTitle"))
.confirmation(Res.get("portfolio.pending.step2_buyer.confirmStart.proof.noneProvided"))
.width(700)
.actionButtonText(Res.get("portfolio.pending.step2_buyer.confirmStart.warningButton"))
.onAction(this::showConfirmPaymentSentPopup)
.closeButtonText(Res.get("shared.cancel"))
.onClose(popup::hide)
.show();
}
private void showConfirmPaymentSentPopup() {
String key = "confirmPaymentSent";
if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) {

View file

@ -21,7 +21,6 @@ import com.jfoenix.controls.JFXBadge;
import haveno.common.UserThread;
import haveno.common.app.DevEnv;
import haveno.core.locale.Res;
import haveno.core.trade.txproof.AssetTxProofResult;
import haveno.core.user.DontShowAgainLookup;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.desktop.components.AutoTooltipButton;
@ -88,15 +87,11 @@ public class BuyerStep4View extends TradeStepView {
} else {
completedTradeLabel.setText(Res.get("portfolio.pending.step5_buyer.groupTitle"));
}
JFXBadge autoConfBadge = new JFXBadge(new Label(""), Pos.BASELINE_RIGHT);
autoConfBadge.setText(Res.get("portfolio.pending.autoConf"));
autoConfBadge.getStyleClass().add("auto-conf");
HBox hBox2 = new HBox(1, completedTradeLabel, autoConfBadge);
HBox hBox2 = new HBox(1, completedTradeLabel);
GridPane.setMargin(hBox2, new Insets(18, -10, -12, -10));
gridPane.getChildren().add(hBox2);
GridPane.setRowSpan(hBox2, 5);
autoConfBadge.setVisible(AssetTxProofResult.COMPLETED == trade.getAssetTxProofResult());
if (trade.getDisputeState().isNotDisputed()) {
addCompactTopLabelTextField(gridPane, gridRow, getBtcTradeAmountLabel(), model.getTradeVolume(), Layout.TWICE_FIRST_ROW_DISTANCE);

View file

@ -40,7 +40,6 @@ import haveno.core.payment.payload.USPostalMoneyOrderAccountPayload;
import haveno.core.payment.payload.WesternUnionAccountPayload;
import haveno.core.trade.Contract;
import haveno.core.trade.Trade;
import haveno.core.trade.txproof.AssetTxProofResult;
import haveno.core.user.DontShowAgainLookup;
import haveno.core.util.VolumeUtil;
import haveno.desktop.components.BusyAnimation;
@ -50,7 +49,6 @@ import haveno.desktop.components.indicator.TxConfidenceIndicator;
import haveno.desktop.main.overlays.popups.Popup;
import haveno.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
import haveno.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
import haveno.desktop.util.GUIUtil;
import haveno.desktop.util.Layout;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Insets;
@ -67,7 +65,6 @@ import org.fxmisc.easybind.Subscription;
import javax.annotation.Nullable;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkNotNull;
import static haveno.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup;
import static haveno.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon;
import static haveno.desktop.util.FormBuilder.addTitledGroupBg;
@ -154,15 +151,6 @@ public class SellerStep3View extends TradeStepView {
}
}
});
if (isXmrTrade()) {
proofResultListener = (observable, oldValue, newValue) -> {
applyAssetTxProofResult(trade.getAssetTxProofResult());
};
trade.getAssetTxProofResultUpdateProperty().addListener(proofResultListener);
applyAssetTxProofResult(trade.getAssetTxProofResult());
}
}
@Override
@ -179,10 +167,6 @@ public class SellerStep3View extends TradeStepView {
if (timeoutTimer != null) {
timeoutTimer.stop();
}
if (isXmrTrade()) {
trade.getAssetTxProofResultUpdateProperty().removeListener(proofResultListener);
}
}
@ -488,42 +472,6 @@ public class SellerStep3View extends TradeStepView {
}
}
private void applyAssetTxProofResult(@Nullable AssetTxProofResult result) {
checkNotNull(assetTxProofResultField);
checkNotNull(assetTxConfidenceIndicator);
String txt = GUIUtil.getProofResultAsString(result);
assetTxProofResultField.setText(txt);
if (result == null) {
assetTxConfidenceIndicator.setProgress(0);
return;
}
switch (result) {
case PENDING:
case COMPLETED:
if (result.getNumRequiredConfirmations() > 0) {
int numRequiredConfirmations = result.getNumRequiredConfirmations();
int numConfirmations = result.getNumConfirmations();
if (numConfirmations == 0) {
assetTxConfidenceIndicator.setProgress(-1);
} else {
double progress = Math.min(1, (double) numConfirmations / (double) numRequiredConfirmations);
assetTxConfidenceIndicator.setProgress(progress);
assetTxConfidenceIndicator.getTooltip().setText(
Res.get("portfolio.pending.autoConf.blocks",
numConfirmations, numRequiredConfirmations));
}
}
break;
default:
// Set invisible by default
assetTxConfidenceIndicator.setProgress(0);
break;
}
}
private Label createPopoverLabel(String text) {
Label label = new Label(text);
label.setPrefWidth(600);

View file

@ -49,7 +49,6 @@ import haveno.core.payment.PaymentAccount;
import haveno.core.payment.PaymentAccountList;
import haveno.core.payment.payload.PaymentMethod;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.txproof.AssetTxProofResult;
import haveno.core.user.DontShowAgainLookup;
import haveno.core.user.Preferences;
import haveno.core.user.User;
@ -104,7 +103,6 @@ import org.bitcoinj.core.Coin;
import org.bitcoinj.uri.BitcoinURI;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -1018,35 +1016,6 @@ public class GUIUtil {
MaterialDesignIcon.APPROVAL : MaterialDesignIcon.ALERT_CIRCLE_OUTLINE;
}
public static String getProofResultAsString(@Nullable AssetTxProofResult result) {
if (result == null) {
return "";
}
String key = "portfolio.pending.autoConf.state." + result.name();
switch (result) {
case UNDEFINED:
return "";
case FEATURE_DISABLED:
return Res.get(key, result.getDetails());
case TRADE_LIMIT_EXCEEDED:
return Res.get(key);
case INVALID_DATA:
return Res.get(key, result.getDetails());
case PAYOUT_TX_ALREADY_PUBLISHED:
case DISPUTE_OPENED:
case REQUESTS_STARTED:
return Res.get(key);
case PENDING:
return Res.get(key, result.getNumSuccessResults(), result.getNumRequiredSuccessResults(), result.getDetails());
case COMPLETED:
case ERROR:
case FAILED:
return Res.get(key);
default:
return result.name();
}
}
public static ScrollPane createScrollPane() {
ScrollPane scrollPane = new ScrollPane();
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);