From 08dde43ffc6c9f3baef10b6a5f19b5b6f9e19643 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 6 Apr 2015 21:44:01 +0200 Subject: [PATCH] Add locktime to payout tx --- .../app/bootstrap/BootstrapNode.java | 3 +- .../app/bootstrap/BootstrapNodeMain.java | 3 +- .../java/io/bitsquare/app/BitsquareApp.java | 108 +++++++------- .../io/bitsquare/btc/TradeWalletService.java | 63 ++++++--- .../java/io/bitsquare/fiat/FiatAccount.java | 22 +-- .../bitsquare/gui/main/debug/DebugView.java | 12 +- .../offer/takeoffer/TakeOfferViewModel.java | 4 +- .../portfolio/pendingtrades/BuyerSubView.java | 15 +- .../pendingtrades/PendingTradesDataModel.java | 26 +++- .../pendingtrades/PendingTradesViewModel.java | 65 ++++++++- .../pendingtrades/SellerSubView.java | 17 ++- .../steps/WaitPayoutLockTimeView.java | 132 ++++++++++++++++++ .../io/bitsquare/gui/util/BSFormatter.java | 7 + .../java/io/bitsquare/offer/OfferBook.java | 2 +- .../io/bitsquare/storage/FileManager.java | 3 +- .../main/java/io/bitsquare/trade/Trade.java | 8 +- .../tasks/BroadcastCreateOfferFeeTx.java | 2 +- .../trade/BuyerAsOffererProtocol.java | 28 ++-- .../protocol/trade/BuyerAsTakerProtocol.java | 26 ++-- .../trade/protocol/trade/ProcessModel.java | 10 -- .../trade/SellerAsOffererProtocol.java | 16 ++- .../protocol/trade/SellerAsTakerProtocol.java | 16 ++- .../trade/protocol/trade/TradeProtocol.java | 29 +++- ...age.java => PayoutTxFinalizedMessage.java} | 4 +- .../tasks/buyer/CreateAndSignPayoutTx.java | 1 + ...a => ProcessPayoutTxFinalizedMessage.java} | 14 +- ...java => SendPayoutTxFinalizedMessage.java} | 14 +- ...utTx.java => SignAndFinalizePayoutTx.java} | 40 ++---- .../SetupPayoutTxLockTimeReachedListener.java | 105 ++++++++++++++ .../tasks/taker/BroadcastTakeOfferFeeTx.java | 2 +- .../trade/states/OffererTradeState.java | 8 +- .../trade/states/TakerTradeState.java | 6 +- core/src/main/resources/logback.xml | 7 +- 33 files changed, 620 insertions(+), 198 deletions(-) create mode 100644 core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/steps/WaitPayoutLockTimeView.java rename core/src/main/java/io/bitsquare/trade/protocol/trade/messages/{PayoutTxPublishedMessage.java => PayoutTxFinalizedMessage.java} (90%) rename core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/{ProcessPayoutTxPublishedMessage.java => ProcessPayoutTxFinalizedMessage.java} (85%) rename core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/{SendPayoutTxPublishedMessage.java => SendPayoutTxFinalizedMessage.java} (85%) rename core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/{SignAndPublishPayoutTx.java => SignAndFinalizePayoutTx.java} (59%) create mode 100644 core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SetupPayoutTxLockTimeReachedListener.java diff --git a/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNode.java b/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNode.java index 3504ac77ea..c31cd7e224 100644 --- a/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNode.java +++ b/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNode.java @@ -17,6 +17,7 @@ package io.bitsquare.app.bootstrap; +import io.bitsquare.p2p.BootstrapNodes; import io.bitsquare.p2p.Node; import net.tomp2p.connection.ChannelClientConfiguration; @@ -52,7 +53,7 @@ public class BootstrapNode { public void start() { String name = env.getRequiredProperty(Node.NAME_KEY); - int port = env.getProperty(Node.PORT_KEY, Integer.class, Node.DEFAULT_PORT); + int port = env.getProperty(Node.PORT_KEY, Integer.class, BootstrapNodes.DEFAULT_PORT); try { Number160 peerId = Number160.createHash(name); diff --git a/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNodeMain.java b/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNodeMain.java index fbd49f9dd0..aef600326c 100644 --- a/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNodeMain.java +++ b/bootstrap/src/main/java/io/bitsquare/app/bootstrap/BootstrapNodeMain.java @@ -19,6 +19,7 @@ package io.bitsquare.app.bootstrap; import io.bitsquare.app.BitsquareEnvironment; import io.bitsquare.app.BitsquareExecutable; +import io.bitsquare.p2p.BootstrapNodes; import io.bitsquare.p2p.Node; import joptsimple.OptionParser; @@ -34,7 +35,7 @@ public class BootstrapNodeMain extends BitsquareExecutable { parser.accepts(Node.NAME_KEY, description("Name of this node", null)) .withRequiredArg() .isRequired(); - parser.accepts(Node.PORT_KEY, description("Port to listen on", Node.DEFAULT_PORT)) + parser.accepts(Node.PORT_KEY, description("Port to listen on", BootstrapNodes.DEFAULT_PORT)) .withRequiredArg() .ofType(int.class); } diff --git a/core/src/main/java/io/bitsquare/app/BitsquareApp.java b/core/src/main/java/io/bitsquare/app/BitsquareApp.java index 6e7f7290f5..3fe6bd0c4d 100644 --- a/core/src/main/java/io/bitsquare/app/BitsquareApp.java +++ b/core/src/main/java/io/bitsquare/app/BitsquareApp.java @@ -34,6 +34,7 @@ import com.google.inject.Guice; import com.google.inject.Injector; import java.io.IOException; +import java.io.InvalidObjectException; import javafx.application.Application; import javafx.scene.*; @@ -69,73 +70,80 @@ public class BitsquareApp extends Application { this.primaryStage = primaryStage; log.trace("BitsquareApp.start"); + try { + bitsquareAppModule = new BitsquareAppModule(env, primaryStage); + injector = Guice.createInjector(bitsquareAppModule); + injector.getInstance(InjectorViewFactory.class).setInjector(injector); - bitsquareAppModule = new BitsquareAppModule(env, primaryStage); - injector = Guice.createInjector(bitsquareAppModule); - injector.getInstance(InjectorViewFactory.class).setInjector(injector); - - // route uncaught exceptions to a user-facing dialog - Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> - Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable))); + // route uncaught exceptions to a user-facing dialog + Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> + Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable))); - // load the main view and create the main scene - log.trace("viewLoader.load(MainView.class)"); - CachingViewLoader viewLoader = injector.getInstance(CachingViewLoader.class); - View view = viewLoader.load(MainView.class); + // load the main view and create the main scene + log.trace("viewLoader.load(MainView.class)"); + CachingViewLoader viewLoader = injector.getInstance(CachingViewLoader.class); + View view = viewLoader.load(MainView.class); - scene = new Scene((Parent) view.getRoot(), 1000, 620); - scene.getStylesheets().setAll( - "/io/bitsquare/gui/bitsquare.css", - "/io/bitsquare/gui/images.css"); + scene = new Scene((Parent) view.getRoot(), 1000, 620); + scene.getStylesheets().setAll( + "/io/bitsquare/gui/bitsquare.css", + "/io/bitsquare/gui/images.css"); - // configure the system tray + // configure the system tray - SystemTray.create(primaryStage, this::stop); - primaryStage.setOnCloseRequest(e -> { - e.consume(); - stop(); - }); - scene.setOnKeyReleased(keyEvent -> { - // For now we exit when closing/quit the app. - // Later we will only hide the window (systemTray.hideStage()) and use the exit item in the system tray for - // shut down. - if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || - new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) + SystemTray.create(primaryStage, this::stop); + primaryStage.setOnCloseRequest(e -> { + e.consume(); stop(); - else if (new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) - showDebugWindow(); - }); + }); + scene.setOnKeyReleased(keyEvent -> { + // For now we exit when closing/quit the app. + // Later we will only hide the window (systemTray.hideStage()) and use the exit item in the system tray for + // shut down. + if (new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN).match(keyEvent) || + new KeyCodeCombination(KeyCode.Q, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) + stop(); + else if (new KeyCodeCombination(KeyCode.D, KeyCombination.SHORTCUT_DOWN).match(keyEvent)) + showDebugWindow(); + }); - // configure the primary stage + // configure the primary stage - primaryStage.setTitle(env.getRequiredProperty(APP_NAME_KEY)); - primaryStage.setScene(scene); - primaryStage.setMinWidth(750); - primaryStage.setMinHeight(620); + primaryStage.setTitle(env.getRequiredProperty(APP_NAME_KEY)); + primaryStage.setScene(scene); + primaryStage.setMinWidth(750); + primaryStage.setMinHeight(620); - // on windows the title icon is also used as task bar icon in a larger size - // on Linux no title icon is supported but also a large task bar icon is derived form that title icon - String iconPath; - if (Utilities.isOSX()) - iconPath = ImageUtil.isRetina() ? "/images/window_icon@2x.png" : "/images/window_icon.png"; - else if (Utilities.isWindows()) - iconPath = "/images/task_bar_icon_windows.png"; - else - iconPath = "/images/task_bar_icon_linux.png"; + // on windows the title icon is also used as task bar icon in a larger size + // on Linux no title icon is supported but also a large task bar icon is derived form that title icon + String iconPath; + if (Utilities.isOSX()) + iconPath = ImageUtil.isRetina() ? "/images/window_icon@2x.png" : "/images/window_icon.png"; + else if (Utilities.isWindows()) + iconPath = "/images/task_bar_icon_windows.png"; + else + iconPath = "/images/task_bar_icon_linux.png"; - primaryStage.getIcons().add(new Image(getClass().getResourceAsStream(iconPath))); + primaryStage.getIcons().add(new Image(getClass().getResourceAsStream(iconPath))); - // make the UI visible + // make the UI visible - log.trace("primaryStage.show"); - primaryStage.show(); + log.trace("primaryStage.show"); + primaryStage.show(); - //TODO just temp. - //showDebugWindow(); + //TODO just temp. + //showDebugWindow(); + } catch (Throwable t) { + if (t instanceof InvalidObjectException) { + Popups.openErrorPopup("There is a problem with different version of persisted objects. " + + "Please delete the db directory inside the app directory."); + } + log.error(t.toString()); + } } private void showDebugWindow() { diff --git a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java index 51245d8f65..3ddefb6c71 100644 --- a/core/src/main/java/io/bitsquare/btc/TradeWalletService.java +++ b/core/src/main/java/io/bitsquare/btc/TradeWalletService.java @@ -23,11 +23,13 @@ import io.bitsquare.btc.exceptions.WalletException; import org.bitcoinj.core.Address; import org.bitcoinj.core.AddressFormatException; +import org.bitcoinj.core.BlockChainListener; import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.StoredBlock; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionInput; import org.bitcoinj.core.TransactionOutPoint; @@ -118,11 +120,6 @@ public class TradeWalletService { return createOfferFeeTx; } - public void broadcastCreateOfferFeeTx(Transaction createOfferFeeTx, FutureCallback callback) { - ListenableFuture future = walletAppKit.peerGroup().broadcastTransaction(createOfferFeeTx).future(); - Futures.addCallback(future, callback); - } - public Transaction createTakeOfferFeeTx(AddressEntry sellerAddressEntry) throws InsufficientMoneyException { Transaction takeOfferFeeTx = new Transaction(params); Coin fee = FeePolicy.TAKE_OFFER_FEE.subtract(FeePolicy.TX_FEE); @@ -139,8 +136,8 @@ public class TradeWalletService { return takeOfferFeeTx; } - public void broadcastTakeOfferFeeTx(Transaction takeOfferFeeTx, FutureCallback callback) { - ListenableFuture future = walletAppKit.peerGroup().broadcastTransaction(takeOfferFeeTx).future(); + public void broadcastTx(Transaction tx, FutureCallback callback) { + ListenableFuture future = walletAppKit.peerGroup().broadcastTransaction(tx).future(); Futures.addCallback(future, callback); } @@ -419,6 +416,7 @@ public class TradeWalletService { Coin sellerPayoutAmount, AddressEntry buyerAddressEntry, String sellerPayoutAddressString, + long lockTimeDelta, byte[] buyerPubKey, byte[] sellerPubKey, byte[] arbitratorPubKey) @@ -438,6 +436,8 @@ public class TradeWalletService { sellerPayoutAmount, buyerAddressEntry.getAddressString(), sellerPayoutAddressString); + preparedPayoutTx.setLockTime(wallet.getLastBlockSeenHeight() + lockTimeDelta); + preparedPayoutTx.getInputs().stream().forEach(i -> i.setSequenceNumber(0)); // MS redeemScript Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey); Sha256Hash sigHash = preparedPayoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); @@ -454,16 +454,16 @@ public class TradeWalletService { return buyerSignature.encodeToDER(); } - public void signAndPublishPayoutTx(Transaction depositTx, - byte[] buyerSignature, - Coin buyerPayoutAmount, - Coin sellerPayoutAmount, - String buyerAddressString, - AddressEntry sellerAddressEntry, - byte[] buyerPubKey, - byte[] sellerPubKey, - byte[] arbitratorPubKey, - FutureCallback callback) + public Transaction signAndFinalizePayoutTx(Transaction depositTx, + byte[] buyerSignature, + Coin buyerPayoutAmount, + Coin sellerPayoutAmount, + String buyerAddressString, + AddressEntry sellerAddressEntry, + long lockTimeDelta, + byte[] buyerPubKey, + byte[] sellerPubKey, + byte[] arbitratorPubKey) throws AddressFormatException, TransactionVerificationException, WalletException, SigningException { log.trace("signAndPublishPayoutTx called"); log.trace("depositTx " + depositTx.toString()); @@ -482,6 +482,8 @@ public class TradeWalletService { sellerPayoutAmount, buyerAddressString, sellerAddressEntry.getAddressString()); + payoutTx.setLockTime(wallet.getLastBlockSeenHeight() + lockTimeDelta); + payoutTx.getInputs().stream().forEach(i -> i.setSequenceNumber(0)); Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey); Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false); ECKey.ECDSASignature sellerSignature = sellerAddressEntry.getKeyPair().sign(sigHash).toCanonicalised(); @@ -508,8 +510,25 @@ public class TradeWalletService { printTxWithInputs("payoutTx", payoutTx); - ListenableFuture broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(payoutTx).future(); - Futures.addCallback(broadcastComplete, callback); + // As we use lockTime the tx will not be relayed as it is not considered standard. + // We need to broadcast on our own when we reahced the block height. Both peers will do the broadcast. + return payoutTx; + } + + public ListenableFuture getBlockHeightFuture(Transaction transaction) { + return walletAppKit.chain().getHeightFuture((int) transaction.getLockTime()); + } + + public int getBestChainHeight() { + return walletAppKit.chain().getBestChainHeight(); + } + + public void addBlockChainListener(BlockChainListener blockChainListener) { + walletAppKit.chain().addListener(blockChainListener); + } + + public void removeBlockChainListener(BlockChainListener blockChainListener) { + walletAppKit.chain().removeListener(blockChainListener); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -639,9 +658,9 @@ public class TradeWalletService { } -/////////////////////////////////////////////////////////////////////////////////////////// -// Inner classes -/////////////////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////////////////// + // Inner classes + /////////////////////////////////////////////////////////////////////////////////////////// public class Result { private final List connectedOutputsForAllInputs; diff --git a/core/src/main/java/io/bitsquare/fiat/FiatAccount.java b/core/src/main/java/io/bitsquare/fiat/FiatAccount.java index b62f8452f0..3d7452e660 100644 --- a/core/src/main/java/io/bitsquare/fiat/FiatAccount.java +++ b/core/src/main/java/io/bitsquare/fiat/FiatAccount.java @@ -32,21 +32,27 @@ public class FiatAccount implements Serializable { // That object is sent over the wire, so we need to take care of version compatibility. private static final long serialVersionUID = 1L; + public static final long HOUR_IN_BLOCKS = 6; + public static final long DAY_IN_BLOCKS = HOUR_IN_BLOCKS * 24; + public static final long WEEK_IN_BLOCKS = DAY_IN_BLOCKS * 7; + public enum Type { - IRC("", ""), - SEPA("IBAN", "BIC"), - WIRE("primary ID", "secondary ID"), - INTERNATIONAL("primary ID", "secondary ID"), - OK_PAY("primary ID", "secondary ID"), - NET_TELLER("primary ID", "secondary ID"), - PERFECT_MONEY("primary ID", "secondary ID"); + IRC("", "", 1), + SEPA("IBAN", "BIC", WEEK_IN_BLOCKS), + WIRE("primary ID", "secondary ID", WEEK_IN_BLOCKS), + INTERNATIONAL("primary ID", "secondary ID", 2 * WEEK_IN_BLOCKS), + OK_PAY("primary ID", "secondary ID", HOUR_IN_BLOCKS), + NET_TELLER("primary ID", "secondary ID", HOUR_IN_BLOCKS), + PERFECT_MONEY("primary ID", "secondary ID", HOUR_IN_BLOCKS); public final String primaryId; public final String secondaryId; + public final long lockTimeDelta; - Type(String primaryId, String secondaryId) { + Type(String primaryId, String secondaryId, long lockTimeDelta) { this.primaryId = primaryId; this.secondaryId = secondaryId; + this.lockTimeDelta = lockTimeDelta; } public static ArrayList getAllBankAccountTypes() { diff --git a/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java b/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java index 00ed6b3ce0..1157642366 100644 --- a/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java +++ b/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java @@ -32,7 +32,7 @@ import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol; import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol; import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateAndSignPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateDepositTxInputs; -import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessRequestDepositTxInputsMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessRequestPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.SendDepositTxPublishedMessage; @@ -46,9 +46,9 @@ import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignDepositTx; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessDepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessFiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessRequestPayDepositMessage; -import io.bitsquare.trade.protocol.trade.tasks.seller.SendPayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.tasks.seller.SendPayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.SendRequestDepositTxInputsMessage; -import io.bitsquare.trade.protocol.trade.tasks.seller.SignAndPublishPayoutTx; +import io.bitsquare.trade.protocol.trade.tasks.seller.SignAndFinalizePayoutTx; import io.bitsquare.trade.protocol.trade.tasks.taker.CreateTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOfferFeePayment; import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOffererAccount; @@ -111,7 +111,7 @@ public class DebugView extends InitializableView { VerifyTakeOfferFeePayment.class, SendFiatTransferStartedMessage.class, - ProcessPayoutTxPublishedMessage.class, + ProcessPayoutTxFinalizedMessage.class, Boolean.class, /* used as seperator*/ @@ -129,9 +129,9 @@ public class DebugView extends InitializableView { ProcessFiatTransferStartedMessage.class, - SignAndPublishPayoutTx.class, + SignAndFinalizePayoutTx.class, VerifyOfferFeePayment.class, - SendPayoutTxPublishedMessage.class + SendPayoutTxFinalizedMessage.class ) ); diff --git a/core/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java b/core/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java index 006166a6f9..54beba6341 100644 --- a/core/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferViewModel.java @@ -255,7 +255,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im "Please try later again." + msg); takeOfferRequested = false; break; - case PAYOUT_PUBLISHED: + case PAYOUT_FINALIZED: break; case EXCEPTION: errorMessage.set(msg); @@ -292,7 +292,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel im "Please try later again." + msg); takeOfferRequested = false; break; - case PAYOUT_PUBLISHED: + case PAYOUT_FINALIZED: break; case EXCEPTION: errorMessage.set(msg); diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/BuyerSubView.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/BuyerSubView.java index 52cfe31cf7..45ffba8af6 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/BuyerSubView.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/BuyerSubView.java @@ -22,6 +22,7 @@ import io.bitsquare.gui.main.portfolio.pendingtrades.steps.CompletedView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.StartFiatView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.TradeWizardItem; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.WaitFiatReceivedView; +import io.bitsquare.gui.main.portfolio.pendingtrades.steps.WaitPayoutLockTimeView; import io.bitsquare.gui.main.portfolio.pendingtrades.steps.WaitTxInBlockchainView; import io.bitsquare.locale.BSResources; @@ -34,6 +35,7 @@ public class BuyerSubView extends TradeSubView { private TradeWizardItem waitTxInBlockchain; private TradeWizardItem startFiat; private TradeWizardItem waitFiatReceived; + private TradeWizardItem payoutUnlock; private TradeWizardItem completed; @@ -60,9 +62,10 @@ public class BuyerSubView extends TradeSubView { waitTxInBlockchain = new TradeWizardItem(WaitTxInBlockchainView.class, "Wait for blockchain confirmation"); startFiat = new TradeWizardItem(StartFiatView.class, "Start payment"); waitFiatReceived = new TradeWizardItem(WaitFiatReceivedView.class, "Wait until payment has arrived"); + payoutUnlock = new TradeWizardItem(WaitPayoutLockTimeView.class, "Wait for payout unlock"); completed = new TradeWizardItem(CompletedView.class, "Completed"); - leftVBox.getChildren().addAll(waitTxInBlockchain, startFiat, waitFiatReceived, completed); + leftVBox.getChildren().addAll(waitTxInBlockchain, startFiat, waitFiatReceived, payoutUnlock, completed); } @@ -110,10 +113,20 @@ public class BuyerSubView extends TradeSubView { "the Bitcoin sellers payment account, the payout transaction will be published.", model.getCurrencyCode())); break; + case BUYER_PAYOUT_FINALIZED: + waitTxInBlockchain.done(); + startFiat.done(); + waitFiatReceived.done(); + showItem(payoutUnlock); + + ((WaitPayoutLockTimeView) tradeStepDetailsView).setInfoLabelText("The payout transaction is signed and finalized by both parties." + + "\nFor reducing bank chargeback risks you need to wait until the payout gets unlocked to transfer your Bitcoin."); + break; case BUYER_COMPLETED: waitTxInBlockchain.done(); startFiat.done(); waitFiatReceived.done(); + payoutUnlock.done(); showItem(completed); CompletedView completedView = (CompletedView) tradeStepDetailsView; diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java index 9765aa4acd..ba01db4921 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesDataModel.java @@ -18,6 +18,7 @@ package io.bitsquare.gui.main.portfolio.pendingtrades; import io.bitsquare.btc.FeePolicy; +import io.bitsquare.btc.TradeWalletService; import io.bitsquare.btc.WalletService; import io.bitsquare.common.viewfx.model.Activatable; import io.bitsquare.common.viewfx.model.DataModel; @@ -35,6 +36,7 @@ import io.bitsquare.trade.TradeManager; import io.bitsquare.trade.states.TradeState; import io.bitsquare.user.User; +import org.bitcoinj.core.BlockChainListener; import org.bitcoinj.core.Coin; import com.google.inject.Inject; @@ -60,6 +62,7 @@ class PendingTradesDataModel implements Activatable, DataModel { private final TradeManager tradeManager; private final WalletService walletService; + private TradeWalletService tradeWalletService; private final User user; private Navigation navigation; @@ -80,9 +83,11 @@ class PendingTradesDataModel implements Activatable, DataModel { /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, User user, Navigation navigation) { + public PendingTradesDataModel(TradeManager tradeManager, WalletService walletService, TradeWalletService tradeWalletService, User user, Navigation + navigation) { this.tradeManager = tradeManager; this.walletService = walletService; + this.tradeWalletService = tradeWalletService; this.user = user; this.navigation = navigation; @@ -281,5 +286,24 @@ class PendingTradesDataModel implements Activatable, DataModel { return txId; } + String getPayoutTxId() { + return trade.getPayoutTx().getHashAsString(); + } + + void addBlockChainListener(BlockChainListener blockChainListener) { + tradeWalletService.addBlockChainListener(blockChainListener); + } + + void removeBlockChainListener(BlockChainListener blockChainListener) { + tradeWalletService.removeBlockChainListener(blockChainListener); + } + + public long getLockTime() { + return trade.getPayoutTx().getLockTime(); + } + + public int getBestChainHeight() { + return tradeWalletService.getBestChainHeight(); + } } diff --git a/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java b/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java index e74f20e117..272036dac0 100644 --- a/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java +++ b/core/src/main/java/io/bitsquare/gui/main/portfolio/pendingtrades/PendingTradesViewModel.java @@ -27,6 +27,7 @@ import io.bitsquare.trade.Trade; import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.TakerTradeState; +import org.bitcoinj.core.BlockChainListener; import org.bitcoinj.core.Coin; import org.bitcoinj.utils.Fiat; @@ -58,11 +59,13 @@ public class PendingTradesViewModel extends ActivatableWithDataModel. + */ + +package io.bitsquare.gui.main.portfolio.pendingtrades.steps; + +import io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel; +import io.bitsquare.gui.util.Layout; + +import org.bitcoinj.core.AbstractBlockChain; +import org.bitcoinj.core.BlockChainListener; +import org.bitcoinj.core.ScriptException; +import org.bitcoinj.core.Sha256Hash; +import org.bitcoinj.core.StoredBlock; +import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.VerificationException; + +import java.util.List; + +import javafx.scene.control.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static io.bitsquare.gui.util.ComponentBuilder.*; + +public class WaitPayoutLockTimeView extends TradeStepDetailsView { + private static final Logger log = LoggerFactory.getLogger(WaitPayoutLockTimeView.class); + + private final BlockChainListener blockChainListener; + + private TextField blockTextField; + private Label infoLabel; + private TextField timeTextField; + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Constructor, Initialisation + /////////////////////////////////////////////////////////////////////////////////////////// + + public WaitPayoutLockTimeView(PendingTradesViewModel model) { + super(model); + + blockChainListener = new BlockChainListener() { + @Override + public void notifyNewBestBlock(StoredBlock block) throws VerificationException { + setLockTime(block.getHeight()); + } + + @Override + public void reorganize(StoredBlock splitPoint, List oldBlocks, List newBlocks) throws VerificationException { + setLockTime(model.getBestChainHeight()); + } + + @Override + public boolean isTransactionRelevant(Transaction tx) throws ScriptException { + return false; + } + + @Override + public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset) throws + VerificationException { + } + + @Override + public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset) + throws VerificationException { + return false; + } + }; + } + + @Override + public void activate() { + super.activate(); + + model.addBlockChainListener(blockChainListener); + setLockTime(model.getBestChainHeight()); + } + + @Override + public void deactivate() { + super.deactivate(); + + model.removeBlockChainListener(blockChainListener); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Setters + /////////////////////////////////////////////////////////////////////////////////////////// + + public void setInfoLabelText(String text) { + if (infoLabel != null) + infoLabel.setText(text); + } + + private void setLockTime(int bestBlocKHeight) { + long missingBlocks = model.getLockTime() - (long) bestBlocKHeight; + blockTextField.setText(String.valueOf(missingBlocks)); + timeTextField.setText(model.getUnlockDate(missingBlocks)); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // Build view + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + protected void buildGridEntries() { + getAndAddTitledGroupBg(gridPane, gridRow, 2, "Payout transaction lock time"); + blockTextField = getAndAddLabelTextFieldPair(gridPane, gridRow++, "Block(s) to wait until unlock:", Layout.FIRST_ROW_DISTANCE).textField; + timeTextField = getAndAddLabelTextFieldPair(gridPane, gridRow++, "Approx. date when payout gets unlocked:").textField; + + getAndAddTitledGroupBg(gridPane, gridRow, 1, "Information", Layout.GROUP_DISTANCE); + infoLabel = getAndAddInfoLabel(gridPane, gridRow++, Layout.FIRST_ROW_AND_GROUP_DISTANCE); + } +} + + diff --git a/core/src/main/java/io/bitsquare/gui/util/BSFormatter.java b/core/src/main/java/io/bitsquare/gui/util/BSFormatter.java index 31fb767794..ed716a244d 100644 --- a/core/src/main/java/io/bitsquare/gui/util/BSFormatter.java +++ b/core/src/main/java/io/bitsquare/gui/util/BSFormatter.java @@ -356,4 +356,11 @@ public class BSFormatter { Double.parseDouble(input); return input; } + + public String getUnlockDate(long missingBlocks) { + DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, locale); + DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, locale); + Date unlockDate = new Date(new Date().getTime() + missingBlocks * 10 * 60 * 1000); + return dateFormatter.format(unlockDate) + " " + timeFormatter.format(unlockDate); + } } diff --git a/core/src/main/java/io/bitsquare/offer/OfferBook.java b/core/src/main/java/io/bitsquare/offer/OfferBook.java index 265eddfa53..1416200ca8 100644 --- a/core/src/main/java/io/bitsquare/offer/OfferBook.java +++ b/core/src/main/java/io/bitsquare/offer/OfferBook.java @@ -49,7 +49,7 @@ import static com.google.common.base.Preconditions.checkArgument; public class OfferBook { private static final Logger log = LoggerFactory.getLogger(OfferBook.class); - private static final int POLLING_INTERVAL = 5000; // in ms + private static final int POLLING_INTERVAL = 2000; // in ms private final OfferBookService offerBookService; private final User user; diff --git a/core/src/main/java/io/bitsquare/storage/FileManager.java b/core/src/main/java/io/bitsquare/storage/FileManager.java index d23412ed90..748dcd6098 100644 --- a/core/src/main/java/io/bitsquare/storage/FileManager.java +++ b/core/src/main/java/io/bitsquare/storage/FileManager.java @@ -252,7 +252,7 @@ public class FileManager { private void saveNowInternal(T serializable) { long now = System.currentTimeMillis(); saveToFile(serializable, dir, storageFile); - log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now); + Platform.runLater(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now)); } private void saveToFile(T serializable, File dir, File storageFile) { @@ -289,7 +289,6 @@ public class FileManager { renameTempFileToFile(tempFile, storageFile); } catch (Throwable t) { log.debug("storageFile " + storageFile.toString()); - log.debug("currentThread " + Thread.currentThread()); t.printStackTrace(); log.error("Error at saveToFile: " + t.getMessage()); } finally { diff --git a/core/src/main/java/io/bitsquare/trade/Trade.java b/core/src/main/java/io/bitsquare/trade/Trade.java index 460b7e4f37..99e3a9a1ab 100644 --- a/core/src/main/java/io/bitsquare/trade/Trade.java +++ b/core/src/main/java/io/bitsquare/trade/Trade.java @@ -169,10 +169,11 @@ abstract public class Trade implements Model, Serializable { createProtocol(); if (mailboxMessage != null) { - tradeProtocol.applyMailboxMessage(mailboxMessage); + tradeProtocol.applyMailboxMessage(mailboxMessage, this); // After applied to protocol we remove it mailboxMessage = null; } + tradeProtocol.checkPayoutTxTimeLock(this); } protected void initStateProperties() { @@ -333,6 +334,11 @@ abstract public class Trade implements Model, Serializable { tradeVolumeProperty.set(getTradeVolume()); } + // TODO support case of multiple fiat accounts + public long getLockTimeDelta() { + return getOffer().getFiatAccountType().lockTimeDelta; + } + @Nullable public Coin getTradeAmount() { return tradeAmount; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/BroadcastCreateOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/BroadcastCreateOfferFeeTx.java index 061b91bf9c..dfdbd67e2f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/BroadcastCreateOfferFeeTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/placeoffer/tasks/BroadcastCreateOfferFeeTx.java @@ -54,7 +54,7 @@ public class BroadcastCreateOfferFeeTx extends Task { Coin balance = model.walletService.getBalanceForAddress(addressEntry.getAddress()); if (balance.compareTo(totalsNeeded) >= 0) { - model.tradeWalletService.broadcastCreateOfferFeeTx(model.getTransaction(), new FutureCallback() { + model.tradeWalletService.broadcastTx(model.getTransaction(), new FutureCallback() { @Override public void onSuccess(Transaction transaction) { log.info("Broadcast of offer fee payment succeeded: transaction = " + transaction.toString()); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java index cf46526d92..84c268cc1f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsOffererProtocol.java @@ -22,16 +22,17 @@ import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.trade.BuyerAsOffererTrade; +import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage; import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage; -import io.bitsquare.trade.protocol.trade.messages.PayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.messages.PayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.messages.RequestDepositTxInputsMessage; import io.bitsquare.trade.protocol.trade.messages.RequestPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.CommitPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateAndSignPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateDepositTxInputs; -import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessRequestDepositTxInputsMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessRequestPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.SendDepositTxPublishedMessage; @@ -41,6 +42,7 @@ import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndPublishDepositTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.VerifyAndSignContract; import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakeOfferFeePayment; import io.bitsquare.trade.protocol.trade.tasks.offerer.VerifyTakerAccount; +import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.states.OffererTradeState; import org.slf4j.Logger; @@ -72,13 +74,16 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage) { + public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + if (trade == null) + this.trade = trade; + log.debug("setMailboxMessage " + mailboxMessage); // Might be called twice, so check that its only processed once if (!processModel.isMailboxMessageProcessed()) { processModel.mailboxMessageProcessed(); - if (mailboxMessage instanceof PayoutTxPublishedMessage) { - handle((PayoutTxPublishedMessage) mailboxMessage); + if (mailboxMessage instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) mailboxMessage); } } } @@ -179,7 +184,7 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc // Incoming message handling /////////////////////////////////////////////////////////////////////////////////////////// - private void handle(PayoutTxPublishedMessage tradeMessage) { + private void handle(PayoutTxFinalizedMessage tradeMessage) { processModel.setTradeMessage(tradeMessage); TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade, @@ -190,8 +195,11 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc }, this::handleTaskRunnerFault); - taskRunner.addTasks(ProcessPayoutTxPublishedMessage.class); - taskRunner.addTasks(CommitPayoutTx.class); + taskRunner.addTasks( + ProcessPayoutTxFinalizedMessage.class, + CommitPayoutTx.class, + SetupPayoutTxLockTimeReachedListener.class + ); taskRunner.run(); } @@ -216,8 +224,8 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc else if (tradeMessage instanceof RequestPublishDepositTxMessage) { handle((RequestPublishDepositTxMessage) tradeMessage); } - else if (tradeMessage instanceof PayoutTxPublishedMessage) { - handle((PayoutTxPublishedMessage) tradeMessage); + else if (tradeMessage instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) tradeMessage); } else { log.error("Incoming tradeMessage not supported. " + tradeMessage); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java index 70cc0c7540..32950759d7 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/BuyerAsTakerProtocol.java @@ -21,19 +21,21 @@ import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.trade.BuyerAsTakerTrade; -import io.bitsquare.trade.protocol.trade.messages.PayoutTxPublishedMessage; +import io.bitsquare.trade.Trade; +import io.bitsquare.trade.protocol.trade.messages.PayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.messages.RequestPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.messages.TradeMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.CommitPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateAndSignPayoutTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.CreateDepositTxInputs; -import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessPayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.ProcessRequestPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.SendDepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.SendFiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.SendRequestPayDepositMessage; import io.bitsquare.trade.protocol.trade.tasks.buyer.SignAndPublishDepositTx; import io.bitsquare.trade.protocol.trade.tasks.buyer.VerifyAndSignContract; +import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.protocol.trade.tasks.taker.BroadcastTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.tasks.taker.CreateTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOfferFeePayment; @@ -69,13 +71,16 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage) { + public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + if (trade == null) + this.trade = trade; + log.debug("setMailboxMessage " + mailboxMessage); // Might be called twice, so check that its only processed once if (!processModel.isMailboxMessageProcessed()) { processModel.mailboxMessageProcessed(); - if (mailboxMessage instanceof PayoutTxPublishedMessage) { - handle((PayoutTxPublishedMessage) mailboxMessage); + if (mailboxMessage instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) mailboxMessage); } } } @@ -142,7 +147,7 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol // After peer has received Fiat tx /////////////////////////////////////////////////////////////////////////////////////////// - private void handle(PayoutTxPublishedMessage tradeMessage) { + private void handle(PayoutTxFinalizedMessage tradeMessage) { processModel.setTradeMessage(tradeMessage); TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade, @@ -154,8 +159,9 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol this::handleTaskRunnerFault); taskRunner.addTasks( - ProcessPayoutTxPublishedMessage.class, - CommitPayoutTx.class); + ProcessPayoutTxFinalizedMessage.class, + CommitPayoutTx.class, + SetupPayoutTxLockTimeReachedListener.class); taskRunner.run(); } @@ -174,8 +180,8 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol if (tradeMessage instanceof RequestPublishDepositTxMessage) { handle((RequestPublishDepositTxMessage) tradeMessage); } - else if (tradeMessage instanceof PayoutTxPublishedMessage) { - handle((PayoutTxPublishedMessage) tradeMessage); + else if (tradeMessage instanceof PayoutTxFinalizedMessage) { + handle((PayoutTxFinalizedMessage) tradeMessage); } else { log.error("Incoming message not supported. " + tradeMessage); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java index 566d902230..b73906ac40 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/ProcessModel.java @@ -69,7 +69,6 @@ public class ProcessModel implements Model, Serializable { transient private boolean mailboxMessageProcessed; transient private TradeMessage tradeMessage; private String takeOfferFeeTxId; - private Transaction payoutTx; private List connectedOutputsForAllInputs; private Coin payoutAmount; private Transaction preparedDepositTx; @@ -166,15 +165,6 @@ public class ProcessModel implements Model, Serializable { return mailboxMessageProcessed; } - @Nullable - public Transaction getPayoutTx() { - return payoutTx; - } - - public void setPayoutTx(Transaction payoutTx) { - this.payoutTx = payoutTx; - } - @Nullable public Transaction getTakeOfferFeeTx() { return takeOfferFeeTx; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java index 8df7765dc0..e1e9a8f845 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsOffererProtocol.java @@ -22,6 +22,7 @@ import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.p2p.listener.SendMessageListener; import io.bitsquare.trade.SellerAsOffererTrade; +import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.availability.messages.ReportOfferAvailabilityMessage; import io.bitsquare.trade.protocol.availability.messages.RequestIsOfferAvailableMessage; import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage; @@ -36,9 +37,10 @@ import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignDepositTx; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessDepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessFiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessRequestPayDepositMessage; -import io.bitsquare.trade.protocol.trade.tasks.seller.SendPayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.tasks.seller.SendPayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.SendRequestPublishDepositTxMessage; -import io.bitsquare.trade.protocol.trade.tasks.seller.SignAndPublishPayoutTx; +import io.bitsquare.trade.protocol.trade.tasks.seller.SignAndFinalizePayoutTx; +import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.states.OffererTradeState; import org.slf4j.Logger; @@ -71,7 +73,10 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage) { + public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + if (trade == null) + this.trade = trade; + log.debug("setMailboxMessage " + mailboxMessage); // Might be called twice, so check that its only processed once if (!processModel.isMailboxMessageProcessed()) { @@ -201,8 +206,9 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt taskRunner.addTasks( VerifyTakeOfferFeePayment.class, - SignAndPublishPayoutTx.class, - SendPayoutTxPublishedMessage.class + SignAndFinalizePayoutTx.class, + SendPayoutTxFinalizedMessage.class, + SetupPayoutTxLockTimeReachedListener.class ); taskRunner.run(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java index b358929222..a09d9e63b6 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/SellerAsTakerProtocol.java @@ -21,6 +21,7 @@ import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.Message; import io.bitsquare.p2p.Peer; import io.bitsquare.trade.SellerAsTakerTrade; +import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.trade.messages.DepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.messages.FiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.messages.RequestPayDepositMessage; @@ -31,10 +32,11 @@ import io.bitsquare.trade.protocol.trade.tasks.seller.CreateAndSignDepositTx; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessDepositTxPublishedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessFiatTransferStartedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.ProcessRequestPayDepositMessage; -import io.bitsquare.trade.protocol.trade.tasks.seller.SendPayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.tasks.seller.SendPayoutTxFinalizedMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.SendRequestDepositTxInputsMessage; import io.bitsquare.trade.protocol.trade.tasks.seller.SendRequestPublishDepositTxMessage; -import io.bitsquare.trade.protocol.trade.tasks.seller.SignAndPublishPayoutTx; +import io.bitsquare.trade.protocol.trade.tasks.seller.SignAndFinalizePayoutTx; +import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.protocol.trade.tasks.taker.BroadcastTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.tasks.taker.CreateTakeOfferFeeTx; import io.bitsquare.trade.protocol.trade.tasks.taker.VerifyOfferFeePayment; @@ -71,7 +73,10 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc /////////////////////////////////////////////////////////////////////////////////////////// @Override - public void applyMailboxMessage(MailboxMessage mailboxMessage) { + public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade) { + if (trade == null) + this.trade = trade; + log.debug("setMailboxMessage " + mailboxMessage); // Might be called twice, so check that its only processed once if (!processModel.isMailboxMessageProcessed()) { @@ -177,8 +182,9 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc taskRunner.addTasks( VerifyOfferFeePayment.class, - SignAndPublishPayoutTx.class, - SendPayoutTxPublishedMessage.class + SignAndFinalizePayoutTx.class, + SendPayoutTxFinalizedMessage.class, + SetupPayoutTxLockTimeReachedListener.class ); taskRunner.run(); } 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 d6a2f3bad3..c8a47399a3 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 @@ -19,8 +19,10 @@ package io.bitsquare.trade.protocol.trade; import io.bitsquare.p2p.MailboxMessage; import io.bitsquare.p2p.MessageHandler; +import io.bitsquare.trade.OffererTrade; import io.bitsquare.trade.TakerTrade; import io.bitsquare.trade.Trade; +import io.bitsquare.trade.protocol.trade.tasks.shared.SetupPayoutTxLockTimeReachedListener; import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.TakerTradeState; @@ -51,7 +53,32 @@ public abstract class TradeProtocol { processModel.getMessageService().removeMessageHandler(messageHandler); } - abstract public void applyMailboxMessage(MailboxMessage mailboxMessage); + abstract public void applyMailboxMessage(MailboxMessage mailboxMessage, Trade trade); + + public void checkPayoutTxTimeLock(Trade trade) { + if (trade == null) + this.trade = trade; + + boolean needPayoutTxBroadcast = false; + if (trade instanceof TakerTrade) + needPayoutTxBroadcast = trade.processStateProperty().get() == TakerTradeState.ProcessState.PAYOUT_FINALIZED + || trade.processStateProperty().get() == TakerTradeState.ProcessState.PAYOUT_FINALIZED_MSG_SENT; + else if (trade instanceof OffererTrade) + needPayoutTxBroadcast = trade.processStateProperty().get() == OffererTradeState.ProcessState.PAYOUT_FINALIZED + || trade.processStateProperty().get() == OffererTradeState.ProcessState.PAYOUT_FINALIZED_MSG_SENT; + + if (needPayoutTxBroadcast) { + TradeTaskRunner taskRunner = new TradeTaskRunner(trade, + () -> { + log.debug("taskRunner needPayoutTxBroadcast completed"); + processModel.onComplete(); + }, + this::handleTaskRunnerFault); + + taskRunner.addTasks(SetupPayoutTxLockTimeReachedListener.class); + taskRunner.run(); + } + } protected void startTimeout() { log.debug("startTimeout"); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayoutTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayoutTxFinalizedMessage.java similarity index 90% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayoutTxPublishedMessage.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayoutTxFinalizedMessage.java index 6a9182ea99..62c5890554 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayoutTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/messages/PayoutTxFinalizedMessage.java @@ -26,13 +26,13 @@ import java.io.Serializable; import javax.annotation.concurrent.Immutable; @Immutable -public class PayoutTxPublishedMessage extends TradeMessage implements MailboxMessage, Serializable { +public class PayoutTxFinalizedMessage extends TradeMessage implements MailboxMessage, Serializable { // That object is sent over the wire, so we need to take care of version compatibility. private static final long serialVersionUID = 1L; public final Transaction payoutTx; - public PayoutTxPublishedMessage(String tradeId, Transaction payoutTx) { + public PayoutTxFinalizedMessage(String tradeId, Transaction payoutTx) { super(tradeId); this.payoutTx = payoutTx; } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignPayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignPayoutTx.java index 5c06f1ac1a..68ab3280fe 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignPayoutTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/CreateAndSignPayoutTx.java @@ -47,6 +47,7 @@ public class CreateAndSignPayoutTx extends TradeTask { sellerPayoutAmount, processModel.getAddressEntry(), processModel.tradingPeer.getPayoutAddressString(), + trade.getLockTimeDelta(), processModel.getTradeWalletPubKey(), processModel.tradingPeer.getTradeWalletPubKey(), processModel.getArbitratorPubKey()); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessPayoutTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessPayoutTxFinalizedMessage.java similarity index 85% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessPayoutTxPublishedMessage.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessPayoutTxFinalizedMessage.java index b35ec81344..d4a61cc4b3 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessPayoutTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/buyer/ProcessPayoutTxFinalizedMessage.java @@ -22,7 +22,7 @@ import io.bitsquare.trade.OffererTrade; import io.bitsquare.trade.TakerTrade; import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.trade.TradeTask; -import io.bitsquare.trade.protocol.trade.messages.PayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.messages.PayoutTxFinalizedMessage; import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.TakerTradeState; @@ -32,26 +32,26 @@ import org.slf4j.LoggerFactory; import static com.google.common.base.Preconditions.checkNotNull; import static io.bitsquare.util.Validator.checkTradeId; -public class ProcessPayoutTxPublishedMessage extends TradeTask { - private static final Logger log = LoggerFactory.getLogger(ProcessPayoutTxPublishedMessage.class); +public class ProcessPayoutTxFinalizedMessage extends TradeTask { + private static final Logger log = LoggerFactory.getLogger(ProcessPayoutTxFinalizedMessage.class); - public ProcessPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) { + public ProcessPayoutTxFinalizedMessage(TaskRunner taskHandler, Trade trade) { super(taskHandler, trade); } @Override protected void doRun() { try { - PayoutTxPublishedMessage message = (PayoutTxPublishedMessage) processModel.getTradeMessage(); + PayoutTxFinalizedMessage message = (PayoutTxFinalizedMessage) processModel.getTradeMessage(); checkTradeId(processModel.getId(), message); checkNotNull(message); trade.setPayoutTx(checkNotNull(message.payoutTx)); if (trade instanceof OffererTrade) - trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_PUBLISHED); + trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_FINALIZED); else if (trade instanceof TakerTrade) - trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_PUBLISHED); + trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_FINALIZED); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendPayoutTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendPayoutTxFinalizedMessage.java similarity index 85% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendPayoutTxPublishedMessage.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendPayoutTxFinalizedMessage.java index 08942e7384..40b7f24e4f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendPayoutTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SendPayoutTxFinalizedMessage.java @@ -23,7 +23,7 @@ import io.bitsquare.trade.OffererTrade; import io.bitsquare.trade.TakerTrade; import io.bitsquare.trade.Trade; import io.bitsquare.trade.protocol.trade.TradeTask; -import io.bitsquare.trade.protocol.trade.messages.PayoutTxPublishedMessage; +import io.bitsquare.trade.protocol.trade.messages.PayoutTxFinalizedMessage; import io.bitsquare.trade.states.OffererTradeState; import io.bitsquare.trade.states.StateUtil; import io.bitsquare.trade.states.TakerTradeState; @@ -31,17 +31,17 @@ import io.bitsquare.trade.states.TakerTradeState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class SendPayoutTxPublishedMessage extends TradeTask { - private static final Logger log = LoggerFactory.getLogger(SendPayoutTxPublishedMessage.class); +public class SendPayoutTxFinalizedMessage extends TradeTask { + private static final Logger log = LoggerFactory.getLogger(SendPayoutTxFinalizedMessage.class); - public SendPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) { + public SendPayoutTxFinalizedMessage(TaskRunner taskHandler, Trade trade) { super(taskHandler, trade); } @Override protected void doRun() { try { - PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(processModel.getId(), processModel.getPayoutTx()); + PayoutTxFinalizedMessage tradeMessage = new PayoutTxFinalizedMessage(processModel.getId(), trade.getPayoutTx()); processModel.getMessageService().sendMessage(trade.getTradingPeer(), tradeMessage, processModel.tradingPeer.getP2pSigPubKey(), @@ -52,9 +52,9 @@ public class SendPayoutTxPublishedMessage extends TradeTask { log.trace("PayoutTxPublishedMessage successfully arrived at peer"); if (trade instanceof TakerTrade) - trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_PUBLISHED_MSG_SENT); + trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_FINALIZED_MSG_SENT); else if (trade instanceof OffererTrade) - trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_PUBLISHED_MSG_SENT); + trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_FINALIZED_MSG_SENT); complete(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishPayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndFinalizePayoutTx.java similarity index 59% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishPayoutTx.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndFinalizePayoutTx.java index 3393267421..787f5f7177 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndPublishPayoutTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/seller/SignAndFinalizePayoutTx.java @@ -27,53 +27,39 @@ import io.bitsquare.trade.states.TakerTradeState; import org.bitcoinj.core.Transaction; -import com.google.common.util.concurrent.FutureCallback; - -import org.jetbrains.annotations.NotNull; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class SignAndPublishPayoutTx extends TradeTask { - private static final Logger log = LoggerFactory.getLogger(SignAndPublishPayoutTx.class); +public class SignAndFinalizePayoutTx extends TradeTask { + private static final Logger log = LoggerFactory.getLogger(SignAndFinalizePayoutTx.class); - public SignAndPublishPayoutTx(TaskRunner taskHandler, Trade trade) { + public SignAndFinalizePayoutTx(TaskRunner taskHandler, Trade trade) { super(taskHandler, trade); } @Override protected void doRun() { try { - processModel.getTradeWalletService().signAndPublishPayoutTx( + Transaction transaction = processModel.getTradeWalletService().signAndFinalizePayoutTx( trade.getDepositTx(), processModel.tradingPeer.getSignature(), processModel.tradingPeer.getPayoutAmount(), processModel.getPayoutAmount(), processModel.tradingPeer.getPayoutAddressString(), processModel.getAddressEntry(), + trade.getLockTimeDelta(), processModel.tradingPeer.getTradeWalletPubKey(), processModel.getTradeWalletPubKey(), - processModel.getArbitratorPubKey(), - new FutureCallback() { - @Override - public void onSuccess(Transaction transaction) { - processModel.setPayoutTx(transaction); + processModel.getArbitratorPubKey() + ); - if (trade instanceof TakerTrade) - trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_PUBLISHED); - else if (trade instanceof OffererTrade) - trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_PUBLISHED); + trade.setPayoutTx(transaction); + if (trade instanceof TakerTrade) + trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_FINALIZED); + else if (trade instanceof OffererTrade) + trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_FINALIZED); - complete(); - } - - @Override - public void onFailure(@NotNull Throwable t) { - t.printStackTrace(); - trade.setThrowable(t); - failed(t); - } - }); + complete(); } catch (Throwable t) { t.printStackTrace(); trade.setThrowable(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SetupPayoutTxLockTimeReachedListener.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SetupPayoutTxLockTimeReachedListener.java new file mode 100644 index 0000000000..651f734ec4 --- /dev/null +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/shared/SetupPayoutTxLockTimeReachedListener.java @@ -0,0 +1,105 @@ +/* + * 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.trade.protocol.trade.tasks.shared; + +import io.bitsquare.common.taskrunner.TaskRunner; +import io.bitsquare.trade.OffererTrade; +import io.bitsquare.trade.TakerTrade; +import io.bitsquare.trade.Trade; +import io.bitsquare.trade.protocol.trade.TradeTask; +import io.bitsquare.trade.states.OffererTradeState; +import io.bitsquare.trade.states.TakerTradeState; + +import org.bitcoinj.core.StoredBlock; +import org.bitcoinj.core.Transaction; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.concurrent.ExecutionException; + +import javafx.application.Platform; + +import org.jetbrains.annotations.NotNull; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SetupPayoutTxLockTimeReachedListener extends TradeTask { + private static final Logger log = LoggerFactory.getLogger(SetupPayoutTxLockTimeReachedListener.class); + + public SetupPayoutTxLockTimeReachedListener(TaskRunner taskHandler, Trade trade) { + super(taskHandler, trade); + } + + @Override + protected void doRun() { + try { + if (processModel.getTradeWalletService().getBestChainHeight() >= trade.getPayoutTx().getLockTime()) { + broadcastTx(); + } + else { + ListenableFuture blockHeightFuture = processModel.getTradeWalletService().getBlockHeightFuture(trade.getPayoutTx()); + blockHeightFuture.addListener( + () -> { + try { + log.debug("Block height reached " + blockHeightFuture.get().getHeight()); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + broadcastTx(); + }, + Platform::runLater); + } + } catch (Throwable t) { + t.printStackTrace(); + trade.setThrowable(t); + failed(t); + } + } + + protected void broadcastTx() { + processModel.getTradeWalletService().broadcastTx(trade.getPayoutTx(), new FutureCallback() { + @Override + public void onSuccess(Transaction transaction) { + log.debug("BroadcastTx succeeded. Transaction:" + transaction); + + if (trade instanceof TakerTrade) + trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_BROAD_CASTED); + else if (trade instanceof OffererTrade) + trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_BROAD_CASTED); + + complete(); + } + + @Override + public void onFailure(@NotNull Throwable t) { + t.printStackTrace(); + trade.setThrowable(t); + + if (trade instanceof TakerTrade) + trade.setProcessState(TakerTradeState.ProcessState.PAYOUT_BROAD_CASTED_FAILED); + else if (trade instanceof OffererTrade) + trade.setProcessState(OffererTradeState.ProcessState.PAYOUT_BROAD_CASTED_FAILED); + + + failed(t); + } + }); + } +} diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java index 8b848df4bc..ec3177bfd2 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/tasks/taker/BroadcastTakeOfferFeeTx.java @@ -42,7 +42,7 @@ public class BroadcastTakeOfferFeeTx extends TradeTask { @Override protected void doRun() { try { - processModel.getTradeWalletService().broadcastTakeOfferFeeTx(processModel.getTakeOfferFeeTx(), + processModel.getTradeWalletService().broadcastTx(processModel.getTakeOfferFeeTx(), new FutureCallback() { @Override public void onSuccess(Transaction transaction) { diff --git a/core/src/main/java/io/bitsquare/trade/states/OffererTradeState.java b/core/src/main/java/io/bitsquare/trade/states/OffererTradeState.java index 3c48587771..da9970396a 100644 --- a/core/src/main/java/io/bitsquare/trade/states/OffererTradeState.java +++ b/core/src/main/java/io/bitsquare/trade/states/OffererTradeState.java @@ -40,9 +40,11 @@ public class OffererTradeState { FIAT_PAYMENT_STARTED, FIAT_PAYMENT_RECEIVED, - PAYOUT_PUBLISHED, - PAYOUT_PUBLISHED_MSG_SENT, - + PAYOUT_FINALIZED, + PAYOUT_FINALIZED_MSG_SENT, + PAYOUT_BROAD_CASTED, + PAYOUT_BROAD_CASTED_FAILED, + MESSAGE_SENDING_FAILED, TIMEOUT, EXCEPTION diff --git a/core/src/main/java/io/bitsquare/trade/states/TakerTradeState.java b/core/src/main/java/io/bitsquare/trade/states/TakerTradeState.java index c68c25edee..c9d1baf4d9 100644 --- a/core/src/main/java/io/bitsquare/trade/states/TakerTradeState.java +++ b/core/src/main/java/io/bitsquare/trade/states/TakerTradeState.java @@ -41,8 +41,10 @@ public class TakerTradeState { FIAT_PAYMENT_STARTED, FIAT_PAYMENT_RECEIVED, - PAYOUT_PUBLISHED, - PAYOUT_PUBLISHED_MSG_SENT, + PAYOUT_FINALIZED, + PAYOUT_FINALIZED_MSG_SENT, + PAYOUT_BROAD_CASTED, + PAYOUT_BROAD_CASTED_FAILED, MESSAGE_SENDING_FAILED, TIMEOUT, diff --git a/core/src/main/resources/logback.xml b/core/src/main/resources/logback.xml index 6bf91a7e7e..a02853c1d2 100644 --- a/core/src/main/resources/logback.xml +++ b/core/src/main/resources/logback.xml @@ -34,12 +34,13 @@ - - + + - + +