changed trade protocol to use tasks, added scheduler framework

This commit is contained in:
Manfred Karrer 2014-07-03 19:22:06 +02:00
parent 4f26d76746
commit 7e55b7325a
93 changed files with 3980 additions and 1273 deletions

17
pom.xml
View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@ -194,6 +194,19 @@
<artifactId>proguard</artifactId>
<version>4.4</version>
<scope>runtime</scope>
</dependency>
-->
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.3</version>
</dependency>
<!--
<dependency>
<groupId>com.netflix.rxjava</groupId>
<artifactId>rxjava-core</artifactId>
<version>0.5.3</version>
</dependency>
-->
</dependencies>

View file

@ -17,7 +17,7 @@ public class BtcFormatter
public static BigInteger mBTC = new BigInteger("100000");
public static String satoshiToString(BigInteger value)
public static String formatSatoshis(BigInteger value)
{
return Utils.bitcoinValueToFriendlyString(value);
}
@ -30,7 +30,14 @@ public class BtcFormatter
public static BigInteger stringValueToSatoshis(String value)
{
return Utils.toNanoCoins(String.valueOf(BitSquareConverter.stringToDouble2(value)));
try
{
return Utils.toNanoCoins(String.valueOf(BitSquareConverter.stringToDouble(value)));
} catch (ArithmeticException e)
{
log.warn("ArithmeticException " + e);
}
return BigInteger.ZERO;
}
public static BigInteger doubleValueToSatoshis(double value)

View file

