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>
@ -196,6 +196,19 @@
<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>
<reporting>

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,13 +834,13 @@ public class WalletFacade
// 3. step: deposit tx
// Offerer signs tx and publishes it
public Transaction offererSignAndPublishTx(String offerersFirstTxAsHex,
String takersSignedTxAsHex,
String takersSignedConnOutAsHex,
String takersSignedScriptSigAsHex,
long offererTxOutIndex,
long takerTxOutIndex,
FutureCallback<Transaction> callback)
public void offererSignAndPublishTx(String offerersFirstTxAsHex,
String takersSignedTxAsHex,
String takersSignedConnOutAsHex,
String takersSignedScriptSigAsHex,
long offererTxOutIndex,
long takerTxOutIndex,
FutureCallback<Transaction> callback)
{
log.debug("offererSignAndPublishTx");
log.trace("inputs: ");
@ -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,24 +1005,22 @@ public class WalletFacade
}
// 6. step payout tx: Taker signs and publish tx
@SuppressWarnings("UnusedReturnValue")
public Transaction takerSignsAndSendsTx(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererAddress,
String tradeID,
FutureCallback<Transaction> callback) throws AddressFormatException
public void takerSignsAndSendsTx(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererAddress,
String tradeID,
FutureCallback<Transaction> callback) throws AddressFormatException
{
log.debug("takerSignsAndSendsTx");
log.trace("inputs: ");
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());
Object indexObject = storage.read(storageId);
if (indexObject != null)
selectionModel.select((int) indexObject);
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,39 +202,38 @@ public class CreateOfferController implements Initializable, ChildController, Hi
settings.getAcceptedLanguageLocales());
FutureCallback<Transaction> callback = new FutureCallback<Transaction>()
{
@Override
public void onSuccess(@javax.annotation.Nullable Transaction transaction)
{
log.info("sendResult onSuccess:" + transaction);
if (transaction != null)
{
offer.setOfferFeePaymentTxID(transaction.getHashAsString());
setupSuccessScreen(transaction);
placeOfferTitle.setText("Transaction sent:");
try
{
trading.addOffer(offer);
} catch (IOException e)
{
Popups.openErrorPopup("Error on adding offer", "Could not add offer to orderbook. " + e.getMessage());
}
}
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t);
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t);
placeOfferButton.setDisable(false);
}
};
try
{
walletFacade.payCreateOfferFee(offer.getId(), callback);
walletFacade.payCreateOfferFee(offer.getId(), new FutureCallback<Transaction>()
{
@Override
public void onSuccess(@javax.annotation.Nullable Transaction transaction)
{
log.info("sendResult onSuccess:" + transaction);
if (transaction != null)
{
offer.setOfferFeePaymentTxID(transaction.getHashAsString());
setupSuccessScreen(transaction);
placeOfferTitle.setText("Transaction sent:");
try
{
trading.addOffer(offer);
} catch (IOException e)
{
Popups.openErrorPopup("Error on adding offer", "Could not add offer to orderbook. " + e.getMessage());
}
}
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t);
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t);
placeOfferButton.setDisable(false);
}
});
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,35 +93,35 @@ 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)
{
amountTextField.setText(requestedAmount.getFormattedValue());
amountTextField.setPromptText(new Bitcoin(offer.getMinAmount()).getFormattedValue() + " - " + new Bitcoin(offer.getAmount()).getFormattedValue());
priceTextField.setText(new Fiat(offer.getPrice()).getFormattedValue());
public void applyData()
{
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(BtcFormatter.formatSatoshis(getFee()));
totalTextField.setText(getFormattedTotal());
bankAccountTypeTextField.setText(offer.getBankAccountType().toString());
countryTextField.setText(offer.getBankAccountCountry().getName());
//todo list
// arbitratorsTextField.setText(offer.getArbitrator().getName());
supportedLanguagesTextField.setText(BitSquareFormatter.languageLocalesToString(offer.getAcceptedLanguageLocales()));
supportedCountriesTextField.setText(BitSquareFormatter.countryLocalesToString(offer.getAcceptedCountries()));
amountTextField.textProperty().addListener(e -> {
applyVolume();
applyCollateral();
applyTotal();
feeTextField.setText(getFee().getFormattedValue());
totalTextField.setText(getFormattedTotal());
bankAccountTypeTextField.setText(offer.getBankAccountType().toString());
countryTextField.setText(offer.getBankAccountCountry().getName());
//todo list
// arbitratorsTextField.setText(offer.getArbitrator().getName());
supportedLanguagesTextField.setText(BitSquareFormatter.languageLocalesToString(offer.getAcceptedLanguageLocales()));
supportedCountriesTextField.setText(BitSquareFormatter.countryLocalesToString(offer.getAcceptedCountries()));
amountTextField.textProperty().addListener(e -> {
applyVolume();
applyCollateral();
applyTotal();
});
}
});
}
@ -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,48 +225,48 @@ public class TakerTradeController implements Initializable, ChildController
return;
}
if (trading.isOfferTradable(offer))
{
trade = trading.createTrade(offer);
trade.setTradeAmount(BtcFormatter.stringValueToSatoshis(amountTextField.getText()));
//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;
} */
amountTextField.setEditable(false);
amountTextField.setEditable(false);
gridPane.getChildren().clear();
gridPane.getChildren().clear();
row = -1;
FormBuilder.addHeaderLabel(gridPane, "Trade request inited", ++row, 0);
row = -1;
FormBuilder.addHeaderLabel(gridPane, "Trade request inited", ++row, 0);
Label statusTextField = FormBuilder.addLabel(gridPane, "Current activity:", "Request confirmation from offerer to take that offer.", ++row);
GridPane.setColumnSpan(statusTextField, 2);
FormBuilder.addLabel(gridPane, "Progress:", "", ++row);
progressBar = new ProgressBar();
progressBar.setProgress(0.0);
progressBar.setPrefWidth(300);
GridPane.setFillWidth(progressBar, true);
gridPane.add(progressBar, 1, row);
Label statusTextField = FormBuilder.addLabel(gridPane, "Current activity:", "Request confirmation from offerer to take that offer.", ++row);
GridPane.setColumnSpan(statusTextField, 2);
FormBuilder.addLabel(gridPane, "Progress:", "", ++row);
progressBar = new ProgressBar();
progressBar.setProgress(0.0);
progressBar.setPrefWidth(300);
GridPane.setFillWidth(progressBar, true);
gridPane.add(progressBar, 1, row);
FormBuilder.addLabel(gridPane, "Status:", "", ++row);
ConfidenceProgressIndicator progressIndicator = new ConfidenceProgressIndicator();
progressIndicator.setPrefSize(20, 20);
progressIndicator.setLayoutY(2);
Pane progressIndicatorHolder = new Pane();
progressIndicatorHolder.getChildren().addAll(progressIndicator);
gridPane.add(progressIndicatorHolder, 1, row);
FormBuilder.addLabel(gridPane, "Status:", "", ++row);
ConfidenceProgressIndicator progressIndicator = new ConfidenceProgressIndicator();
progressIndicator.setPrefSize(20, 20);
progressIndicator.setLayoutY(2);
Pane progressIndicatorHolder = new Pane();
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,20 +164,15 @@ 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)
{
if (openTradesTable.getSelectionModel().getSelectedItem() == null && tradeItems.size() > 0)
{
openTradesTable.getSelectionModel().select(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

@ -15,21 +15,11 @@ public class BitSquareConverter
{
try
{
return stringToDouble2(input);
input = input.replace(",", ".");
return Double.parseDouble(input);
} catch (NumberFormatException | NullPointerException e)
{
return Double.NEGATIVE_INFINITY;
return 0;
}
}
/**
* @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);
}
}

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,21 +139,12 @@ 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)
{
final Map<String, Serializable> map = readRootMap();
if (map != null)
{
rootMap = map;
dirty = false;
}
}
if (rootMap.containsKey(key))
{
// log.trace("Read object with key = " + key + " / value = " + rootMap.get(key));
@ -163,8 +152,21 @@ public class Storage
}
else
{
log.warn("Object with key = " + key + " not found.");
return null;
final Map<String, Serializable> map = readRootMap();
if (map != null)
{
rootMap = map;
}
if (rootMap.containsKey(key))
{
// log.trace("Read object with key = " + key + " / value = " + rootMap.get(key));
return rootMap.get(key);
}
else
{
log.info("Object with key = " + key + " not found.");
return null;
}
}
} finally
{
@ -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)
public Trade takeOffer(BigInteger amount, Offer offer, TakerAsSellerProtocolListener listener, WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
TakerPaymentProtocol takerPaymentProtocol = new TakerPaymentProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
Trade trade = createTrade(offer);
trade.setTradeAmount(amount);
TakerAsSellerProtocol takerPaymentProtocol = new TakerAsSellerProtocol(trade, listener, resultHandler, faultHandler, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
takerPaymentProtocols.put(trade.getId(), takerPaymentProtocol);
return takerPaymentProtocol;
return trade;
}
OffererPaymentProtocol addOffererPaymentProtocol(Trade trade, OffererPaymentProtocolListener listener)
public void createOffererAsBuyerProtocol(String offerId, PeerAddress sender)
{
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))
log.trace("createOffererAsBuyerProtocol offerId = " + offerId);
Offer offer = offers.get(offerId);
if (offer != null && offers.containsKey(offer.getId()))
{
Trade trade = createTrade(offer);
OffererPaymentProtocol offererPaymentProtocol = addOffererPaymentProtocol(trade, new OffererPaymentProtocolListener()
offers.remove(offer);
currentPendingTrade = createTrade(offer);
OffererAsBuyerProtocolListener listener = new OffererAsBuyerProtocolListener()
{
@Override
public void onProgress(double progress)
{
//log.debug("onProgress " + progress);
}
@Override
public void onFailure(String failureMessage)
{
log.warn(failureMessage);
}
@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,91 +1,350 @@
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,
MessageFacade messageFacade,
WalletFacade walletFacade,
BlockChainFacade blockChainFacade,
CryptoFacade cryptoFacade,
User user)
public TakerAsSellerProtocol(Trade trade,
TakerAsSellerProtocolListener listener,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
MessageFacade messageFacade,
WalletFacade walletFacade,
BlockChainFacade blockChainFacade,
CryptoFacade cryptoFacade,
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();
}
}