diff --git a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java index 0381de4e30..ead2649045 100644 --- a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java +++ b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java @@ -827,6 +827,137 @@ public class TradeWalletService { } + // Emergency payout tool. Used only in cased when the payput from the arbitrator does not work because some data + // in the trade/dispute are messed up. + public Transaction emergencySignAndPublishPayoutTx(String depositTxHex, + Coin buyerPayoutAmount, + Coin sellerPayoutAmount, + Coin arbitratorPayoutAmount, + String buyerAddressString, + String sellerAddressString, + String arbitratorAddressString, + @Nullable String buyerPrivateKeyAsHex, + @Nullable String sellerPrivateKeyAsHex, + String arbitratorPrivateKeyAsHex, + String buyerPubKeyAsHex, + String sellerPubKeyAsHex, + String arbitratorPubKeyAsHex, + String P2SHMultiSigOutputScript, + List buyerPubKeys, + List sellerPubKeys, + FutureCallback callback) + throws AddressFormatException, TransactionVerificationException, WalletException { + log.trace("signAndPublishPayoutTx called"); + log.trace("depositTxHex " + depositTxHex); + log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString()); + log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString()); + log.trace("arbitratorPayoutAmount " + arbitratorPayoutAmount.toFriendlyString()); + log.trace("buyerAddressString " + buyerAddressString); + log.trace("sellerAddressString " + sellerAddressString); + log.trace("arbitratorAddressString " + arbitratorAddressString); + log.trace("buyerPrivateKeyAsHex " + buyerPrivateKeyAsHex); + log.trace("sellerPrivateKeyAsHex " + sellerPrivateKeyAsHex); + log.trace("arbitratorPrivateKeyAsHex " + arbitratorPrivateKeyAsHex); + log.trace("buyerPubKeyAsHex " + buyerPubKeyAsHex); + log.trace("sellerPubKeyAsHex " + sellerPubKeyAsHex); + log.trace("arbitratorPubKeyAsHex " + arbitratorPubKeyAsHex); + + log.trace("P2SHMultiSigOutputScript " + P2SHMultiSigOutputScript); + log.trace("buyerPubKeys " + buyerPubKeys); + log.trace("sellerPubKeys " + sellerPubKeys); + + checkNotNull((buyerPrivateKeyAsHex != null || sellerPrivateKeyAsHex != null), "either buyerPrivateKeyAsHex or sellerPrivateKeyAsHex must not be null"); + + byte[] buyerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(buyerPubKeyAsHex)).getPubKey(); + byte[] sellerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(sellerPubKeyAsHex)).getPubKey(); + final byte[] arbitratorPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(arbitratorPubKeyAsHex)).getPubKey(); + + Script p2SHMultiSigOutputScript = getP2SHMultiSigOutputScript(buyerPubKey, sellerPubKey, arbitratorPubKey); + + if (!p2SHMultiSigOutputScript.toString().contains(P2SHMultiSigOutputScript)) { + if (buyerPubKeys.isEmpty()) + buyerPubKeys.add(buyerPubKeyAsHex); + if (sellerPubKeys.isEmpty()) + sellerPubKeys.add(sellerPubKeyAsHex); + + boolean found = false; + for (String b : buyerPubKeys) { + if (found) + break; + byte[] bk = ECKey.fromPublicOnly(Utils.HEX.decode(b)).getPubKey(); + for (String s : sellerPubKeys) { + byte[] sk = ECKey.fromPublicOnly(Utils.HEX.decode(s)).getPubKey(); + p2SHMultiSigOutputScript = getP2SHMultiSigOutputScript(bk, sk, arbitratorPubKey); + if (p2SHMultiSigOutputScript.toString().contains(P2SHMultiSigOutputScript)) { + log.trace("Found buyers pub key " + b); + log.trace("Found sellers pub key " + s); + buyerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(b)).getPubKey(); + sellerPubKey = ECKey.fromPublicOnly(Utils.HEX.decode(s)).getPubKey(); + found = true; + break; + } + } + } + if (!found) + log.warn("We did not find any matching pub keys for generating the required p2SHMultiSigOutputScript"); + } + + Coin msOutput = buyerPayoutAmount.add(sellerPayoutAmount).add(arbitratorPayoutAmount).add(FeePolicy.getFixedTxFeeForTrades()); + TransactionOutput p2SHMultiSigOutput = new TransactionOutput(params, null, msOutput, p2SHMultiSigOutputScript.getProgram()); + Transaction depositTx = new Transaction(params); + depositTx.addOutput(p2SHMultiSigOutput); + + Transaction payoutTx = new Transaction(params); + Sha256Hash spendTxHash = Sha256Hash.wrap(depositTxHex); + payoutTx.addInput(new TransactionInput(params, depositTx, p2SHMultiSigOutputScript.getProgram(), new TransactionOutPoint(params, 0, spendTxHash), msOutput)); + + if (buyerPayoutAmount.isGreaterThan(Coin.ZERO)) + payoutTx.addOutput(buyerPayoutAmount, new Address(params, buyerAddressString)); + if (sellerPayoutAmount.isGreaterThan(Coin.ZERO)) + payoutTx.addOutput(sellerPayoutAmount, new Address(params, sellerAddressString)); + if (arbitratorPayoutAmount.isGreaterThan(Coin.ZERO)) + payoutTx.addOutput(arbitratorPayoutAmount, new Address(params, arbitratorAddressString)); + + // take care of sorting! + Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey); + Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); + + ECKey.ECDSASignature tradersSignature; + if (buyerPrivateKeyAsHex != null && !buyerPrivateKeyAsHex.isEmpty()) { + final ECKey buyerPrivateKey = ECKey.fromPrivate(Utils.HEX.decode(buyerPrivateKeyAsHex)); + checkNotNull(buyerPrivateKey, "buyerPrivateKey must not be null"); + tradersSignature = buyerPrivateKey.sign(sigHash, aesKey).toCanonicalised(); + } else { + checkNotNull(sellerPrivateKeyAsHex, "sellerPrivateKeyAsHex must not be null"); + final ECKey sellerPrivateKey = ECKey.fromPrivate(Utils.HEX.decode(sellerPrivateKeyAsHex)); + checkNotNull(sellerPrivateKey, "sellerPrivateKey must not be null"); + tradersSignature = sellerPrivateKey.sign(sigHash, aesKey).toCanonicalised(); + } + final ECKey key = ECKey.fromPrivate(Utils.HEX.decode(arbitratorPrivateKeyAsHex)); + checkNotNull(key, "key must not be null"); + ECKey.ECDSASignature arbitratorSignature = key.sign(sigHash, aesKey).toCanonicalised(); + + TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false); + TransactionSignature arbitratorTxSig = new TransactionSignature(arbitratorSignature, Transaction.SigHash.ALL, false); + // Take care of order of signatures. See comment below at getMultiSigRedeemScript (sort order needed here: arbitrator, seller, buyer) + Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(arbitratorTxSig, tradersTxSig), redeemScript); + TransactionInput input = payoutTx.getInput(0); + input.setScriptSig(inputScript); + + printTxWithInputs("payoutTx", payoutTx); + + verifyTransaction(payoutTx); + checkWalletConsistency(); + + if (walletAppKit != null) { + ListenableFuture future = walletAppKit.peerGroup().broadcastTransaction(payoutTx).future(); + Futures.addCallback(future, callback); + } + + return payoutTx; + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Misc /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java index e7503331ef..ccaac1bfcd 100644 --- a/core/src/main/java/io/bitsquare/btc/WalletService.java +++ b/core/src/main/java/io/bitsquare/btc/WalletService.java @@ -172,6 +172,7 @@ public class WalletService { addressEntryList.onWalletReady(wallet); + walletAppKit.peerGroup().addEventListener(new PeerEventListener() { @Override public void onPeersDiscovered(Set peerAddresses) { @@ -385,7 +386,9 @@ public class WalletService { return "BitcoinJ wallet:\n" + wallet.toString(includePrivKeys, true, true, walletAppKit.chain()) + "\n\n" + "Bitsquare address entry list:\n" + - addressEntryListData.toString(); + addressEntryListData.toString() + + "All pubkeys as hex:\n" + + wallet.printAllPubKeysAsHex(); } public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java index 437fda8fce..4cfb65322c 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/TradeProtocol.java @@ -110,7 +110,7 @@ public abstract class TradeProtocol { stopTimeout(); timeoutTimer = UserThread.runAfter(() -> { - log.error("Timeout reached"); + log.error("Timeout reached. TradeID=" + trade.getId()); trade.setErrorMessage("A timeout occurred."); cleanupTradable(); cleanup(); diff --git a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java index 0327ef347a..aaee5bf64d 100644 --- a/gui/src/main/java/io/bitsquare/app/BitsquareApp.java +++ b/gui/src/main/java/io/bitsquare/app/BitsquareApp.java @@ -23,6 +23,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; import io.bitsquare.alert.AlertManager; import io.bitsquare.arbitration.ArbitratorManager; +import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.CommonOptionKeys; import io.bitsquare.common.UserThread; @@ -41,10 +42,7 @@ import io.bitsquare.gui.main.MainView; import io.bitsquare.gui.main.MainViewModel; import io.bitsquare.gui.main.debug.DebugView; import io.bitsquare.gui.main.overlays.popups.Popup; -import io.bitsquare.gui.main.overlays.windows.EmptyWalletWindow; -import io.bitsquare.gui.main.overlays.windows.FilterWindow; -import io.bitsquare.gui.main.overlays.windows.SendAlertMessageWindow; -import io.bitsquare.gui.main.overlays.windows.ShowWalletDataWindow; +import io.bitsquare.gui.main.overlays.windows.*; import io.bitsquare.gui.util.ImageUtil; import io.bitsquare.p2p.P2PService; import io.bitsquare.storage.Storage; @@ -197,27 +195,33 @@ public class BitsquareApp extends Application { stop(); }); scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEvent -> { - if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || new KeyCodeCombination(KeyCode.W, KeyCombination.CONTROL_DOWN).match(keyEvent)) { stop(); - } else if (new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + } else if (new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || new KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN).match(keyEvent)) { stop(); - } else if (new KeyCodeCombination(KeyCode.E, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + } else if (new KeyCodeCombination(KeyCode.E, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || new KeyCodeCombination(KeyCode.E, KeyCombination.CONTROL_DOWN).match(keyEvent)) { showEmptyWalletPopup(); - } else if (new KeyCodeCombination(KeyCode.M, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + } else if (new KeyCodeCombination(KeyCode.M, KeyCombination.ALT_DOWN).match(keyEvent)) { showSendAlertMessagePopup(); - } else if (new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + } else if (new KeyCodeCombination(KeyCode.F, KeyCombination.ALT_DOWN).match(keyEvent)) { showFilterPopup(); - } else if (new KeyCodeCombination(KeyCode.F, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + } else if (new KeyCodeCombination(KeyCode.F, KeyCombination.ALT_DOWN).match(keyEvent)) { showFPSWindow(); - } else if (new KeyCodeCombination(KeyCode.J, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + } else if (new KeyCodeCombination(KeyCode.J, KeyCombination.ALT_DOWN).match(keyEvent)) { WalletService walletService = injector.getInstance(WalletService.class); if (walletService.getWallet() != null) new ShowWalletDataWindow(walletService).information("Wallet raw data").show(); else new Popup<>().warning("The wallet is not initialized yet").show(); - } else if (DevFlags.DEV_MODE) { - if (new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) - showDebugWindow(); + } else if (DevFlags.DEV_MODE && new KeyCodeCombination(KeyCode.G, KeyCombination.ALT_DOWN).match(keyEvent)) { + TradeWalletService tradeWalletService = injector.getInstance(TradeWalletService.class); + WalletService walletService = injector.getInstance(WalletService.class); + if (walletService.getWallet() != null) + new SpendFromDepositTxWindow(tradeWalletService).information("Emergency wallet tool").show(); + else + new Popup<>().warning("The wallet is not initialized yet").show(); + } else if (DevFlags.DEV_MODE && new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) { + showDebugWindow(); } }); @@ -256,9 +260,14 @@ public class BitsquareApp extends Application { UserThread.runPeriodically(() -> Profiler.printSystemLoad(log), LOG_MEMORY_PERIOD_MIN, TimeUnit.MINUTES); - } catch (Throwable throwable) { + } catch ( + Throwable throwable + ) + + { showErrorPopup(throwable, false); } + } private void showSendAlertMessagePopup() { diff --git a/gui/src/main/java/io/bitsquare/gui/main/account/AccountView.java b/gui/src/main/java/io/bitsquare/gui/main/account/AccountView.java index c54fbbb57a..b2b95f07fb 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/account/AccountView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/account/AccountView.java @@ -79,7 +79,7 @@ public class AccountView extends ActivatableView { }; keyEventEventHandler = event -> { - if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event) && + if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event) && arbitratorRegistrationTab == null) { arbitratorRegistrationTab = new Tab("Arbitrator registration"); arbitratorRegistrationTab.setClosable(false); diff --git a/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java b/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java index c0934ff231..957d09de76 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/disputes/trader/TraderDisputeView.java @@ -208,7 +208,7 @@ public class TraderDisputeView extends ActivatableView { disputeDirectMessageListListener = c -> scrollToBottom(); keyEventEventHandler = event -> { - if (new KeyCodeCombination(KeyCode.L, KeyCombination.SHORTCUT_DOWN).match(event)) { + if (new KeyCodeCombination(KeyCode.L, KeyCombination.ALT_DOWN).match(event)) { Map> map = new HashMap<>(); disputeManager.getDisputesAsObservableList().stream().forEach(dispute -> { String tradeId = dispute.getTradeId(); @@ -269,14 +269,14 @@ public class TraderDisputeView extends ActivatableView { .actionButtonText("Copy") .onAction(() -> Utilities.copyToClipboard(message)) .show(); - } else if (new KeyCodeCombination(KeyCode.U, KeyCombination.SHORTCUT_DOWN).match(event)) { + } else if (new KeyCodeCombination(KeyCode.U, KeyCombination.ALT_DOWN).match(event)) { // Hidden shortcut to re-open a dispute. Allow it also for traders not only arbitrator. if (selectedDispute != null) { if (selectedDisputeClosedPropertyListener != null) selectedDispute.isClosedProperty().removeListener(selectedDisputeClosedPropertyListener); selectedDispute.setIsClosed(false); } - } else if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event)) { + } else if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event)) { if (selectedDispute != null) { PubKeyRing pubKeyRing = selectedDispute.getTraderPubKeyRing(); NodeAddress nodeAddress; diff --git a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java index 587325af40..f78305039f 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/funds/transactions/TransactionsView.java @@ -184,9 +184,9 @@ public class TransactionsView extends ActivatableView { }; keyEventEventHandler = event -> { - if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event)) + if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event)) revertTxColumn.setVisible(!revertTxColumn.isVisible()); - else if (new KeyCodeCombination(KeyCode.A, KeyCombination.SHORTCUT_DOWN).match(event)) + else if (new KeyCodeCombination(KeyCode.A, KeyCombination.ALT_DOWN).match(event)) showStatisticsPopup(); }; diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/editor/PeerInfoWithTagEditor.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/editor/PeerInfoWithTagEditor.java index 87cd1b0135..be8ca3e8c2 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/editor/PeerInfoWithTagEditor.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/editor/PeerInfoWithTagEditor.java @@ -147,7 +147,7 @@ public class PeerInfoWithTagEditor extends Overlay { inputTextField.setText(tag); keyEventEventHandler = event -> { - if (new KeyCodeCombination(KeyCode.R, KeyCombination.SHORTCUT_DOWN).match(event)) { + if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event)) { new SendPrivateNotificationWindow(offer.getPubKeyRing(), offer.getOffererNodeAddress()) .onAddAlertMessage(privateNotificationManager::sendPrivateNotificationMessageIfKeyIsValid) .show(); diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java index 07e6ded448..ae7fdf57df 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/ContractWindow.java @@ -177,7 +177,10 @@ public class ContractWindow extends Overlay { viewContractButton.setDefaultButton(false); viewContractButton.setOnAction(e -> { TextArea textArea = new TextArea(); - textArea.setText(dispute.getContractAsJson()); + String contractAsJson = dispute.getContractAsJson(); + contractAsJson += "\n\nBuyerPubKeyHex: " + Utils.HEX.encode(dispute.getContract().getBuyerBtcPubKey()); + contractAsJson += "\nSellerPubKeyHex: " + Utils.HEX.encode(dispute.getContract().getSellerBtcPubKey()); + textArea.setText(contractAsJson); textArea.setPrefHeight(50); textArea.setEditable(false); textArea.setWrapText(true); diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/SpendFromDepositTxWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/SpendFromDepositTxWindow.java new file mode 100644 index 0000000000..9bbaa6391f --- /dev/null +++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/SpendFromDepositTxWindow.java @@ -0,0 +1,193 @@ +/* + * This file is part of Bitsquare. + * + * Bitsquare 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. + * + * Bitsquare 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 Bitsquare. If not, see . + */ + +package io.bitsquare.gui.main.overlays.windows; + +import com.google.common.util.concurrent.FutureCallback; +import io.bitsquare.btc.TradeWalletService; +import io.bitsquare.btc.exceptions.TransactionVerificationException; +import io.bitsquare.btc.exceptions.WalletException; +import io.bitsquare.common.UserThread; +import io.bitsquare.gui.components.InputTextField; +import io.bitsquare.gui.main.overlays.Overlay; +import io.bitsquare.gui.main.overlays.popups.Popup; +import javafx.scene.Scene; +import javafx.scene.input.KeyCode; +import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.Coin; +import org.bitcoinj.core.Transaction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; + +import static io.bitsquare.gui.util.FormBuilder.addLabelInputTextField; + +public class SpendFromDepositTxWindow extends Overlay { + private static final Logger log = LoggerFactory.getLogger(SpendFromDepositTxWindow.class); + private TradeWalletService tradeWalletService; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Public API + /////////////////////////////////////////////////////////////////////////////////////////// + + public SpendFromDepositTxWindow(TradeWalletService tradeWalletService) { + this.tradeWalletService = tradeWalletService; + type = Type.Attention; + } + + public void show() { + if (headLine == null) + headLine = "Emergency MS payout tool"; + + width = 1000; + createGridPane(); + addHeadLine(); + addSeparator(); + addContent(); + addCloseButton(); + applyStyles(); + display(); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Protected + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + protected void setupKeyHandler(Scene scene) { + if (!hideCloseButton) { + scene.setOnKeyPressed(e -> { + if (e.getCode() == KeyCode.ESCAPE) { + e.consume(); + doClose(); + } + }); + } + } + + private void addContent() { + InputTextField depositTxHex = addLabelInputTextField(gridPane, ++rowIndex, "depositTxHex:").second; + + InputTextField buyerPayoutAmount = addLabelInputTextField(gridPane, ++rowIndex, "buyerPayoutAmount:").second; + InputTextField sellerPayoutAmount = addLabelInputTextField(gridPane, ++rowIndex, "sellerPayoutAmount:").second; + InputTextField arbitratorPayoutAmount = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorPayoutAmount:").second; + + InputTextField buyerAddressString = addLabelInputTextField(gridPane, ++rowIndex, "buyerAddressString:").second; + InputTextField sellerAddressString = addLabelInputTextField(gridPane, ++rowIndex, "sellerAddressString:").second; + InputTextField arbitratorAddressString = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorAddressString:").second; + + InputTextField buyerPrivateKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "buyerPrivateKeyAsHex:").second; + InputTextField sellerPrivateKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "sellerPrivateKeyAsHex:").second; + InputTextField arbitratorPrivateKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorPrivateKeyAsHex:").second; + + InputTextField buyerPubKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "buyerPubKeyAsHex:").second; + InputTextField sellerPubKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "sellerPubKeyAsHex:").second; + InputTextField arbitratorPubKeyAsHex = addLabelInputTextField(gridPane, ++rowIndex, "arbitratorPubKeyAsHex:").second; + + InputTextField P2SHMultiSigOutputScript = addLabelInputTextField(gridPane, ++rowIndex, "P2SHMultiSigOutputScript:").second; + InputTextField buyerPubKeysInputTextField = addLabelInputTextField(gridPane, ++rowIndex, "buyerPubKeys:").second; + InputTextField sellerPubKeysInputTextField = addLabelInputTextField(gridPane, ++rowIndex, "sellerPubKeys:").second; + + List buyerPubKeys = Arrays.asList(buyerPubKeysInputTextField.getText().split(",")); + List sellerPubKeys = Arrays.asList(sellerPubKeysInputTextField.getText().split(",")); + + /* + depositTxHex.setText(""); + + buyerPayoutAmount.setText("1.01"); + sellerPayoutAmount.setText("0.01"); + arbitratorPayoutAmount.setText("0"); + + buyerAddressString.setText(""); + buyerPubKeyAsHex.setText(""); + buyerPrivateKeyAsHex.setText(""); + + sellerAddressString.setText(""); + sellerPubKeyAsHex.setText(""); + sellerPrivateKeyAsHex.setText(""); + + arbitratorAddressString.setText("19xdeiQM2Hn2M2wbpT5imcYWzqhiSDHPy4"); + arbitratorPubKeyAsHex.setText("02c62e794fe67f3a2115e2de4757143ff7f27bdf38aa4ae58a3595baa6d676875b"); + + arbitratorAddressString.setText("1FdFzBazmHQxbUbdCUJwuCtR37DrZrEobu"); + arbitratorPubKeyAsHex.setText("030fdc2ebc297df4047442f6079f1ce3b7d1938a41f88bd11497545cc94fcfd315"); + + P2SHMultiSigOutputScript.setText(""); + + sellerPubKeys = Arrays.asList(); + */ + + + actionButtonText("Sign and publish transaction"); + + final List finalSellerPubKeys = sellerPubKeys; + FutureCallback callback = new FutureCallback() { + @Override + public void onSuccess(@Nullable Transaction result) { + log.error("onSuccess"); + UserThread.execute(() -> { + String txId = result != null ? result.getHashAsString() : "null"; + new Popup<>() + .information("Transaction successful published. Transaction ID: " + txId) + .show(); + }); + } + + @Override + public void onFailure(Throwable t) { + log.error(t.toString()); + log.error("onFailure"); + UserThread.execute(() -> new Popup<>().warning(t.toString()).show()); + } + }; + onAction(() -> { + try { + tradeWalletService.emergencySignAndPublishPayoutTx(depositTxHex.getText(), + Coin.parseCoin(buyerPayoutAmount.getText()), + Coin.parseCoin(sellerPayoutAmount.getText()), + Coin.parseCoin(arbitratorPayoutAmount.getText()), + buyerAddressString.getText(), + sellerAddressString.getText(), + arbitratorAddressString.getText(), + buyerPrivateKeyAsHex.getText(), + sellerPrivateKeyAsHex.getText(), + arbitratorPrivateKeyAsHex.getText(), + buyerPubKeyAsHex.getText(), + sellerPubKeyAsHex.getText(), + arbitratorPubKeyAsHex.getText(), + P2SHMultiSigOutputScript.getText(), + buyerPubKeys, + finalSellerPubKeys, + callback); + } catch (AddressFormatException | WalletException | TransactionVerificationException e) { + log.error(e.toString()); + e.printStackTrace(); + UserThread.execute(() -> new Popup<>().warning(e.toString()).show()); + } + }); + } + + @Override + protected void addCloseButton() { + super.addCloseButton(); + actionButton.setOnAction(event -> actionHandlerOptional.ifPresent(Runnable::run)); + } +} diff --git a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java index 6205591682..1278c524be 100644 --- a/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java +++ b/gui/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesView.java @@ -126,7 +126,7 @@ public class PendingTradesView extends ActivatableViewAndModel