@ -19,7 +19,7 @@ public class BtcValidator
public static boolean isMinSpendableAmount(BigInteger amount)
{
return amount != null && amount.compareTo(FeePolicy.TX_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
}
public boolean isAddressValid(String addressString)

View file

@ -2,22 +2,16 @@ package io.bitsquare.btc;
import com.google.bitcoin.core.*;
import com.google.inject.Inject;
import io.bitsquare.currency.Bitcoin;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class FeePolicy
{
public static final BigInteger TX_FEE_depr = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
public static final BigInteger ACCOUNT_REGISTRATION_FEE_depr = Utils.toNanoCoins("0.01");
public static final BigInteger CREATE_OFFER_FEE_depr = Utils.toNanoCoins("0.001");
public static final BigInteger TAKE_OFFER_FEE_depr = CREATE_OFFER_FEE_depr;
public static final Bitcoin TX_FEE = new Bitcoin(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
public static final Bitcoin CREATE_OFFER_FEE = new Bitcoin(Utils.toNanoCoins("0.001"));
public static final Bitcoin TAKE_OFFER_FEE = CREATE_OFFER_FEE;
public static final BigInteger TX_FEE = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
public static final BigInteger ACCOUNT_REGISTRATION_FEE = Utils.toNanoCoins("0.01");
public static final BigInteger CREATE_OFFER_FEE = Utils.toNanoCoins("0.001");
public static final BigInteger TAKE_OFFER_FEE = CREATE_OFFER_FEE;
private static final Logger log = LoggerFactory.getLogger(FeePolicy.class);
private static final String registrationFeeAddress = "mvkDXt4QmN4Nq9dRUsRigBCaovde9nLkZR";
private static final String createOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg";

View file

@ -86,7 +86,7 @@ public class WalletFacade
public void initWallet()
{
// Tell bitcoinj to run event handlers on the JavaFX UI thread. This keeps things simple and means
// Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
// a future version.
@ -94,7 +94,7 @@ public class WalletFacade
if (params == RegTestParams.get())
{
walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally.
walletAppKit.connectToLocalHost(); // You should execute a regtest mode bitcoind locally.
}
else if (params == MainNetParams.get())
{
@ -527,21 +527,21 @@ public class WalletFacade
public boolean isRegistrationFeeBalanceSufficient()
{
return getRegistrationBalance().compareTo(FeePolicy.ACCOUNT_REGISTRATION_FEE_depr) >= 0;
return getRegistrationBalance().compareTo(FeePolicy.ACCOUNT_REGISTRATION_FEE) >= 0;
}
public boolean isUnusedTradeAddressBalanceAboveCreationFee()
{
AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo();
BigInteger unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress());
return unUsedAddressInfoBalance.compareTo(FeePolicy.CREATE_OFFER_FEE_depr) > 0;
return unUsedAddressInfoBalance.compareTo(FeePolicy.CREATE_OFFER_FEE) > 0;
}
public boolean isUnusedTradeAddressBalanceAboveTakeOfferFee()
{
AddressEntry unUsedAddressEntry = getUnusedTradeAddressInfo();
BigInteger unUsedAddressInfoBalance = getBalanceForAddress(unUsedAddressEntry.getAddress());
return unUsedAddressInfoBalance.compareTo(FeePolicy.TAKE_OFFER_FEE_depr) > 0;
return unUsedAddressInfoBalance.compareTo(FeePolicy.TAKE_OFFER_FEE) > 0;
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -573,8 +573,8 @@ public class WalletFacade
byte[] data = cryptoFacade.getEmbeddedAccountRegistrationData(getRegistrationAddressInfo().getKey(), stringifiedBankAccounts);
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build());
BigInteger fee = FeePolicy.ACCOUNT_REGISTRATION_FEE_depr.subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(FeePolicy.TX_FEE_depr);
log.trace("fee: " + BtcFormatter.satoshiToString(fee));
BigInteger fee = FeePolicy.ACCOUNT_REGISTRATION_FEE.subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(FeePolicy.TX_FEE);
log.trace("fee: " + BtcFormatter.formatSatoshis(fee));
tx.addOutput(fee, feePolicy.getAddressForRegistrationFee());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
@ -588,12 +588,11 @@ public class WalletFacade
printInputs("payRegistrationFee", tx);
}
@SuppressWarnings("UnusedReturnValue")
public String payCreateOfferFee(String offerId, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{
Transaction tx = new Transaction(params);
BigInteger fee = FeePolicy.CREATE_OFFER_FEE_depr.subtract(FeePolicy.TX_FEE_depr);
log.trace("fee: " + BtcFormatter.satoshiToString(fee));
BigInteger fee = FeePolicy.CREATE_OFFER_FEE.subtract(FeePolicy.TX_FEE);
log.trace("fee: " + BtcFormatter.formatSatoshis(fee));
tx.addOutput(fee, feePolicy.getAddressForCreateOfferFee());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
@ -603,18 +602,18 @@ public class WalletFacade
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback);
printInputs("payTakeOfferFee", tx);
printInputs("payCreateOfferFee", tx);
log.debug("tx=" + tx);
return tx.getHashAsString();
}
@SuppressWarnings("UnusedReturnValue")
public String payTakeOfferFee(String offerId, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{
Transaction tx = new Transaction(params);
BigInteger fee = FeePolicy.TAKE_OFFER_FEE_depr.subtract(FeePolicy.TX_FEE_depr);
log.trace("fee: " + BtcFormatter.satoshiToString(fee));
BigInteger fee = FeePolicy.TAKE_OFFER_FEE.subtract(FeePolicy.TX_FEE);
log.trace("fee: " + BtcFormatter.formatSatoshis(fee));
tx.addOutput(fee, feePolicy.getAddressForTakeOfferFee());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
@ -643,7 +642,7 @@ public class WalletFacade
FutureCallback<Transaction> callback) throws AddressFormatException, InsufficientMoneyException, IllegalArgumentException
{
Transaction tx = new Transaction(params);
tx.addOutput(amount.subtract(FeePolicy.TX_FEE_depr), new Address(params, withdrawToAddress));
tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, withdrawToAddress));
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation)
@ -672,7 +671,7 @@ public class WalletFacade
{
log.debug("offererCreatesMSTxAndAddPayment");
log.trace("inputs: ");
log.trace("offererInputAmount=" + BtcFormatter.satoshiToString(offererInputAmount));
log.trace("offererInputAmount=" + BtcFormatter.formatSatoshis(offererInputAmount));
log.trace("offererPubKey=" + offererPubKey);
log.trace("takerPubKey=" + takerPubKey);
log.trace("arbitratorPubKey=" + arbitratorPubKey);
@ -730,8 +729,8 @@ public class WalletFacade
{
log.debug("takerAddPaymentAndSignTx");
log.trace("inputs: ");
log.trace("takerInputAmount=" + BtcFormatter.satoshiToString(takerInputAmount));
log.trace("msOutputAmount=" + BtcFormatter.satoshiToString(msOutputAmount));
log.trace("takerInputAmount=" + BtcFormatter.formatSatoshis(takerInputAmount));
log.trace("msOutputAmount=" + BtcFormatter.formatSatoshis(msOutputAmount));
log.trace("offererPubKey=" + offererPubKey);
log.trace("takerPubKey=" + takerPubKey);
log.trace("arbitratorPubKey=" + arbitratorPubKey);
@ -786,7 +785,7 @@ public class WalletFacade
tx.addOutput(tempTx.getOutput(1));
// We add the btc tx fee to the msOutputAmount and apply the change to the multiSig output
msOutputAmount = msOutputAmount.add(FeePolicy.TX_FEE_depr);
msOutputAmount = msOutputAmount.add(FeePolicy.TX_FEE);
tx.getOutput(0).setValue(msOutputAmount);
// Now we sign our input
@ -835,7 +834,7 @@ public class WalletFacade
// 3. step: deposit tx
// Offerer signs tx and publishes it
public Transaction offererSignAndPublishTx(String offerersFirstTxAsHex,
public void offererSignAndPublishTx(String offerersFirstTxAsHex,
String takersSignedTxAsHex,
String takersSignedConnOutAsHex,
String takersSignedScriptSigAsHex,
@ -881,7 +880,7 @@ public class WalletFacade
takersSignedTxInput.setParent(tx);
tx.addInput(takersSignedTxInput);
//TODO handle non change output cases
//TODO onResult non change output cases
// add outputs from takers tx, they are already correct
tx.addOutput(takersSignedTx.getOutput(0));
if (takersSignedTx.getOutputs().size() > 1)
@ -942,7 +941,6 @@ public class WalletFacade
Futures.addCallback(broadcastComplete, callback);
printInputs("tx", tx);
log.debug("tx = " + tx);
return tx;
}
// 4 step deposit tx: Offerer send deposit tx to taker
@ -981,8 +979,8 @@ public class WalletFacade
log.debug("offererCreatesAndSignsPayoutTx");
log.trace("inputs: ");
log.trace("depositTxID=" + depositTxID);
log.trace("offererPaybackAmount=" + BtcFormatter.satoshiToString(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.satoshiToString(takerPaybackAmount));
log.trace("offererPaybackAmount=" + BtcFormatter.formatSatoshis(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.formatSatoshis(takerPaybackAmount));
log.trace("takerAddress=" + takerAddress);
// Offerer has published depositTx earlier, so he has it in his wallet
@ -1007,9 +1005,7 @@ public class WalletFacade
}
// 6. step payout tx: Taker signs and publish tx
@SuppressWarnings("UnusedReturnValue")
public Transaction takerSignsAndSendsTx(String depositTxAsHex,
public void takerSignsAndSendsTx(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
@ -1023,8 +1019,8 @@ public class WalletFacade
log.trace("depositTxAsHex=" + depositTxAsHex);
log.trace("offererSignatureR=" + offererSignatureR);
log.trace("offererSignatureS=" + offererSignatureS);
log.trace("offererPaybackAmount=" + BtcFormatter.satoshiToString(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.satoshiToString(takerPaybackAmount));
log.trace("offererPaybackAmount=" + BtcFormatter.formatSatoshis(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.formatSatoshis(takerPaybackAmount));
log.trace("offererAddress=" + offererAddress);
log.trace("callback=" + callback);
@ -1062,7 +1058,6 @@ public class WalletFacade
log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("takerSignsAndSendsTx", tx);
log.debug("tx = " + tx);
return tx;
}
@ -1103,8 +1098,8 @@ public class WalletFacade
log.trace("createPayoutTx");
log.trace("inputs: ");
log.trace("depositTxAsHex=" + depositTxAsHex);
log.trace("offererPaybackAmount=" + BtcFormatter.satoshiToString(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.satoshiToString(takerPaybackAmount));
log.trace("offererPaybackAmount=" + BtcFormatter.formatSatoshis(offererPaybackAmount));
log.trace("takerPaybackAmount=" + BtcFormatter.formatSatoshis(takerPaybackAmount));
log.trace("offererAddress=" + offererAddress);
log.trace("takerAddress=" + takerAddress);
@ -1122,7 +1117,7 @@ public class WalletFacade
{
for (TransactionInput input : tx.getInputs())
if (input.getConnectedOutput() != null)
log.trace(tracePrefix + ": " + BtcFormatter.satoshiToString(input.getConnectedOutput().getValue()));
log.trace(tracePrefix + " input value : " + BtcFormatter.formatSatoshis(input.getConnectedOutput().getValue()));
else
log.trace(tracePrefix + ": " + "Transaction already has inputs but we don't have the connected outputs, so we don't know the value.");
}

View file

@ -1,110 +0,0 @@
package io.bitsquare.currency;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.btc.BtcFormatter;
import java.math.BigInteger;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Bitcoin extends BigInteger
{
private static final Logger log = LoggerFactory.getLogger(Bitcoin.class);
private static final long serialVersionUID = 6436341706716520132L;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Bitcoin(BigInteger val)
{
super(val.toByteArray());
}
public Bitcoin(byte[] val)
{
super(val);
}
public Bitcoin(int signum, byte[] magnitude)
{
super(signum, magnitude);
}
public Bitcoin(String val, int radix)
{
super(val, radix);
}
public Bitcoin(String val)
{
super(val);
}
public Bitcoin(int numBits, Random rnd)
{
super(numBits, rnd);
}
public Bitcoin(int bitLength, int certainty, Random rnd)
{
super(bitLength, certainty, rnd);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public boolean isZero()
{
return this.compareTo(BigInteger.ZERO) == 0;
}
public boolean isMinValue()
{
return this.compareTo(Transaction.MIN_NONDUST_OUTPUT) >= 0;
}
public Bitcoin addBitcoin(Bitcoin other)
{
return new Bitcoin(this.add(other));
}
public Bitcoin subtractBitcoin(Bitcoin other)
{
return new Bitcoin(this.subtract(other));
}
public Bitcoin multiplyBitcoin(Bitcoin other)
{
return new Bitcoin(this.multiply(other));
}
public boolean isLarger(Bitcoin other)
{
return this.compareTo(other) > 0;
}
public boolean isLess(Bitcoin other)
{
return this.compareTo(other) < 0;
}
public boolean isEqual(Bitcoin other)
{
return this.compareTo(other) == 0;
}
public String getFormattedValue()
{
return BtcFormatter.satoshiToString(this);
}
}

View file

@ -1,51 +0,0 @@
package io.bitsquare.currency;
import io.bitsquare.gui.util.BitSquareFormatter;
import java.util.Currency;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Fiat
{
private static final Logger log = LoggerFactory.getLogger(Fiat.class);
private double value;
private Currency currency;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Fiat(double value)
{
this.value = value;
this.currency = Currency.getInstance(Locale.getDefault());
}
public Fiat(double value, Currency currency)
{
this.value = value;
this.currency = currency;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public String getFormattedValue()
{
return BitSquareFormatter.formatPrice(value);
}
public String getCurrencyCode()
{
return currency.getCurrencyCode();
}
public Currency getCurrency()
{
return currency;
}
}

View file

@ -8,11 +8,11 @@ import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.market.MarketController;
import io.bitsquare.gui.orders.OrdersController;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.gui.util.Transitions;
import io.bitsquare.locale.Localisation;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Trading;
@ -42,7 +42,7 @@ import org.slf4j.LoggerFactory;
public class MainController implements Initializable, NavigationController
{
private static final Logger log = LoggerFactory.getLogger(MainController.class);
private static MainController mainController;
private static MainController INSTANCE;
private final User user;
private final WalletFacade walletFacade;
@ -85,7 +85,7 @@ public class MainController implements Initializable, NavigationController
this.trading = trading;
this.storage = storage;
MainController.mainController = this;
MainController.INSTANCE = this;
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -93,9 +93,9 @@ public class MainController implements Initializable, NavigationController
///////////////////////////////////////////////////////////////////////////////////////////
public static MainController INSTANCE()
public static MainController GET_INSTANCE()
{
return mainController;
return INSTANCE;
}
@ -173,7 +173,7 @@ public class MainController implements Initializable, NavigationController
private void init()
{
messageFacade.init();
messageFacade.addTakeOfferRequestListener(this::showTakeOfferRequest);
messageFacade.addTakeOfferRequestListener(this::onTakeOfferRequested);
walletFacade.addDownloadListener(new WalletFacade.DownloadListener()
{
@ -209,13 +209,15 @@ public class MainController implements Initializable, NavigationController
}
private void showTakeOfferRequest(TradeMessage tradeMessage, PeerAddress sender)
private void onTakeOfferRequested(String offerId, PeerAddress sender)
{
trading.createOffererPaymentProtocol(tradeMessage, sender);
final Button alertButton = new Button("", Icons.getIconImageView(Icons.MSG_ALERT));
alertButton.setId("nav-alert-button");
alertButton.relocate(36, 19);
alertButton.setOnAction((e) -> ordersButton.fire());
alertButton.setOnAction((e) -> {
OrdersController.GET_INSTANCE().setSelectedTabIndex(1);
ordersButton.fire();
});
Tooltip.install(alertButton, new Tooltip("Someone accepted your offer"));
ordersButtonButtonHolder.getChildren().add(alertButton);
}
@ -289,13 +291,13 @@ public class MainController implements Initializable, NavigationController
balanceTextField.setEditable(false);
balanceTextField.setPrefWidth(90);
balanceTextField.setId("nav-balance-label");
balanceTextField.setText(BtcFormatter.satoshiToString(walletFacade.getWalletBalance()));
balanceTextField.setText(BtcFormatter.formatSatoshis(walletFacade.getWalletBalance()));
walletFacade.addBalanceListener(new BalanceListener()
{
@Override
public void onBalanceChanged(BigInteger balance)
{
balanceTextField.setText(BtcFormatter.satoshiToString(balance));
balanceTextField.setText(BtcFormatter.formatSatoshis(balance));
}
});

View file

@ -19,7 +19,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
@ -181,7 +180,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
///////////////////////////////////////////////////////////////////////////////////////////
@FXML
public void onPrevious(ActionEvent actionEvent)
public void onPrevious()
{
if (index > 0)
{
@ -193,7 +192,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
}
@FXML
public void onNext(ActionEvent actionEvent)
public void onNext()
{
if (index < allArbitrators.size() - 1)
{
@ -205,14 +204,14 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
}
@FXML
public void onSelect(ActionEvent actionEvent)
public void onSelect()
{
settings.addAcceptedArbitrator(currentArbitrator);
storage.write(settings.getClass().getName(), settings);
}
@FXML
public void onClose(ActionEvent actionEvent)
public void onClose()
{
Stage stage = (Stage) rootContainer.getScene().getWindow();
stage.close();

View file

@ -29,7 +29,6 @@ import java.math.BigInteger;
import java.net.URL;
import java.util.*;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
@ -232,7 +231,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
///////////////////////////////////////////////////////////////////////////////////////////
@FXML
public void onSelectIDType(ActionEvent actionEvent)
public void onSelectIDType()
{
idType = idTypeComboBox.getSelectionModel().getSelectedItem();
if (idType != null)
@ -259,7 +258,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
}
@FXML
public void onAddLanguage(ActionEvent actionEvent)
public void onAddLanguage()
{
Locale item = languageComboBox.getSelectionModel().getSelectedItem();
if (!languageList.contains(item) && item != null)
@ -271,14 +270,14 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
}
@FXML
public void onClearLanguages(ActionEvent actionEvent)
public void onClearLanguages()
{
languageList.clear();
languagesTextField.setText("");
}
@FXML
public void onAddMethod(ActionEvent actionEvent)
public void onAddMethod()
{
Arbitrator.METHOD item = methodsComboBox.getSelectionModel().getSelectedItem();
if (!methodList.contains(item) && item != null)
@ -290,7 +289,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
}
@FXML
public void onClearMethods(ActionEvent actionEvent)
public void onClearMethods()
{
methodList.clear();
methodsTextField.setText("");
@ -298,7 +297,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
@FXML
public void onAddIDVerification(ActionEvent actionEvent)
public void onAddIDVerification()
{
Arbitrator.ID_VERIFICATION idVerification = idVerificationsComboBox.getSelectionModel().getSelectedItem();
if (idVerification != null)
@ -314,14 +313,14 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
}
@FXML
public void onClearIDVerifications(ActionEvent actionEvent)
public void onClearIDVerifications()
{
idVerificationList.clear();
idVerificationsTextField.setText("");
}
@FXML
public void onSaveProfile(ActionEvent actionEvent)
public void onSaveProfile()
{
arbitrator = getEditedArbitrator();
if (arbitrator != null)
@ -349,7 +348,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
}
@FXML
public void onPaymentDone(ActionEvent actionEvent)
public void onPaymentDone()
{
//To change body of created methods use File | Settings | File Templates.
}

View file

@ -13,9 +13,12 @@ import javafx.scene.Node;
import javafx.scene.control.SingleSelectionModel;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LazyLoadingTabPane extends TabPane
{
private static final Logger log = LoggerFactory.getLogger(LazyLoadingTabPane.class);
private final Map<Integer, Node> views = new HashMap<>();
private final Map<Integer, ChildController> controllers = new HashMap<>();
private SingleSelectionModel<Tab> selectionModel;
@ -24,6 +27,7 @@ public class LazyLoadingTabPane extends TabPane
private String[] tabContentFXMLUrls;
private Storage storage;
private ChildController childController;
private int selectedTabIndex = -1;
///////////////////////////////////////////////////////////////////////////////////////////
@ -50,9 +54,17 @@ public class LazyLoadingTabPane extends TabPane
selectionModel = getSelectionModel();
selectionModel.selectedItemProperty().addListener((observableValue, oldTab, newTab) -> onTabSelectedIndexChanged());
if (selectedTabIndex == -1)
{
Object indexObject = storage.read(storageId);
log.trace("saved index" + indexObject);
if (indexObject != null)
selectionModel.select((int) indexObject);
}
else
{
selectionModel.select(selectedTabIndex);
}
onTabSelectedIndexChanged();
}
@ -81,6 +93,7 @@ public class LazyLoadingTabPane extends TabPane
private void onTabSelectedIndexChanged()
{
int index = selectionModel.getSelectedIndex();
log.trace("onTabSelectedIndexChanged index" + index);
if (index < tabContentFXMLUrls.length && index >= 0)
{
if (childController != null)
@ -121,4 +134,8 @@ public class LazyLoadingTabPane extends TabPane
}
}
public void setSelectedTabIndex(int selectedTabIndex)
{
this.selectedTabIndex = selectedTabIndex;
}
}

View file

@ -0,0 +1,212 @@
package io.bitsquare.gui.components;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.*;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TextField;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Effect;
import javafx.scene.paint.Color;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>
* TextField with regex-based real-time input validation.
* JavaFX 2 and FXML compatible. </p>
* <p>
* FXML code example:<div>
* {@code <ValidatedTextField fx:id="validatedTextField" minLength="1" maxLength="1" mask="^[0-9]*$" />}
* </div>
* </p>
*/
public class ValidatedTextField extends TextField
{
private static final Logger log = LoggerFactory.getLogger(ValidatedTextField.class);
private final BooleanProperty invalid = new SimpleBooleanProperty(false);
private final StringProperty mask;
private final IntegerProperty minLength;
private final IntegerProperty maxLength;
private Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0);
public ValidatedTextField()
{
super();
this.mask = new SimpleStringProperty("^[0-9.,]*$");
this.minLength = new SimpleIntegerProperty(1);
this.maxLength = new SimpleIntegerProperty(12);
bind();
}
public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable)
{
this(mask, minLength, maxLength, nullable, null);
}
public ValidatedTextField(String mask, int minLength, int maxLength, boolean nullable, String string)
{
super(string);
this.mask = new SimpleStringProperty(mask);
this.minLength = new SimpleIntegerProperty(minLength);
this.maxLength = new SimpleIntegerProperty(maxLength);
bind();
}
public ReadOnlyBooleanProperty invalidProperty()
{
return invalid;
}
public ReadOnlyStringProperty maskProperty()
{
return mask;
}
public ReadOnlyIntegerProperty minLengthProperty()
{
return minLength;
}
public ReadOnlyIntegerProperty maxLengthProperty()
{
return maxLength;
}
public boolean isInvalid()
{
return invalid.get();
}
public String getMask()
{
return mask.get();
}
public void setMask(String mask)
{
this.mask.set(mask);
}
public int getMinLength()
{
return minLength.get();
}
public void setMinLength(int minLength)
{
this.minLength.set(minLength);
}
public int getMaxLength()
{
return maxLength.get();
}
public void setMaxLength(int maxLength)
{
this.maxLength.set(maxLength);
}
public Effect getInvalidEffect()
{
return this.invalidEffect;
}
public void setInvalidEffect(Effect effect)
{
this.invalidEffect = effect;
}
private void bind()
{
this.invalid.bind(maskCheck().or(minLengthCheck()));
this.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> ov, String t, String t1)
{
if (textProperty().get().length() > maxLength.get())
{
setText(t);
}
}
});
this.invalid.addListener(new ChangeListener<Boolean>()
{
@Override
public void changed(ObservableValue<? extends Boolean> ov, Boolean t, Boolean t1)
{
if (t ^ t1)
{
if (t1)
{
// setStyle("-fx-font-weight: bold; -fx-text-fill: red;");
setEffect(invalidEffect);
}
else
{
// setStyle("-fx-font-weight: normal; -fx-text-fill: inherit;");
setEffect(null);
}
}
}
});
}
private BooleanBinding maskCheck()
{
return new BooleanBinding()
{
{
super.bind(textProperty(), mask);
}
@Override
protected boolean computeValue()
{
return !textProperty().get().matches(mask.get());
}
};
}
private BooleanBinding minLengthCheck()
{
return new BooleanBinding()
{
{
super.bind(textProperty(), minLength);
}
@Override
protected boolean computeValue()
{
return textProperty().get().length() < minLength.get();
}
};
}
private BooleanBinding maxLengthCheck()
{
return new BooleanBinding()
{
{
super.bind(textProperty(), maxLength);
}
@Override
protected boolean computeValue()
{
return textProperty().get().length() > maxLength.get();
}
};
}
}

View file

@ -40,7 +40,7 @@ public class TransactionsListItem
Address address = null;
if (valueSentToMe.compareTo(BigInteger.ZERO) == 0)
{
amount.set("-" + BtcFormatter.satoshiToString(valueSentFromMe));
amount.set("-" + BtcFormatter.formatSatoshis(valueSentFromMe));
for (TransactionOutput transactionOutput : transaction.getOutputs())
{
@ -62,7 +62,7 @@ public class TransactionsListItem
}
else if (valueSentFromMe.compareTo(BigInteger.ZERO) == 0)
{
amount.set(BtcFormatter.satoshiToString(valueSentToMe));
amount.set(BtcFormatter.formatSatoshis(valueSentToMe));
type.set("Received with");
for (TransactionOutput transactionOutput : transaction.getOutputs())
@ -83,7 +83,7 @@ public class TransactionsListItem
}
else
{
amount.set(BtcFormatter.satoshiToString(valueSentToMe.subtract(valueSentFromMe)));
amount.set(BtcFormatter.formatSatoshis(valueSentToMe.subtract(valueSentFromMe)));
boolean outgoing = false;
for (TransactionOutput transactionOutput : transaction.getOutputs())

View file

@ -83,7 +83,7 @@ public class WithdrawalController implements Initializable, ChildController, Hib
if (BigInteger.ZERO.compareTo(newValue.getBalance()) <= 0)
{
amountTextField.setText(BtcFormatter.satoshiToString(newValue.getBalance()));
amountTextField.setText(BtcFormatter.formatSatoshis(newValue.getBalance()));
withdrawFromTextField.setText(newValue.getAddressEntry().getAddressString());
changeAddressTextField.setText(newValue.getAddressEntry().getAddressString());
}
@ -174,8 +174,8 @@ public class WithdrawalController implements Initializable, ChildController, Hib
"Amount: " + amountTextField.getText() + " BTC\n" +
"Sending address: " + withdrawFromTextField.getText() + "\n" +
"Receiving address: " + withdrawToTextField.getText() + "\n" +
"Transaction fee: " + BtcFormatter.satoshiToString(FeePolicy.TX_FEE_depr) + "\n" +
"You receive in total: " + BtcFormatter.satoshiToString(amount.subtract(FeePolicy.TX_FEE_depr)) + " BTC\n\n" +
"Transaction fee: " + BtcFormatter.formatSatoshis(FeePolicy.TX_FEE) + "\n" +
"You receive in total: " + BtcFormatter.formatSatoshis(amount.subtract(FeePolicy.TX_FEE)) + " BTC\n\n" +
"Are you sure you withdraw that amount?");
if (response == Dialog.Actions.OK)
{

View file

@ -84,7 +84,7 @@ public class WithdrawalListItem
this.balance = balance;
if (balance != null)
{
balanceLabel.setText(BtcFormatter.satoshiToString(balance));
balanceLabel.setText(BtcFormatter.formatSatoshis(balance));
}
}

View file

@ -34,8 +34,6 @@ public class MarketController implements Initializable, NavigationController, Ch
public void initialize(URL url, ResourceBundle rb)
{
navigateToView(NavigationItem.ORDER_BOOK);
navigateToView(NavigationItem.TAKE_OFFER);
}

View file

@ -127,7 +127,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
}
acceptedCountriesTextField.setText(BitSquareFormatter.countryLocalesToString(settings.getAcceptedCountries()));
acceptedLanguagesTextField.setText(BitSquareFormatter.languageLocalesToString(settings.getAcceptedLanguageLocales()));
feeLabel.setText(BtcFormatter.satoshiToString(FeePolicy.CREATE_OFFER_FEE_depr));
feeLabel.setText(BtcFormatter.formatSatoshis(FeePolicy.CREATE_OFFER_FEE));
}
@ -175,7 +175,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
return;
}
int collateral = (int) (BitSquareConverter.stringToDouble2(collateralTextField.getText()));
int collateral = (int) (BitSquareConverter.stringToDouble(collateralTextField.getText()));
Arbitrator arbitrator = settings.getRandomArbitrator(collateral, getAmountAsBI());
if (arbitrator == null)
{
@ -189,7 +189,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
{
offer = new Offer(user.getMessagePubKeyAsHex(),
direction,
BitSquareConverter.stringToDouble2(priceTextField.getText()),
BitSquareConverter.stringToDouble(priceTextField.getText()),
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
BtcFormatter.stringValueToSatoshis(minAmountTextField.getText()),
user.getCurrentBankAccount().getBankAccountType(),
@ -202,7 +202,9 @@ public class CreateOfferController implements Initializable, ChildController, Hi
settings.getAcceptedLanguageLocales());
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
try
{
walletFacade.payCreateOfferFee(offer.getId(), new FutureCallback<Transaction>()
{
@Override
public void onSuccess(@javax.annotation.Nullable Transaction transaction)
@ -231,10 +233,7 @@ public class CreateOfferController implements Initializable, ChildController, Hi
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t);
placeOfferButton.setDisable(false);
}
};
try
{
walletFacade.payCreateOfferFee(offer.getId(), callback);
});
placeOfferButton.setDisable(true);
} catch (InsufficientMoneyException e1)
{
@ -279,8 +278,8 @@ public class CreateOfferController implements Initializable, ChildController, Hi
private double getVolume()
{
double amountAsDouble = BitSquareConverter.stringToDouble2(amountTextField.getText());
double priceAsDouble = BitSquareConverter.stringToDouble2(priceTextField.getText());
double amountAsDouble = BitSquareConverter.stringToDouble(amountTextField.getText());
double priceAsDouble = BitSquareConverter.stringToDouble(priceTextField.getText());
return amountAsDouble * priceAsDouble;
}
@ -293,10 +292,10 @@ public class CreateOfferController implements Initializable, ChildController, Hi
@SuppressWarnings("UnusedAssignment")
private boolean inputValid()
{
double priceAsDouble = BitSquareConverter.stringToDouble2(priceTextField.getText());
double minAmountAsDouble = BitSquareConverter.stringToDouble2(minAmountTextField.getText());
double amountAsDouble = BitSquareConverter.stringToDouble2(amountTextField.getText());
double collateralAsDouble = BitSquareConverter.stringToDouble2(collateralTextField.getText());
double priceAsDouble = BitSquareConverter.stringToDouble(priceTextField.getText());
double minAmountAsDouble = BitSquareConverter.stringToDouble(minAmountTextField.getText());
double amountAsDouble = BitSquareConverter.stringToDouble(amountTextField.getText());
double collateralAsDouble = BitSquareConverter.stringToDouble(collateralTextField.getText());
return priceAsDouble > 0 &&
amountAsDouble > 0 &&

View file

@ -13,7 +13,7 @@ import io.bitsquare.gui.MainController;
import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.NavigationItem;
import io.bitsquare.gui.market.createOffer.CreateOfferController;
import io.bitsquare.gui.market.trade.TakerTradeController;
import io.bitsquare.gui.market.trade.TakerOfferController;
import io.bitsquare.gui.popups.Popups;
import io.bitsquare.gui.util.BitSquareConverter;
import io.bitsquare.gui.util.BitSquareFormatter;
@ -115,7 +115,7 @@ public class OrderBookController implements Initializable, ChildController
{
orderBook.init();
// setup table
// init table
setCountryColumnCellFactory();
setBankAccountTypeColumnCellFactory();
setDirectionColumnCellFactory();
@ -223,16 +223,16 @@ public class OrderBookController implements Initializable, ChildController
Action response = Popups.openErrorPopup("Registration fee not confirmed yet", "The registration fee transaction has not been confirmed yet in the blockchain. Please wait until it has at least 1 confirmation.");
if (response == Dialog.Actions.OK)
{
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS);
MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
}
}
}
else
{
Action response = Popups.openErrorPopup("Missing registration fee", "You have not funded the full registration fee of " + BtcFormatter.satoshiToString(FeePolicy.ACCOUNT_REGISTRATION_FEE_depr) + " BTC.");
Action response = Popups.openErrorPopup("Missing registration fee", "You have not funded the full registration fee of " + BtcFormatter.formatSatoshis(FeePolicy.ACCOUNT_REGISTRATION_FEE) + " BTC.");
if (response == Dialog.Actions.OK)
{
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS);
MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
}
}
}
@ -255,11 +255,11 @@ public class OrderBookController implements Initializable, ChildController
Action registrationMissingAction = Popups.openRegistrationMissingPopup("Not registered yet", "Please follow these steps:", "You need to register before you can place an offer.", commandLinks, selectedIndex);
if (registrationMissingAction == settingsCommandLink)
{
MainController.INSTANCE().navigateToView(NavigationItem.SETTINGS);
MainController.GET_INSTANCE().navigateToView(NavigationItem.SETTINGS);
}
else if (registrationMissingAction == depositFeeCommandLink)
{
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS);
MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
}
else if (registrationMissingAction == sendRegistrationCommandLink)
{
@ -315,7 +315,7 @@ public class OrderBookController implements Initializable, ChildController
Action response = Popups.openErrorPopup("No funds for a trade", "You have to add some funds before you create a new offer.");
if (response == Dialog.Actions.OK)
{
MainController.INSTANCE().navigateToView(NavigationItem.FUNDS);
MainController.GET_INSTANCE().navigateToView(NavigationItem.FUNDS);
}
}
}
@ -329,7 +329,7 @@ public class OrderBookController implements Initializable, ChildController
{
if (isRegistered())
{
TakerTradeController takerTradeController = (TakerTradeController) navigationController.navigateToView(NavigationItem.TAKE_OFFER);
TakerOfferController takerOfferController = (TakerOfferController) navigationController.navigateToView(NavigationItem.TAKE_OFFER);
BigInteger requestedAmount;
if (!"".equals(amount.getText()))
@ -337,8 +337,8 @@ public class OrderBookController implements Initializable, ChildController
else
requestedAmount = offer.getAmount();
if (takerTradeController != null)
takerTradeController.initWithData(offer, requestedAmount);
if (takerOfferController != null)
takerOfferController.initWithData(offer, requestedAmount);
}
else
{
@ -541,7 +541,7 @@ public class OrderBookController implements Initializable, ChildController
} catch (ParseException e)
{
amount.setText(oldValue);
d = BitSquareConverter.stringToDouble2(oldValue);
d = BitSquareConverter.stringToDouble(oldValue);
}
}
return d;

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import io.bitsquare.gui.components.ValidatedTextField?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
@ -8,7 +9,7 @@
<Accordion fx:id="accordion" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<panes>
<TitledPane fx:id="profileTitledPane" text="Offer details">
<TitledPane fx:id="takeOfferTitledPane" text="Offer details">
<ScrollPane fitToWidth="true">
<GridPane hgap="5.0" vgap="5.0">
<padding>
@ -18,43 +19,41 @@
<Label text="Take offer:" id="headline-label"/>
<Label text="Amount (BTC):" GridPane.rowIndex="1"/>
<TextField fx:id="amountTextField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<ValidatedTextField fx:id="amountTextField" GridPane.rowIndex="1" GridPane.columnIndex="1"/>
<Label text="Price (EUR/BTC):" GridPane.rowIndex="2"/>
<TextField fx:id="priceTextField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<TextField fx:id="priceTextField" editable="false" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Label text="Volume (EUR):" GridPane.rowIndex="3"/>
<TextField fx:id="volumeTextField" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<TextField fx:id="volumeTextField" editable="false" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<Label text="Collateral (BTC):" GridPane.rowIndex="4"/>
<TextField fx:id="collateralTextField" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
<TextField fx:id="collateralTextField" editable="false" GridPane.rowIndex="4" GridPane.columnIndex="1"/>
<Label text="Fee (BTC):" GridPane.rowIndex="5"/>
<TextField fx:id="feeTextField" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
<TextField fx:id="feeTextField" editable="false" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
<Label text="Total (BTC):" GridPane.rowIndex="6"/>
<TextField fx:id="totalTextField" GridPane.rowIndex="6" GridPane.columnIndex="1"/>
<Button text="Take offer and pay" onAction="#onTakeOffer" defaultButton="true" GridPane.rowIndex="7" GridPane.columnIndex="1"/>
<TextField fx:id="totalTextField" editable="false" GridPane.rowIndex="6" GridPane.columnIndex="1"/>
<Button fx:id="takeOfferButton" text="Take offer and pay" onAction="#onTakeOffer" defaultButton="true" GridPane.rowIndex="7" GridPane.columnIndex="1"/>
<Label text="Offer details:" id="headline-label" GridPane.rowIndex="8"/>
<Label text="Bank account type:" GridPane.rowIndex="9"/>
<TextField fx:id="bankAccountTypeTextField" GridPane.rowIndex="9" GridPane.columnIndex="1"/>
<TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.rowIndex="9" GridPane.columnIndex="1"/>
<Label text="Country:" GridPane.rowIndex="10"/>
<TextField fx:id="countryTextField" GridPane.rowIndex="10" GridPane.columnIndex="1"/>
<TextField fx:id="countryTextField" editable="false" GridPane.rowIndex="10" GridPane.columnIndex="1"/>
<Label text="Arbitrators:" GridPane.rowIndex="11"/>
<TextField fx:id="arbitratorsTextField" GridPane.rowIndex="11" GridPane.columnIndex="1"/>
<TextField fx:id="arbitratorsTextField" editable="false" GridPane.rowIndex="11" GridPane.columnIndex="1"/>
<Label text="Supported languages:" GridPane.rowIndex="12"/>
<TextField fx:id="supportedLanguagesTextField" GridPane.rowIndex="12" GridPane.columnIndex="1"/>
<TextField fx:id="supportedLanguagesTextField" editable="false" GridPane.rowIndex="12" GridPane.columnIndex="1"/>
<Label text="Supported countries:" GridPane.rowIndex="13"/>
<TextField fx:id="supportedCountriesTextField" GridPane.rowIndex="13" GridPane.columnIndex="1"/>
<TextField fx:id="supportedCountriesTextField" editable="false" GridPane.rowIndex="13" GridPane.columnIndex="1"/>
<columnConstraints>
@ -63,30 +62,110 @@
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints>
</GridPane>
</ScrollPane>
</TitledPane>
<TitledPane fx:id="payCollateralTitledPane" text="Wait for bank transfer">
<TitledPane fx:id="waitBankTxTitledPane" text="Wait for bank transfer">
<GridPane hgap="5.0" vgap="5.0">
<padding>
<Insets left="10" right="10" top="10" bottom="10"/>
</padding>
<Label fx:id="headLineLabel" text="Deposit transaction published" id="headline-label"/>
<Label text="Status information:" GridPane.rowIndex="1" GridPane.valignment="TOP"/>
<Label fx:id="infoLabel" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.valignment="TOP"/>
<Label text="Deposit transaction ID:" GridPane.rowIndex="2"/>
<TextField fx:id="depositTxIdTextField" editable="false" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Button fx:id="receivedFiatButton" text="I have received the money at my bank account" onAction="#onReceivedFiat" defaultButton="true" disable="true" GridPane.rowIndex="3"
GridPane.columnIndex="1"/>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="NEVER" minWidth="10.0"/>
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints>
</GridPane>
</TitledPane>
<TitledPane fx:id="payCollateralTitledPane" text="Summary">
<TitledPane fx:id="summaryTitledPane" text="Summary">
<GridPane hgap="5.0" vgap="5.0">
<padding>
<Insets left="10" right="10" top="10" bottom="10"/>
</padding>
<Label text="Trade completed" id="headline-label"/>
<Label text="Summary:" GridPane.rowIndex="1"/>
<Label text="You have sold (BTC):" GridPane.rowIndex="2"/>
<ValidatedTextField fx:id="summaryPaidTextField" GridPane.rowIndex="2" GridPane.columnIndex="1"/>
<Label text="You have received (EUR):" GridPane.rowIndex="3"/>
<TextField fx:id="summaryReceivedTextField" editable="false" GridPane.rowIndex="3" GridPane.columnIndex="1"/>
<Label text="Details:" GridPane.rowIndex="4"/>
<Label text="Total fees (take offer fee + tx fee):" GridPane.rowIndex="5"/>
<TextField fx:id="summaryFeesTextField" editable="false" GridPane.rowIndex="5" GridPane.columnIndex="1"/>
<Label text="Refunded collateral (BTC):" GridPane.rowIndex="6"/>
<TextField fx:id="summaryCollateralTextField" editable="false" GridPane.rowIndex="6" GridPane.columnIndex="1"/>
<Label text="Deposit transaction ID:" GridPane.rowIndex="7"/>
<TextField fx:id="summaryDepositTxIdTextField" editable="false" GridPane.rowIndex="7" GridPane.columnIndex="1"/>
<Label text="Payout transaction ID:" GridPane.rowIndex="8"/>
<TextField fx:id="summaryPayoutTxIdTextField" editable="false" GridPane.rowIndex="8" GridPane.columnIndex="1"/>
<Button text="Close" onAction="#onClose" defaultButton="true" GridPane.rowIndex="9" GridPane.columnIndex="1"/>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0"/>
<ColumnConstraints hgrow="ALWAYS" minWidth="10.0"/>
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
<RowConstraints minHeight="30.0" prefHeight="30.0" vgrow="NEVER"/>
</rowConstraints>
</GridPane>
</TitledPane>
</panes>

View file

@ -1,32 +1,30 @@
package io.bitsquare.gui.market.trade;
import com.google.inject.Inject;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.btc.AddressEntry;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.currency.Bitcoin;
import io.bitsquare.currency.Fiat;
import io.bitsquare.gui.ChildController;
import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.NavigationItem;
import io.bitsquare.gui.components.ValidatedTextField;
import io.bitsquare.gui.popups.Popups;
import io.bitsquare.gui.util.BitSquareConverter;
import io.bitsquare.gui.util.BitSquareFormatter;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.LanguageUtil;
import io.bitsquare.gui.util.BitSquareValidator;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.Trading;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocolListener;
import java.math.BigInteger;
import java.net.URL;
import java.util.Currency;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Accordion;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -38,18 +36,29 @@ public class TakerOfferController implements Initializable, ChildController
private final Trading trading;
private final WalletFacade walletFacade;
private final MessageFacade messageFacade;
private NavigationController navigationController;
private Offer offer;
private Bitcoin requestedAmount;
private BigInteger requestedAmount;
private String tradeId;
private String depositTxId;
@FXML
private AnchorPane rootContainer;
@FXML
private Accordion accordion;
@FXML
private TitledPane profileTitledPane;
private TitledPane takeOfferTitledPane, waitBankTxTitledPane, summaryTitledPane;
@FXML
private TextField amountTextField, priceTextField, volumeTextField, collateralTextField, feeTextField, totalTextField, bankAccountTypeTextField, countryTextField,
arbitratorsTextField, supportedLanguagesTextField, supportedCountriesTextField;
private ValidatedTextField amountTextField;
@FXML
private TextField priceTextField, volumeTextField, collateralTextField, feeTextField, totalTextField, bankAccountTypeTextField, countryTextField,
arbitratorsTextField, supportedLanguagesTextField, supportedCountriesTextField, depositTxIdTextField, summaryPaidTextField, summaryReceivedTextField, summaryFeesTextField,
summaryCollateralTextField, summaryDepositTxIdTextField, summaryPayoutTxIdTextField;
@FXML
private Label infoLabel, headLineLabel;
@FXML
private Button takeOfferButton, receivedFiatButton;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -61,23 +70,6 @@ public class TakerOfferController implements Initializable, ChildController
this.trading = trading;
this.walletFacade = walletFacade;
this.messageFacade = messageFacade;
Offer offer = new Offer("m",
Direction.BUY,
111,
new BigInteger("100000000"),
new BigInteger("10000000"),
BankAccountType.OK_PAY,
Currency.getInstance("EUR"),
CountryUtil.getDefaultCountry(),
"baid",
new Arbitrator(),
10,
CountryUtil.getAllCountriesFor(CountryUtil.getAllRegions().get(0)),
LanguageUtil.getAllLanguageLocales());
initWithData(offer, new Bitcoin("50000000"));
}
@ -85,10 +77,13 @@ public class TakerOfferController implements Initializable, ChildController
// Public methods
///////////////////////////////////////////////////////////////////////////////////////////
public void initWithData(Offer offer, Bitcoin requestedAmount)
public void initWithData(Offer offer, BigInteger requestedAmount)
{
this.offer = offer;
this.requestedAmount = requestedAmount.isZero() ? new Bitcoin(offer.getAmount()) : requestedAmount;
this.requestedAmount = requestedAmount.compareTo(BigInteger.ZERO) == 0 ? offer.getAmount() : requestedAmount;
if (amountTextField != null)
applyData();
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -98,17 +93,18 @@ public class TakerOfferController implements Initializable, ChildController
@Override
public void initialize(URL url, ResourceBundle rb)
{
accordion.setExpandedPane(profileTitledPane);
accordion.setExpandedPane(takeOfferTitledPane);
}
if (offer != null && requestedAmount != null)
public void applyData()
{
amountTextField.setText(requestedAmount.getFormattedValue());
amountTextField.setPromptText(new Bitcoin(offer.getMinAmount()).getFormattedValue() + " - " + new Bitcoin(offer.getAmount()).getFormattedValue());
priceTextField.setText(new Fiat(offer.getPrice()).getFormattedValue());
amountTextField.setText(BtcFormatter.formatSatoshis(requestedAmount));
amountTextField.setPromptText(BtcFormatter.formatSatoshis(offer.getMinAmount()) + " - " + BtcFormatter.formatSatoshis(offer.getAmount()));
priceTextField.setText(BitSquareFormatter.formatPrice(offer.getPrice()));
applyVolume();
applyCollateral();
applyTotal();
feeTextField.setText(getFee().getFormattedValue());
feeTextField.setText(BtcFormatter.formatSatoshis(getFee()));
totalTextField.setText(getFormattedTotal());
bankAccountTypeTextField.setText(offer.getBankAccountType().toString());
@ -127,7 +123,6 @@ public class TakerOfferController implements Initializable, ChildController
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -145,19 +140,103 @@ public class TakerOfferController implements Initializable, ChildController
{
}
///////////////////////////////////////////////////////////////////////////////////////////
// GUI handlers
///////////////////////////////////////////////////////////////////////////////////////////
@FXML
public void onTakeOffer()
{
AddressEntry addressEntry = walletFacade.getAddressInfoByTradeID(offer.getId());
BigInteger amount = BtcFormatter.stringValueToSatoshis(amountTextField.getText());
// TODO more validation (fee payment, blacklist,...)
if (amountTextField.isInvalid())
{
Popups.openErrorPopup("Invalid input", "The requested amount you entered is not a valid amount.");
}
else if (BitSquareValidator.tradeAmountOutOfRange(amount, offer))
{
Popups.openErrorPopup("Invalid input", "The requested amount you entered is outside of the range of the offered amount.");
}
else if (addressEntry == null || getTotal().compareTo(walletFacade.getBalanceForAddress(addressEntry.getAddress())) > 0)
{
Popups.openErrorPopup("Insufficient money", "You don't have enough funds for that trade.");
}
else if (trading.isOfferAlreadyInTrades(offer))
{
Popups.openErrorPopup("Offer previously accepted", "You have that offer already taken. Open the offer section to find that trade.");
}
else
{
takeOfferButton.setDisable(true);
amountTextField.setEditable(false);
trading.takeOffer(amount, offer, new TakerAsSellerProtocolListener()
{
@Override
public void onDepositTxPublished(String depositTxId)
{
setDepositTxId(depositTxId);
accordion.setExpandedPane(waitBankTxTitledPane);
infoLabel.setText("Deposit transaction published by offerer.\n" +
"As soon as the offerer starts the \n" +
"Bank transfer, you will get informed.");
depositTxIdTextField.setText(depositTxId);
}
@Override
public void onBankTransferInited(String tradeId)
{
setTradeId(tradeId);
headLineLabel.setText("Bank transfer inited");
infoLabel.setText("Check your bank account and continue \n" +
"when you have received the money.");
receivedFiatButton.setDisable(false);
}
@Override
public void onTradeCompleted(Trade trade, String payoutTxId)
{
accordion.setExpandedPane(summaryTitledPane);
summaryPaidTextField.setText(BtcFormatter.formatSatoshis(trade.getTradeAmount()));
summaryReceivedTextField.setText(BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount())));
summaryFeesTextField.setText(BtcFormatter.formatSatoshis(FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE)));
summaryCollateralTextField.setText(BtcFormatter.formatSatoshis(trade.getCollateralAmount()));
summaryDepositTxIdTextField.setText(depositTxId);
summaryPayoutTxIdTextField.setText(payoutTxId);
}
}, (task) -> {
//log.trace(task.toString());
}, throwable -> {
log.error(throwable.toString());
}
);
}
}
@FXML
public void onReceivedFiat()
{
trading.releaseBTC(tradeId);
}
@FXML
public void onClose()
{
TabPane tabPane = ((TabPane) (rootContainer.getParent().getParent()));
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
navigationController.navigateToView(NavigationItem.ORDER_BOOK);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private void applyCollateral()
{
collateralTextField.setText(getFormattedCollateral());
@ -181,18 +260,23 @@ public class TakerOfferController implements Initializable, ChildController
private String getFormattedTotal()
{
return BitSquareFormatter.formatVolume(getTotal());
return BitSquareFormatter.formatVolume(getTotal().doubleValue());
}
private String getFormattedCollateral()
{
return BtcFormatter.satoshiToString(getCollateralInSatoshis());
return BtcFormatter.formatSatoshis(getCollateralInSatoshis());
}
// values
private double getAmountAsDouble()
{
return BitSquareConverter.stringToDouble2(amountTextField.getText());
return BitSquareConverter.stringToDouble(amountTextField.getText());
}
private BigInteger getAmountInSatoshis()
{
return BtcFormatter.stringValueToSatoshis(amountTextField.getText());
}
private double getVolume()
@ -200,22 +284,32 @@ public class TakerOfferController implements Initializable, ChildController
return offer.getPrice() * getAmountAsDouble();
}
private Bitcoin getFee()
private BigInteger getFee()
{
return FeePolicy.TAKE_OFFER_FEE.addBitcoin(FeePolicy.TX_FEE);
return FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE);
}
private double getTotal()
private BigInteger getTotal()
{
return getFee().doubleValue() + getVolume();
return getFee().add(getAmountInSatoshis()).add(getCollateralInSatoshis());
}
private BigInteger getCollateralInSatoshis()
{
double amount = BitSquareConverter.stringToDouble2(amountTextField.getText());
double amount = BitSquareConverter.stringToDouble(amountTextField.getText());
double resultDouble = amount * (double) offer.getCollateral() / 100.0;
return BtcFormatter.doubleValueToSatoshis(resultDouble);
}
public void setTradeId(String tradeId)
{
this.tradeId = tradeId;
}
public void setDepositTxId(String depositTxId)
{
this.depositTxId = depositTxId;
}
}

View file

@ -17,13 +17,10 @@ import io.bitsquare.gui.util.BitSquareConverter;
import io.bitsquare.gui.util.BitSquareFormatter;
import io.bitsquare.gui.util.FormBuilder;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.Trading;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocol;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocolListener;
import io.bitsquare.util.Utilities;
import java.math.BigInteger;
import java.net.URL;
@ -149,21 +146,21 @@ public class TakerTradeController implements Initializable, ChildController
row = -1;
FormBuilder.addHeaderLabel(gridPane, "Take offer:", ++row);
amountTextField = FormBuilder.addTextField(gridPane, "Amount (BTC):", BtcFormatter.satoshiToString(requestedAmount), ++row, true, true);
amountTextField = FormBuilder.addTextField(gridPane, "Amount (BTC):", BtcFormatter.formatSatoshis(requestedAmount), ++row, true, true);
amountTextField.textProperty().addListener(e -> {
applyVolume();
applyCollateral();
totalToPayLabel.setText(getTotalToPayAsString());
});
Label amountRangeLabel = new Label("(" + BtcFormatter.satoshiToString(offer.getMinAmount()) + " - " + BtcFormatter.satoshiToString(offer.getAmount()) + ")");
Label amountRangeLabel = new Label("(" + BtcFormatter.formatSatoshis(offer.getMinAmount()) + " - " + BtcFormatter.formatSatoshis(offer.getAmount()) + ")");
gridPane.add(amountRangeLabel, 2, row);
FormBuilder.addTextField(gridPane, "Price (" + offer.getCurrency() + "/BTC):", BitSquareFormatter.formatPrice(offer.getPrice()), ++row);
totalLabel = FormBuilder.addTextField(gridPane, "Total (" + offer.getCurrency() + "):", BitSquareFormatter.formatVolume(getVolume()), ++row);
collateralTextField = FormBuilder.addTextField(gridPane, "Collateral (BTC):", "", ++row);
applyCollateral();
FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.satoshiToString(FeePolicy.TAKE_OFFER_FEE_depr.add(FeePolicy.TX_FEE_depr)), ++row);
FormBuilder.addTextField(gridPane, "Offer fee (BTC):", BtcFormatter.formatSatoshis(FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE)), ++row);
totalToPayLabel = FormBuilder.addTextField(gridPane, "Total to pay (BTC):", getTotalToPayAsString(), ++row);
isOnlineTextField = FormBuilder.addTextField(gridPane, "Online status:", "Checking offerers online status...", ++row);
@ -228,18 +225,18 @@ public class TakerTradeController implements Initializable, ChildController
return;
}
if (trading.isOfferTradable(offer))
{
//if (trading.isOfferAlreadyInTrades(offer))
// {
trade = trading.createTrade(offer);
trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText()));
/* if (!blockChainFacade.verifyAccountRegistration(offer.getAccountID()))
/* if (!blockChainFacade.verifyAccountRegistration(offer.getAccountId()))
{
Popups.openErrorPopup("Offerers account ID not valid", "Offerers registration tx is not found in blockchain or does not match the requirements.");
return;
}
if (blockChainFacade.isAccountIDBlacklisted(offer.getAccountID()))
if (blockChainFacade.isAccountIDBlacklisted(offer.getAccountId()))
{
Popups.openErrorPopup("Offerers account ID is blacklisted", "Offerers account ID is blacklisted.");
return;
@ -269,7 +266,7 @@ public class TakerTradeController implements Initializable, ChildController
progressIndicatorHolder.getChildren().addAll(progressIndicator);
gridPane.add(progressIndicatorHolder, 1, row);
TakerPaymentProtocol takerPaymentProtocol = trading.addTakerPaymentProtocol(trade, new TakerPaymentProtocolListener()
/*TakerPaymentProtocol takerPaymentProtocol = trading.addTakerPaymentProtocol(trade, new TakerPaymentProtocolListener()
{
@Override
public void onProgress(double progress)
@ -297,6 +294,7 @@ public class TakerTradeController implements Initializable, ChildController
statusTextField.setText("Offer fee payed. Send offerer payment transaction ID for confirmation.");
break;
} */
/*
}
@Override
@ -306,13 +304,13 @@ public class TakerTradeController implements Initializable, ChildController
}
@Override
public void onDepositTxPublished(String depositTxID)
public void onDepositTxPublishedMessage(String depositTxID)
{
buildDepositPublishedScreen(depositTxID);
}
@Override
public void onBankTransferInited(TradeMessage tradeMessage)
public void onBankTransferInitedMessage(TradeMessage tradeMessage)
{
buildBankTransferInitedScreen(tradeMessage);
}
@ -324,9 +322,9 @@ public class TakerTradeController implements Initializable, ChildController
}
});
takerPaymentProtocol.takeOffer();
}
takerPaymentProtocol.takeOffer(); */
}
//}
@SuppressWarnings("EmptyMethod")
private void updateTx(Trade trade)
@ -347,7 +345,7 @@ public class TakerTradeController implements Initializable, ChildController
// confidenceDisplay = new ConfidenceDisplay(walletFacade.getWallet(), confirmationLabel, transaction, progressIndicator);
}
private void buildBankTransferInitedScreen(TradeMessage tradeMessage)
private void buildBankTransferInitedScreen()
{
processStepBar.next();
@ -355,13 +353,13 @@ public class TakerTradeController implements Initializable, ChildController
infoLabel.setText("Check your bank account and continue \nwhen you have received the money.");
gridPane.add(nextButton, 1, ++row);
nextButton.setText("I have received the money at my bank");
nextButton.setOnAction(e -> releaseBTC(tradeMessage));
nextButton.setOnAction(e -> releaseBTC());
}
private void releaseBTC(TradeMessage tradeMessage)
private void releaseBTC()
{
processStepBar.next();
trading.releaseBTC(trade.getId(), tradeMessage);
trading.releaseBTC(trade.getId());
nextButton.setText("Close");
nextButton.setOnAction(e -> close());
@ -372,19 +370,19 @@ public class TakerTradeController implements Initializable, ChildController
String fiatReceived = BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount()));
FormBuilder.addTextField(gridPane, "You have sold (BTC):", BtcFormatter.satoshiToString(trade.getTradeAmount()), ++row);
FormBuilder.addTextField(gridPane, "You have sold (BTC):", BtcFormatter.formatSatoshis(trade.getTradeAmount()), ++row);
if (takerIsSelling())
{
FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):\"", fiatReceived, ++row);
FormBuilder.addTextField(gridPane, "Total fees (take offer fee + tx fee):", BtcFormatter.satoshiToString(FeePolicy.TAKE_OFFER_FEE_depr.add(FeePolicy.TX_FEE_depr)), ++row);
FormBuilder.addTextField(gridPane, "Refunded collateral:", BtcFormatter.satoshiToString(trade.getCollateralAmount()), ++row);
FormBuilder.addTextField(gridPane, "Total fees (take offer fee + tx fee):", BtcFormatter.formatSatoshis(FeePolicy.TAKE_OFFER_FEE.add(FeePolicy.TX_FEE)), ++row);
FormBuilder.addTextField(gridPane, "Refunded collateral:", BtcFormatter.formatSatoshis(trade.getCollateralAmount()), ++row);
}
else
{
//TODO
FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.satoshiToString(getCollateralInSatoshis()), ++row);
FormBuilder.addTextField(gridPane, "You got returned collateral (BTC):", BtcFormatter.formatSatoshis(getCollateralInSatoshis()), ++row);
FormBuilder.addTextField(gridPane, "You have received (" + offer.getCurrency() + "):", BitSquareFormatter.formatVolume(getVolume()), ++row);
FormBuilder.addTextField(gridPane, "You have received (BTC):", BtcFormatter.satoshiToString(offer.getAmount()), ++row);
FormBuilder.addTextField(gridPane, "You have received (BTC):", BtcFormatter.formatSatoshis(offer.getAmount()), ++row);
}
gridPane.add(nextButton, 1, ++row);
@ -440,18 +438,18 @@ public class TakerTradeController implements Initializable, ChildController
private double getVolume()
{
return offer.getPrice() * BitSquareConverter.stringToDouble2(amountTextField.getText());
return offer.getPrice() * BitSquareConverter.stringToDouble(amountTextField.getText());
}
private String getTotalToPayAsString()
{
if (takerIsSelling())
{
return BtcFormatter.satoshiToString(getTotalToPay());
return BtcFormatter.formatSatoshis(getTotalToPay());
}
else
{
return BtcFormatter.satoshiToString(getTotalToPay()) + "\n" +
return BtcFormatter.formatSatoshis(getTotalToPay()) + "\n" +
BitSquareFormatter.formatVolume(getVolume(), offer.getCurrency());
}
}
@ -460,22 +458,22 @@ public class TakerTradeController implements Initializable, ChildController
{
if (takerIsSelling())
{
return getAmountInSatoshis().add(FeePolicy.TAKE_OFFER_FEE_depr).add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE_depr).add(getCollateralInSatoshis());
return getAmountInSatoshis().add(FeePolicy.TAKE_OFFER_FEE).add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE).add(getCollateralInSatoshis());
}
else
{
return FeePolicy.TAKE_OFFER_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE_depr).add(getCollateralInSatoshis());
return FeePolicy.TAKE_OFFER_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(FeePolicy.TX_FEE).add(getCollateralInSatoshis());
}
}
private void applyCollateral()
{
collateralTextField.setText(BtcFormatter.satoshiToString(getCollateralInSatoshis()));
collateralTextField.setText(BtcFormatter.formatSatoshis(getCollateralInSatoshis()));
}
private BigInteger getCollateralInSatoshis()
{
double amount = BitSquareConverter.stringToDouble2(amountTextField.getText());
double amount = BitSquareConverter.stringToDouble(amountTextField.getText());
double resultDouble = amount * (double) offer.getCollateral() / 100.0;
return BtcFormatter.doubleValueToSatoshis(resultDouble);
}

View file

@ -16,20 +16,38 @@ import org.slf4j.LoggerFactory;
public class OrdersController implements Initializable, ChildController, NavigationController
{
private static final Logger log = LoggerFactory.getLogger(OrdersController.class);
private static int SELECTED_TAB_INDEX = -1;
private static OrdersController INSTANCE;
private final Storage storage;
@FXML
private LazyLoadingTabPane tabPane;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private OrdersController(Storage storage)
{
this.storage = storage;
INSTANCE = this;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public static OrdersController GET_INSTANCE()
{
return INSTANCE;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void setSelectedTabIndex(int index)
{
log.trace("setSelectedTabIndex " + index);
tabPane.setSelectedTabIndex(index);
storage.write(this.getClass().getName() + ".selectedTabIndex", index);
}
@ -40,6 +58,7 @@ public class OrdersController implements Initializable, ChildController, Navigat
@Override
public void initialize(URL url, ResourceBundle rb)
{
log.trace("initialize ");
tabPane.initialize(this, storage, NavigationItem.OFFER.getFxmlUrl(), NavigationItem.PENDING_TRADE.getFxmlUrl(), NavigationItem.CLOSED_TRADE.getFxmlUrl());
}
@ -59,6 +78,7 @@ public class OrdersController implements Initializable, ChildController, Navigat
tabPane.cleanup();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: NavigationController
///////////////////////////////////////////////////////////////////////////////////////////

View file

@ -68,7 +68,6 @@ public class OfferController implements Initializable, ChildController, Hibernat
@Override
public void setNavigationController(NavigationController navigationController)
{
log.debug("setNavigationController" + this);
}
@Override

View file

@ -35,7 +35,6 @@ import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Pos;
@ -165,21 +164,16 @@ public class PendingTradeController implements Initializable, ChildController, H
initCopyIcons();
if (tradeItems.size() > 0)
{
openTradesTable.getSelectionModel().select(0);
}
// select
Optional<PendingTradesListItem> currentTradeItemOptional = tradeItems.stream().filter((e) -> e.getTrade().getId().equals(trading.getCurrentPendingTrade().getId())).findFirst();
if (currentTradeItemOptional.isPresent())
openTradesTable.getSelectionModel().select(currentTradeItemOptional.get());
tradeItems.addListener(new ListChangeListener<PendingTradesListItem>()
{
@Override
public void onChanged(Change<? extends PendingTradesListItem> change)
{
tradeItems.addListener((ListChangeListener<PendingTradesListItem>) change -> {
if (openTradesTable.getSelectionModel().getSelectedItem() == null && tradeItems.size() > 0)
{
openTradesTable.getSelectionModel().select(0);
}
}
});
}
@ -187,13 +181,13 @@ public class PendingTradeController implements Initializable, ChildController, H
// GUI handlers
///////////////////////////////////////////////////////////////////////////////////////////
public void bankTransferInited(ActionEvent actionEvent)
public void bankTransferInited()
{
trading.onBankTransferInited(currentTrade.getId());
trading.onUIEventBankTransferInited(currentTrade.getId());
bankTransferInitedButton.setDisable(true);
}
public void close(ActionEvent actionEvent)
public void close()
{
}
@ -328,10 +322,10 @@ public class PendingTradeController implements Initializable, ChildController, H
String fiatPayed = BitSquareFormatter.formatVolume(trade.getOffer().getPrice() * BtcFormatter.satoshiToBTC(trade.getTradeAmount()));
bankAccountTypeTextField.setText(BtcFormatter.satoshiToString(trade.getTradeAmount()));
bankAccountTypeTextField.setText(BtcFormatter.formatSatoshis(trade.getTradeAmount()));
holderNameTextField.setText(fiatPayed);
primaryBankAccountIDTextField.setText(BtcFormatter.satoshiToString(FeePolicy.CREATE_OFFER_FEE_depr.add(FeePolicy.TX_FEE_depr)));
secondaryBankAccountIDTextField.setText(BtcFormatter.satoshiToString(trade.getCollateralAmount()));
primaryBankAccountIDTextField.setText(BtcFormatter.formatSatoshis(FeePolicy.CREATE_OFFER_FEE.add(FeePolicy.TX_FEE)));
secondaryBankAccountIDTextField.setText(BtcFormatter.formatSatoshis(trade.getCollateralAmount()));
holderNameCopyIcon.setVisible(false);
primaryBankAccountIDCopyIcon.setVisible(false);

View file

@ -14,22 +14,12 @@ public class BitSquareConverter
public static double stringToDouble(String input)
{
try
{
return stringToDouble2(input);
} catch (NumberFormatException | NullPointerException e)
{
return Double.NEGATIVE_INFINITY;
}
}
/**
* @param input String to be converted to a double. Both decimal points "." and "," are supported. Thousands separator is not supported.
* @return Returns a double value. Any invalid value throws an exception.
*/
public static double stringToDouble2(String input)
{
input = input.replace(",", ".");
return Double.parseDouble(input);
} catch (NumberFormatException | NullPointerException e)
{
return 0;
}
}
}

View file

@ -1,6 +1,8 @@
package io.bitsquare.gui.util;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.trade.Offer;
import java.math.BigInteger;
import javafx.scene.control.TextField;
import javafx.scene.effect.BlurType;
import javafx.scene.effect.DropShadow;
@ -13,6 +15,16 @@ public class BitSquareValidator
private static final Effect invalidEffect = new DropShadow(BlurType.GAUSSIAN, Color.RED, 4, 0.0, 0, 0);
private static final String invalidStyle = "-fx-border-color: red";
public static boolean tradeAmountOutOfRange(BigInteger tradeAmount, Offer offer)
{
return tradeAmount.compareTo(offer.getAmount()) > 0 || tradeAmount.compareTo(offer.getMinAmount()) < 0;
}
public static boolean greaterThanZero(BigInteger value)
{
return value.compareTo(BigInteger.ZERO) > 0;
}
public static void textFieldsNotEmptyWithReset(TextField... textFields) throws ValidationException
{
resetTextFields(textFields);

View file

@ -187,7 +187,7 @@ public class ConfidenceDisplay
}
if (balanceTextField != null)
balanceTextField.setText(BtcFormatter.satoshiToString(balance));
balanceTextField.setText(BtcFormatter.formatSatoshis(balance));
}
private void updateConfidence(Transaction tx)

View file

@ -4,8 +4,14 @@ import com.google.inject.Inject;
import io.bitsquare.BitSquare;
import io.bitsquare.msg.listeners.*;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.payment.offerer.OffererPaymentProtocol;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocol;
import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocol;
import io.bitsquare.trade.payment.offerer.messages.*;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocol;
import io.bitsquare.trade.payment.taker.listeners.GetPeerAddressListener;
import io.bitsquare.trade.payment.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.payment.taker.messages.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.payment.taker.messages.RequestTakeOfferMessage;
import io.bitsquare.trade.payment.taker.messages.TakeOfferFeePayedMessage;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.util.DSAKeyUtil;
import io.bitsquare.util.FileUtil;
@ -46,9 +52,10 @@ public class MessageFacade
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
private final List<TakeOfferRequestListener> takeOfferRequestListeners = new ArrayList<>();
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
// //TODO change to map (key: offerID) instead of list (offererPaymentProtocols, takerPaymentProtocols)
private final List<TakerPaymentProtocol> takerPaymentProtocols = new ArrayList<>();
private final List<OffererPaymentProtocol> offererPaymentProtocols = new ArrayList<>();
private final Map<String, TakerAsSellerProtocol> takerPaymentProtocols = new HashMap<>();
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocols = new HashMap<>();
private final List<PingPeerListener> pingPeerListeners = new ArrayList<>();
private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
private Peer myPeer;
@ -66,7 +73,7 @@ public class MessageFacade
{
/* try
{
masterPeer = BootstrapMasterPeer.INSTANCE(MASTER_PEER_PORT);
masterPeer = BootstrapMasterPeer.GET_INSTANCE(MASTER_PEER_PORT);
} catch (Exception e)
{
if (masterPeer != null)
@ -97,7 +104,7 @@ public class MessageFacade
} catch (IOException e)
{
shutDown();
log.error("Error at setup myPeerInstance" + e.getMessage());
log.error("Error at init myPeerInstance" + e.getMessage());
}
}
@ -323,7 +330,7 @@ public class MessageFacade
private void onGetDirtyFlag(long timeStamp)
{
// TODO don't get updates at first run....
// TODO don't get updates at first execute....
if (lastTimeStamp != timeStamp)
{
isDirty.setValue(!isDirty.get());
@ -423,7 +430,8 @@ public class MessageFacade
// Find peer address
///////////////////////////////////////////////////////////////////////////////////////////
public void getPeerAddress(final String pubKeyAsHex, AddressLookupListener listener)
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
{
final Number160 location = Number160.createHash(pubKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
@ -435,32 +443,44 @@ public class MessageFacade
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
{
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
Platform.runLater(() -> onAddressFound(peerAddress, listener));
Platform.runLater(() -> listener.onResult(peerAddress));
}
else
{
Platform.runLater(() -> onGetPeerAddressFailed(listener));
Platform.runLater(() -> listener.onFailed());
}
}
});
}
private void onAddressFound(final PeerAddress peerAddress, AddressLookupListener listener)
{
listener.onResult(peerAddress);
}
private void onGetPeerAddressFailed(AddressLookupListener listener)
{
listener.onFailed();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trade process
///////////////////////////////////////////////////////////////////////////////////////////
public void sendTradeMessage(final PeerAddress peerAddress, final TradeMessage tradeMessage, TradeMessageListener listener)
public void sendTradingMessage(final PeerAddress peerAddress, TradeMessage tradeMessage, TradeMessageListener listener)
{
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (sendFuture.isSuccess())
{
Platform.runLater(() -> listener.onResult());
}
else
{
Platform.runLater(() -> listener.onFailed());
}
}
}
);
}
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, TradeMessageListener listener)
{
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
@ -499,51 +519,47 @@ public class MessageFacade
private void processTradingMessage(TradeMessage tradeMessage, PeerAddress sender)
{
//TODO change to map (key: offerID) instead of list (offererPaymentProtocols, takerPaymentProtocols)
log.info("processTradingMessage " + tradeMessage.getType());
switch (tradeMessage.getType())
{
case REQUEST_TAKE_OFFER:
// That is used to initiate the OffererPaymentProtocol and to show incoming requests in the view
for (TakeOfferRequestListener takeOfferRequestListener : takeOfferRequestListeners)
takeOfferRequestListener.onTakeOfferRequested(tradeMessage, sender);
break;
case ACCEPT_TAKE_OFFER_REQUEST:
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
takeOfferTradeListener.onTakeOfferRequestAccepted();
break;
case REJECT_TAKE_OFFER_REQUEST:
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
takeOfferTradeListener.onTakeOfferRequestRejected();
break;
case TAKE_OFFER_FEE_PAYED:
for (OffererPaymentProtocol offererPaymentProtocol : offererPaymentProtocols)
offererPaymentProtocol.onTakeOfferFeePayed(tradeMessage);
break;
case REQUEST_TAKER_DEPOSIT_PAYMENT:
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
takeOfferTradeListener.onTakerDepositPaymentRequested(tradeMessage);
break;
case REQUEST_OFFERER_DEPOSIT_PUBLICATION:
for (OffererPaymentProtocol offererPaymentProtocol : offererPaymentProtocols)
offererPaymentProtocol.onDepositTxReadyForPublication(tradeMessage);
break;
case DEPOSIT_TX_PUBLISHED:
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
takeOfferTradeListener.onDepositTxPublished(tradeMessage);
break;
case BANK_TX_INITED:
for (TakerPaymentProtocol takeOfferTradeListener : takerPaymentProtocols)
takeOfferTradeListener.onBankTransferInited(tradeMessage);
break;
case PAYOUT_TX_PUBLISHED:
for (OffererPaymentProtocol offererPaymentProtocol : offererPaymentProtocols)
offererPaymentProtocol.onPayoutTxPublished(tradeMessage);
break;
// log.trace("processTradingMessage TradeId " + tradeMessage.getTradeId());
log.trace("processTradingMessage instance " + tradeMessage.getClass().getSimpleName());
log.trace("processTradingMessage instance " + tradeMessage.getClass().getName());
log.trace("processTradingMessage instance " + tradeMessage.getClass().getCanonicalName());
log.trace("processTradingMessage instance " + tradeMessage.getClass().getTypeName());
default:
log.info("default");
break;
if (tradeMessage instanceof RequestTakeOfferMessage)
{
takeOfferRequestListeners.stream().forEach(e -> e.onTakeOfferRequested(tradeMessage.getTradeId(), sender));
}
else if (tradeMessage instanceof AcceptTakeOfferRequestMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onAcceptTakeOfferRequestMessage();
}
else if (tradeMessage instanceof RejectTakeOfferRequestMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onRejectTakeOfferRequestMessage();
}
else if (tradeMessage instanceof TakeOfferFeePayedMessage)
{
offererAsBuyerProtocols.get(tradeMessage.getTradeId()).onTakeOfferFeePayedMessage((TakeOfferFeePayedMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestTakerDepositPaymentMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onRequestTakerDepositPaymentMessage((RequestTakerDepositPaymentMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestOffererPublishDepositTxMessage)
{
offererAsBuyerProtocols.get(tradeMessage.getTradeId()).onRequestOffererPublishDepositTxMessage((RequestOffererPublishDepositTxMessage) tradeMessage);
}
else if (tradeMessage instanceof DepositTxPublishedMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onDepositTxPublishedMessage((DepositTxPublishedMessage) tradeMessage);
}
else if (tradeMessage instanceof BankTransferInitedMessage)
{
takerPaymentProtocols.get(tradeMessage.getTradeId()).onBankTransferInitedMessage((BankTransferInitedMessage) tradeMessage);
}
else if (tradeMessage instanceof PayoutTxPublishedMessage)
{
offererAsBuyerProtocols.get(tradeMessage.getTradeId()).onPayoutTxPublishedMessage((PayoutTxPublishedMessage) tradeMessage);
}
}
@ -616,12 +632,12 @@ public class MessageFacade
// Misc
///////////////////////////////////////////////////////////////////////////////////////////
public PublicKey getPubKey()
{
return keyPair.getPublic();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Event Listeners
///////////////////////////////////////////////////////////////////////////////////////////
@ -646,24 +662,24 @@ public class MessageFacade
takeOfferRequestListeners.remove(listener);
}
public void addTakerPaymentProtocol(TakerPaymentProtocol listener)
public void addTakerPaymentProtocol(TakerAsSellerProtocol protocol)
{
takerPaymentProtocols.add(listener);
takerPaymentProtocols.put(protocol.getId(), protocol);
}
public void removeTakerPaymentProtocol(TakerPaymentProtocol listener)
public void removeTakerPaymentProtocol(TakerAsSellerProtocol protocol)
{
takerPaymentProtocols.remove(listener);
takerPaymentProtocols.remove(protocol);
}
public void addOffererPaymentProtocol(OffererPaymentProtocol listener)
public void addOffererPaymentProtocol(OffererAsBuyerProtocol protocol)
{
offererPaymentProtocols.add(listener);
offererAsBuyerProtocols.put(protocol.getId(), protocol);
}
public void removeOffererPaymentProtocol(OffererPaymentProtocol listener)
public void removeOffererPaymentProtocol(OffererAsBuyerProtocol protocol)
{
offererPaymentProtocols.remove(listener);
offererAsBuyerProtocols.remove(protocol);
}
public void addPingPeerListener(PingPeerListener listener)
@ -781,5 +797,4 @@ public class MessageFacade
} */
}
}

View file

@ -1,277 +1,6 @@
package io.bitsquare.msg;
import io.bitsquare.bank.BankAccount;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.UUID;
//TODO refactor
@SuppressWarnings("SameParameterValue")
public class TradeMessage implements Serializable
public interface TradeMessage
{
private static final long serialVersionUID = 7916445031849763995L;
private final String uid;
private final TradeMessageType type;
private final String offerUID;
private String takerMessagePubKey;
private String signedTakerDepositTxAsHex;
private String txScriptSigAsHex;
private String txConnOutAsHex;
private String contractAsJson;
private String takerContractSignature;
private String takerPayoutAddress;
private String depositTxID;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
private BigInteger takerPaybackAmount;
private String offererPayoutAddress;
private BigInteger tradeAmount;
private String takeOfferFeeTxID;
private String takerMultiSigPubKey;
private BankAccount bankAccount;
private String accountID;
private String offererPubKey;
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
private long takerTxOutIndex;
private String payoutTxAsHex;
public TradeMessage(TradeMessageType type, String offerUID)
{
this.offerUID = offerUID;
this.type = type;
uid = UUID.randomUUID().toString();
}
public TradeMessage(TradeMessageType type, String offerUID, BigInteger tradeAmount, String takeOfferFeeTxID, String takerMultiSigPubKey)
{
this.offerUID = offerUID;
this.type = type;
this.tradeAmount = tradeAmount;
this.takeOfferFeeTxID = takeOfferFeeTxID;
this.takerMultiSigPubKey = takerMultiSigPubKey;
uid = UUID.randomUUID().toString();
}
public TradeMessage(TradeMessageType type, String offerUID, BankAccount bankAccount, String accountID, String offererPubKey, String preparedOffererDepositTxAsHex, long offererTxOutIndex)
{
this.offerUID = offerUID;
this.type = type;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.offererPubKey = offererPubKey;
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
this.offererTxOutIndex = offererTxOutIndex;
uid = UUID.randomUUID().toString();
}
public TradeMessage(TradeMessageType type, String offerUID,
BankAccount bankAccount,
String accountID,
String takerMessagePubKey,
String signedTakerDepositTxAsHex,
String txScriptSigAsHex,
String txConnOutAsHex,
String contractAsJson,
String takerContractSignature,
String takerPayoutAddress,
long takerTxOutIndex,
long offererTxOutIndex)
{
this.offerUID = offerUID;
this.type = type;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.takerMessagePubKey = takerMessagePubKey;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txScriptSigAsHex = txScriptSigAsHex;
this.txConnOutAsHex = txConnOutAsHex;
this.contractAsJson = contractAsJson;
this.takerContractSignature = takerContractSignature;
this.takerPayoutAddress = takerPayoutAddress;
this.takerTxOutIndex = takerTxOutIndex;
this.offererTxOutIndex = offererTxOutIndex;
uid = UUID.randomUUID().toString();
}
public TradeMessage(TradeMessageType type, String offerUID, String depositTxAsHex)
{
this.offerUID = offerUID;
this.type = type;
this.depositTxAsHex = depositTxAsHex;
uid = UUID.randomUUID().toString();
}
// 3.10
public TradeMessage(TradeMessageType type, String offerUID,
String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererPayoutAddress)
{
this.offerUID = offerUID;
this.type = type;
this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount;
this.takerPaybackAmount = takerPaybackAmount;
this.offererPayoutAddress = offererPayoutAddress;
uid = UUID.randomUUID().toString();
}
public String getUid()
{
return uid;
}
public TradeMessageType getType()
{
return type;
}
public String getTakeOfferFeeTxID()
{
return takeOfferFeeTxID;
}
public String getOfferUID()
{
return offerUID;
}
public BankAccount getBankAccount()
{
return bankAccount;
}
public String getAccountID()
{
return accountID;
}
public String getTakerMultiSigPubKey()
{
return takerMultiSigPubKey;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public BigInteger getTradeAmount()
{
return tradeAmount;
}
public String getTakerMessagePubKey()
{
return takerMessagePubKey;
}
public String getSignedTakerDepositTxAsHex()
{
return signedTakerDepositTxAsHex;
}
public String getContractAsJson()
{
return contractAsJson;
}
public String getTakerContractSignature()
{
return takerContractSignature;
}
public String getTxScriptSigAsHex()
{
return txScriptSigAsHex;
}
public String getTxConnOutAsHex()
{
return txConnOutAsHex;
}
public String getDepositTxID()
{
return depositTxID;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public String getTakerPayoutAddress()
{
return takerPayoutAddress;
}
public String getOffererPayoutAddress()
{
return offererPayoutAddress;
}
public String getOffererSignatureR()
{
return offererSignatureR;
}
public String getOffererSignatureS()
{
return offererSignatureS;
}
public BigInteger getOffererPaybackAmount()
{
return offererPaybackAmount;
}
public BigInteger getTakerPaybackAmount()
{
return takerPaybackAmount;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
public String getPayoutTxAsHex()
{
return payoutTxAsHex;
}
public void setPayoutTxAsHex(String payoutTxAsHex)
{
this.payoutTxAsHex = payoutTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
public String getTradeId();
}

View file

@ -1,15 +0,0 @@
package io.bitsquare.msg;
public enum TradeMessageType
{
REQUEST_TAKE_OFFER,
ACCEPT_TAKE_OFFER_REQUEST,
REJECT_TAKE_OFFER_REQUEST,
TAKE_OFFER_FEE_PAYED,
REQUEST_TAKER_DEPOSIT_PAYMENT,
REQUEST_OFFERER_DEPOSIT_PUBLICATION,
DEPOSIT_TX_PUBLISHED,
BANK_TX_INITED,
PAYOUT_TX_PUBLISHED
}

View file

@ -1,9 +1,8 @@
package io.bitsquare.msg.listeners;
import io.bitsquare.msg.TradeMessage;
import net.tomp2p.peers.PeerAddress;
public interface TakeOfferRequestListener
{
void onTakeOfferRequested(TradeMessage tradeMessage, PeerAddress sender);
void onTakeOfferRequested(String offerId, PeerAddress sender);
}

View file

@ -27,7 +27,6 @@ public class Storage
@GuardedBy("lock")
private Map<String, Serializable> rootMap = new HashMap<>();
private boolean dirty;
///////////////////////////////////////////////////////////////////////////////////////////
@ -120,7 +119,6 @@ public class Storage
try
{
lock.lock();
dirty = true;
rootMap.put(key, value);
saveObjectToFile((Serializable) rootMap);
} finally
@ -141,20 +139,23 @@ public class Storage
return read(classInstance.getClass().getName() + "." + propertyKey);
}
// read from local rootMap, just if not found read from disc
public Serializable read(String key)
{
try
{
lock.lock();
if (dirty)
if (rootMap.containsKey(key))
{
// log.trace("Read object with key = " + key + " / value = " + rootMap.get(key));
return rootMap.get(key);
}
else
{
final Map<String, Serializable> map = readRootMap();
if (map != null)
{
rootMap = map;
dirty = false;
}
}
if (rootMap.containsKey(key))
{
@ -163,9 +164,10 @@ public class Storage
}
else
{
log.warn("Object with key = " + key + " not found.");
log.info("Object with key = " + key + " not found.");
return null;
}
}
} finally
{
lock.unlock();
@ -205,7 +207,7 @@ public class Storage
} catch (FileNotFoundException e)
{
log.trace("File not found is ok for the first run.");
log.trace("File not found is ok for the first execute.");
return null;
} catch (ClassNotFoundException | IOException e2)
{

View file

@ -23,7 +23,7 @@ public class Trade implements Serializable
private Transaction depositTransaction;
private Transaction payoutTransaction;
private State state = State.NONE;
private State state = State.OPEN;
public Trade(Offer offer)
{
@ -39,21 +39,6 @@ public class Trade implements Serializable
// Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
{
return offer.getId();
}
public Offer getOffer()
{
return offer;
}
public String getTakeOfferFeeTxID()
{
return takeOfferFeeTxID;
}
public void setTakeOfferFeeTxID(String takeOfferFeeTxID)
{
this.takeOfferFeeTxID = takeOfferFeeTxID;
@ -80,16 +65,31 @@ public class Trade implements Serializable
contractChangedProperty.set(!contractChangedProperty.get());
}
public String getId()
{
return offer.getId();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public Offer getOffer()
{
return offer;
}
public String getTakeOfferFeeTxId()
{
return takeOfferFeeTxID;
}
public String getContractAsJson()
{
return contractAsJson;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public void setContractAsJson(String contractAsJson)
{
this.contractAsJson = contractAsJson;
@ -122,7 +122,6 @@ public class Trade implements Serializable
payoutTxChangedProperty.set(!payoutTxChangedProperty.get());
}
public State getState()
{
return state;
@ -134,7 +133,6 @@ public class Trade implements Serializable
stateChangedProperty.set(state.toString());
}
public SimpleBooleanProperty getDepositTxChangedProperty()
{
return depositTxChangedProperty;
@ -186,7 +184,7 @@ public class Trade implements Serializable
public static enum State
{
NONE,
OPEN,
ACCEPTED,
COMPLETED
}

View file

@ -7,16 +7,18 @@ import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.gui.popups.Popups;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.payment.offerer.OffererPaymentProtocol;
import io.bitsquare.trade.payment.offerer.OffererPaymentProtocolListener;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocol;
import io.bitsquare.trade.payment.taker.TakerPaymentProtocolListener;
import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocol;
import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocolListener;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocol;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocolListener;
import io.bitsquare.user.User;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.io.IOException;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import javafx.beans.property.SimpleStringProperty;
@ -32,8 +34,8 @@ import org.slf4j.LoggerFactory;
public class Trading
{
private static final Logger log = LoggerFactory.getLogger(Trading.class);
private final Map<String, TakerPaymentProtocol> takerPaymentProtocols = new HashMap<>();
private final Map<String, OffererPaymentProtocol> offererPaymentProtocols = new HashMap<>();
private final Map<String, TakerAsSellerProtocol> takerPaymentProtocols = new HashMap<>();
private final Map<String, OffererAsBuyerProtocol> offererPaymentProtocols = new HashMap<>();
private final String storageKey;
private final User user;
@ -44,9 +46,10 @@ public class Trading
private final CryptoFacade cryptoFacade;
private final StringProperty newTradeProperty = new SimpleStringProperty();
private Map<String, Offer> myOffers = new HashMap<>();
private Map<String, Offer> offers = new HashMap<>();
private Map<String, Trade> trades = new HashMap<>();
private Trade currentPendingTrade;
///////////////////////////////////////////////////////////////////////////////////////////
@ -73,15 +76,15 @@ public class Trading
Object offersObject = storage.read(storageKey + ".offers");
if (offersObject instanceof HashMap)
myOffers = (Map<String, Offer>) offersObject;
offers = (Map<String, Offer>) offersObject;
Object tradesObject = storage.read(storageKey + ".trades");
if (tradesObject instanceof HashMap)
trades = (Map<String, Trade>) tradesObject;
messageFacade.addTakeOfferRequestListener((offerId, sender) -> createOffererAsBuyerProtocol(offerId, sender));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
@ -92,15 +95,15 @@ public class Trading
private void saveOffers()
{
storage.write(storageKey + ".offers", myOffers);
storage.write(storageKey + ".offers", offers);
}
public void addOffer(Offer offer) throws IOException
{
if (myOffers.containsKey(offer.getId()))
if (offers.containsKey(offer.getId()))
throw new IllegalStateException("offers contains already a offer with the ID " + offer.getId());
myOffers.put(offer.getId(), offer);
offers.put(offer.getId(), offer);
saveOffers();
messageFacade.addOffer(offer);
@ -108,17 +111,12 @@ public class Trading
public void removeOffer(Offer offer) throws IOException
{
myOffers.remove(offer.getId());
offers.remove(offer.getId());
saveOffers();
messageFacade.removeOffer(offer);
}
public boolean isOfferTradable(Offer offer)
{
return !trades.containsKey(offer.getId());
}
public Trade createTrade(Offer offer)
{
@ -144,85 +142,88 @@ public class Trading
return this.newTradeProperty;
}
public TakerPaymentProtocol addTakerPaymentProtocol(Trade trade, TakerPaymentProtocolListener listener)
{
TakerPaymentProtocol takerPaymentProtocol = new TakerPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
takerPaymentProtocols.put(trade.getId(), takerPaymentProtocol);
return takerPaymentProtocol;
}
OffererPaymentProtocol addOffererPaymentProtocol(Trade trade, OffererPaymentProtocolListener listener)
{
OffererPaymentProtocol offererPaymentProtocol = new OffererPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
offererPaymentProtocols.put(trade.getId(), offererPaymentProtocol);
return offererPaymentProtocol;
}
public void createOffererPaymentProtocol(TradeMessage tradeMessage, PeerAddress sender)
{
Offer offer = myOffers.get(tradeMessage.getOfferUID());
if (isOfferTradable(offer))
public Trade takeOffer(BigInteger amount, Offer offer, TakerAsSellerProtocolListener listener, WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
Trade trade = createTrade(offer);
OffererPaymentProtocol offererPaymentProtocol = addOffererPaymentProtocol(trade, new OffererPaymentProtocolListener()
{
@Override
public void onProgress(double progress)
{
//log.debug("onProgress " + progress);
trade.setTradeAmount(amount);
TakerAsSellerProtocol takerPaymentProtocol = new TakerAsSellerProtocol(trade, listener, resultHandler, faultHandler, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
takerPaymentProtocols.put(trade.getId(), takerPaymentProtocol);
return trade;
}
@Override
public void onFailure(String failureMessage)
{
log.warn(failureMessage);
}
public void createOffererAsBuyerProtocol(String offerId, PeerAddress sender)
{
log.trace("createOffererAsBuyerProtocol offerId = " + offerId);
Offer offer = offers.get(offerId);
if (offer != null && offers.containsKey(offer.getId()))
{
offers.remove(offer);
currentPendingTrade = createTrade(offer);
OffererAsBuyerProtocolListener listener = new OffererAsBuyerProtocolListener()
{
@Override
public void onDepositTxPublished(String depositTxID)
{
log.debug("trading onDepositTxPublished " + depositTxID);
log.trace("trading onDepositTxPublishedMessage " + depositTxID);
}
@Override
public void onDepositTxConfirmedUpdate(TransactionConfidence confidence)
{
log.debug("trading onDepositTxConfirmedUpdate");
log.trace("trading onDepositTxConfirmedUpdate");
}
@Override
public void onPayoutTxPublished(String payoutTxAsHex)
{
Transaction payoutTx = new Transaction(walletFacade.getWallet().getParams(), Utils.parseAsHexOrBase58(payoutTxAsHex));
trade.setPayoutTransaction(payoutTx);
trade.setState(Trade.State.COMPLETED);
log.debug("trading onPayoutTxPublished");
currentPendingTrade.setPayoutTransaction(payoutTx);
currentPendingTrade.setState(Trade.State.COMPLETED);
log.debug("trading onPayoutTxPublishedMessage");
}
@Override
public void onDepositTxConfirmedInBlockchain()
{
log.debug("trading onDepositTxConfirmedInBlockchain");
log.trace("trading onDepositTxConfirmedInBlockchain");
}
});
};
// the handler was not called there because the object was not created when the event occurred (and therefor no listener)
// will probably created earlier, so let it for the moment like that....
offererPaymentProtocol.onTakeOfferRequested(sender);
WorkerResultHandler resultHandler = new WorkerResultHandler()
{
@Override
public void onResult(Worker worker)
{
//log.trace("onResult " + worker.toString());
}
};
WorkerFaultHandler faultHandler = new WorkerFaultHandler()
{
@Override
public void onFault(Throwable throwable)
{
log.error("onFault " + throwable);
}
};
OffererAsBuyerProtocol offererAsBuyerProtocol = new OffererAsBuyerProtocol(currentPendingTrade, sender, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user, resultHandler, faultHandler, listener);
offererPaymentProtocols.put(currentPendingTrade.getId(), offererAsBuyerProtocol);
}
else
{
Popups.openWarningPopup("Offer already taken", "You have that offer already taken. Find that trade under Orders/Open or Pending.");
log.warn("Incoming offer take request does not match with any saved offer. We ignore that request.");
}
}
public void onBankTransferInited(String tradeUID)
public void onUIEventBankTransferInited(String tradeUID)
{
offererPaymentProtocols.get(tradeUID).bankTransferInited();
offererPaymentProtocols.get(tradeUID).onUIEventBankTransferInited();
}
@ -232,9 +233,9 @@ public class Trading
// 6
public void releaseBTC(String tradeUID, TradeMessage tradeMessage)
public void releaseBTC(String tradeUID)
{
takerPaymentProtocols.get(tradeUID).releaseBTC(tradeMessage);
takerPaymentProtocols.get(tradeUID).onUIEventFiatReceived();
}
///////////////////////////////////////////////////////////////////////////////////////////
@ -250,11 +251,22 @@ public class Trading
public Map<String, Offer> getOffers()
{
return myOffers;
return offers;
}
public Offer getOffer(String offerId)
{
return myOffers.get(offerId);
return offers.get(offerId);
}
public boolean isOfferAlreadyInTrades(Offer offer)
{
return trades.containsKey(offer.getId());
}
public Trade getCurrentPendingTrade()
{
return currentPendingTrade;
}
}

View file

@ -0,0 +1,26 @@
package io.bitsquare.trade.payment;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.nucleo.scheduler.model.PropertyProviderModel;
import net.tomp2p.peers.PeerAddress;
public class PaymentModel extends PropertyProviderModel
{
public final MessageFacade messageFacade;
public final Offer offer;
public PeerAddress peerAddress;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public PaymentModel(MessageFacade messageFacade, Trade trade)
{
this.messageFacade = messageFacade;
this.offer = trade.getOffer();
}
}

View file

@ -1,88 +1,320 @@
package io.bitsquare.trade.payment.offerer;
import com.google.bitcoin.core.*;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.msg.TradeMessageType;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.payment.offerer.tasks.*;
import io.bitsquare.trade.payment.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.payment.taker.messages.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.payment.taker.messages.TakeOfferFeePayedMessage;
import io.bitsquare.user.User;
import io.bitsquare.util.Utilities;
import java.math.BigInteger;
import javafx.util.Pair;
import io.nucleo.scheduler.SequenceScheduler;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.util.ArrayList;
import java.util.List;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
//TODO refactor to process based pattern
public class OffererPaymentProtocol
public class OffererAsBuyerProtocol
{
private static final Logger log = LoggerFactory.getLogger(OffererPaymentProtocol.class);
private static final Logger log = LoggerFactory.getLogger(OffererAsBuyerProtocol.class);
public final PeerAddress peerAddress;
// provided data
private final String id;
private final Trade trade;
private final Offer offer;
private final OffererPaymentProtocolListener offererPaymentProtocolListener;
private final OffererAsBuyerProtocolListener listener;
private final WorkerResultHandler resultHandler;
private final WorkerFaultHandler faultHandler;
private final MessageFacade messageFacade;
private final WalletFacade walletFacade;
private final BlockChainFacade blockChainFacade;
private final CryptoFacade cryptoFacade;
private final User user;
private PeerAddress peerAddress;
private boolean isTakeOfferRequested;
private int currentStep = 0;
// private
private final SequenceScheduler scheduler_1;
// data written/read by tasks
private String preparedOffererDepositTxAsHex;
private Transaction depositTransaction;
private long offererTxOutIndex;
private String offererPubKey;
// data written by messages, read by tasks
private String takeOfferFeeTxId;
private String takerMultiSigPubKey;
private String takerPayoutAddress;
private String peersAccountId;
private BankAccount peersBankAccount;
private String takerMessagePubKey;
private String peersContractAsJson;
private String signedTakerDepositTxAsHex;
private String txConnOutAsHex;
private String txScriptSigAsHex;
private long takerTxOutIndex;
private SequenceScheduler scheduler_2;
private SequenceScheduler scheduler_3;
private SequenceScheduler scheduler_4;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public OffererPaymentProtocol(Trade trade,
OffererPaymentProtocolListener offererPaymentProtocolListener,
public OffererAsBuyerProtocol(Trade trade,
PeerAddress peerAddress,
MessageFacade messageFacade,
WalletFacade walletFacade,
BlockChainFacade blockChainFacade,
CryptoFacade cryptoFacade,
User user)
User user,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
OffererAsBuyerProtocolListener listener)
{
checkNotNull(trade);
checkNotNull(messageFacade);
checkNotNull(walletFacade);
checkNotNull(blockChainFacade);
checkNotNull(cryptoFacade);
checkNotNull(user);
this.trade = trade;
this.offererPaymentProtocolListener = offererPaymentProtocolListener;
this.peerAddress = peerAddress;
this.listener = listener;
this.resultHandler = resultHandler;
this.faultHandler = faultHandler;
this.messageFacade = messageFacade;
this.walletFacade = walletFacade;
this.blockChainFacade = blockChainFacade;
this.cryptoFacade = cryptoFacade;
this.user = user;
offer = trade.getOffer();
id = trade.getId();
messageFacade.addOffererPaymentProtocol(this);
log.debug("0 Constr");
log.debug("OffererAsBuyerProtocol created");
List<Worker> tasks = new ArrayList<>();
tasks.add(new HandleTakeOfferRequest(resultHandler, faultHandler));
scheduler_1 = new SequenceScheduler(tasks, this);
scheduler_1.execute();
}
public void onTakeOfferFeePayedMessage(TakeOfferFeePayedMessage message)
{
log.debug("onTakeOfferFeePayedMessage");
getTrade().setTakeOfferFeeTxID(message.getTakeOfferFeeTxID());
getTrade().setTradeAmount(message.getTradeAmount());
takeOfferFeeTxId = message.getTakeOfferFeeTxID();
takerMultiSigPubKey = message.getTakerMultiSigPubKey();
if (scheduler_1.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new VerifyTakeOfferFeePayment(getResultHandler(), getFaultHandler()));
tasks.add(new CreateDepositTx(getResultHandler(), getFaultHandler()));
tasks.add(new RequestTakerDepositPayment(getResultHandler(), getFaultHandler()));
scheduler_2 = new SequenceScheduler(tasks, this);
scheduler_2.execute();
}
else
{
log.error("scheduler_1 has not completed yet.");
}
}
public void onRequestOffererPublishDepositTxMessage(RequestOffererPublishDepositTxMessage message)
{
log.debug("onRequestOffererPublishDepositTxMessage");
takerPayoutAddress = message.getTakerPayoutAddress();
peersAccountId = message.getAccountId();
peersBankAccount = message.getBankAccount();
takerMessagePubKey = message.getTakerMessagePubKey();
peersContractAsJson = message.getContractAsJson();
signedTakerDepositTxAsHex = message.getSignedTakerDepositTxAsHex();
txConnOutAsHex = message.getTxConnOutAsHex();
txScriptSigAsHex = message.getTxScriptSigAsHex();
takerTxOutIndex = message.getTakerTxOutIndex();
if (scheduler_2.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new VerifyTakerAccount(getResultHandler(), getFaultHandler()));
tasks.add(new VerifyAndSignContract(getResultHandler(), getFaultHandler()));
tasks.add(new SignAndPublishDepositTx(getResultHandler(), getFaultHandler()));
tasks.add(new SendDepositTxIdToTaker(getResultHandler(), getFaultHandler()));
tasks.add(new SetupListenerForBlockChainConfirmation(getResultHandler(), getFaultHandler()));
scheduler_3 = new SequenceScheduler(tasks, this);
scheduler_3.execute();
}
else
{
log.error("scheduler_2 has not completed yet.");
}
}
// Triggered from UI event: Button click "Bank transfer inited"
public void onUIEventBankTransferInited()
{
log.debug("onUIEventBankTransferInited");
if (scheduler_3.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new SendSignedPayoutTx(getResultHandler(), getFaultHandler()));
scheduler_4 = new SequenceScheduler(tasks, this);
scheduler_4.execute();
}
else
{
log.error("scheduler_3 has not completed yet.");
}
}
public void onPayoutTxPublishedMessage(PayoutTxPublishedMessage tradeMessage)
{
log.debug("onPayoutTxPublishedMessage");
listener.onPayoutTxPublished(tradeMessage.getPayoutTxAsHex());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
{
return id;
}
public String getTakeOfferFeeTxId()
{
return takeOfferFeeTxId;
}
public String getTakerMultiSigPubKey()
{
return takerMultiSigPubKey;
}
public String getTakerPayoutAddress()
{
return takerPayoutAddress;
}
public String getPeersAccountId()
{
return peersAccountId;
}
public BankAccount getPeersBankAccount()
{
return peersBankAccount;
}
public String getTakerMessagePubKey()
{
return takerMessagePubKey;
}
public String getPeersContractAsJson()
{
return peersContractAsJson;
}
public String getSignedTakerDepositTxAsHex()
{
return signedTakerDepositTxAsHex;
}
public String getTxConnOutAsHex()
{
return txConnOutAsHex;
}
public String getTxScriptSigAsHex()
{
return txScriptSigAsHex;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public void setPreparedOffererDepositTxAsHex(String preparedOffererDepositTxAsHex)
{
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public void setOffererTxOutIndex(long offererTxOutIndex)
{
this.offererTxOutIndex = offererTxOutIndex;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public void setOffererPubKey(String offererPubKey)
{
this.offererPubKey = offererPubKey;
}
public Trade getTrade()
{
return trade;
}
public OffererAsBuyerProtocolListener getListener()
{
return listener;
}
public WorkerResultHandler getResultHandler()
{
return resultHandler;
}
public WorkerFaultHandler getFaultHandler()
{
return faultHandler;
}
public MessageFacade getMessageFacade()
{
return messageFacade;
}
public WalletFacade getWalletFacade()
{
return walletFacade;
}
public BlockChainFacade getBlockChainFacade()
{
return blockChainFacade;
}
public CryptoFacade getCryptoFacade()
{
return cryptoFacade;
}
public User getUser()
{
return user;
}
@ -98,7 +330,7 @@ public class OffererPaymentProtocol
// We got a take offer request and check if the offer is not already reserved for another user.
// If the offer is free we send an accept message.
public void onTakeOfferRequested(PeerAddress sender)
/* public void onTakeOfferRequested(PeerAddress sender)
{
log.debug("1.3 onTakeOfferRequested");
@ -108,7 +340,7 @@ public class OffererPaymentProtocol
if (isTakeOfferRequested)
{
log.debug("1.3 offer already requested REJECT_TAKE_OFFER_REQUEST");
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REJECT_TAKE_OFFER_REQUEST, trade.getId());
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REJECT_TAKE_OFFER_REQUEST, trade.getId());
TradeMessageListener listener = new TradeMessageListener()
{
@Override
@ -140,7 +372,7 @@ public class OffererPaymentProtocol
log.debug("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onResult");
// The accept message has arrived at the peer
// We set requested flag and remove the offer from the orderbook
offererPaymentProtocolListener.onProgress(getProgress());
// offererPaymentProtocolListener.onProgress(getProgress());
isTakeOfferRequested = true;
log.debug("1.3 messageFacade.removeOffer");
@ -153,18 +385,18 @@ public class OffererPaymentProtocol
public void onFailed()
{
log.warn("1.3 onTakeOfferRequested ACCEPT_TAKE_OFFER_REQUEST onFailed");
offererPaymentProtocolListener.onFailure("onTakeOfferRequested onSendTradingMessageFailed");
// offererPaymentProtocolListener.onFailure("onTakeOfferRequested onSendTradingMessageFailed");
}
};
offererPaymentProtocolListener.onProgress(getProgress());
// offererPaymentProtocolListener.onProgress(getProgress());
// 1.3a Send accept take offer message
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.ACCEPT_TAKE_OFFER_REQUEST, trade.getId());
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.ACCEPT_TAKE_OFFER_REQUEST, trade.getId());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
}
*/
//************************************************************************************************
// 1.4, 2.1 - 2.2 Takers task, we wait until the next incoming message
@ -175,37 +407,36 @@ public class OffererPaymentProtocol
// Step 2.3
///////////////////////////////////////////////////////////////////////////////////////////
public void onTakeOfferFeePayed(TradeMessage requestTradeMessage)
/* public void onTakeOfferFeePayedMessage(SendProofOfTakerOfferFeePaymentMessage requestTradeMessage)
{
log.debug("2.3 onTakeOfferFeePayed");
trade.setTakeOfferFeeTxID(requestTradeMessage.getTakeOfferFeeTxID());
log.debug("2.3 onTakeOfferFeePayedMessage");
trade.setTakeOfferFeeTxID(requestTradeMessage.getTakeOfferFeeTxId());
trade.setTradeAmount(requestTradeMessage.getTradeAmount());
verifyTakeOfferFeePayment(requestTradeMessage);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.4
///////////////////////////////////////////////////////////////////////////////////////////
private void verifyTakeOfferFeePayment(TradeMessage requestTradeMessage)
/* private void verifyTakeOfferFeePayment(TradeMessageOld requestTradeMessage)
{
log.debug("2.4 verifyTakeOfferFeePayment");
//TODO just dummy now, will be async
int numOfPeersSeenTx = walletFacade.getNumOfPeersSeenTx(requestTradeMessage.getTakeOfferFeeTxID());
int numOfPeersSeenTx = walletFacade.getNumOfPeersSeenTx(requestTradeMessage.getTakeOfferFeeTxId());
if (numOfPeersSeenTx > 2)
{
createDepositTx(requestTradeMessage);
}
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.5
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantConditions")
private void createDepositTx(TradeMessage requestTradeMessage)
/* private void createDepositTx(TradeMessageOld requestTradeMessage)
{
checkNotNull(requestTradeMessage);
@ -222,7 +453,7 @@ public class OffererPaymentProtocol
checkNotNull(arbitratorPubKey);
log.debug("2.5 offererCreatesMSTxAndAddPayment");
log.debug("offererInputAmount " + BtcFormatter.satoshiToString(offererInputAmount));
log.debug("offererInputAmount " + BtcFormatter.formatSatoshis(offererInputAmount));
log.debug("offerer pubkey " + offererPubKey);
log.debug("taker pubkey " + takerPubKey);
log.debug("arbitrator pubkey " + arbitratorPubKey);
@ -242,13 +473,13 @@ public class OffererPaymentProtocol
}
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.6
///////////////////////////////////////////////////////////////////////////////////////////
private void sendDepositTxAndDataForContract(String preparedOffererDepositTxAsHex, String offererPubKey, long offererTxOutIndex)
/*private void sendDepositTxAndDataForContract(String preparedOffererDepositTxAsHex, String offererPubKey, long offererTxOutIndex)
{
log.debug("2.6 sendDepositTxAndDataForContract");
// Send all the requested data
@ -260,7 +491,7 @@ public class OffererPaymentProtocol
{
log.debug("2.6 sendDepositTxAndDataForContract onResult");
// Message arrived at taker
offererPaymentProtocolListener.onProgress(getProgress());
// offererPaymentProtocolListener.onProgress(getProgress());
// We wait until we get the signed tx back
}
@ -269,19 +500,19 @@ public class OffererPaymentProtocol
public void onFailed()
{
log.debug("2.6 sendDepositTxAndDataForContract onFailed");
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
// offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
}
};
offererPaymentProtocolListener.onProgress(getProgress());
// offererPaymentProtocolListener.onProgress(getProgress());
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID());
String accountID = user.getAccountID();
String accountID = user.getAccountId();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKER_DEPOSIT_PAYMENT, trade.getId(), bankAccount, accountID, offererPubKey, preparedOffererDepositTxAsHex, offererTxOutIndex);
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REQUEST_TAKER_DEPOSIT_PAYMENT, trade.getId(), bankAccount, accountID, offererPubKey, preparedOffererDepositTxAsHex, offererTxOutIndex);
log.debug("2.6 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
} */
//************************************************************************************************
// 2.7 - 2.11 Takers task, we wait until the next incoming message
@ -292,28 +523,28 @@ public class OffererPaymentProtocol
// Step 3.1 Incoming msg from taker
///////////////////////////////////////////////////////////////////////////////////////////
public void onDepositTxReadyForPublication(TradeMessage requestTradeMessage)
/* public void onRequestOffererPublishDepositTxMessage(TradeMessageOld requestTradeMessage)
{
log.debug("3.1 onDepositTxReadyForPublication");
log.debug("3.1 onRequestOffererPublishDepositTxMessage");
takerPayoutAddress = requestTradeMessage.getTakerPayoutAddress();
verifyTaker(requestTradeMessage);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.2 Verify offerers account registration and against the blacklist
///////////////////////////////////////////////////////////////////////////////////////////
private void verifyTaker(TradeMessage requestTradeMessage)
/* private void verifyTaker(TradeMessageOld requestTradeMessage)
{
log.debug("3.2 verifyTaker");
log.debug("3.2.1 verifyAccountRegistration");
if (blockChainFacade.verifyAccountRegistration())
{
log.debug("3.2.2 isAccountBlackListed");
if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountID(), requestTradeMessage.getBankAccount()))
if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountId(), requestTradeMessage.getBankAccount()))
{
offererPaymentProtocolListener.onFailure("Taker is blacklisted.");
// offererPaymentProtocolListener.onFailure("Taker is blacklisted.");
}
else
{
@ -322,20 +553,19 @@ public class OffererPaymentProtocol
}
else
{
offererPaymentProtocolListener.onFailure("Takers account registration is invalid.");
}
// offererPaymentProtocolListener.onFailure("Takers account registration is invalid.");
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.3 Verify and sign the contract
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantConditions")
private void verifyAndSignContract(TradeMessage requestTradeMessage)
/* private void verifyAndSignContract(TradeMessageOld requestTradeMessage)
{
Contract contract = new Contract(offer,
trade.getTradeAmount(),
trade.getTakeOfferFeeTxID(),
trade.getTakeOfferFeeTxId(),
user.getAccountID(),
requestTradeMessage.getAccountID(),
user.getCurrentBankAccount(),
@ -364,13 +594,13 @@ public class OffererPaymentProtocol
{
log.error("3.3 verifyContract failed");
}
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.4 Sign and publish the deposit tx
///////////////////////////////////////////////////////////////////////////////////////////
private void signAndPublishDepositTx(TradeMessage requestTradeMessage)
/* private void signAndPublishDepositTx(TradeMessageOld requestTradeMessage)
{
log.debug("3.4 signAndPublishDepositTx");
@ -410,13 +640,13 @@ public class OffererPaymentProtocol
e.getStackTrace();// Could not understand form of connected output script: RETURN
}
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.5 Send tx id of published deposit tx to taker
///////////////////////////////////////////////////////////////////////////////////////////
private void sendDepositTxIdToTaker(Transaction transaction)
/*private void sendDepositTxIdToTaker(Transaction transaction)
{
log.debug("3.5 sendDepositTxIdToTaker");
@ -426,24 +656,24 @@ public class OffererPaymentProtocol
public void onResult()
{
log.debug("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onResult");
offererPaymentProtocolListener.onProgress(getProgress());
// offererPaymentProtocolListener.onProgress(getProgress());
}
@Override
public void onFailed()
{
log.warn("3.5 sendDepositTxIdToTaker DEPOSIT_TX_PUBLISHED onFailed");
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
// offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
}
};
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getId(), Utils.bytesToHexString(transaction.bitcoinSerialize()));
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getId(), Utils.bytesToHexString(transaction.bitcoinSerialize()));
log.debug("3.5 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
// wait for at least 1 confirmation, then pay Fiat
offererPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxID());
offererPaymentProtocolListener.onDepositTxPublishedMessage(tradeMessage.getDepositTxID());
setupListenerForBlockChainConfirmation(transaction);
}
} */
//************************************************************************************************
// 3.6 Taker got informed, but no action from his side required.
@ -451,10 +681,10 @@ public class OffererPaymentProtocol
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.7 We setup a listener for block chain confirmation
// Step 3.7 We init a listener for block chain confirmation
///////////////////////////////////////////////////////////////////////////////////////////
private void setupListenerForBlockChainConfirmation(Transaction transaction)
/* private void setupListenerForBlockChainConfirmation(Transaction transaction)
{
log.debug("3.7 setupListenerForBlockChainConfirmation");
@ -477,6 +707,7 @@ public class OffererPaymentProtocol
onDepositTxConfirmedInBlockchain();
transaction.getConfidenceForAddress().removeEventListener(this);
} */
/*
}
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
@ -487,27 +718,27 @@ public class OffererPaymentProtocol
}
}
);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.8 We check if the block chain confirmation is >= 1
///////////////////////////////////////////////////////////////////////////////////////////
private void updateConfirmation(TransactionConfidence confidence)
/* private void updateConfirmation(TransactionConfidence confidence)
{
log.debug("3.8 updateConfirmation " + confidence);
offererPaymentProtocolListener.onDepositTxConfirmedUpdate(confidence);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.9 Blockchain confirmation received, so tell user he should start bank transfer
///////////////////////////////////////////////////////////////////////////////////////////
private void onDepositTxConfirmedInBlockchain()
/* private void onDepositTxConfirmedInBlockchain()
{
log.debug("3.9 readyForBankTransfer");
offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain();
}
} */
//************************************************************************************************
// Offerer need to start bank tx, after he done it he call the next step
@ -518,25 +749,25 @@ public class OffererPaymentProtocol
// Step 3.10 User clicked the "bank transfer inited" button, so we tell the peer that we started the bank tx
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("ConstantConditions")
public void bankTransferInited()
/*
public void onBankTransferInitedMessage()
{
log.debug("3.10 bankTransferInited");
log.debug("3.10 onBankTransferInitedMessage");
TradeMessageListener listener = new TradeMessageListener()
{
@Override
public void onResult()
{
log.debug("3.10 bankTransferInited BANK_TX_INITED onResult");
offererPaymentProtocolListener.onProgress(getProgress());
log.debug("3.10 onBankTransferInitedMessage BANK_TX_INITED onResult");
// offererPaymentProtocolListener.onProgress(getProgress());
}
@Override
public void onFailed()
{
log.warn("3.10 bankTransferInited BANK_TX_INITED onFailed");
offererPaymentProtocolListener.onFailure("bankTransferInited BANK_TX_INITED");
log.warn("3.10 onBankTransferInitedMessage BANK_TX_INITED onFailed");
// offererPaymentProtocolListener.onFailure("onBankTransferInitedMessage BANK_TX_INITED");
}
};
@ -560,7 +791,7 @@ public class OffererPaymentProtocol
String offererSignatureS = offererSignature.s.toString();
String depositTxAsHex = result.getValue();
String offererPayoutAddress = walletFacade.getAddressInfoByTradeID(trade.getId()).getAddressString();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getId(),
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.BANK_TX_INITED, trade.getId(),
depositTxAsHex,
offererSignatureR,
offererSignatureS,
@ -583,7 +814,7 @@ public class OffererPaymentProtocol
log.error("3.10 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage());
}
}
*/
//************************************************************************************************
// We wait until taker has received the money on his bank account, that might take a while.
@ -594,25 +825,23 @@ public class OffererPaymentProtocol
// Step 3.14 We received the payout tx. Trade is completed
///////////////////////////////////////////////////////////////////////////////////////////
public void onPayoutTxPublished(TradeMessage tradeMessage)
/* public void onPayoutTxPublishedMessage(PayoutTxPublishedMessage tradeMessage)
{
log.debug("3.14 onPayoutTxPublished");
log.debug("3.14 TRADE COMPLETE!!!!!!!!!!!");
offererPaymentProtocolListener.onPayoutTxPublished(tradeMessage.getPayoutTxAsHex());
}
log.debug("onPayoutTxPublishedMessage");
offererPaymentProtocolListener.onPayoutTxPublishedMessage(tradeMessage.getPayoutTxAsHex());
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Util
///////////////////////////////////////////////////////////////////////////////////////////
private double getProgress()
/* private double getProgress()
{
currentStep++;
int numberOfSteps = 10;
return (double) currentStep / (double) numberOfSteps;
}
} */
}

View file

@ -2,19 +2,12 @@ package io.bitsquare.trade.payment.offerer;
import com.google.bitcoin.core.TransactionConfidence;
@SuppressWarnings("EmptyMethod")
public interface OffererPaymentProtocolListener
public interface OffererAsBuyerProtocolListener
{
@SuppressWarnings("UnusedParameters")
void onProgress(double progress);
void onFailure(String failureMessage);
void onDepositTxPublished(String depositTxID);
void onDepositTxConfirmedInBlockchain();
@SuppressWarnings("UnusedParameters")
void onDepositTxConfirmedUpdate(TransactionConfidence confidence);
void onPayoutTxPublished(String payoutTxID);

View file

@ -0,0 +1,22 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class AcceptTakeOfferRequestMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 6177387534087739018L;
private final String tradeId;
public AcceptTakeOfferRequestMessage(String tradeId)
{
this.tradeId = tradeId;
}
@Override
public String getTradeId()
{
return tradeId;
}
}

View file

@ -0,0 +1,70 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
import java.math.BigInteger;
public class BankTransferInitedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -3479634129543632523L;
private final String tradeId;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
private BigInteger takerPaybackAmount;
private String offererPayoutAddress;
public BankTransferInitedMessage(String tradeId, String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererPayoutAddress)
{
this.tradeId = tradeId;
this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount;
this.takerPaybackAmount = takerPaybackAmount;
this.offererPayoutAddress = offererPayoutAddress;
}
@Override
public String getTradeId()
{
return tradeId;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
public String getOffererPayoutAddress()
{
return offererPayoutAddress;
}
public String getOffererSignatureS()
{
return offererSignatureS;
}
public BigInteger getOffererPaybackAmount()
{
return offererPaybackAmount;
}
public BigInteger getTakerPaybackAmount()
{
return takerPaybackAmount;
}
public String getOffererSignatureR()
{
return offererSignatureR;
}
}

View file

@ -0,0 +1,30 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class DepositTxPublishedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -1532231540167406581L;
private final String tradeId;
private String depositTxAsHex;
public DepositTxPublishedMessage(String tradeId, String depositTxAsHex)
{
this.tradeId = tradeId;
this.depositTxAsHex = depositTxAsHex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
}

View file

@ -0,0 +1,21 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RejectTakeOfferRequestMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -8088557759642128139L;
private final String tradeId;
public RejectTakeOfferRequestMessage(String tradeId)
{
this.tradeId = tradeId;
}
@Override
public String getTradeId()
{
return tradeId;
}
}

View file

@ -0,0 +1,59 @@
package io.bitsquare.trade.payment.offerer.messages;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RequestTakerDepositPaymentMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -3988720410493712913L;
private final String tradeId;
private BankAccount bankAccount;
private String accountID;
private String offererPubKey;
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
public RequestTakerDepositPaymentMessage(String tradeId, BankAccount bankAccount, String accountID, String offererPubKey, String preparedOffererDepositTxAsHex, long offererTxOutIndex)
{
this.tradeId = tradeId;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.offererPubKey = offererPubKey;
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
this.offererTxOutIndex = offererTxOutIndex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public BankAccount getBankAccount()
{
return bankAccount;
}
public String getAccountID()
{
return accountID;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
}

View file

@ -0,0 +1,29 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.trade.payment.offerer.OffererAsBuyerProtocol;
import io.nucleo.scheduler.tasks.AbstractTask;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractOffererAsBuyerTask extends AbstractTask
{
private static final Logger log = LoggerFactory.getLogger(AbstractOffererAsBuyerTask.class);
protected OffererAsBuyerProtocol sharedModel;
public AbstractOffererAsBuyerTask(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
addResultHandlers(resultHandler);
addFaultHandlers(faultHandler);
}
@Override
public void setModel(Object model)
{
sharedModel = (OffererAsBuyerProtocol) model;
super.setModel(model);
}
}

View file

@ -0,0 +1,44 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateDepositTx extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(CreateDepositTx.class);
public CreateDepositTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
sharedModel.setOffererPubKey(sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getPubKeyAsHexString());
Transaction tx = sharedModel.getWalletFacade().offererCreatesMSTxAndAddPayment(sharedModel.getTrade().getCollateralAmount(),
sharedModel.getOffererPubKey(),
sharedModel.getTakerMultiSigPubKey(),
sharedModel.getTrade().getOffer().getArbitrator().getPubKeyAsHex(),
sharedModel.getTrade().getId());
sharedModel.setPreparedOffererDepositTxAsHex(Utils.bytesToHexString(tx.bitcoinSerialize()));
sharedModel.setOffererTxOutIndex(tx.getInput(0).getOutpoint().getIndex());
complete();
} catch (InsufficientMoneyException e)
{
log.error("Create deposit tx failed due InsufficientMoneyException " + e);
failed(new Exception("Create deposit tx failed due InsufficientMoneyException " + e));
}
}
}

View file

@ -0,0 +1,70 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.payment.offerer.messages.AcceptTakeOfferRequestMessage;
import io.bitsquare.trade.payment.offerer.messages.RejectTakeOfferRequestMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HandleTakeOfferRequest extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(HandleTakeOfferRequest.class);
public HandleTakeOfferRequest(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
if (sharedModel.getTrade().getState() == Trade.State.OPEN)
{
AcceptTakeOfferRequestMessage msg = new AcceptTakeOfferRequestMessage(sharedModel.getTrade().getId());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("AcceptTakeOfferRequestMessage successfully arrived at peer");
sharedModel.getTrade().setState(Trade.State.ACCEPTED);
sharedModel.getMessageFacade().removeOffer(sharedModel.getTrade().getOffer());
complete();
}
@Override
public void onFailed()
{
log.error("AcceptTakeOfferRequestMessage failed to arrive at peer");
failed(new Exception("AcceptTakeOfferRequestMessage failed to arrive at peer"));
}
});
}
else
{
RejectTakeOfferRequestMessage msg = new RejectTakeOfferRequestMessage(sharedModel.getTrade().getId());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RejectTakeOfferRequestMessage successfully arrived at peer");
}
@Override
public void onFailed()
{
log.error("RejectTakeOfferRequestMessage failed to arrive at peer");
}
});
log.error("Offer not marked as open.");
failed(new Exception("Offer not marked as open."));
}
}
}

View file

@ -0,0 +1,48 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.offerer.messages.RequestTakerDepositPaymentMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakerDepositPayment extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(RequestTakerDepositPayment.class);
public RequestTakerDepositPayment(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
RequestTakerDepositPaymentMessage tradeMessage = new RequestTakerDepositPaymentMessage(sharedModel.getTrade().getId(),
sharedModel.getUser().getBankAccount(sharedModel.getTrade().getOffer().getBankAccountUID()),
sharedModel.getUser().getAccountID(),
sharedModel.getOffererPubKey(),
sharedModel.getPreparedOffererDepositTxAsHex(),
sharedModel.getOffererTxOutIndex());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("RequestTakerDepositPaymentMessage failed to arrive at peer");
failed(new Exception("RequestTakerDepositPaymentMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.Utils;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.offerer.messages.DepositTxPublishedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendDepositTxIdToTaker extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SendDepositTxIdToTaker.class);
public SendDepositTxIdToTaker(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(sharedModel.getTrade().getId(), Utils.bytesToHexString(sharedModel.getTrade().getDepositTransaction().bitcoinSerialize()));
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("DepositTxPublishedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("DepositTxPublishedMessage failed to arrive at peer");
failed(new Exception("DepositTxPublishedMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,91 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.offerer.messages.BankTransferInitedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedPayoutTx extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SendSignedPayoutTx.class);
public SendSignedPayoutTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
Transaction depositTransaction = sharedModel.getTrade().getDepositTransaction();
BigInteger collateral = sharedModel.getTrade().getCollateralAmount();
BigInteger offererPaybackAmount = sharedModel.getTrade().getTradeAmount().add(collateral);
BigInteger takerPaybackAmount = collateral;
log.trace("offererPaybackAmount " + offererPaybackAmount);
log.trace("takerPaybackAmount " + takerPaybackAmount);
log.trace("depositTransaction.getHashAsString() " + depositTransaction.getHashAsString());
log.trace("takerPayoutAddress " + sharedModel.getTakerPayoutAddress());
Pair<ECKey.ECDSASignature, String> result = sharedModel.getWalletFacade().offererCreatesAndSignsPayoutTx(depositTransaction.getHashAsString(),
offererPaybackAmount,
takerPaybackAmount,
sharedModel.getTakerPayoutAddress(),
sharedModel.getTrade().getId());
ECKey.ECDSASignature offererSignature = result.getKey();
String offererSignatureR = offererSignature.r.toString();
String offererSignatureS = offererSignature.s.toString();
String depositTxAsHex = result.getValue();
String offererPayoutAddress = sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getAddressString();
BankTransferInitedMessage tradeMessage = new BankTransferInitedMessage(sharedModel.getTrade().getId(),
depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
log.trace("depositTxAsHex " + depositTxAsHex);
log.trace("offererSignatureR " + offererSignatureR);
log.trace("offererSignatureS " + offererSignatureS);
log.trace("offererPaybackAmount " + offererPaybackAmount);
log.trace("takerPaybackAmount " + takerPaybackAmount);
log.trace("offererPayoutAddress " + offererPayoutAddress);
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("BankTransferInitedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("BankTransferInitedMessage failed to arrive at peer");
failed(new Exception("BankTransferInitedMessage failed to arrive at peer"));
}
});
} catch (Exception e)
{
log.error("Exception at OffererCreatesAndSignsPayoutTx " + e);
failed(e);
}
}
}

View file

@ -0,0 +1,48 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionConfidence;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SetupListenerForBlockChainConfirmation extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SetupListenerForBlockChainConfirmation.class);
public SetupListenerForBlockChainConfirmation(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO
// sharedModel.offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain();
Transaction tx = sharedModel.getTrade().getDepositTransaction();
tx.getConfidence().addEventListener(new TransactionConfidence.Listener()
{
@Override
public void onConfidenceChanged(Transaction tx, ChangeReason reason)
{
log.trace("onConfidenceChanged " + tx.getConfidence());
if (reason == ChangeReason.SEEN_PEERS)
{
sharedModel.getListener().onDepositTxConfirmedUpdate(tx.getConfidence());
}
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
{
sharedModel.getListener().onDepositTxConfirmedInBlockchain();
tx.getConfidence().removeEventListener(this);
log.trace("Tx is in blockchain");
complete();
}
}
}
);
}
}

View file

@ -0,0 +1,56 @@
package io.bitsquare.trade.payment.offerer.tasks;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishDepositTx extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTx.class);
public SignAndPublishDepositTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
sharedModel.getWalletFacade().offererSignAndPublishTx(sharedModel.getPreparedOffererDepositTxAsHex(),
sharedModel.getSignedTakerDepositTxAsHex(),
sharedModel.getTxConnOutAsHex(),
sharedModel.getTxScriptSigAsHex(),
sharedModel.getOffererTxOutIndex(),
sharedModel.getTakerTxOutIndex(),
new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.trace("offererSignAndPublishTx succeeded " + transaction);
sharedModel.getTrade().setDepositTransaction(transaction);
sharedModel.getListener().onDepositTxPublished(transaction.getHashAsString());
complete();
}
@Override
public void onFailure(Throwable t)
{
log.error("offererSignAndPublishTx failed:" + t);
failed(t);
}
});
} catch (Exception e)
{
log.error("offererSignAndPublishTx failed:" + e);
failed(e);
}
}
}

View file

@ -0,0 +1,59 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.bitsquare.trade.Contract;
import io.bitsquare.util.Utilities;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyAndSignContract extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyAndSignContract.class);
public VerifyAndSignContract(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
Contract contract = new Contract(sharedModel.getTrade().getOffer(),
sharedModel.getTrade().getTradeAmount(),
sharedModel.getTrade().getTakeOfferFeeTxId(),
sharedModel.getUser().getAccountID(),
sharedModel.getPeersAccountId(),
sharedModel.getUser().getCurrentBankAccount(),
sharedModel.getPeersBankAccount(),
sharedModel.getTrade().getOffer().getMessagePubKeyAsHex(),
sharedModel.getTakerMessagePubKey());
String contractAsJson = Utilities.objectToJson(contract);
// log.trace("Offerer contract created: " + contract);
// log.trace("Offerers contractAsJson: " + contractAsJson);
// log.trace("Takers contractAsJson: " + sharedModel.peersContractAsJson);
if (contractAsJson.equals(sharedModel.getPeersContractAsJson()))
{
log.trace("The 2 contracts as json does match");
String signature = sharedModel.getCryptoFacade().signContract(sharedModel.getWalletFacade().getRegistrationAddressInfo().getKey(), contractAsJson);
sharedModel.getTrade().setContract(contract);
sharedModel.getTrade().setContractAsJson(contractAsJson);
sharedModel.getTrade().setContractTakerSignature(signature);
//log.trace("signature: " + signature);
complete();
}
else
{
// TODO use diff output as feedback ?
log.error("Contracts are not matching.");
log.error("Offerers contractAsJson: " + contractAsJson);
log.error("Takers contractAsJson: " + sharedModel.getPeersContractAsJson());
failed(new Exception("Contracts are not matching"));
}
}
}

View file

@ -0,0 +1,29 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakeOfferFeePayment extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyTakeOfferFeePayment.class);
public VerifyTakeOfferFeePayment(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO mocked yet, need a confidence listeners
int numOfPeersSeenTx = sharedModel.getWalletFacade().getNumOfPeersSeenTx(sharedModel.getTakeOfferFeeTxId());
if (numOfPeersSeenTx > 2)
{
complete();
}
}
}

View file

@ -0,0 +1,41 @@
package io.bitsquare.trade.payment.offerer.tasks;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakerAccount extends AbstractOffererAsBuyerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyTakerAccount.class);
public VerifyTakerAccount(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO mocked yet
if (sharedModel.getBlockChainFacade().verifyAccountRegistration())
{
if (sharedModel.getBlockChainFacade().isAccountBlackListed(sharedModel.getPeersAccountId(), sharedModel.getPeersBankAccount()))
{
log.error("Taker is blacklisted");
failed(new Exception("Taker is blacklisted"));
}
else
{
complete();
}
}
else
{
log.error("Account registration validation for peer failed.");
failed(new Exception("Account registration validation for peer failed."));
}
}
}

View file

@ -1,58 +1,78 @@
package io.bitsquare.trade.payment.taker;
import com.google.bitcoin.core.AddressFormatException;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.msg.TradeMessageType;
import io.bitsquare.msg.listeners.AddressLookupListener;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.payment.offerer.messages.BankTransferInitedMessage;
import io.bitsquare.trade.payment.offerer.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.payment.offerer.messages.RequestTakerDepositPaymentMessage;
import io.bitsquare.trade.payment.taker.tasks.*;
import io.bitsquare.user.User;
import io.bitsquare.util.Utilities;
import io.nucleo.scheduler.SequenceScheduler;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
//TODO use states
@SuppressWarnings("ConstantConditions")
public class TakerPaymentProtocol
public class TakerAsSellerProtocol
{
private static final Logger log = LoggerFactory.getLogger(TakerPaymentProtocol.class);
private static final Logger log = LoggerFactory.getLogger(TakerAsSellerProtocol.class);
// provided data
private final String id;
private final Trade trade;
private final Offer offer;
private final TakerPaymentProtocolListener takerPaymentProtocolListener;
private final TakerAsSellerProtocolListener listener;
private final WorkerResultHandler resultHandler;
private final WorkerFaultHandler faultHandler;
private final MessageFacade messageFacade;
private final WalletFacade walletFacade;
private final BlockChainFacade blockChainFacade;
private final CryptoFacade cryptoFacade;
private final User user;
// private
private final SequenceScheduler scheduler_1;
// written/read by task
private String payoutTxAsHex;
private PeerAddress peerAddress;
private Transaction signedTakerDepositTx;
private long takerTxOutIndex;
// written by message, read by tasks
private String peersAccountId;
private BankAccount peersBankAccount;
private String offererPubKey;
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
private BigInteger takerPaybackAmount;
private String offererPayoutAddress;
private int currentStep = 0;
private SequenceScheduler scheduler_2;
private SequenceScheduler scheduler_3;
private SequenceScheduler scheduler_4;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public TakerPaymentProtocol(Trade trade,
TakerPaymentProtocolListener takerPaymentProtocolListener,
public TakerAsSellerProtocol(Trade trade,
TakerAsSellerProtocolListener listener,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
MessageFacade messageFacade,
WalletFacade walletFacade,
BlockChainFacade blockChainFacade,
@ -60,32 +80,271 @@ public class TakerPaymentProtocol
User user)
{
this.trade = trade;
this.takerPaymentProtocolListener = takerPaymentProtocolListener;
this.listener = listener;
this.resultHandler = resultHandler;
this.faultHandler = faultHandler;
this.messageFacade = messageFacade;
this.walletFacade = walletFacade;
this.blockChainFacade = blockChainFacade;
this.cryptoFacade = cryptoFacade;
this.user = user;
offer = trade.getOffer();
id = trade.getId();
log.debug("TakerAsSellerProtocol created");
messageFacade.addTakerPaymentProtocol(this);
// start with first task sequence
List<Worker> tasks = new ArrayList<>();
tasks.add(new GetPeerAddress(resultHandler, faultHandler));
tasks.add(new RequestTakeOffer(resultHandler, faultHandler));
scheduler_1 = new SequenceScheduler(tasks, this);
scheduler_1.execute();
}
public void takeOffer()
public void onAcceptTakeOfferRequestMessage()
{
log.debug("1 takeOffer");
findPeerAddress();
log.debug("onAcceptTakeOfferRequestMessage");
if (scheduler_1.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new PayTakeOfferFee(resultHandler, faultHandler));
tasks.add(new SendTakeOfferFeePayedTxId(resultHandler, faultHandler));
scheduler_2 = new SequenceScheduler(tasks, this);
scheduler_2.execute();
}
else
{
log.error("scheduler_1 has not completed yet.");
}
}
public void onRejectTakeOfferRequestMessage()
{
log.debug("onRejectTakeOfferRequestMessage");
if (scheduler_1.getHasCompleted())
{
//TODO
}
else
{
log.error("scheduler_1 has not completed yet.");
}
}
public void onRequestTakerDepositPaymentMessage(RequestTakerDepositPaymentMessage message)
{
log.debug("onRequestTakerDepositPaymentMessage");
peersAccountId = message.getAccountID();
peersBankAccount = message.getBankAccount();
offererPubKey = message.getOffererPubKey();
preparedOffererDepositTxAsHex = message.getPreparedOffererDepositTxAsHex();
offererTxOutIndex = message.getOffererTxOutIndex();
if (scheduler_2.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new VerifyOffererAccount(resultHandler, faultHandler));
tasks.add(new CreateAndSignContract(resultHandler, faultHandler));
tasks.add(new PayDeposit(resultHandler, faultHandler));
tasks.add(new SendSignedTakerDepositTxAsHex(resultHandler, faultHandler));
scheduler_3 = new SequenceScheduler(tasks, this);
scheduler_3.execute();
}
else
{
log.error("scheduler_2 has not completed yet.");
}
}
// informational, does only trigger UI feedback/update
public void onDepositTxPublishedMessage(DepositTxPublishedMessage message)
{
log.debug("onDepositTxPublishedMessage");
String txID = getWalletFacade().takerCommitDepositTx(message.getDepositTxAsHex());
listener.onDepositTxPublished(txID);
}
// informational, store data for later, does only trigger UI feedback/update
public void onBankTransferInitedMessage(BankTransferInitedMessage tradeMessage)
{
log.debug("onBankTransferInitedMessage");
depositTxAsHex = tradeMessage.getDepositTxAsHex();
offererSignatureR = tradeMessage.getOffererSignatureR();
offererSignatureS = tradeMessage.getOffererSignatureS();
offererPaybackAmount = tradeMessage.getOffererPaybackAmount();
takerPaybackAmount = tradeMessage.getTakerPaybackAmount();
offererPayoutAddress = tradeMessage.getOffererPayoutAddress();
listener.onBankTransferInited(tradeMessage.getTradeId());
}
// User clicked the "bank transfer received" button, so we release the funds for pay out
public void onUIEventFiatReceived()
{
log.debug("onUIEventFiatReceived");
if (scheduler_3.getHasCompleted())
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new SignAndPublishPayoutTx(resultHandler, faultHandler));
tasks.add(new SendPayoutTxToOfferer(resultHandler, faultHandler));
scheduler_4 = new SequenceScheduler(tasks, this);
scheduler_4.execute();
}
else
{
log.error("scheduler_3 has not completed yet.");
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
{
return id;
}
public Trade getTrade()
{
return trade;
}
public TakerAsSellerProtocolListener getListener()
{
return listener;
}
public MessageFacade getMessageFacade()
{
return messageFacade;
}
public WalletFacade getWalletFacade()
{
return walletFacade;
}
public BlockChainFacade getBlockChainFacade()
{
return blockChainFacade;
}
public CryptoFacade getCryptoFacade()
{
return cryptoFacade;
}
public User getUser()
{
return user;
}
public PeerAddress getPeerAddress()
{
return peerAddress;
}
public void setPeerAddress(PeerAddress peerAddress)
{
this.peerAddress = peerAddress;
}
public Transaction getSignedTakerDepositTx()
{
return signedTakerDepositTx;
}
public void setSignedTakerDepositTx(Transaction signedTakerDepositTx)
{
this.signedTakerDepositTx = signedTakerDepositTx;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
public void setTakerTxOutIndex(long takerTxOutIndex)
{
this.takerTxOutIndex = takerTxOutIndex;
}
public String getPeersAccountId()
{
return peersAccountId;
}
public BankAccount getPeersBankAccount()
{
return peersBankAccount;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public String getPreparedOffererDepositTxAsHex()
{
return preparedOffererDepositTxAsHex;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
public String getOffererSignatureR()
{
return offererSignatureR;
}
public String getOffererSignatureS()
{
return offererSignatureS;
}
public BigInteger getOffererPaybackAmount()
{
return offererPaybackAmount;
}
public BigInteger getTakerPaybackAmount()
{
return takerPaybackAmount;
}
public String getOffererPayoutAddress()
{
return offererPayoutAddress;
}
public String getPayoutTxAsHex()
{
return payoutTxAsHex;
}
public void setPayoutTxAsHex(String payoutTxAsHex)
{
this.payoutTxAsHex = payoutTxAsHex;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Step 1.1
///////////////////////////////////////////////////////////////////////////////////////////
private void findPeerAddress()
/* private void findPeerAddress()
{
log.debug("1.1 findPeerAddress");
AddressLookupListener addressLookupListener = new AddressLookupListener()
@ -115,14 +374,13 @@ public class TakerPaymentProtocol
// Request the peers address from the DHT
messageFacade.getPeerAddress(offer.getMessagePubKeyAsHex(), addressLookupListener);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 1.2
///////////////////////////////////////////////////////////////////////////////////////////
private void requestTakeOffer()
/* private void requestTakeOffer()
{
log.debug("1.2 requestTakeOffer");
TradeMessageListener listener = new TradeMessageListener()
@ -153,7 +411,7 @@ public class TakerPaymentProtocol
// Send the take offer request
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_TAKE_OFFER, trade.getId());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
} */
//************************************************************************************************
@ -167,27 +425,27 @@ public class TakerPaymentProtocol
// 1.4a offerer has accepted the take offer request. Move on to step 2.
public void onTakeOfferRequestAccepted()
/* public void onAcceptTakeOfferRequestMessage()
{
log.debug("1.4a onTakeOfferRequestAccepted");
takerPaymentProtocolListener.onProgress(getProgress());
log.debug("1.4a onAcceptTakeOfferRequestMessage");
// listener.onProgress(getProgress());
payOfferFee(trade);
}
// 1.4b Offerer has rejected the take offer request. The UI controller will handle the case.
public void onTakeOfferRequestRejected()
// 1.4b Offerer has rejected the take offer request. The UI controller will onResult the case.
public void onRejectTakeOfferRequestMessage()
{
log.debug("1.4b onTakeOfferRequestRejected");
takerPaymentProtocolListener.onProgress(getProgress());
}
log.debug("1.4b onRejectTakeOfferRequestMessage");
// listener.onProgress(getProgress());
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.1
///////////////////////////////////////////////////////////////////////////////////////////
private void payOfferFee(Trade trade)
/* private void payOfferFee(Trade trade)
{
log.debug("2.1 payTakeOfferFee");
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
@ -201,7 +459,7 @@ public class TakerPaymentProtocol
// Offer fee payed successfully.
trade.setTakeOfferFeeTxID(transaction.getHashAsString());
takerPaymentProtocolListener.onProgress(getProgress());
// listener.onProgress(getProgress());
// move on
sendTakerOfferFeeTxID(transaction.getHashAsString());
@ -211,19 +469,19 @@ public class TakerPaymentProtocol
public void onFailure(Throwable t)
{
log.debug("2.1 payTakeOfferFee onFailure");
takerPaymentProtocolListener.onFailure("payTakeOfferFee onFailure " + t.getMessage());
// listener.onFailure("payTakeOfferFee onFailure " + t.getMessage());
}
};
try
{
// Pay the offer fee
takerPaymentProtocolListener.onProgress(getProgress());
// listener.onProgress(getProgress());
walletFacade.payTakeOfferFee(trade.getId(), callback);
} catch (InsufficientMoneyException e)
{
takerPaymentProtocolListener.onProgress(getProgress());
}
// listener.onProgress(getProgress());
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
@ -235,7 +493,7 @@ public class TakerPaymentProtocol
// 0 block confirmation is acceptable for the fee to not block the process
// The offerer will wait until a minimum of peers seen the tx before sending his data.
// We also get the multisig tx delivered
private void sendTakerOfferFeeTxID(String takeOfferFeeTxID)
/* private void sendTakerOfferFeeTxID(String takeOfferFeeTxID)
{
log.debug("2.2 sendTakerOfferFeeTxID");
TradeMessageListener listener = new TradeMessageListener()
@ -245,7 +503,7 @@ public class TakerPaymentProtocol
{
log.debug("2.2 sendTakerOfferFeeTxID onResult");
// Message has arrived
takerPaymentProtocolListener.onProgress(getProgress());
//listener.onProgress(getProgress());
// We wait until the offerer send us the data
}
@ -254,21 +512,21 @@ public class TakerPaymentProtocol
public void onFailed()
{
log.debug("2.2 sendTakerOfferFeeTxID onFailed");
takerPaymentProtocolListener.onFailure("requestAccountDetails onSendTradingMessageFailed");
// //TakerAsSellerProtocol.this. listener.onFailure("requestAccountDetails onSendTradingMessageFailed");
}
};
takerPaymentProtocolListener.onProgress(getProgress());
// this.listener.onProgress(getProgress());
// 2.3. send request for the account details and send fee tx id so offerer can verify that the fee has been paid.
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.TAKE_OFFER_FEE_PAYED,
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.TAKE_OFFER_FEE_PAYED,
trade.getId(),
trade.getTradeAmount(),
takeOfferFeeTxID,
walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
*/
//************************************************************************************************
// 2.3 - 2.6 Offerers tasks, we are in waiting mode
@ -279,27 +537,27 @@ public class TakerPaymentProtocol
// Step 2.7 Incoming msg from offerer
///////////////////////////////////////////////////////////////////////////////////////////
public void onTakerDepositPaymentRequested(TradeMessage requestTradeMessage)
/*public void onRequestTakerDepositPaymentMessage(TradeMessageOld requestTradeMessage)
{
log.debug("2.7 onTakerDepositPaymentRequested");
log.debug("2.7 onRequestTakerDepositPaymentMessage");
verifyOfferer(requestTradeMessage);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.8 Verify offerers account registration and against the blacklist
///////////////////////////////////////////////////////////////////////////////////////////
private void verifyOfferer(TradeMessage requestTradeMessage)
/*private void verifyOfferer(TradeMessageOld requestTradeMessage)
{
log.debug("2.8 verifyOfferer");
log.debug("2.8.1 verifyAccountRegistration");
if (blockChainFacade.verifyAccountRegistration())
{
log.debug("2.8.2 isAccountBlackListed");
if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountID(), requestTradeMessage.getBankAccount()))
if (blockChainFacade.isAccountBlackListed(requestTradeMessage.getAccountId(), requestTradeMessage.getBankAccount()))
{
takerPaymentProtocolListener.onFailure("Offerer is blacklisted.");
// listener.onFailure("Offerer is blacklisted.");
}
else
{
@ -308,35 +566,35 @@ public class TakerPaymentProtocol
}
else
{
takerPaymentProtocolListener.onFailure("Offerers account registration is invalid.");
}
// listener.onFailure("Offerers account registration is invalid.");
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.9 Create and sign the contract
///////////////////////////////////////////////////////////////////////////////////////////
private void createAndSignContract(TradeMessage requestTradeMessage)
/* private void createAndSignContract(TradeMessageOld requestTradeMessage)
{
log.debug("2.9 createAndSignContract");
checkNotNull(offer);
checkNotNull(trade.getOffer());
checkNotNull(trade.getTradeAmount());
checkNotNull(trade.getTakeOfferFeeTxID());
checkNotNull(requestTradeMessage.getAccountID());
checkNotNull(user.getAccountID());
checkNotNull(trade.getTakeOfferFeeTxId());
checkNotNull(requestTradeMessage.getAccountId());
checkNotNull(user.getAccountId());
checkNotNull(requestTradeMessage.getBankAccount());
checkNotNull(user.getCurrentBankAccount());
checkNotNull(user.getMessagePubKeyAsHex());
Contract contract = new Contract(offer,
Contract contract = new Contract(trade.getOffer(),
trade.getTradeAmount(),
trade.getTakeOfferFeeTxID(),
requestTradeMessage.getAccountID(),
user.getAccountID(),
trade.getTakeOfferFeeTxId(),
requestTradeMessage.getAccountId(),
user.getAccountId(),
requestTradeMessage.getBankAccount(),
user.getCurrentBankAccount(),
offer.getMessagePubKeyAsHex(),
trade.getOffer().getMessagePubKeyAsHex(),
user.getMessagePubKeyAsHex()
);
@ -352,14 +610,14 @@ public class TakerPaymentProtocol
trade.setContractTakerSignature(signature);
payDeposit(requestTradeMessage);
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.10 Pay in the funds to the deposit tx and sign it
///////////////////////////////////////////////////////////////////////////////////////////
private void payDeposit(TradeMessage requestTradeMessage)
/*private void payDeposit(TradeMessageOld requestTradeMessage)
{
log.debug("2.10 payDeposit");
@ -369,7 +627,7 @@ public class TakerPaymentProtocol
String offererPubKey = requestTradeMessage.getOffererPubKey();
String takerPubKey = walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString();
String arbitratorPubKey = offer.getArbitrator().getPubKeyAsHex();
String arbitratorPubKey = trade.getOffer().getArbitrator().getPubKeyAsHex();
String preparedOffererDepositTxAsHex = requestTradeMessage.getPreparedOffererDepositTxAsHex();
checkNotNull(takerInputAmount);
@ -380,8 +638,8 @@ public class TakerPaymentProtocol
checkNotNull(preparedOffererDepositTxAsHex);
log.debug("2.10 offererCreatesMSTxAndAddPayment");
log.debug("takerAmount " + BtcFormatter.satoshiToString(takerInputAmount));
log.debug("msOutputAmount " + BtcFormatter.satoshiToString(msOutputAmount));
log.debug("takerAmount " + BtcFormatter.formatSatoshis(takerInputAmount));
log.debug("msOutputAmount " + BtcFormatter.formatSatoshis(msOutputAmount));
log.debug("offerer pubkey " + offererPubKey);
log.debug("taker pubkey " + takerPubKey);
log.debug("arbitrator pubkey " + arbitratorPubKey);
@ -396,14 +654,14 @@ public class TakerPaymentProtocol
{
log.error("2.10 error at walletFacade.takerAddPaymentAndSign: " + e.getMessage());
}
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 2.11 Send the tx to the offerer
///////////////////////////////////////////////////////////////////////////////////////////
private void sendSignedTakerDepositTxAsHex(Transaction signedTakerDepositTx, long takerTxOutIndex, long offererTxOutIndex)
/* private void sendSignedTakerDepositTxAsHex(Transaction signedTakerDepositTx, long takerTxOutIndex, long offererTxOutIndex)
{
log.debug("2.11 sendSignedTakerDepositTxAsHex");
@ -414,21 +672,21 @@ public class TakerPaymentProtocol
{
log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onResult");
// Message arrived at taker
takerPaymentProtocolListener.onProgress(getProgress());
// //TakerAsSellerProtocol.this. listener.onFailure(getProgress());
}
@Override
public void onFailed()
{
log.debug("2.11 sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
takerPaymentProtocolListener.onFailure("sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
////TakerAsSellerProtocol.this. listener.onFailure("sendSignedTakerDepositTxAsHex REQUEST_TAKER_DEPOSIT_PAYMENT onFailed");
}
};
takerPaymentProtocolListener.onProgress(getProgress());
// this.listener.onProgress(getProgress());
BankAccount bankAccount = user.getCurrentBankAccount();
String accountID = user.getAccountID();
String accountID = user.getAccountId();
String messagePubKey = user.getMessagePubKeyAsHex();
String contractAsJson = trade.getContractAsJson();
String signature = trade.getTakerSignature();
@ -443,7 +701,7 @@ public class TakerPaymentProtocol
log.debug("2.10 txConnOutAsHex: " + txConnOutAsHex);
log.debug("2.10 payoutAddress: " + payoutAddress);
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.REQUEST_OFFERER_DEPOSIT_PUBLICATION,
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REQUEST_OFFERER_DEPOSIT_PUBLICATION,
trade.getId(),
bankAccount,
accountID,
@ -460,7 +718,7 @@ public class TakerPaymentProtocol
log.debug("2.11 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
} */
//************************************************************************************************
@ -472,14 +730,14 @@ public class TakerPaymentProtocol
// Step 3.6 Incoming msg from offerer
///////////////////////////////////////////////////////////////////////////////////////////
public void onDepositTxPublished(TradeMessage tradeMessage)
/* public void onDepositTxPublishedMessage(TradeMessageOld tradeMessage)
{
log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxAsHex());
String txID = walletFacade.takerCommitDepositTx(tradeMessage.getDepositTxAsHex());
takerPaymentProtocolListener.onProgress(getProgress());
takerPaymentProtocolListener.onDepositTxPublished(txID);
}
// listener.onProgress(getProgress());
listener.onDepositTxPublishedMessage(txID);
} */
//************************************************************************************************
@ -491,11 +749,11 @@ public class TakerPaymentProtocol
// Step 3.11 Incoming msg from offerer
///////////////////////////////////////////////////////////////////////////////////////////
public void onBankTransferInited(TradeMessage tradeMessage)
/* public void onBankTransferInitedMessage(BankTransferInitedMessage tradeMessage)
{
log.debug("3.11 Bank transfer inited msg received");
takerPaymentProtocolListener.onBankTransferInited(tradeMessage);
}
listener.onBankTransferInitedMessage(tradeMessage);
} */
//************************************************************************************************
// Taker will check periodically his bank account until he received the money. That might take a while...
@ -506,9 +764,9 @@ public class TakerPaymentProtocol
// Step 3.12 User clicked the "bank transfer received" button, so we release the funds for pay out
///////////////////////////////////////////////////////////////////////////////////////////
public void releaseBTC(TradeMessage tradeMessage)
/* public void onUIEventFiatReceived(TradeMessageOld tradeMessage)
{
log.debug("3.12 releaseBTC");
log.debug("3.12 onUIEventFiatReceived");
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
{
@Override
@ -516,7 +774,7 @@ public class TakerPaymentProtocol
{
System.out.println("######### 3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction);
log.debug("3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction);
takerPaymentProtocolListener.onTradeCompleted(transaction.getHashAsString());
listener.onTradeCompleted(transaction.getHashAsString());
sendPayoutTxToOfferer(Utils.bytesToHexString(transaction.bitcoinSerialize()));
}
@ -526,7 +784,7 @@ public class TakerPaymentProtocol
{
log.error("######### 3.12 onFailure walletFacade.takerSignsAndSendsTx");
System.err.println("3.12 onFailure walletFacade.takerSignsAndSendsTx");
takerPaymentProtocolListener.onFailure("takerSignsAndSendsTx failed " + t.getMessage());
// listener.onFailure("takerSignsAndSendsTx failed " + t.getMessage());
}
};
try
@ -551,13 +809,13 @@ public class TakerPaymentProtocol
{
log.error("3.12 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage());
}
}
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Step 3.13 Send payout txID to offerer
///////////////////////////////////////////////////////////////////////////////////////////
void sendPayoutTxToOfferer(String payoutTxAsHex)
/*void sendPayoutTxToOfferer(String payoutTxAsHex)
{
log.debug("3.13 sendPayoutTxToOfferer ");
TradeMessageListener listener = new TradeMessageListener()
@ -567,27 +825,28 @@ public class TakerPaymentProtocol
{
log.debug("3.13 sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onResult");
log.debug("3.13 TRADE COMPLETE!!!!!!!!!!!");
takerPaymentProtocolListener.onProgress(getProgress());
//TakerAsSellerProtocol.this. listener.onFailure(getProgress());
}
@Override
public void onFailed()
{
log.debug("3.13 sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed");
takerPaymentProtocolListener.onFailure("sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed");
//TakerAsSellerProtocol.this. listener.onFailure("sendPayoutTxToOfferer PAYOUT_TX_PUBLISHED onFailed");
}
};
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.PAYOUT_TX_PUBLISHED, trade.getId());
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.PAYOUT_TX_PUBLISHED, trade.getId());
tradeMessage.setPayoutTxAsHex(payoutTxAsHex);
log.debug("3.13 sendTradeMessage PAYOUT_TX_PUBLISHED");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
} */
private double getProgress()
/* private double getProgress()
{
currentStep++;
int numberOfSteps = 10;
return (double) currentStep / (double) numberOfSteps;
}
} */
}

View file

@ -0,0 +1,12 @@
package io.bitsquare.trade.payment.taker;
import io.bitsquare.trade.Trade;
public interface TakerAsSellerProtocolListener
{
void onDepositTxPublished(String depositTxId);
void onBankTransferInited(String tradeId);
void onTradeCompleted(Trade trade, String hashAsString);
}

View file

@ -1,16 +0,0 @@
package io.bitsquare.trade.payment.taker;
import io.bitsquare.msg.TradeMessage;
public interface TakerPaymentProtocolListener
{
void onProgress(double progress);
void onFailure(String failureMessage);
void onDepositTxPublished(String depositTxID);
void onBankTransferInited(TradeMessage tradeMessage);
void onTradeCompleted(String hashAsString);
}

View file

@ -1,8 +1,8 @@
package io.bitsquare.msg.listeners;
package io.bitsquare.trade.payment.taker.listeners;
import net.tomp2p.peers.PeerAddress;
public interface AddressLookupListener
public interface GetPeerAddressListener
{
void onResult(PeerAddress peerAddress);

View file

@ -0,0 +1,28 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class PayoutTxPublishedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 1288653559218403873L;
private final String tradeId;
private String payoutTxAsHex;
public PayoutTxPublishedMessage(String tradeId, String payoutTxAsHex)
{
this.tradeId = tradeId;
this.payoutTxAsHex = payoutTxAsHex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public String getPayoutTxAsHex()
{
return payoutTxAsHex;
}
}

View file

@ -0,0 +1,115 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 2179683654379803071L;
private final String tradeId;
private BankAccount bankAccount;
private String accountID;
private String takerMessagePubKey;
private String signedTakerDepositTxAsHex;
private String txScriptSigAsHex;
private String txConnOutAsHex;
private String contractAsJson;
private String takerContractSignature;
private String takerPayoutAddress;
private long takerTxOutIndex;
private long offererTxOutIndex;
public RequestOffererPublishDepositTxMessage(String tradeId,
BankAccount bankAccount,
String accountID,
String takerMessagePubKey,
String signedTakerDepositTxAsHex,
String txScriptSigAsHex,
String txConnOutAsHex,
String contractAsJson,
String takerContractSignature,
String takerPayoutAddress,
long takerTxOutIndex,
long offererTxOutIndex)
{
this.tradeId = tradeId;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.takerMessagePubKey = takerMessagePubKey;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txScriptSigAsHex = txScriptSigAsHex;
this.txConnOutAsHex = txConnOutAsHex;
this.contractAsJson = contractAsJson;
this.takerContractSignature = takerContractSignature;
this.takerPayoutAddress = takerPayoutAddress;
this.takerTxOutIndex = takerTxOutIndex;
this.offererTxOutIndex = offererTxOutIndex;
}
@Override
public String getTradeId()
{
return tradeId;
}
public long getOffererTxOutIndex()
{
return offererTxOutIndex;
}
public BankAccount getBankAccount()
{
return bankAccount;
}
public String getAccountId()
{
return accountID;
}
public String getTakerMessagePubKey()
{
return takerMessagePubKey;
}
public String getSignedTakerDepositTxAsHex()
{
return signedTakerDepositTxAsHex;
}
public String getTxScriptSigAsHex()
{
return txScriptSigAsHex;
}
public String getTxConnOutAsHex()
{
return txConnOutAsHex;
}
public String getContractAsJson()
{
return contractAsJson;
}
public String getTakerContractSignature()
{
return takerContractSignature;
}
public String getTakerPayoutAddress()
{
return takerPayoutAddress;
}
public long getTakerTxOutIndex()
{
return takerTxOutIndex;
}
}

View file

@ -0,0 +1,23 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
public class RequestTakeOfferMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = 4660151440192191798L;
private final String tradeId;
public RequestTakeOfferMessage(String tradeId)
{
this.tradeId = tradeId;
}
@Override
public String getTradeId()
{
return tradeId;
}
}

View file

@ -0,0 +1,45 @@
package io.bitsquare.trade.payment.taker.messages;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
import java.math.BigInteger;
public class TakeOfferFeePayedMessage implements Serializable, TradeMessage
{
private static final long serialVersionUID = -5057935061275354312L;
private final String tradeId;
private BigInteger tradeAmount;
private String takeOfferFeeTxID;
private String takerMultiSigPubKey;
public TakeOfferFeePayedMessage(String tradeId, String takeOfferFeeTxID, BigInteger tradeAmount, String takerMultiSigPubKey)
{
this.tradeId = tradeId;
this.takeOfferFeeTxID = takeOfferFeeTxID;
this.tradeAmount = tradeAmount;
this.takerMultiSigPubKey = takerMultiSigPubKey;
}
@Override
public String getTradeId()
{
return tradeId;
}
public BigInteger getTradeAmount()
{
return tradeAmount;
}
public String getTakeOfferFeeTxID()
{
return takeOfferFeeTxID;
}
public String getTakerMultiSigPubKey()
{
return takerMultiSigPubKey;
}
}

View file

@ -0,0 +1,29 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocol;
import io.nucleo.scheduler.tasks.AbstractTask;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractTakerAsSellerTask extends AbstractTask
{
private static final Logger log = LoggerFactory.getLogger(AbstractTakerAsSellerTask.class);
protected TakerAsSellerProtocol sharedModel;
public AbstractTakerAsSellerTask(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
addResultHandlers(resultHandler);
addFaultHandlers(faultHandler);
}
@Override
public void setModel(Object model)
{
sharedModel = (TakerAsSellerProtocol) model;
super.setModel(model);
}
}

View file

@ -0,0 +1,49 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.util.Utilities;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateAndSignContract extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(CreateAndSignContract.class);
public CreateAndSignContract(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
Trade trade = sharedModel.getTrade();
Contract contract = new Contract(trade.getOffer(),
trade.getTradeAmount(),
trade.getTakeOfferFeeTxId(),
sharedModel.getPeersAccountId(),
sharedModel.getUser().getAccountID(),
sharedModel.getPeersBankAccount(),
sharedModel.getUser().getCurrentBankAccount(),
trade.getOffer().getMessagePubKeyAsHex(),
sharedModel.getUser().getMessagePubKeyAsHex()
);
String contractAsJson = Utilities.objectToJson(contract);
String signature = sharedModel.getCryptoFacade().signContract(sharedModel.getWalletFacade().getRegistrationAddressInfo().getKey(), contractAsJson);
//log.trace("contract: " + contract);
//log.debug("contractAsJson: " + contractAsJson);
//log.trace("contract signature: " + signature);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setContractTakerSignature(signature);
complete();
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.trade.payment.taker.listeners.GetPeerAddressListener;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetPeerAddress extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public GetPeerAddress(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
sharedModel.getMessageFacade().getPeerAddress(sharedModel.getTrade().getOffer().getMessagePubKeyAsHex(), new GetPeerAddressListener()
{
@Override
public void onResult(PeerAddress address)
{
log.trace("Received address = " + address.toString());
sharedModel.setPeerAddress(address);
complete();
}
@Override
public void onFailed()
{
log.error("Lookup for peer address failed.");
failed(new Exception("Lookup for peer address failed."));
}
});
}
}

View file

@ -0,0 +1,47 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.InsufficientMoneyException;
import io.bitsquare.trade.Trade;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayDeposit extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(PayDeposit.class);
public PayDeposit(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
Trade trade = sharedModel.getTrade();
BigInteger collateralAmount = trade.getCollateralAmount();
sharedModel.setSignedTakerDepositTx(sharedModel.getWalletFacade().takerAddPaymentAndSignTx(trade.getTradeAmount().add(collateralAmount),
trade.getTradeAmount().add(collateralAmount).add(collateralAmount),
sharedModel.getOffererPubKey(),
sharedModel.getWalletFacade().getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString(),
trade.getOffer().getArbitrator().getPubKeyAsHex(),
sharedModel.getPreparedOffererDepositTxAsHex(),
trade.getId()));
log.trace("sharedModel.signedTakerDepositTx: " + sharedModel.getSignedTakerDepositTx());
sharedModel.setTakerTxOutIndex(sharedModel.getSignedTakerDepositTx().getInput(1).getOutpoint().getIndex());
complete();
} catch (InsufficientMoneyException e)
{
log.error("Pay deposit failed due InsufficientMoneyException " + e);
failed(new Exception("Pay deposit failed due InsufficientMoneyException " + e));
}
}
}

View file

@ -0,0 +1,50 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayTakeOfferFee extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(PayTakeOfferFee.class);
public PayTakeOfferFee(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
sharedModel.getWalletFacade().payTakeOfferFee(sharedModel.getTrade().getId(), new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.debug("Take offer fee paid successfully. Transaction ID = " + transaction.getHashAsString());
sharedModel.getTrade().setTakeOfferFeeTxID(transaction.getHashAsString());
complete();
}
@Override
public void onFailure(Throwable t)
{
log.error("Take offer fee paid failed with exception: " + t);
failed(new Exception("Take offer fee paid failed with exception: " + t));
}
});
} catch (InsufficientMoneyException e)
{
log.error("Take offer fee paid failed due InsufficientMoneyException " + e);
failed(new Exception("Take offer fee paid failed due InsufficientMoneyException " + e));
}
}
}

View file

@ -0,0 +1,43 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.RequestTakeOfferMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakeOffer extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(RequestTakeOffer.class);
public RequestTakeOffer(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
RequestTakeOfferMessage msg = new RequestTakeOfferMessage(sharedModel.getTrade().getId());
sharedModel.getMessageFacade().sendTradingMessage(sharedModel.getPeerAddress(), msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestTakeOfferMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("RequestTakeOfferMessage failed to arrive at peer");
failed(new Exception("RequestTakeOfferMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,42 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.PayoutTxPublishedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendPayoutTxToOfferer extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SendPayoutTxToOfferer.class);
public SendPayoutTxToOfferer(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(sharedModel.getTrade().getId(), sharedModel.getPayoutTxAsHex());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.getPeerAddress(), tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("PayoutTxPublishedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("PayoutTxPublishedMessage failed to arrive at peer");
failed(new Exception("PayoutTxPublishedMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,55 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.Utils;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.RequestOffererPublishDepositTxMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedTakerDepositTxAsHex extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTxAsHex.class);
public SendSignedTakerDepositTxAsHex(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(sharedModel.getTrade().getId(),
sharedModel.getUser().getCurrentBankAccount(),
sharedModel.getUser().getAccountID(),
sharedModel.getUser().getMessagePubKeyAsHex(),
Utils.bytesToHexString(sharedModel.getSignedTakerDepositTx().bitcoinSerialize()),
Utils.bytesToHexString(sharedModel.getSignedTakerDepositTx().getInput(1).getScriptBytes()),
Utils.bytesToHexString(sharedModel.getSignedTakerDepositTx().getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize()),
sharedModel.getTrade().getContractAsJson(),
sharedModel.getTrade().getTakerSignature(),
sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getAddressString(),
sharedModel.getTakerTxOutIndex(),
sharedModel.getOffererTxOutIndex()
);
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.getPeerAddress(), tradeMessage, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestOffererDepositPublicationMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("RequestOffererDepositPublicationMessage failed to arrive at peer");
failed(new Exception("RequestOffererDepositPublicationMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,46 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.bitsquare.msg.listeners.TradeMessageListener;
import io.bitsquare.trade.payment.taker.messages.TakeOfferFeePayedMessage;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendTakeOfferFeePayedTxId extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SendTakeOfferFeePayedTxId.class);
public SendTakeOfferFeePayedTxId(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
TakeOfferFeePayedMessage msg = new TakeOfferFeePayedMessage(sharedModel.getTrade().getId(),
sharedModel.getTrade().getTakeOfferFeeTxId(),
sharedModel.getTrade().getTradeAmount(),
sharedModel.getWalletFacade().getAddressInfoByTradeID(sharedModel.getTrade().getId()).getPubKeyAsHexString());
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.getPeerAddress(), msg, new TradeMessageListener()
{
@Override
public void onResult()
{
log.trace("TakeOfferFeePayedMessage successfully arrived at peer");
complete();
}
@Override
public void onFailed()
{
log.error("TakeOfferFeePayedMessage failed to arrive at peer");
failed(new Exception("TakeOfferFeePayedMessage failed to arrive at peer"));
}
});
}
}

View file

@ -0,0 +1,66 @@
package io.bitsquare.trade.payment.taker.tasks;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import com.google.common.util.concurrent.FutureCallback;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishPayoutTx extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(SignAndPublishPayoutTx.class);
public SignAndPublishPayoutTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
try
{
String depositTxAsHex = sharedModel.getDepositTxAsHex();
String offererSignatureR = sharedModel.getOffererSignatureR();
String offererSignatureS = sharedModel.getOffererSignatureS();
BigInteger offererPaybackAmount = sharedModel.getOffererPaybackAmount();
BigInteger takerPaybackAmount = sharedModel.getTakerPaybackAmount();
String offererPayoutAddress = sharedModel.getOffererPayoutAddress();
sharedModel.getWalletFacade().takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress,
sharedModel.getTrade().getId(),
new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.debug("takerSignsAndSendsTx " + transaction);
sharedModel.getListener().onTradeCompleted(sharedModel.getTrade(), transaction.getHashAsString());
sharedModel.setPayoutTxAsHex(Utils.bytesToHexString(transaction.bitcoinSerialize()));
complete();
}
@Override
public void onFailure(Throwable t)
{
log.error("Exception at takerSignsAndSendsTx " + t);
failed(t);
}
});
} catch (Exception e)
{
log.error("Exception at takerSignsAndSendsTx " + e);
failed(e);
}
}
}

View file

@ -0,0 +1,42 @@
package io.bitsquare.trade.payment.taker.tasks;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyOffererAccount extends AbstractTakerAsSellerTask
{
private static final Logger log = LoggerFactory.getLogger(VerifyOffererAccount.class);
public VerifyOffererAccount(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
super(resultHandler, faultHandler);
}
@Override
public void execute()
{
log.trace("execute");
//TODO mocked yet
if (sharedModel.getBlockChainFacade().verifyAccountRegistration())
{
if (sharedModel.getBlockChainFacade().isAccountBlackListed(sharedModel.getPeersAccountId(), sharedModel.getPeersBankAccount()))
{
log.error("Offerer is blacklisted");
failed(new Exception("Offerer is blacklisted"));
}
else
{
complete();
}
}
else
{
log.error("Account Registration for peer failed.");
failed(new Exception("Account Registration for peer failed."));
}
}
}

View file

@ -36,7 +36,7 @@ public class DSAKeyUtil
public static PublicKey getPublicKeyFromHexString(String publicKeyAsHex) throws NoSuchAlgorithmException, InvalidKeySpecException
{
final byte[] bytes = Utils.parseAsHexOrBase58(publicKeyAsHex);
final KeyFactory keyFactory = KeyFactory.INSTANCE("DSA");
final KeyFactory keyFactory = KeyFactory.GET_INSTANCE("DSA");
return keyFactory.generatePublic(new X509EncodedKeySpec(bytes));
} */
@ -60,7 +60,7 @@ public class DSAKeyUtil
} catch (Throwable throwable)
{
if (throwable instanceof FileNotFoundException)
log.debug("Files not found. That is ok for the first run.");
log.debug("Files not found. That is ok for the first execute.");
else
log.error("Could not read key files. " + throwable);

View file

@ -0,0 +1,37 @@
package io.nucleo.scheduler;
import com.sun.istack.internal.NotNull;
import io.nucleo.scheduler.worker.AbstractWorker;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractScheduler extends AbstractWorker implements WorkerResultHandler, WorkerFaultHandler
{
protected List<Worker> workerElements = new ArrayList<>();
public void setWorkers(@NotNull List<Worker> workerElements)
{
this.workerElements = workerElements;
}
protected void executeWorker(Worker worker)
{
((AbstractWorker) worker).setModel(model);
worker.addResultHandlers(this);
worker.addFaultHandlers(this);
worker.execute();
}
public void onResult()
{
}
public void onFault(Throwable throwable)
{
failed(throwable);
}
}

View file

@ -0,0 +1,47 @@
package io.nucleo.scheduler;
import io.nucleo.scheduler.tasks.AbstractDependencyManagedTask;
import io.nucleo.scheduler.worker.AbstractWorker;
import io.nucleo.scheduler.worker.Worker;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Not tested yet as not used...
*/
public class DependencyManagedScheduler extends AbstractScheduler
{
private static final Logger log = LoggerFactory.getLogger(DependencyManagedScheduler.class);
@Override
public void execute()
{
if (workerElements.size() > 0)
workerElements.stream().forEach(this::executeWorker);
else
complete();
}
@Override
protected void executeWorker(Worker worker)
{
((AbstractWorker) worker).setModel(model);
if (((AbstractDependencyManagedTask) worker).areAllDependenciesAvailable())
{
worker.addResultHandlers(this);
worker.addFaultHandlers(this);
worker.execute();
}
}
@Override
public void onResult(Worker worker)
{
Predicate<Worker> notCompleted = w -> !w.getHasCompleted();
if (workerElements.stream().filter(notCompleted).count() == 0)
complete();
else
execute();
}
}

View file

@ -0,0 +1,34 @@
package io.nucleo.scheduler;
import io.nucleo.scheduler.worker.Worker;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Not tested yet as not used...
*/
public class ParallelScheduler extends AbstractScheduler
{
private static final Logger log = LoggerFactory.getLogger(ParallelScheduler.class);
private long numberOfChildrenCompleted;
@Override
public void execute()
{
if (workerElements.size() > 0)
workerElements.stream().forEach(this::executeWorker);
else
complete();
}
@Override
public void onResult(Worker worker)
{
Predicate<Worker> notCompleted = w -> !w.getHasCompleted();
if (workerElements.stream().filter(notCompleted).count() == 0)
complete();
}
}

View file

@ -0,0 +1,44 @@
package io.nucleo.scheduler;
import com.sun.istack.internal.NotNull;
import io.nucleo.scheduler.worker.Worker;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
public class SequenceScheduler extends AbstractScheduler
{
private Iterator<Worker> workerIterator;
public SequenceScheduler(List<Worker> workerElements, Object model)
{
setWorkers(workerElements);
setModel(model);
}
public SequenceScheduler()
{
}
@Override
public void setWorkers(@NotNull List<Worker> workerElements)
{
workerIterator = new LinkedList<>(workerElements).iterator();
}
@Override
public void execute()
{
if (workerIterator != null && workerIterator.hasNext())
executeWorker(workerIterator.next());
else
complete();
}
@Override
public void onResult(Worker worker)
{
execute();
}
}

View file

@ -0,0 +1,15 @@
package io.nucleo.scheduler.example;
import io.nucleo.scheduler.model.PropertyProviderModel;
public class ExamplePropertyProviderModel extends PropertyProviderModel
{
public final Object flashVars;
public Object user;
public ExamplePropertyProviderModel(Object flashVars)
{
this.flashVars = flashVars;
}
}

View file

@ -0,0 +1,48 @@
package io.nucleo.scheduler.example;
import io.nucleo.scheduler.worker.Worker;
import io.nucleo.scheduler.worker.WorkerFaultHandler;
import io.nucleo.scheduler.worker.WorkerResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SchedulerTestRunner implements WorkerResultHandler, WorkerFaultHandler
{
private static final Logger log = LoggerFactory.getLogger(SchedulerTestRunner.class);
private static SchedulerTestRunner schedulerTestRunner;
public SchedulerTestRunner()
{
/* Map<Object, Object> flashVars = new HashMap<>();
flashVars.put("userName", "bully");
Object model = new ExamplePropertyProviderModel(flashVars);
ExampleAS3Scheduler exampleScheduler = new ExampleAS3Scheduler();
exampleScheduler.setModel(model);
exampleScheduler.setResultHandler(() -> {
log.debug("setResultHandler ");
});
exampleScheduler.setFaultHandler((throwable) -> {
log.debug("setFaultHandler ");
});
exampleScheduler.execute(); */
}
public static void main(String[] args)
{
schedulerTestRunner = new SchedulerTestRunner();
}
@Override
public void onFault(Throwable throwable)
{
log.debug("onFault " + this);
}
@Override
public void onResult(Worker worker)
{
log.debug("onResult " + this);
}
}

View file

@ -0,0 +1,54 @@
package io.nucleo.scheduler.model;
import java.lang.reflect.Field;
import java.util.List;
import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class PropertyProviderModel
{
private static final Logger log = LoggerFactory.getLogger(PropertyProviderModel.class);
public PropertyProviderModel()
{
}
public boolean areAllDependenciesAvailable(List<String> propertyNames)
{
Predicate<String> isPropertyNotNull = (propertyName) -> read(propertyName) != null;
return propertyNames.stream().allMatch(isPropertyNotNull);
}
public Object read(String key)
{
try
{
return getField(key).get(this);
} catch (IllegalAccessException e)
{
e.printStackTrace();
return null;
}
}
// -------------------------------------------------------------------
// Private
// -------------------------------------------------------------------
private Field getField(String key)
{
Class clazz = this.getClass();
try
{
Field field = clazz.getDeclaredField(key);
field.setAccessible(true); // make sure a private field is accessible for reflection
return field;
} catch (Exception e)
{
e.printStackTrace();
return null;
}
}
}

View file

@ -0,0 +1,68 @@
package io.nucleo.scheduler.tasks;
import io.nucleo.scheduler.model.PropertyProviderModel;
import java.util.ArrayList;
import java.util.List;
/**
* The base class for all tasks using a IPropertyProviderModel instance as a shared model.
*/
public abstract class AbstractDependencyManagedTask extends AbstractTask
{
private PropertyProviderModel propertyProviderModel;
private List<String> readDependencyKeys = new ArrayList<>();
public AbstractDependencyManagedTask()
{
}
// -------------------------------------------------------------------
// IRunnable implementation
// -------------------------------------------------------------------
@Override
public void setModel(Object model)
{
propertyProviderModel = (PropertyProviderModel) model;
super.setModel(model);
initReadDependencies();
}
// -------------------------------------------------------------------
// Abstract Methods
// -------------------------------------------------------------------
/**
* To be overwritten in subclasses
* Used to read the needed data objects for the task.
* Typically stored as instance variable in the task.
*/
protected void initReadDependencies()
{
// user = read(IUser);
// channel = read("channel");
}
// -------------------------------------------------------------------
// Final protected
// -------------------------------------------------------------------
final protected Object read(String key)
{
readDependencyKeys.add(key);
return propertyProviderModel.read(key);
}
public boolean areAllDependenciesAvailable()
{
return propertyProviderModel.areAllDependenciesAvailable(readDependencyKeys);
}
}

View file

@ -0,0 +1,13 @@
package io.nucleo.scheduler.tasks;
import io.nucleo.scheduler.worker.AbstractWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The base class for all tasks.
*/
public abstract class AbstractTask extends AbstractWorker
{
private static final Logger log = LoggerFactory.getLogger(AbstractTask.class);
}

View file

@ -0,0 +1,55 @@
package io.nucleo.scheduler.worker;
import java.util.ArrayList;
import java.util.List;
public abstract class AbstractWorker implements Worker
{
protected List<WorkerResultHandler> resultHandlers = new ArrayList<>();
protected List<WorkerFaultHandler> faultHandlers = new ArrayList<>();
protected Object model;
protected boolean hasFailed;
protected boolean hasCompleted;
@Override
abstract public void execute();
@Override
public void addResultHandlers(WorkerResultHandler resultHandler)
{
resultHandlers.add(resultHandler);
}
@Override
public void addFaultHandlers(WorkerFaultHandler faultHandler)
{
faultHandlers.add(faultHandler);
}
public void setModel(Object model)
{
this.model = model;
}
@Override
public boolean getHasCompleted()
{
return hasCompleted;
}
protected void complete()
{
hasCompleted = true;
resultHandlers.stream().forEach(e -> e.onResult(this));
}
protected void failed(Throwable throwable)
{
hasFailed = true;
faultHandlers.stream().forEach(e -> e.onFault(throwable));
}
protected void destroy()
{
}
}

View file

@ -0,0 +1,19 @@
package io.nucleo.scheduler.worker;
/**
* The base interface for all runnable objects (tasks, schedulers)
*/
public interface Worker
{
/**
* Starts the execution.
*/
void execute();
void addResultHandlers(WorkerResultHandler resultHandler);
void addFaultHandlers(WorkerFaultHandler faultHandler);
boolean getHasCompleted();
}

View file

@ -0,0 +1,6 @@
package io.nucleo.scheduler.worker;
public interface WorkerFaultHandler
{
void onFault(Throwable throwable);
}

View file

@ -0,0 +1,6 @@
package io.nucleo.scheduler.worker;
public interface WorkerResultHandler
{
void onResult(Worker worker);
}

View file

@ -1,9 +1,9 @@
package io.bitsquare;
import io.bitsquare.btc.BtcValidatorTest;
import io.bitsquare.currency.BitcoinTest;
import io.bitsquare.gui.util.BitSquareConverterTest;
import io.bitsquare.gui.util.BitSquareValidatorTest;
import io.nucleo.scheduler.SequenceSchedulerTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@ -12,7 +12,7 @@ import org.junit.runners.Suite;
BtcValidatorTest.class,
BitSquareConverterTest.class,
BitSquareValidatorTest.class,
BitcoinTest.class
SequenceSchedulerTest.class
})
public class BitSquareTestSuite

View file

@ -19,16 +19,16 @@ public class BtcValidatorTest
amount = BigInteger.ZERO;
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = FeePolicy.TX_FEE_depr;
amount = FeePolicy.TX_FEE;
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = Transaction.MIN_NONDUST_OUTPUT;
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = FeePolicy.TX_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT);
amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT);
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
amount = FeePolicy.TX_FEE_depr.add(Transaction.MIN_NONDUST_OUTPUT).add(BigInteger.ONE);
amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(BigInteger.ONE);
assertTrue("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
}
}

View file

@ -1,50 +0,0 @@
package io.bitsquare.currency;
import com.google.bitcoin.core.Transaction;
import java.math.BigInteger;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BitcoinTest
{
@Test
public void testBitcoin()
{
Bitcoin bitcoin;
Bitcoin compareValue;
bitcoin = new Bitcoin("0");
assertTrue(bitcoin.isZero());
bitcoin = new Bitcoin("0");
compareValue = new Bitcoin("1");
assertTrue(bitcoin.isLess(compareValue));
assertFalse(compareValue.isLess(bitcoin));
assertFalse(compareValue.isEqual(bitcoin));
bitcoin = new Bitcoin("1");
compareValue = new Bitcoin("0");
assertFalse(bitcoin.isLess(compareValue));
assertTrue(compareValue.isLess(bitcoin));
assertFalse(compareValue.isEqual(bitcoin));
bitcoin = new Bitcoin("1");
compareValue = new Bitcoin("1");
assertFalse(bitcoin.isLess(compareValue));
assertFalse(compareValue.isLess(bitcoin));
assertTrue(compareValue.isEqual(bitcoin));
bitcoin = new Bitcoin(Transaction.MIN_NONDUST_OUTPUT);
assertTrue(bitcoin.isMinValue());
bitcoin = new Bitcoin(Transaction.MIN_NONDUST_OUTPUT.subtract(BigInteger.ONE));
assertFalse(bitcoin.isMinValue());
bitcoin = new Bitcoin(Transaction.MIN_NONDUST_OUTPUT.add(BigInteger.ONE));
assertTrue(bitcoin.isMinValue());
}
}

View file

@ -1,5 +1,14 @@
package io.bitsquare.gui.util;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.locale.Country;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.user.Arbitrator;
import java.math.BigInteger;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@ -34,5 +43,37 @@ public class BitSquareValidatorTest
assertFalse(BitSquareValidator.validateStringNotEmpty(null));
}
@Test
public void testTradeAmountOutOfRange()
{
assertFalse(BitSquareValidator.tradeAmountOutOfRange(BigInteger.ZERO, new MockOffer(BigInteger.TEN, BigInteger.valueOf(0))));
assertTrue(BitSquareValidator.tradeAmountOutOfRange(BigInteger.ZERO, new MockOffer(BigInteger.TEN, BigInteger.valueOf(1))));
assertFalse(BitSquareValidator.tradeAmountOutOfRange(BigInteger.TEN, new MockOffer(BigInteger.TEN, BigInteger.valueOf(0))));
assertFalse(BitSquareValidator.tradeAmountOutOfRange(BigInteger.TEN, new MockOffer(BigInteger.TEN, BigInteger.valueOf(2))));
assertTrue(BitSquareValidator.tradeAmountOutOfRange(BigInteger.TEN, new MockOffer(BigInteger.ONE, BigInteger.valueOf(0))));
}
@Test
public void testGreaterThanZero()
{
assertFalse(BitSquareValidator.greaterThanZero(BigInteger.ZERO));
assertFalse(BitSquareValidator.greaterThanZero(BigInteger.valueOf(-1)));
assertTrue(BitSquareValidator.greaterThanZero(BigInteger.ONE));
}
public static class MockOffer extends Offer
{
public MockOffer(BigInteger amount, BigInteger minAmount)
{
super(null, null, 0, amount, minAmount, null, null, null, null, null, 0, null, null);
}
public MockOffer(String messagePubKeyAsHex, Direction direction, double price, BigInteger amount, BigInteger minAmount, BankAccountType bankAccountType, Currency currency, Country bankAccountCountry, String bankAccountUID, Arbitrator arbitrator, int collateral, List<Country> acceptedCountries, List<Locale> acceptedLanguageLocales)
{
super(messagePubKeyAsHex, direction, price, amount, minAmount, bankAccountType, currency, bankAccountCountry, bankAccountUID, arbitrator, collateral, acceptedCountries, acceptedLanguageLocales);
}
}
}

View file

@ -0,0 +1,228 @@
package io.nucleo.scheduler;
import io.nucleo.scheduler.tasks.SyncWorker1;
import io.nucleo.scheduler.tasks.SyncWorker2;
import io.nucleo.scheduler.worker.Worker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
public class SequenceSchedulerTest
{
private final boolean[] hasCompleted = new boolean[1];
private final boolean[] hasFailed = new boolean[1];
private final boolean[] worker1HasCompleted = new boolean[1];
private final boolean[] worker1HasFailed = new boolean[1];
private final boolean[] worker2HasCompleted = new boolean[1];
private final boolean[] worker2HasFailed = new boolean[1];
private SequenceScheduler sequenceScheduler;
private Map<String, String> model = new HashMap<>();
private List<Worker> workerList = new ArrayList<>();
private Throwable worker1Throwable;
@Before
public void setUp()
{
sequenceScheduler = new SequenceScheduler();
sequenceScheduler.addResultHandlers((worker) -> {
hasCompleted[0] = true;
});
sequenceScheduler.addFaultHandlers(throwable -> {
hasFailed[0] = true;
});
}
@After
public void tearDown() throws Exception
{
hasCompleted[0] = false;
hasFailed[0] = false;
workerList.clear();
model.clear();
worker1Throwable = null;
}
@Test
public void testEmpty()
{
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
}
@Test
public void testEmpty2()
{
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
}
@Test
public void testOneWithCompleted()
{
Worker worker1 = getWorker1(false);
workerList.add(worker1);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
}
@Test
public void testOneWithFailed()
{
Worker worker1 = getWorker1(true);
workerList.add(worker1);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertFalse(sequenceScheduler.getHasCompleted());
assertFalse(hasCompleted[0]);
assertTrue(hasFailed[0]);
assertFalse(worker1.getHasCompleted());
assertFalse(worker1HasCompleted[0]);
assertTrue(worker1HasFailed[0]);
assertEquals(SyncWorker1.ERR_MSG, worker1Throwable.getMessage());
}
// @Test
public void testTwoCompleted()
{
Worker worker1 = getWorker1(false);
Worker worker2 = getWorker2(false);
workerList.add(worker1);
workerList.add(worker2);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
assertTrue(worker2.getHasCompleted());
assertTrue(worker2HasCompleted[0]);
assertFalse(worker2HasFailed[0]);
}
@Test
public void testTwoReverseOrder()
{
model.put("worker1State", "");
model.put("worker2State", "");
Worker worker1 = getWorker1(false);
Worker worker2 = getWorker2(false);
workerList.add(worker2);
workerList.add(worker1);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.setModel(model);
sequenceScheduler.execute();
assertEquals(SyncWorker1.STATE, model.get("worker1State"));
assertEquals(SyncWorker2.STATE, model.get("worker2State"));
assertTrue(sequenceScheduler.getHasCompleted());
assertTrue(hasCompleted[0]);
assertFalse(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
assertTrue(worker2.getHasCompleted());
assertTrue(worker2HasCompleted[0]);
assertFalse(worker2HasFailed[0]);
}
@Test
public void testTwoFirstFailed()
{
Worker worker1 = getWorker1(true);
Worker worker2 = getWorker2(false);
workerList.add(worker1);
workerList.add(worker2);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertFalse(sequenceScheduler.getHasCompleted());
assertFalse(hasCompleted[0]);
assertTrue(hasFailed[0]);
assertFalse(worker1.getHasCompleted());
assertFalse(worker1HasCompleted[0]);
assertTrue(worker1HasFailed[0]);
assertFalse(worker2.getHasCompleted());
assertFalse(worker2HasCompleted[0]);
assertFalse(worker2HasFailed[0]); // second has not been executed and is not failed!
}
@Test
public void testTwoSecondFailed()
{
Worker worker1 = getWorker1(false);
Worker worker2 = getWorker2(true);
workerList.add(worker1);
workerList.add(worker2);
sequenceScheduler.setWorkers(workerList);
sequenceScheduler.execute();
assertFalse(sequenceScheduler.getHasCompleted());
assertFalse(hasCompleted[0]);
assertTrue(hasFailed[0]);
assertTrue(worker1.getHasCompleted());
assertTrue(worker1HasCompleted[0]);
assertFalse(worker1HasFailed[0]);
assertFalse(worker2.getHasCompleted());
assertFalse(worker2HasCompleted[0]);
assertTrue(worker2HasFailed[0]); // second has not been executed and is not failed!
}
private Worker getWorker1(boolean letItFail)
{
Worker worker1 = new SyncWorker1(letItFail);
worker1.addResultHandlers((worker) -> {
worker1HasCompleted[0] = true;
});
worker1.addFaultHandlers(throwable -> {
worker1HasFailed[0] = true;
worker1Throwable = throwable;
});
return worker1;
}
private Worker getWorker2(boolean letItFail)
{
Worker worker2 = new SyncWorker2(letItFail);
worker2.addResultHandlers((worker) -> {
worker2HasCompleted[0] = true;
});
worker2.addFaultHandlers(throwable -> {
worker2HasFailed[0] = true;
});
return worker2;
}
}

View file

@ -0,0 +1,29 @@
package io.nucleo.scheduler.tasks;
import java.util.Map;
public class SyncWorker1 extends AbstractTask
{
public static String ERR_MSG = "Failure message";
public static String STATE = "ok";
private boolean letItFail;
public SyncWorker1(boolean letItFail)
{
this.letItFail = letItFail;
}
@Override
public void execute()
{
System.out.println("execute " + this);
if (model != null) ((Map<String, String>) model).put("worker1State", STATE);
if (letItFail)
failed(new Exception(ERR_MSG));
else
complete();
}
}

View file

@ -0,0 +1,27 @@
package io.nucleo.scheduler.tasks;
import java.util.Map;
public class SyncWorker2 extends AbstractTask
{
public static String ERR_MSG = "Failure message";
public static String STATE = "ok";
private boolean letItFail;
public SyncWorker2(boolean letItFail)
{
this.letItFail = letItFail;
}
@Override
public void execute()
{
System.out.println("execute " + this);
if (model != null) ((Map<String, String>) model).put("worker2State", STATE);
if (letItFail)
failed(new Exception(ERR_MSG));
else
complete();
}
}