refactoring trade protocol

This commit is contained in:
Manfred Karrer 2014-07-04 18:16:51 +02:00
parent 7e55b7325a
commit 5da272bdbf
96 changed files with 2074 additions and 2702 deletions

View File

@ -166,6 +166,7 @@ public class MainController implements Initializable, NavigationController
return childController;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
@ -173,7 +174,8 @@ public class MainController implements Initializable, NavigationController
private void init()
{
messageFacade.init();
messageFacade.addTakeOfferRequestListener(this::onTakeOfferRequested);
trading.addTakeOfferRequestListener(this::onTakeOfferRequested);
walletFacade.addDownloadListener(new WalletFacade.DownloadListener()
{

View File

@ -194,7 +194,7 @@ public class OrderBookController implements Initializable, ChildController
private boolean isRegistered()
{
return user.getAccountID() != null;
return user.getAccountId() != null;
}
private boolean areSettingsValid()

View File

@ -17,7 +17,8 @@ import io.bitsquare.msg.MessageFacade;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.Trading;
import io.bitsquare.trade.payment.taker.TakerAsSellerProtocolListener;
import io.bitsquare.trade.protocol.taker.TakerAsSellerProtocol;
import io.bitsquare.trade.protocol.taker.TakerAsSellerProtocolListener;
import java.math.BigInteger;
import java.net.URL;
import java.util.ResourceBundle;
@ -173,8 +174,6 @@ public class TakerOfferController implements Initializable, ChildController
amountTextField.setEditable(false);
trading.takeOffer(amount, offer, new TakerAsSellerProtocolListener()
{
@Override
public void onDepositTxPublished(String depositTxId)
{
@ -197,7 +196,7 @@ public class TakerOfferController implements Initializable, ChildController
}
@Override
public void onTradeCompleted(Trade trade, String payoutTxId)
public void onPayoutTxPublished(Trade trade, String payoutTxId)
{
accordion.setExpandedPane(summaryTitledPane);
summaryPaidTextField.setText(BtcFormatter.formatSatoshis(trade.getTradeAmount()));
@ -207,10 +206,32 @@ public class TakerOfferController implements Initializable, ChildController
summaryDepositTxIdTextField.setText(depositTxId);
summaryPayoutTxIdTextField.setText(payoutTxId);
}
}, (task) -> {
//log.trace(task.toString());
}, throwable -> {
log.error(throwable.toString());
@Override
public void onFault(Throwable throwable, TakerAsSellerProtocol.State state)
{
log.error("Error while executing trade process at state: " + state + " / " + throwable);
Popups.openErrorPopup("Error while executing trade process", "Error while executing trade process at state: " + state + " / " + throwable);
}
@Override
public void onWaitingForPeerResponse(TakerAsSellerProtocol.State state)
{
log.debug("Waiting for peers response at state " + state);
}
@Override
public void onCompleted(TakerAsSellerProtocol.State state)
{
log.debug("Trade protocol completed at state " + state);
}
@Override
public void onTakeOfferRequestRejected(Trade trade)
{
log.error("Take offer request rejected");
Popups.openErrorPopup("Take offer request rejected", "Your take offer request has been rejected. It might be that the offerer got another request shortly before your request arrived.");
}
}
);
}
@ -220,7 +241,7 @@ public class TakerOfferController implements Initializable, ChildController
@FXML
public void onReceivedFiat()
{
trading.releaseBTC(tradeId);
trading.onFiatReceived(tradeId);
}
@FXML

View File

@ -172,7 +172,7 @@ public class TakerTradeController implements Initializable, ChildController
gridPane.add(isOnlineCheckerHolder, 2, row);
//TODO
messageFacade.pingPeer(offer.getMessagePubKeyAsHex());
// messageFacade.pingPeer(offer.getMessagePubKeyAsHex());
checkOnlineStatusTimer = Utilities.setTimeout(1000, (AnimationTimer animationTimer) -> {
setIsOnlineStatus(true);
//noinspection ReturnOfNull
@ -316,7 +316,7 @@ public class TakerTradeController implements Initializable, ChildController
}
@Override
public void onTradeCompleted(String hashAsString)
public void onPayoutTxPublished(String hashAsString)
{
showSummary(hashAsString);
}
@ -359,7 +359,7 @@ public class TakerTradeController implements Initializable, ChildController
private void releaseBTC()
{
processStepBar.next();
trading.releaseBTC(trade.getId());
trading.onFiatReceived(trade.getId());
nextButton.setText("Close");
nextButton.setOnAction(e -> close());

View File

@ -7,7 +7,6 @@ import io.bitsquare.gui.NavigationController;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trading;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@ -110,13 +109,7 @@ public class OfferController implements Initializable, ChildController, Hibernat
private void removeOffer(OfferListItem offerListItem)
{
try
{
trading.removeOffer(offerListItem.getOffer());
} catch (IOException e)
{
e.printStackTrace();
}
trading.removeOffer(offerListItem.getOffer());
offerListItems.remove(offerListItem);
}

View File

@ -152,20 +152,16 @@ public class PendingTradeController implements Initializable, ChildController, H
}
});
trading.getNewTradeProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observableValue, String oldTradeUid, String newTradeUid)
{
Trade newTrade = trading.getTrades().get(newTradeUid);
trading.getNewTradeProperty().addListener((observableValue, oldTradeId, newTradeId) -> {
Trade newTrade = trading.getTrade(newTradeId);
if (newTrade != null)
tradeItems.add(new PendingTradesListItem(newTrade));
}
});
initCopyIcons();
// select
Optional<PendingTradesListItem> currentTradeItemOptional = tradeItems.stream().filter((e) -> e.getTrade().getId().equals(trading.getCurrentPendingTrade().getId())).findFirst();
Optional<PendingTradesListItem> currentTradeItemOptional = tradeItems.stream().filter((e) -> e.getTrade().getId().equals(trading.getPendingTrade().getId())).findFirst();
if (currentTradeItemOptional.isPresent())
openTradesTable.getSelectionModel().select(currentTradeItemOptional.get());
@ -183,7 +179,7 @@ public class PendingTradeController implements Initializable, ChildController, H
public void bankTransferInited()
{
trading.onUIEventBankTransferInited(currentTrade.getId());
trading.bankTransferInited(currentTrade.getId());
bankTransferInitedButton.setDisable(true);
}

View File

@ -4,14 +4,6 @@ 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.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;
@ -29,7 +21,6 @@ import net.tomp2p.p2p.Peer;
import net.tomp2p.p2p.PeerMaker;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.rpc.ObjectDataReply;
import net.tomp2p.storage.Data;
import net.tomp2p.storage.StorageDisk;
import net.tomp2p.utils.Utils;
@ -44,19 +35,18 @@ import org.slf4j.LoggerFactory;
@SuppressWarnings({"EmptyMethod", "ConstantConditions"})
public class MessageFacade
{
private static final String PING = "ping";
private static final String PONG = "pong";
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
// private static final String PING = "ping";
// private static final String PONG = "pong";
private static final int MASTER_PEER_PORT = 5000;
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
private final List<TakeOfferRequestListener> takeOfferRequestListeners = new ArrayList<>();
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
private final Map<String, TakerAsSellerProtocol> takerPaymentProtocols = new HashMap<>();
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocols = new HashMap<>();
private final List<IncomingTradeMessageListener> incomingTradeMessageListeners = new ArrayList<>();
private final List<PingPeerListener> pingPeerListeners = new ArrayList<>();
// private final List<PingPeerListener> pingPeerListeners = new ArrayList<>();
private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
private Peer myPeer;
@ -71,17 +61,9 @@ public class MessageFacade
@Inject
public MessageFacade()
{
/* try
{
masterPeer = BootstrapMasterPeer.GET_INSTANCE(MASTER_PEER_PORT);
} catch (Exception e)
{
if (masterPeer != null)
masterPeer.shutdown();
System.err.println("masterPeer already instantiated by another app. " + e.getMessage());
} */
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
@ -115,6 +97,146 @@ public class MessageFacade
}
///////////////////////////////////////////////////////////////////////////////////////////
// Find peer address
///////////////////////////////////////////////////////////////////////////////////////////
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
{
final Number160 location = Number160.createHash(pubKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
{
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
Platform.runLater(() -> listener.onResult(peerAddress));
}
else
{
Platform.runLater(() -> listener.onFailed());
}
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Publish offer
///////////////////////////////////////////////////////////////////////////////////////////
public void addOffer(Offer offer) throws IOException
{
log.trace("addOffer");
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
final Number160 contentKey = Number160.createHash(offer.getId());
final Data offerData = new Data(offer);
//offerData.setTTLSeconds(5);
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, offerData).start();
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> onOfferAdded(offerData, future.isSuccess(), locationKey));
}
});
}
private void onOfferAdded(Data offerData, boolean success, Number160 locationKey)
{
log.trace("onOfferAdded");
setDirty(locationKey);
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(offerData, success));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Get offers
///////////////////////////////////////////////////////////////////////////////////////////
public void getOffers(String currency)
{
log.trace("getOffers");
final Number160 locationKey = Number160.createHash(currency);
final FutureDHT getOffersFuture = myPeer.get(locationKey).setAll().start();
getOffersFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
final Map<Number160, Data> dataMap = getOffersFuture.getDataMap();
Platform.runLater(() -> onOffersReceived(dataMap, future.isSuccess()));
}
});
}
private void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
{
log.trace("onOffersReceived");
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(dataMap, success));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Remove offer
///////////////////////////////////////////////////////////////////////////////////////////
public void removeOffer(Offer offer)
{
log.trace("removeOffer");
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
Number160 contentKey = Number160.createHash(offer.getId());
log.debug("removeOffer");
FutureDHT removeFuture = myPeer.remove(locationKey).setReturnResults().setContentKey(contentKey).start();
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> onOfferRemoved(removeFuture.getData(), future.isSuccess(), locationKey));
}
});
}
private void onOfferRemoved(Data data, boolean success, Number160 locationKey)
{
log.trace("onOfferRemoved");
setDirty(locationKey);
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferRemoved(data, success));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trade process
///////////////////////////////////////////////////////////////////////////////////////////
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, OutgoingTradeMessageListener 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());
}
}
);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Reputation
///////////////////////////////////////////////////////////////////////////////////////////
public void setupReputationRoot() throws IOException
{
String pubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(getPubKey()); // out message ID
@ -149,6 +271,7 @@ public class MessageFacade
myPeer.put(locationKey).setData(contentKey, reputationData).start();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Arbitrators
///////////////////////////////////////////////////////////////////////////////////////////
@ -199,100 +322,11 @@ public class MessageFacade
arbitratorListener.onArbitratorsReceived(dataMap, success);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Publish offer
///////////////////////////////////////////////////////////////////////////////////////////
//TODO use Offer and do proper serialisation here
public void addOffer(Offer offer) throws IOException
{
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
final Number160 contentKey = Number160.createHash(offer.getId());
final Data offerData = new Data(offer);
//offerData.setTTLSeconds(5);
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, offerData).start();
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> onOfferAdded(offerData, future.isSuccess(), locationKey));
}
});
}
private void onOfferAdded(Data offerData, boolean success, Number160 locationKey)
{
setDirty(locationKey);
for (OrderBookListener orderBookListener : orderBookListeners)
orderBookListener.onOfferAdded(offerData, success);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Get offers
///////////////////////////////////////////////////////////////////////////////////////////
public void getOffers(String currency)
{
final Number160 locationKey = Number160.createHash(currency);
final FutureDHT getOffersFuture = myPeer.get(locationKey).setAll().start();
getOffersFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
final Map<Number160, Data> dataMap = getOffersFuture.getDataMap();
Platform.runLater(() -> onOffersReceived(dataMap, future.isSuccess()));
}
});
}
private void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
{
for (OrderBookListener orderBookListener : orderBookListeners)
orderBookListener.onOffersReceived(dataMap, success);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Remove offer
///////////////////////////////////////////////////////////////////////////////////////////
public void removeOffer(Offer offer)
{
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
Number160 contentKey = Number160.createHash(offer.getId());
log.debug("removeOffer");
FutureDHT removeFuture = myPeer.remove(locationKey).setReturnResults().setContentKey(contentKey).start();
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Data data = removeFuture.getData();
Platform.runLater(() -> onOfferRemoved(data, future.isSuccess(), locationKey));
}
});
}
private void onOfferRemoved(Data data, boolean success, Number160 locationKey)
{
log.debug("onOfferRemoved");
setDirty(locationKey);
for (OrderBookListener orderBookListener : orderBookListeners)
orderBookListener.onOfferRemoved(data, success);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Check dirty flag for a location key
///////////////////////////////////////////////////////////////////////////////////////////
// TODO just temp...
public BooleanProperty getIsDirtyProperty()
{
return isDirty;
@ -312,11 +346,7 @@ public class MessageFacade
{
Object object = data.getObject();
if (object instanceof Long)
{
final long lastTimeStamp = (Long) object;
//System.out.println("getDirtyFlag " + lastTimeStamp);
Platform.runLater(() -> onGetDirtyFlag(lastTimeStamp));
}
Platform.runLater(() -> onGetDirtyFlag((Long) object));
}
}
@ -426,149 +456,12 @@ public class MessageFacade
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Find peer address
///////////////////////////////////////////////////////////////////////////////////////////
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
{
final Number160 location = Number160.createHash(pubKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
{
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
Platform.runLater(() -> listener.onResult(peerAddress));
}
else
{
Platform.runLater(() -> listener.onFailed());
}
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trade process
///////////////////////////////////////////////////////////////////////////////////////////
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();
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (sendFuture.isSuccess())
{
Platform.runLater(() -> onSendTradingMessageResult(listener));
}
else
{
Platform.runLater(() -> onSendTradingMessageFailed(listener));
}
}
}
);
}
private void onSendTradingMessageResult(TradeMessageListener listener)
{
listener.onResult();
}
private void onSendTradingMessageFailed(TradeMessageListener listener)
{
listener.onFailed();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Process incoming tradingMessage
///////////////////////////////////////////////////////////////////////////////////////////
private void processTradingMessage(TradeMessage tradeMessage, PeerAddress sender)
{
// 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());
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);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Ping peer
///////////////////////////////////////////////////////////////////////////////////////////
//TODO not working anymore...
public void pingPeer(String publicKeyAsHex)
/* public void pingPeer(String publicKeyAsHex)
{
Number160 location = Number160.createHash(publicKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
@ -587,8 +480,7 @@ public class MessageFacade
});
}
private void onAddressFoundPingPeer(PeerAddress peerAddress)
private void onAddressFoundPingPeer(PeerAddress peerAddress)
{
try
{
@ -626,16 +518,7 @@ public class MessageFacade
for (PingPeerListener pingPeerListener : pingPeerListeners)
pingPeerListener.onPingPeerResult(success);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////////////////
public PublicKey getPubKey()
{
return keyPair.getPublic();
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
@ -652,37 +535,7 @@ public class MessageFacade
orderBookListeners.remove(listener);
}
public void addTakeOfferRequestListener(TakeOfferRequestListener listener)
{
takeOfferRequestListeners.add(listener);
}
public void removeTakeOfferRequestListener(TakeOfferRequestListener listener)
{
takeOfferRequestListeners.remove(listener);
}
public void addTakerPaymentProtocol(TakerAsSellerProtocol protocol)
{
takerPaymentProtocols.put(protocol.getId(), protocol);
}
public void removeTakerPaymentProtocol(TakerAsSellerProtocol protocol)
{
takerPaymentProtocols.remove(protocol);
}
public void addOffererPaymentProtocol(OffererAsBuyerProtocol protocol)
{
offererAsBuyerProtocols.put(protocol.getId(), protocol);
}
public void removeOffererPaymentProtocol(OffererAsBuyerProtocol protocol)
{
offererAsBuyerProtocols.remove(protocol);
}
public void addPingPeerListener(PingPeerListener listener)
/* public void addPingPeerListener(PingPeerListener listener)
{
pingPeerListeners.add(listener);
}
@ -690,7 +543,7 @@ public class MessageFacade
public void removePingPeerListener(PingPeerListener listener)
{
pingPeerListeners.remove(listener);
}
} */
public void addArbitratorListener(ArbitratorListener listener)
{
@ -702,6 +555,44 @@ public class MessageFacade
arbitratorListeners.remove(listener);
}
public void addIncomingTradeMessageListener(IncomingTradeMessageListener listener)
{
incomingTradeMessageListeners.add(listener);
}
public void removeIncomingTradeMessageListener(IncomingTradeMessageListener listener)
{
incomingTradeMessageListeners.remove(listener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public PublicKey getPubKey()
{
return keyPair.getPublic();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message handler
///////////////////////////////////////////////////////////////////////////////////////////
private void onMessage(Object request, PeerAddress sender)
{
if (request instanceof TradeMessage)
{
incomingTradeMessageListeners.stream().forEach(e -> e.onMessage((TradeMessage) request, sender));
}
/* else
{
for (OrderBookListener orderBookListener : orderBookListeners)
orderBookListener.onMessage(request);
} */
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private Methods
///////////////////////////////////////////////////////////////////////////////////////////
@ -739,6 +630,18 @@ public class MessageFacade
});
}
private void setupReplyHandler()
{
myPeer.setObjectDataReply((sender, request) -> {
if (!sender.equals(myPeer.getPeerAddress()))
Platform.runLater(() -> onMessage(request, sender));
else
log.error("Received msg from myself. That should never happen.");
//noinspection ReturnOfNull
return null;
});
}
private void setupStorage()
{
myPeer.getPeerBean().setStorage(new StorageDisk(FileUtil.getDirectory(BitSquare.ID + "_tomP2P").getAbsolutePath()));
@ -752,49 +655,4 @@ public class MessageFacade
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message handler
///////////////////////////////////////////////////////////////////////////////////////////
private void setupReplyHandler()
{
/* myPeer.setObjectDataReply((sender, request) -> {
if (!sender.equals(myPeer.getPeerAddress()))
{
Platform.runLater(() -> onMessage(request, sender));
}
//noinspection ReturnOfNull
return null;
}); */
//noinspection Convert2Lambda
myPeer.setObjectDataReply(new ObjectDataReply()
{
@Override
public Object reply(PeerAddress sender, Object request) throws Exception
{
if (!sender.equals(myPeer.getPeerAddress()))
{
Platform.runLater(() -> onMessage(request, sender));
}
//noinspection ReturnOfNull
return null;
}
});
}
private void onMessage(Object request, PeerAddress sender)
{
if (request instanceof TradeMessage)
{
processTradingMessage((TradeMessage) request, sender);
}
/* else
{
for (OrderBookListener orderBookListener : orderBookListeners)
orderBookListener.onMessage(request);
} */
}
}

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.taker.listeners;
package io.bitsquare.msg.listeners;
import net.tomp2p.peers.PeerAddress;

View File

@ -0,0 +1,9 @@
package io.bitsquare.msg.listeners;
import io.bitsquare.msg.TradeMessage;
import net.tomp2p.peers.PeerAddress;
public interface IncomingTradeMessageListener
{
void onMessage(TradeMessage tradeMessage, PeerAddress sender);
}

View File

@ -1,6 +1,6 @@
package io.bitsquare.msg.listeners;
public interface TradeMessageListener
public interface OutgoingTradeMessageListener
{
void onFailed();

View File

@ -167,7 +167,7 @@ public class Offer implements Serializable
return collateral;
}
public String getBankAccountUID()
public String getBankAccountId()
{
return bankAccountUID;
}

View File

@ -6,7 +6,6 @@ import java.math.BigInteger;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
@SuppressWarnings("SameParameterValue")
public class Trade implements Serializable
{
private static final long serialVersionUID = -8275323072940974077L;
@ -22,7 +21,6 @@ public class Trade implements Serializable
private String takerSignature;
private Transaction depositTransaction;
private Transaction payoutTransaction;
private State state = State.OPEN;
public Trade(Offer offer)
@ -30,15 +28,16 @@ public class Trade implements Serializable
this.offer = offer;
}
public void setContractTakerSignature(String takerSignature)
{
this.takerSignature = takerSignature;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setters
///////////////////////////////////////////////////////////////////////////////////////////
public void setContractTakerSignature(String takerSignature)
{
this.takerSignature = takerSignature;
}
public void setTakeOfferFeeTxID(String takeOfferFeeTxID)
{
this.takeOfferFeeTxID = takeOfferFeeTxID;

View File

@ -8,55 +8,61 @@ import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.TradeMessage;
import io.bitsquare.msg.listeners.TakeOfferRequestListener;
import io.bitsquare.storage.Storage;
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.trade.protocol.messages.offerer.*;
import io.bitsquare.trade.protocol.messages.taker.PayoutTxPublishedMessage;
import io.bitsquare.trade.protocol.messages.taker.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.protocol.messages.taker.RequestTakeOfferMessage;
import io.bitsquare.trade.protocol.messages.taker.TakeOfferFeePayedMessage;
import io.bitsquare.trade.protocol.offerer.OffererAsBuyerProtocol;
import io.bitsquare.trade.protocol.offerer.OffererAsBuyerProtocolListener;
import io.bitsquare.trade.protocol.taker.TakerAsSellerProtocol;
import io.bitsquare.trade.protocol.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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import net.tomp2p.peers.PeerAddress;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents trade domain. Keeps complexity of process apart from view controller
*/
@SuppressWarnings("EmptyMethod")
public class Trading
{
private static final Logger log = LoggerFactory.getLogger(Trading.class);
private final Map<String, TakerAsSellerProtocol> takerPaymentProtocols = new HashMap<>();
private final Map<String, OffererAsBuyerProtocol> offererPaymentProtocols = new HashMap<>();
private final String storageKey;
private final User user;
private final String storageKey = this.getClass().getName();
private final User user;
private final Storage storage;
private final MessageFacade messageFacade;
private final BlockChainFacade blockChainFacade;
private final WalletFacade walletFacade;
private final CryptoFacade cryptoFacade;
private final List<TakeOfferRequestListener> takeOfferRequestListeners = new ArrayList<>();
private final Map<String, TakerAsSellerProtocol> takerAsSellerProtocolMap = new HashMap<>();
private final Map<String, OffererAsBuyerProtocol> offererAsBuyerProtocolMap = new HashMap<>();
private final StringProperty newTradeProperty = new SimpleStringProperty();
private Map<String, Offer> offers = new HashMap<>();
private final Map<String, Offer> offers;
private final Map<String, Trade> trades;
private Map<String, Trade> trades = new HashMap<>();
private Trade currentPendingTrade;
private Trade pendingTrade;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@SuppressWarnings("unchecked")
@Inject
public Trading(User user,
Storage storage,
@ -72,36 +78,55 @@ public class Trading
this.walletFacade = walletFacade;
this.cryptoFacade = cryptoFacade;
storageKey = this.getClass().getName();
Object offersObject = storage.read(storageKey + ".offers");
if (offersObject instanceof HashMap)
offers = (Map<String, Offer>) offersObject;
else
offers = new HashMap<>();
Object tradesObject = storage.read(storageKey + ".trades");
if (tradesObject instanceof HashMap)
trades = (Map<String, Trade>) tradesObject;
else
trades = new HashMap<>();
messageFacade.addTakeOfferRequestListener((offerId, sender) -> createOffererAsBuyerProtocol(offerId, sender));
messageFacade.addIncomingTradeMessageListener(this::onIncomingTradeMessage);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void cleanup()
{
messageFacade.removeIncomingTradeMessageListener(this::onIncomingTradeMessage);
}
private void saveOffers()
///////////////////////////////////////////////////////////////////////////////////////////
// Event Listeners
///////////////////////////////////////////////////////////////////////////////////////////
public void addTakeOfferRequestListener(TakeOfferRequestListener listener)
{
storage.write(storageKey + ".offers", offers);
takeOfferRequestListeners.add(listener);
}
public void removeTakeOfferRequestListener(TakeOfferRequestListener listener)
{
takeOfferRequestListeners.remove(listener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Manage offers
///////////////////////////////////////////////////////////////////////////////////////////
public void addOffer(Offer offer) throws IOException
{
if (offers.containsKey(offer.getId()))
throw new IllegalStateException("offers contains already a offer with the ID " + offer.getId());
throw new IllegalStateException("offers contains already an offer with the ID " + offer.getId());
offers.put(offer.getId(), offer);
saveOffers();
@ -109,22 +134,44 @@ public class Trading
messageFacade.addOffer(offer);
}
public void removeOffer(Offer offer) throws IOException
public void removeOffer(Offer offer)
{
if (!offers.containsKey(offer.getId()))
throw new IllegalStateException("offers does not contain the offer with the ID " + offer.getId());
offers.remove(offer.getId());
saveOffers();
messageFacade.removeOffer(offer);
}
public Trade takeOffer(BigInteger amount, Offer offer, TakerAsSellerProtocolListener listener)
{
Trade trade = createTrade(offer);
trade.setTradeAmount(amount);
TakerAsSellerProtocol takerAsSellerProtocol = new TakerAsSellerProtocol(trade, listener, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user);
takerAsSellerProtocolMap.put(trade.getId(), takerAsSellerProtocol);
takerAsSellerProtocol.start();
return trade;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Manage trades
///////////////////////////////////////////////////////////////////////////////////////////
public Trade createTrade(Offer offer)
{
if (trades.containsKey(offer.getId()))
throw new IllegalStateException("trades contains already an trade with the ID " + offer.getId());
Trade trade = new Trade(offer);
trades.put(offer.getId(), trade);
//TODO for testing
//storage.write(storageKey + ".trades", trades);
saveTrades();
// for updating UIs
this.newTradeProperty.set(trade.getId());
return trade;
@ -132,39 +179,39 @@ public class Trading
public void removeTrade(Trade trade)
{
if (!trades.containsKey(trade.getId()))
throw new IllegalStateException("trades does not contain the trade with the ID " + trade.getId());
trades.remove(trade.getId());
storage.write(storageKey + ".trades", trades);
saveTrades();
// for updating UIs
this.newTradeProperty.set(null);
}
public final StringProperty getNewTradeProperty()
{
return this.newTradeProperty;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trading protocols
///////////////////////////////////////////////////////////////////////////////////////////
public Trade takeOffer(BigInteger amount, Offer offer, TakerAsSellerProtocolListener listener, WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
{
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 trade;
}
public void createOffererAsBuyerProtocol(String offerId, PeerAddress sender)
private void createOffererAsBuyerProtocol(String offerId, PeerAddress sender)
{
log.trace("createOffererAsBuyerProtocol offerId = " + offerId);
Offer offer = offers.get(offerId);
if (offer != null && offers.containsKey(offer.getId()))
if (offers.containsKey(offerId))
{
offers.remove(offer);
Offer offer = offers.get(offerId);
currentPendingTrade = createTrade(offer);
OffererAsBuyerProtocolListener listener = new OffererAsBuyerProtocolListener()
Trade trade = createTrade(offer);
pendingTrade = trade;
OffererAsBuyerProtocol offererAsBuyerProtocol = new OffererAsBuyerProtocol(trade, sender, messageFacade, walletFacade, blockChainFacade, cryptoFacade, user, new OffererAsBuyerProtocolListener()
{
@Override
public void onOfferAccepted(Offer offer)
{
removeOffer(offer);
}
@Override
public void onDepositTxPublished(String depositTxID)
{
@ -181,8 +228,8 @@ public class Trading
public void onPayoutTxPublished(String payoutTxAsHex)
{
Transaction payoutTx = new Transaction(walletFacade.getWallet().getParams(), Utils.parseAsHexOrBase58(payoutTxAsHex));
currentPendingTrade.setPayoutTransaction(payoutTx);
currentPendingTrade.setState(Trade.State.COMPLETED);
trade.setPayoutTransaction(payoutTx);
trade.setState(Trade.State.COMPLETED);
log.debug("trading onPayoutTxPublishedMessage");
}
@ -192,27 +239,9 @@ public class Trading
log.trace("trading onDepositTxConfirmedInBlockchain");
}
};
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);
});
this.offererAsBuyerProtocolMap.put(trade.getId(), offererAsBuyerProtocol);
offererAsBuyerProtocol.start();
}
else
{
@ -220,35 +249,89 @@ public class Trading
}
}
public void onUIEventBankTransferInited(String tradeUID)
public void bankTransferInited(String tradeUID)
{
offererPaymentProtocols.get(tradeUID).onUIEventBankTransferInited();
offererAsBuyerProtocolMap.get(tradeUID).onUIEventBankTransferInited();
}
public void onFiatReceived(String tradeUID)
{
takerAsSellerProtocolMap.get(tradeUID).onUIEventFiatReceived();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Process incoming tradeMessages
///////////////////////////////////////////////////////////////////////////////////////////
private void onIncomingTradeMessage(TradeMessage tradeMessage, PeerAddress sender)
{
// 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());
String tradeId = tradeMessage.getTradeId();
if (tradeMessage instanceof RequestTakeOfferMessage)
{
createOffererAsBuyerProtocol(tradeId, sender);
takeOfferRequestListeners.stream().forEach(e -> e.onTakeOfferRequested(tradeId, sender));
}
else if (tradeMessage instanceof AcceptTakeOfferRequestMessage)
{
takerAsSellerProtocolMap.get(tradeId).onAcceptTakeOfferRequestMessage();
}
else if (tradeMessage instanceof RejectTakeOfferRequestMessage)
{
takerAsSellerProtocolMap.get(tradeId).onRejectTakeOfferRequestMessage();
}
else if (tradeMessage instanceof TakeOfferFeePayedMessage)
{
offererAsBuyerProtocolMap.get(tradeId).onTakeOfferFeePayedMessage((TakeOfferFeePayedMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestTakerDepositPaymentMessage)
{
takerAsSellerProtocolMap.get(tradeId).onRequestTakerDepositPaymentMessage((RequestTakerDepositPaymentMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestOffererPublishDepositTxMessage)
{
offererAsBuyerProtocolMap.get(tradeId).onRequestOffererPublishDepositTxMessage((RequestOffererPublishDepositTxMessage) tradeMessage);
}
else if (tradeMessage instanceof DepositTxPublishedMessage)
{
takerAsSellerProtocolMap.get(tradeId).onDepositTxPublishedMessage((DepositTxPublishedMessage) tradeMessage);
}
else if (tradeMessage instanceof BankTransferInitedMessage)
{
takerAsSellerProtocolMap.get(tradeId).onBankTransferInitedMessage((BankTransferInitedMessage) tradeMessage);
}
else if (tradeMessage instanceof PayoutTxPublishedMessage)
{
offererAsBuyerProtocolMap.get(tradeId).onPayoutTxPublishedMessage((PayoutTxPublishedMessage) tradeMessage);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trade process
// Utils
///////////////////////////////////////////////////////////////////////////////////////////
// 6
public void releaseBTC(String tradeUID)
public boolean isOfferAlreadyInTrades(Offer offer)
{
takerPaymentProtocols.get(tradeUID).onUIEventFiatReceived();
return trades.containsKey(offer.getId());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public Map<String, Trade> getTrades()
{
return trades;
}
public Map<String, Offer> getOffers()
{
return offers;
@ -259,14 +342,37 @@ public class Trading
return offers.get(offerId);
}
public boolean isOfferAlreadyInTrades(Offer offer)
public Trade getPendingTrade()
{
return trades.containsKey(offer.getId());
return pendingTrade;
}
public Trade getCurrentPendingTrade()
public final StringProperty getNewTradeProperty()
{
return currentPendingTrade;
return this.newTradeProperty;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void saveOffers()
{
storage.write(storageKey + ".offers", offers);
}
private void saveTrades()
{
storage.write(storageKey + ".trades", trades);
}
@Nullable
public Trade getTrade(String tradeId)
{
if (trades.containsKey(tradeId))
return trades.get(trades);
else
return null;
}
}

View File

@ -77,13 +77,7 @@ public class OrderBook implements OrderBookListener
public void removeOffer(Offer offer)
{
try
{
trading.removeOffer(offer);
} catch (IOException e)
{
e.printStackTrace();
}
trading.removeOffer(offer);
}
public void applyFilter(OrderBookFilter orderBookFilter)

View File

@ -1,26 +0,0 @@
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,29 +0,0 @@
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

@ -1,44 +0,0 @@
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

@ -1,70 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -1,56 +0,0 @@
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

@ -1,59 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,41 +0,0 @@
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,12 +0,0 @@
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,29 +0,0 @@
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

@ -1,49 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,47 +0,0 @@
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

@ -1,50 +0,0 @@
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

@ -1,43 +0,0 @@
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

@ -1,42 +0,0 @@
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

@ -1,55 +0,0 @@
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

@ -1,46 +0,0 @@
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

@ -1,66 +0,0 @@
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

@ -1,42 +0,0 @@
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

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.offerer.messages;
package io.bitsquare.trade.protocol.messages.offerer;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.offerer.messages;
package io.bitsquare.trade.protocol.messages.offerer;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.offerer.messages;
package io.bitsquare.trade.protocol.messages.offerer;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.offerer.messages;
package io.bitsquare.trade.protocol.messages.offerer;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.offerer.messages;
package io.bitsquare.trade.protocol.messages.offerer;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.taker.messages;
package io.bitsquare.trade.protocol.messages.taker;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.taker.messages;
package io.bitsquare.trade.protocol.messages.taker;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.taker.messages;
package io.bitsquare.trade.protocol.messages.taker;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.taker.messages;
package io.bitsquare.trade.protocol.messages.taker;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
@ -32,7 +32,7 @@ public class TakeOfferFeePayedMessage implements Serializable, TradeMessage
return tradeAmount;
}
public String getTakeOfferFeeTxID()
public String getTakeOfferFeeTxId()
{
return takeOfferFeeTxID;
}

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.process;
package io.bitsquare.trade.protocol.mock;
//TODO not used but let it for reference until all use cases are impl.
class BuyOffererPaymentProcess extends PaymentProcess

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.process;
package io.bitsquare.trade.protocol.mock;
//TODO not used but let it for reference until all use cases are impl.
public class BuyTakerPaymentProcess extends PaymentProcess

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.process;
package io.bitsquare.trade.protocol.mock;
import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade;

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.process;
package io.bitsquare.trade.protocol.mock;
//TODO not used but let it for reference until all use cases are impl.
class SellOffererPaymentProcess extends PaymentProcess

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.process;
package io.bitsquare.trade.protocol.mock;
//TODO not used but let it for reference until all use cases are impl.
class SellTakerPaymentProcess extends PaymentProcess

View File

@ -1,26 +1,27 @@
package io.bitsquare.trade.payment.offerer;
package io.bitsquare.trade.protocol.offerer;
import com.google.bitcoin.core.ECKey;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade;
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.trade.protocol.messages.taker.PayoutTxPublishedMessage;
import io.bitsquare.trade.protocol.messages.taker.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.protocol.messages.taker.TakeOfferFeePayedMessage;
import io.bitsquare.trade.protocol.tasks.offerer.*;
import io.bitsquare.user.User;
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 java.math.BigInteger;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.*;
//TODO refactor to process based pattern
public class OffererAsBuyerProtocol
{
@ -31,19 +32,22 @@ public class OffererAsBuyerProtocol
private final String id;
private final Trade trade;
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 final String tradeId;
private final Offer offer;
// private
private final SequenceScheduler scheduler_1;
private State state;
// data written/read by tasks
private String preparedOffererDepositTxAsHex;
private long offererTxOutIndex;
private String offererPubKey;
// data written by messages, read by tasks
private String takeOfferFeeTxId;
private String takerMultiSigPubKey;
@ -56,14 +60,6 @@ public class OffererAsBuyerProtocol
private String txConnOutAsHex;
private String txScriptSigAsHex;
private long takerTxOutIndex;
private SequenceScheduler scheduler_2;
private SequenceScheduler scheduler_3;
private SequenceScheduler scheduler_4;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public OffererAsBuyerProtocol(Trade trade,
PeerAddress peerAddress,
@ -72,15 +68,11 @@ public class OffererAsBuyerProtocol
BlockChainFacade blockChainFacade,
CryptoFacade cryptoFacade,
User user,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
OffererAsBuyerProtocolListener listener)
{
this.trade = trade;
this.peerAddress = peerAddress;
this.listener = listener;
this.resultHandler = resultHandler;
this.faultHandler = faultHandler;
this.messageFacade = messageFacade;
this.walletFacade = walletFacade;
this.blockChainFacade = blockChainFacade;
@ -89,43 +81,166 @@ public class OffererAsBuyerProtocol
id = trade.getId();
messageFacade.addOffererPaymentProtocol(this);
tradeId = trade.getId();
offer = trade.getOffer();
log.debug("OffererAsBuyerProtocol created");
state = State.Start;
}
List<Worker> tasks = new ArrayList<>();
tasks.add(new HandleTakeOfferRequest(resultHandler, faultHandler));
scheduler_1 = new SequenceScheduler(tasks, this);
scheduler_1.execute();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
// Instantiation is triggered by an incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
public void start()
{
HandleTakeOfferRequest.run(this::onResultHandleTakeOfferRequest, this::onFaultHandleTakeOfferRequest, peerAddress, messageFacade, trade.getState(), tradeId);
state = State.HandleTakeOfferRequest;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultHandleTakeOfferRequest(Trade.State tradeState)
{
checkState(tradeState == Trade.State.ACCEPTED);
listener.onOfferAccepted(offer);
trade.setState(tradeState);
messageFacade.removeOffer(offer);
// waiting for incoming msg from peer (-> onTakeOfferFeePayedMessage)
}
public void onFaultHandleTakeOfferRequest(Throwable throwable)
{
// TODO
}
public void onTakeOfferFeePayedMessage(TakeOfferFeePayedMessage message)
{
log.debug("onTakeOfferFeePayedMessage");
getTrade().setTakeOfferFeeTxID(message.getTakeOfferFeeTxID());
getTrade().setTradeAmount(message.getTradeAmount());
takeOfferFeeTxId = message.getTakeOfferFeeTxID();
takerMultiSigPubKey = message.getTakerMultiSigPubKey();
checkState(state == State.HandleTakeOfferRequest);
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.");
}
String takeOfferFeeTxId = message.getTakeOfferFeeTxId();
BigInteger tradeAmount = message.getTradeAmount();
String takerMultiSigPubKey = message.getTakerMultiSigPubKey();
// validate input
checkNotNull(takeOfferFeeTxId);
checkArgument(takeOfferFeeTxId.length() > 0);
checkNotNull(tradeAmount);
// TODO make validator for amounts with testing against trades amount range
//checkArgument(bigIntegerValidator.isValue(tradeAmount).inRangeOf(trade.getMinAmount(), trade.getAmount()));
checkNotNull(takerMultiSigPubKey);
checkArgument(takerMultiSigPubKey.length() > 0);
state = State.onTakeOfferFeePayedMessage;
this.takeOfferFeeTxId = takeOfferFeeTxId;
this.takerMultiSigPubKey = takerMultiSigPubKey;
trade.setTakeOfferFeeTxID(takeOfferFeeTxId);
trade.setTradeAmount(tradeAmount);
sequence2();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
private void sequence2()
{
VerifyTakeOfferFeePayment.run(this::onResultVerifyTakeOfferFeePayment, this::onFaultVerifyTakeOfferFeePayment, walletFacade, takeOfferFeeTxId);
state = State.VerifyTakeOfferFeePayment;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultVerifyTakeOfferFeePayment()
{
BigInteger collateralAmount = trade.getCollateralAmount();
String arbitratorPubKeyAsHex = offer.getArbitrator().getPubKeyAsHex();
CreateDepositTx.run(this::onResultCreateDepositTx,
this::onFaultCreateDepositTx,
walletFacade,
tradeId,
collateralAmount,
takerMultiSigPubKey,
arbitratorPubKeyAsHex);
state = State.CreateDepositTx;
}
public void onFaultVerifyTakeOfferFeePayment(Throwable throwable)
{
// TODO
}
public void onResultCreateDepositTx(String offererPubKey, String preparedOffererDepositTxAsHex, long offererTxOutIndex)
{
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
this.offererTxOutIndex = offererTxOutIndex;
BankAccount bankAccount = user.getBankAccount(trade.getOffer().getBankAccountId());
String accountId = user.getAccountId();
RequestTakerDepositPayment.run(this::onResultRequestTakerDepositPayment,
this::onFaultRequestTakerDepositPayment,
peerAddress,
messageFacade,
tradeId,
bankAccount,
accountId,
offererPubKey,
preparedOffererDepositTxAsHex,
offererTxOutIndex);
state = State.RequestTakerDepositPayment;
}
public void onFaultCreateDepositTx(Throwable throwable)
{
// TODO
}
public void onResultRequestTakerDepositPayment()
{
// waiting for incoming msg from peer (-> onRequestOffererPublishDepositTxMessage)
}
public void onFaultRequestTakerDepositPayment(Throwable throwable)
{
// TODO
}
public void onRequestOffererPublishDepositTxMessage(RequestOffererPublishDepositTxMessage message)
{
log.debug("onRequestOffererPublishDepositTxMessage");
checkState(state == State.RequestTakerDepositPayment);
checkNotNull(message);
//TODO validation
state = State.onRequestOffererPublishDepositTxMessage;
//TODO
takerPayoutAddress = message.getTakerPayoutAddress();
peersAccountId = message.getAccountId();
peersBankAccount = message.getBankAccount();
@ -136,50 +251,172 @@ public class OffererAsBuyerProtocol
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.");
}
sequence3();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
public void sequence3()
{
VerifyTakerAccount.run(this::onResultVerifyTakerAccount, this::onFaultVerifyTakerAccount, blockChainFacade, peersAccountId, peersBankAccount);
state = State.VerifyTakerAccount;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultVerifyTakerAccount()
{
String accountId = user.getAccountId();
BigInteger tradeAmount = trade.getTradeAmount();
String messagePubKeyAsHex = user.getMessagePubKeyAsHex();
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountId());
ECKey registrationKey = walletFacade.getRegistrationAddressInfo().getKey();
VerifyAndSignContract.run(this::onResultVerifyAndSignContract,
this::onFaultVerifyAndSignContract,
cryptoFacade,
accountId,
tradeAmount,
takeOfferFeeTxId,
messagePubKeyAsHex,
offer,
peersAccountId,
bankAccount,
peersBankAccount,
takerMessagePubKey,
peersContractAsJson,
registrationKey);
state = State.VerifyAndSignContract;
}
public void onFaultVerifyTakerAccount(Throwable throwable)
{
// TODO
}
public void onResultVerifyAndSignContract(Contract contract, String contractAsJson, String signature)
{
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setContractTakerSignature(signature);
SignAndPublishDepositTx.run(this::onResultSignAndPublishDepositTx,
this::onFaultSignAndPublishDepositTx,
walletFacade,
preparedOffererDepositTxAsHex,
signedTakerDepositTxAsHex,
txConnOutAsHex,
txScriptSigAsHex,
offererTxOutIndex,
takerTxOutIndex);
state = State.SignAndPublishDepositTx;
}
public void onFaultVerifyAndSignContract(Throwable throwable)
{
// TODO
}
public void onResultSignAndPublishDepositTx(Transaction transaction)
{
trade.setDepositTransaction(transaction);
listener.onDepositTxPublished(transaction.getHashAsString());
SendDepositTxIdToTaker.run(this::onResultSendDepositTxIdToTaker, this::onFaultSendDepositTxIdToTaker, peerAddress, messageFacade, trade);
state = State.SendDepositTxIdToTaker;
}
public void onFaultSignAndPublishDepositTx(Throwable throwable)
{
// TODO
}
public void onResultSendDepositTxIdToTaker()
{
SetupListenerForBlockChainConfirmation.run(this::onResultSetupListenerForBlockChainConfirmation, this::onFaultSetupListenerForBlockChainConfirmation, trade.getDepositTransaction(), listener);
state = State.SetupListenerForBlockChainConfirmation;
}
public void onFaultSendDepositTxIdToTaker(Throwable throwable)
{
// TODO
}
public void onResultSetupListenerForBlockChainConfirmation()
{
// waiting for UI event (-> bankTransferInited)
}
public void onFaultSetupListenerForBlockChainConfirmation(Throwable throwable)
{
// TODO
}
// Triggered from UI event: Button click "Bank transfer inited"
public void onUIEventBankTransferInited()
{
log.debug("onUIEventBankTransferInited");
log.debug("bankTransferInited");
if (scheduler_3.getHasCompleted())
if (state == State.SetupListenerForBlockChainConfirmation)
{
List<Worker> tasks = new ArrayList<>();
tasks.add(new SendSignedPayoutTx(getResultHandler(), getFaultHandler()));
scheduler_4 = new SequenceScheduler(tasks, this);
scheduler_4.execute();
state = State.onUIEventBankTransferInited;
sequence4();
}
else
{
log.error("scheduler_3 has not completed yet.");
log.error("Invalid state. Actual state is: " + state);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// UI event
///////////////////////////////////////////////////////////////////////////////////////////
public void sequence4()
{
SendSignedPayoutTx.run(this::onResultSendSignedPayoutTx, this::onFaultSendSignedPayoutTx, peerAddress, messageFacade, walletFacade, trade, takerPayoutAddress);
state = State.SendSignedPayoutTx;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultSendSignedPayoutTx()
{ // waiting for incoming msg from peer (-> onPayoutTxPublishedMessage)
}
public void onFaultSendSignedPayoutTx(Throwable throwable)
{
// TODO
}
public void onPayoutTxPublishedMessage(PayoutTxPublishedMessage tradeMessage)
{
log.debug("onPayoutTxPublishedMessage");
listener.onPayoutTxPublished(tradeMessage.getPayoutTxAsHex());
if (state == State.SendSignedPayoutTx)
{
state = State.onPayoutTxPublishedMessage;
listener.onPayoutTxPublished(tradeMessage.getPayoutTxAsHex());
}
else
{
log.error("Invalid state. Actual state is: " + state);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
@ -187,134 +424,35 @@ public class OffererAsBuyerProtocol
return id;
}
public String getTakeOfferFeeTxId()
{
return takeOfferFeeTxId;
}
public String getTakerMultiSigPubKey()
{
return takerMultiSigPubKey;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getTakerPayoutAddress()
enum State
{
return takerPayoutAddress;
}
Start,
HandleTakeOfferRequest,
public String getPeersAccountId()
{
return peersAccountId;
}
onTakeOfferFeePayedMessage,
public BankAccount getPeersBankAccount()
{
return peersBankAccount;
}
VerifyTakeOfferFeePayment,
CreateDepositTx,
RequestTakerDepositPayment,
public String getTakerMessagePubKey()
{
return takerMessagePubKey;
}
onRequestOffererPublishDepositTxMessage,
public String getPeersContractAsJson()
{
return peersContractAsJson;
}
VerifyTakerAccount,
VerifyAndSignContract,
SignAndPublishDepositTx,
SendDepositTxIdToTaker,
SetupListenerForBlockChainConfirmation,
public String getSignedTakerDepositTxAsHex()
{
return signedTakerDepositTxAsHex;
}
onUIEventBankTransferInited,
public String getTxConnOutAsHex()
{
return txConnOutAsHex;
}
SendSignedPayoutTx,
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;
onPayoutTxPublishedMessage
}
@ -506,7 +644,7 @@ public class OffererAsBuyerProtocol
// offererPaymentProtocolListener.onProgress(getProgress());
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID());
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountId());
String accountID = user.getAccountId();
TradeMessageOld tradeMessage = new TradeMessageOld(TradeMessageType.REQUEST_TAKER_DEPOSIT_PAYMENT, trade.getId(), bankAccount, accountID, offererPubKey, preparedOffererDepositTxAsHex, offererTxOutIndex);
@ -566,8 +704,8 @@ public class OffererAsBuyerProtocol
Contract contract = new Contract(offer,
trade.getTradeAmount(),
trade.getTakeOfferFeeTxId(),
user.getAccountID(),
requestTradeMessage.getAccountID(),
user.getAccountId(),
requestTradeMessage.getAccountId(),
user.getCurrentBankAccount(),
requestTradeMessage.getBankAccount(),
offer.getMessagePubKeyAsHex(),

View File

@ -1,9 +1,12 @@
package io.bitsquare.trade.payment.offerer;
package io.bitsquare.trade.protocol.offerer;
import com.google.bitcoin.core.TransactionConfidence;
import io.bitsquare.trade.Offer;
public interface OffererAsBuyerProtocolListener
{
void onOfferAccepted(Offer offer);
void onDepositTxPublished(String depositTxID);
void onDepositTxConfirmedInBlockchain();

View File

@ -1,4 +1,4 @@
package io.bitsquare.trade.payment.taker;
package io.bitsquare.trade.protocol.taker;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.bank.BankAccount;
@ -6,42 +6,36 @@ import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade;
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.trade.protocol.messages.offerer.BankTransferInitedMessage;
import io.bitsquare.trade.protocol.messages.offerer.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.messages.offerer.RequestTakerDepositPaymentMessage;
import io.bitsquare.trade.protocol.tasks.taker.*;
import io.bitsquare.user.User;
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;
//TODO use states
@SuppressWarnings("ConstantConditions")
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static io.bitsquare.util.Validator.*;
public class TakerAsSellerProtocol
{
private static final Logger log = LoggerFactory.getLogger(TakerAsSellerProtocol.class);
// provided data
private final String id;
private final Trade trade;
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;
private final String id;
private final Offer offer;
private final String tradeId;
// written/read by task
private String payoutTxAsHex;
private PeerAddress peerAddress;
@ -59,20 +53,11 @@ public class TakerAsSellerProtocol
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
///////////////////////////////////////////////////////////////////////////////////////////
//private
private State state;
public TakerAsSellerProtocol(Trade trade,
TakerAsSellerProtocolListener listener,
WorkerResultHandler resultHandler,
WorkerFaultHandler faultHandler,
MessageFacade messageFacade,
WalletFacade walletFacade,
BlockChainFacade blockChainFacade,
@ -81,263 +66,301 @@ public class TakerAsSellerProtocol
{
this.trade = trade;
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();
tradeId = trade.getId();
id = trade.getId();
state = State.Init;
}
log.debug("TakerAsSellerProtocol created");
messageFacade.addTakerPaymentProtocol(this);
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
// 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();
// generic fault handler
public void onFault(Throwable throwable)
{
listener.onFault(throwable, state);
}
public void start()
{
String messagePubKeyAsHex = validString(offer.getMessagePubKeyAsHex());
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, messagePubKeyAsHex);
state = State.GetPeerAddress;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultGetPeerAddress(PeerAddress peerAddress)
{
this.peerAddress = validPeerAddress(peerAddress);
RequestTakeOffer.run(this::onResultRequestTakeOffer, this::onFault, this.peerAddress, messageFacade, tradeId);
state = State.RequestTakeOffer;
}
public void onResultRequestTakeOffer()
{
listener.onWaitingForPeerResponse(state);
}
public void onAcceptTakeOfferRequestMessage()
{
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.");
}
checkState(state == State.RequestTakeOffer);
PayTakeOfferFee.run(this::onResultPayTakeOfferFee, this::onFault, walletFacade, tradeId);
state = State.PayTakeOfferFee;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// OR
public void onRejectTakeOfferRequestMessage()
{
log.debug("onRejectTakeOfferRequestMessage");
if (scheduler_1.getHasCompleted())
{
//TODO
}
else
{
log.error("scheduler_1 has not completed yet.");
}
checkState(state == State.RequestTakeOffer);
state = State.onRejectTakeOfferRequestMessage;
listener.onTakeOfferRequestRejected(trade);
}
public void onResultPayTakeOfferFee(Transaction transaction)
{
checkNotNull(transaction);
String transactionId = validString(transaction.getHashAsString());
trade.setTakeOfferFeeTxID(transactionId);
String takeOfferFeeTxId = trade.getTakeOfferFeeTxId();
BigInteger tradeAmount = trade.getTradeAmount();
String pubKeyAsHexString = walletFacade.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
SendTakeOfferFeePayedTxId.run(this::onResultSendTakeOfferFeePayedTxId,
this::onFault,
peerAddress,
messageFacade,
tradeId,
takeOfferFeeTxId,
tradeAmount,
pubKeyAsHexString);
state = State.SendTakeOfferFeePayedTxId;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultSendTakeOfferFeePayedTxId()
{
listener.onWaitingForPeerResponse(state);
}
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.");
}
checkState(state == State.SendTakeOfferFeePayedTxId);
peersAccountId = validString(message.getAccountID());
peersBankAccount = checkNotNull(message.getBankAccount());
offererPubKey = validString(message.getOffererPubKey());
preparedOffererDepositTxAsHex = validString(message.getPreparedOffererDepositTxAsHex());
offererTxOutIndex = validNonNegativeLong(message.getOffererTxOutIndex());
VerifyOffererAccount.run(this::onResultVerifyOffererAccount,
this::onFault,
blockChainFacade,
peersAccountId,
peersBankAccount);
state = State.VerifyOffererAccount;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultVerifyOffererAccount()
{
CreateAndSignContract.run(this::onResultCreateAndSignContract,
this::onFault,
cryptoFacade,
trade,
user,
peersAccountId,
peersBankAccount,
walletFacade.getRegistrationAddressInfo().getKey());
state = State.CreateAndSignContract;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultCreateAndSignContract()
{
PayDeposit.run(this::onResultPayDeposit, this::onFault, walletFacade, trade, offererPubKey, preparedOffererDepositTxAsHex);
state = State.PayDeposit;
}
public void onResultPayDeposit(Transaction signedTakerDepositTx, long takerTxOutIndex)
{
SendSignedTakerDepositTxAsHex.run(this::onResultSendSignedTakerDepositTxAsHex,
this::onFault,
peerAddress,
messageFacade,
walletFacade,
trade,
user,
signedTakerDepositTx,
takerTxOutIndex,
offererTxOutIndex);
state = State.SendSignedTakerDepositTxAsHex;
}
public void onResultSendSignedTakerDepositTxAsHex()
{
listener.onWaitingForPeerResponse(state);
}
// informational, does only trigger UI feedback/update
public void onDepositTxPublishedMessage(DepositTxPublishedMessage message)
{
log.debug("onDepositTxPublishedMessage");
String txID = getWalletFacade().takerCommitDepositTx(message.getDepositTxAsHex());
listener.onDepositTxPublished(txID);
if (state.ordinal() > State.SendSignedTakerDepositTxAsHex.ordinal() && state.ordinal() < State.SignAndPublishPayoutTx.ordinal())
{
state = State.onDepositTxPublishedMessage;
listener.onDepositTxPublished(walletFacade.takerCommitDepositTx(message.getDepositTxAsHex()));
}
else
{
log.error("Invalid state. Actual state is: " + state);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 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();
if (state.ordinal() > State.SendSignedTakerDepositTxAsHex.ordinal() && state.ordinal() < State.SignAndPublishPayoutTx.ordinal())
{
state = State.onBankTransferInitedMessage;
listener.onBankTransferInited(tradeMessage.getTradeId());
depositTxAsHex = tradeMessage.getDepositTxAsHex();
offererSignatureR = tradeMessage.getOffererSignatureR();
offererSignatureS = tradeMessage.getOffererSignatureS();
offererPaybackAmount = tradeMessage.getOffererPaybackAmount();
takerPaybackAmount = tradeMessage.getTakerPaybackAmount();
offererPayoutAddress = tradeMessage.getOffererPayoutAddress();
listener.onBankTransferInited(tradeMessage.getTradeId());
}
else
{
log.error("Invalid state. Actual state is: " + state);
}
}
// 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())
if (state.ordinal() > State.SendSignedTakerDepositTxAsHex.ordinal() && state.ordinal() < State.SignAndPublishPayoutTx.ordinal())
{
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();
state = State.onUIEventFiatReceived;
SignAndPublishPayoutTx.run(this::onResultSignAndPublishPayoutTx,
this::onFault,
walletFacade,
trade,
depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
state = State.SignAndPublishPayoutTx;
}
else
{
log.error("scheduler_3 has not completed yet.");
log.error("Invalid state. Actual state is: " + state);
}
}
public void onResultSignAndPublishPayoutTx(Transaction transaction, String payoutTxAsHex)
{
listener.onPayoutTxPublished(trade, transaction.getHashAsString());
SendPayoutTxToOfferer.run(this::onResultSendPayoutTxToOfferer, this::onFault, peerAddress, messageFacade, trade, payoutTxAsHex);
state = State.SendPayoutTxToOfferer;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Tasks
///////////////////////////////////////////////////////////////////////////////////////////
public void onResultSendPayoutTxToOfferer()
{
checkState(state == State.SendPayoutTxToOfferer);
listener.onCompleted(state);
}
public String getId()
{
return id;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getters, Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getId()
public enum State
{
return id;
Init,
GetPeerAddress,
RequestTakeOffer,
PayTakeOfferFee,
onRejectTakeOfferRequestMessage,
SendTakeOfferFeePayedTxId,
VerifyOffererAccount,
CreateAndSignContract,
PayDeposit,
SendSignedTakerDepositTxAsHex,
onDepositTxPublishedMessage,
onBankTransferInitedMessage,
onUIEventFiatReceived,
SignAndPublishPayoutTx,
SendPayoutTxToOfferer
}
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
@ -774,7 +797,7 @@ public class TakerAsSellerProtocol
{
System.out.println("######### 3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction);
log.debug("3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction);
listener.onTradeCompleted(transaction.getHashAsString());
listener.onPayoutTxPublished(transaction.getHashAsString());
sendPayoutTxToOfferer(Utils.bytesToHexString(transaction.bitcoinSerialize()));
}

View File

@ -0,0 +1,20 @@
package io.bitsquare.trade.protocol.taker;
import io.bitsquare.trade.Trade;
public interface TakerAsSellerProtocolListener
{
void onDepositTxPublished(String depositTxId);
void onBankTransferInited(String tradeId);
void onPayoutTxPublished(Trade trade, String hashAsString);
void onFault(Throwable throwable, TakerAsSellerProtocol.State state);
void onWaitingForPeerResponse(TakerAsSellerProtocol.State state);
void onCompleted(TakerAsSellerProtocol.State state);
void onTakeOfferRequestRejected(Trade trade);
}

View File

@ -0,0 +1,6 @@
package io.bitsquare.trade.protocol.tasks;
public interface FaultHandler
{
void onFault(Throwable throwable);
}

View File

@ -0,0 +1,6 @@
package io.bitsquare.trade.protocol.tasks;
public interface ResultHandler
{
void onResult();
}

View File

@ -0,0 +1,49 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateDepositTx
{
private static final Logger log = LoggerFactory.getLogger(CreateDepositTx.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
WalletFacade walletFacade,
String tradeId,
BigInteger collateralAmount,
String takerMultiSigPubKey,
String arbitratorPubKeyAsHex)
{
try
{
String offererPubKey = walletFacade.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
Transaction transaction = walletFacade.offererCreatesMSTxAndAddPayment(collateralAmount,
offererPubKey,
takerMultiSigPubKey,
arbitratorPubKeyAsHex,
tradeId);
String preparedOffererDepositTxAsHex = Utils.bytesToHexString(transaction.bitcoinSerialize());
long offererTxOutIndex = transaction.getInput(0).getOutpoint().getIndex();
resultHandler.onResult(offererPubKey, preparedOffererDepositTxAsHex, offererTxOutIndex);
} catch (InsufficientMoneyException e)
{
log.error("Create deposit tx faultHandler.onFault due InsufficientMoneyException " + e);
faultHandler.onFault(new Exception("Create deposit tx faultHandler.onFault due InsufficientMoneyException " + e));
}
}
public interface ResultHandler
{
void onResult(String offererPubKey, String preparedOffererDepositTxAsHex, long offererTxOutIndex);
}
}

View File

@ -0,0 +1,65 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.messages.offerer.AcceptTakeOfferRequestMessage;
import io.bitsquare.trade.protocol.messages.offerer.RejectTakeOfferRequestMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HandleTakeOfferRequest
{
private static final Logger log = LoggerFactory.getLogger(HandleTakeOfferRequest.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, PeerAddress peerAddress, MessageFacade messageFacade, Trade.State tradeState, String tradeId)
{
if (tradeState == Trade.State.OPEN)
{
messageFacade.sendTradeMessage(peerAddress, new AcceptTakeOfferRequestMessage(tradeId), new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("AcceptTakeOfferRequestMessage successfully arrived at peer");
resultHandler.onResult(Trade.State.ACCEPTED);
}
@Override
public void onFailed()
{
log.error("AcceptTakeOfferRequestMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("AcceptTakeOfferRequestMessage faultHandler.onFault to arrive at peer"));
}
});
}
else
{
RejectTakeOfferRequestMessage msg = new RejectTakeOfferRequestMessage(tradeId);
messageFacade.sendTradeMessage(peerAddress, msg, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RejectTakeOfferRequestMessage successfully arrived at peer");
}
@Override
public void onFailed()
{
log.error("RejectTakeOfferRequestMessage faultHandler.onFault to arrive at peer");
}
});
log.error("Offer not marked as open.");
faultHandler.onFault(new Exception("Offer not marked as open."));
}
}
public interface ResultHandler
{
void onResult(Trade.State tradeState);
}
}

View File

@ -0,0 +1,52 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.protocol.messages.offerer.RequestTakerDepositPaymentMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakerDepositPayment
{
private static final Logger log = LoggerFactory.getLogger(RequestTakerDepositPayment.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
PeerAddress peerAddress,
MessageFacade messageFacade,
String tradeId,
BankAccount bankAccount,
String accountId,
String offererPubKey,
String preparedOffererDepositTxAsHex,
long offererTxOutIndex)
{
RequestTakerDepositPaymentMessage tradeMessage = new RequestTakerDepositPaymentMessage(tradeId,
bankAccount,
accountId,
offererPubKey,
preparedOffererDepositTxAsHex,
offererTxOutIndex);
messageFacade.sendTradeMessage(peerAddress, tradeMessage, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestTakerDepositPaymentMessage successfully arrived at peer");
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("RequestTakerDepositPaymentMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("RequestTakerDepositPaymentMessage faultHandler.onFault to arrive at peer"));
}
});
}
}

View File

@ -0,0 +1,39 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import com.google.bitcoin.core.Utils;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.messages.offerer.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendDepositTxIdToTaker
{
private static final Logger log = LoggerFactory.getLogger(SendDepositTxIdToTaker.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, PeerAddress peerAddress, MessageFacade messageFacade, Trade trade)
{
DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(trade.getId(), Utils.bytesToHexString(trade.getDepositTransaction().bitcoinSerialize()));
messageFacade.sendTradeMessage(peerAddress, tradeMessage, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("DepositTxPublishedMessage successfully arrived at peer");
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("DepositTxPublishedMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("DepositTxPublishedMessage faultHandler.onFault to arrive at peer"));
}
});
}
}

View File

@ -1,55 +1,57 @@
package io.bitsquare.trade.payment.offerer.tasks;
package io.bitsquare.trade.protocol.tasks.offerer;
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 io.bitsquare.btc.WalletFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.messages.offerer.BankTransferInitedMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import java.math.BigInteger;
import javafx.util.Pair;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedPayoutTx extends AbstractOffererAsBuyerTask
public class SendSignedPayoutTx
{
private static final Logger log = LoggerFactory.getLogger(SendSignedPayoutTx.class);
public SendSignedPayoutTx(WorkerResultHandler resultHandler, WorkerFaultHandler faultHandler)
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
PeerAddress peerAddress,
MessageFacade messageFacade,
WalletFacade walletFacade,
Trade trade,
String takerPayoutAddress)
{
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);
Transaction depositTransaction = trade.getDepositTransaction();
BigInteger collateral = trade.getCollateralAmount();
BigInteger offererPaybackAmount = trade.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());
log.trace("takerPayoutAddress " + takerPayoutAddress);
Pair<ECKey.ECDSASignature, String> result = sharedModel.getWalletFacade().offererCreatesAndSignsPayoutTx(depositTransaction.getHashAsString(),
Pair<ECKey.ECDSASignature, String> result = walletFacade.offererCreatesAndSignsPayoutTx(depositTransaction.getHashAsString(),
offererPaybackAmount,
takerPaybackAmount,
sharedModel.getTakerPayoutAddress(),
sharedModel.getTrade().getId());
takerPayoutAddress,
trade.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();
String offererPayoutAddress = walletFacade.getAddressInfoByTradeID(trade.getId()).getAddressString();
BankTransferInitedMessage tradeMessage = new BankTransferInitedMessage(sharedModel.getTrade().getId(),
BankTransferInitedMessage tradeMessage = new BankTransferInitedMessage(trade.getId(),
depositTxAsHex,
offererSignatureR,
offererSignatureS,
@ -64,28 +66,27 @@ public class SendSignedPayoutTx extends AbstractOffererAsBuyerTask
log.trace("takerPaybackAmount " + takerPaybackAmount);
log.trace("offererPayoutAddress " + offererPayoutAddress);
sharedModel.getMessageFacade().sendTradeMessage(sharedModel.peerAddress, tradeMessage, new TradeMessageListener()
messageFacade.sendTradeMessage(peerAddress, tradeMessage, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("BankTransferInitedMessage successfully arrived at peer");
complete();
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("BankTransferInitedMessage failed to arrive at peer");
failed(new Exception("BankTransferInitedMessage failed to arrive at peer"));
log.error("BankTransferInitedMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("BankTransferInitedMessage faultHandler.onFault to arrive at peer"));
}
});
} catch (Exception e)
{
log.error("Exception at OffererCreatesAndSignsPayoutTx " + e);
failed(e);
faultHandler.onFault(e);
}
}
}

View File

@ -0,0 +1,40 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.TransactionConfidence;
import io.bitsquare.trade.protocol.offerer.OffererAsBuyerProtocolListener;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SetupListenerForBlockChainConfirmation
{
private static final Logger log = LoggerFactory.getLogger(SetupListenerForBlockChainConfirmation.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, Transaction depositTransaction, OffererAsBuyerProtocolListener listener)
{ //TODO
// sharedModel.offererPaymentProtocolListener.onDepositTxConfirmedInBlockchain();
depositTransaction.getConfidence().addEventListener(new TransactionConfidence.Listener()
{
@Override
public void onConfidenceChanged(Transaction tx, ChangeReason reason)
{
log.trace("onConfidenceChanged " + tx.getConfidence());
if (reason == ChangeReason.SEEN_PEERS)
{
listener.onDepositTxConfirmedUpdate(tx.getConfidence());
}
if (reason == ChangeReason.TYPE && tx.getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
{
listener.onDepositTxConfirmedInBlockchain();
depositTransaction.getConfidence().removeEventListener(this);
log.trace("Tx is in blockchain");
resultHandler.onResult();
}
}
}
);
}
}

View File

@ -0,0 +1,60 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishDepositTx
{
private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTx.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
WalletFacade walletFacade,
String preparedOffererDepositTxAsHex,
String signedTakerDepositTxAsHex,
String txConnOutAsHex,
String txScriptSigAsHex,
long offererTxOutIndex,
long takerTxOutIndex)
{
try
{
walletFacade.offererSignAndPublishTx(preparedOffererDepositTxAsHex,
signedTakerDepositTxAsHex,
txConnOutAsHex,
txScriptSigAsHex,
offererTxOutIndex,
takerTxOutIndex,
new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.trace("offererSignAndPublishTx succeeded " + transaction);
resultHandler.onResult(transaction);
}
@Override
public void onFailure(Throwable t)
{
log.error("offererSignAndPublishTx faultHandler.onFault:" + t);
faultHandler.onFault(t);
}
});
} catch (Exception e)
{
log.error("offererSignAndPublishTx faultHandler.onFault:" + e);
faultHandler.onFault(e);
}
}
public interface ResultHandler
{
void onResult(Transaction transaction);
}
}

View File

@ -0,0 +1,70 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import com.google.bitcoin.core.ECKey;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.util.Utilities;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyAndSignContract
{
private static final Logger log = LoggerFactory.getLogger(VerifyAndSignContract.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
CryptoFacade cryptoFacade,
String accountId,
BigInteger tradeAmount,
String takeOfferFeeTxId,
String messagePubKeyAsHex,
Offer offer,
String peersAccountId,
BankAccount bankAccount,
BankAccount peersBankAccount,
String takerMessagePubKey,
String peersContractAsJson,
ECKey registrationKey)
{
Contract contract = new Contract(offer,
tradeAmount,
takeOfferFeeTxId,
accountId,
peersAccountId,
bankAccount,
peersBankAccount,
messagePubKeyAsHex,
takerMessagePubKey);
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(peersContractAsJson))
{
log.trace("The 2 contracts as json does match");
String signature = cryptoFacade.signContract(registrationKey, contractAsJson);
//log.trace("signature: " + signature);
resultHandler.onResult(contract, contractAsJson, signature);
}
else
{
// TODO use diff output as feedback ?
log.error("Contracts are not matching.");
log.error("Offerers contractAsJson: " + contractAsJson);
log.error("Takers contractAsJson: " + peersContractAsJson);
faultHandler.onFault(new Exception("Contracts are not matching"));
}
}
public interface ResultHandler
{
void onResult(Contract contract, String contractAsJson, String signature);
}
}

View File

@ -0,0 +1,22 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakeOfferFeePayment
{
private static final Logger log = LoggerFactory.getLogger(VerifyTakeOfferFeePayment.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, WalletFacade walletFacade, String takeOfferFeeTxId)
{ //TODO mocked yet, need a confidence listeners
int numOfPeersSeenTx = walletFacade.getNumOfPeersSeenTx(takeOfferFeeTxId);
if (numOfPeersSeenTx > 2)
{
resultHandler.onResult();
}
}
}

View File

@ -0,0 +1,36 @@
package io.bitsquare.trade.protocol.tasks.offerer;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakerAccount
{
private static final Logger log = LoggerFactory.getLogger(VerifyTakerAccount.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, BlockChainFacade blockChainFacade, String peersAccountId, BankAccount peersBankAccount)
{
//TODO mocked yet
if (blockChainFacade.verifyAccountRegistration())
{
if (blockChainFacade.isAccountBlackListed(peersAccountId, peersBankAccount))
{
log.error("Taker is blacklisted");
faultHandler.onFault(new Exception("Taker is blacklisted"));
}
else
{
resultHandler.onResult();
}
}
else
{
log.error("Account registration validation for peer faultHandler.onFault.");
faultHandler.onFault(new Exception("Account registration validation for peer faultHandler.onFault."));
}
}
}

View File

@ -0,0 +1,60 @@
package io.bitsquare.trade.protocol.tasks.taker;
import com.google.bitcoin.core.ECKey;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import io.bitsquare.user.User;
import io.bitsquare.util.Utilities;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateAndSignContract
{
private static final Logger log = LoggerFactory.getLogger(CreateAndSignContract.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
CryptoFacade cryptoFacade,
Trade trade,
User user,
String peersAccountId,
BankAccount peersBankAccount,
ECKey registrationKey)
{
try
{
Contract contract = new Contract(trade.getOffer(),
trade.getTradeAmount(),
trade.getTakeOfferFeeTxId(),
peersAccountId,
user.getAccountId(),
peersBankAccount,
user.getCurrentBankAccount(),
trade.getOffer().getMessagePubKeyAsHex(),
user.getMessagePubKeyAsHex()
);
String contractAsJson = Utilities.objectToJson(contract);
String signature = cryptoFacade.signContract(registrationKey, contractAsJson);
//log.trace("contract: " + contract);
//log.debug("contractAsJson: " + contractAsJson);
//log.trace("contract signature: " + signature);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setContractTakerSignature(signature);
resultHandler.onResult();
} catch (Throwable t)
{
log.error("Exception at sign contract " + t);
faultHandler.onFault(t);
}
}
}

View File

@ -0,0 +1,39 @@
package io.bitsquare.trade.protocol.tasks.taker;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.GetPeerAddressListener;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetPeerAddress
{
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, MessageFacade messageFacade, String messagePubKeyAsHex)
{
messageFacade.getPeerAddress(messagePubKeyAsHex, new GetPeerAddressListener()
{
@Override
public void onResult(PeerAddress peerAddress)
{
log.trace("Received address = " + peerAddress.toString());
resultHandler.onResult(peerAddress);
}
@Override
public void onFailed()
{
log.error("Lookup for peer address faultHandler.onFault.");
faultHandler.onFault(new Exception("Lookup for peer address faultHandler.onFault."));
}
});
}
public interface ResultHandler
{
void onResult(PeerAddress peerAddress);
}
}

View File

@ -0,0 +1,46 @@
package io.bitsquare.trade.protocol.tasks.taker;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayDeposit
{
private static final Logger log = LoggerFactory.getLogger(PayDeposit.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, WalletFacade walletFacade, Trade trade, String offererPubKey, String preparedOffererDepositTxAsHex)
{
try
{
BigInteger collateralAmount = trade.getCollateralAmount();
Transaction signedTakerDepositTx = walletFacade.takerAddPaymentAndSignTx(trade.getTradeAmount().add(collateralAmount),
trade.getTradeAmount().add(collateralAmount).add(collateralAmount),
offererPubKey,
walletFacade.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString(),
trade.getOffer().getArbitrator().getPubKeyAsHex(),
preparedOffererDepositTxAsHex,
trade.getId());
log.trace("sharedModel.signedTakerDepositTx: " + signedTakerDepositTx);
long takerTxOutIndex = signedTakerDepositTx.getInput(1).getOutpoint().getIndex();
resultHandler.onResult(signedTakerDepositTx, takerTxOutIndex);
} catch (InsufficientMoneyException e)
{
log.error("Pay deposit faultHandler.onFault due InsufficientMoneyException " + e);
faultHandler.onFault(new Exception("Pay deposit faultHandler.onFault due InsufficientMoneyException " + e));
}
}
public interface ResultHandler
{
void onResult(Transaction signedTakerDepositTx, long takerTxOutIndex);
}
}

View File

@ -0,0 +1,48 @@
package io.bitsquare.trade.protocol.tasks.taker;
import com.google.bitcoin.core.InsufficientMoneyException;
import com.google.bitcoin.core.Transaction;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayTakeOfferFee
{
private static final Logger log = LoggerFactory.getLogger(PayTakeOfferFee.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, WalletFacade walletFacade, String tradeId)
{
log.trace("execute");
try
{
walletFacade.payTakeOfferFee(tradeId, new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.debug("Take offer fee paid successfully. Transaction ID = " + transaction.getHashAsString());
resultHandler.onResult(transaction);
}
@Override
public void onFailure(Throwable t)
{
log.error("Take offer fee paid faultHandler.onFault with exception: " + t);
faultHandler.onFault(new Exception("Take offer fee paid faultHandler.onFault with exception: " + t));
}
});
} catch (InsufficientMoneyException e)
{
log.error("Take offer fee paid faultHandler.onFault due InsufficientMoneyException " + e);
faultHandler.onFault(new Exception("Take offer fee paid faultHandler.onFault due InsufficientMoneyException " + e));
}
}
public interface ResultHandler
{
void onResult(Transaction transaction);
}
}

View File

@ -0,0 +1,35 @@
package io.bitsquare.trade.protocol.tasks.taker;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.protocol.messages.taker.RequestTakeOfferMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakeOffer
{
private static final Logger log = LoggerFactory.getLogger(RequestTakeOffer.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, PeerAddress peerAddress, MessageFacade messageFacade, String tradeId)
{
messageFacade.sendTradeMessage(peerAddress, new RequestTakeOfferMessage(tradeId), new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestTakeOfferMessage successfully arrived at peer");
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("RequestTakeOfferMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("RequestTakeOfferMessage faultHandler.onFault to arrive at peer"));
}
});
}
}

View File

@ -0,0 +1,39 @@
package io.bitsquare.trade.protocol.tasks.taker;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.messages.taker.PayoutTxPublishedMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendPayoutTxToOfferer
{
private static final Logger log = LoggerFactory.getLogger(SendPayoutTxToOfferer.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, PeerAddress peerAddress, MessageFacade messageFacade, Trade trade, String payoutTxAsHex)
{
PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(trade.getId(), payoutTxAsHex);
messageFacade.sendTradeMessage(peerAddress, tradeMessage, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("PayoutTxPublishedMessage successfully arrived at peer");
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("PayoutTxPublishedMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("PayoutTxPublishedMessage faultHandler.onFault to arrive at peer"));
}
});
}
}

View File

@ -0,0 +1,61 @@
package io.bitsquare.trade.protocol.tasks.taker;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.messages.taker.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import io.bitsquare.user.User;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedTakerDepositTxAsHex
{
private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTxAsHex.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
PeerAddress peerAddress,
MessageFacade messageFacade,
WalletFacade walletFacade,
Trade trade,
User user,
Transaction signedTakerDepositTx,
long takerTxOutIndex,
long offererTxOutIndex)
{
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(trade.getId(),
user.getCurrentBankAccount(),
user.getAccountId(),
user.getMessagePubKeyAsHex(),
Utils.bytesToHexString(signedTakerDepositTx.bitcoinSerialize()),
Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes()),
Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize()),
trade.getContractAsJson(),
trade.getTakerSignature(),
walletFacade.getAddressInfoByTradeID(trade.getId()).getAddressString(),
takerTxOutIndex,
offererTxOutIndex);
messageFacade.sendTradeMessage(peerAddress, tradeMessage, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("RequestOffererDepositPublicationMessage successfully arrived at peer");
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("RequestOffererDepositPublicationMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("RequestOffererDepositPublicationMessage faultHandler.onFault to arrive at peer"));
}
});
}
}

View File

@ -0,0 +1,48 @@
package io.bitsquare.trade.protocol.tasks.taker;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.protocol.messages.taker.TakeOfferFeePayedMessage;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import java.math.BigInteger;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendTakeOfferFeePayedTxId
{
private static final Logger log = LoggerFactory.getLogger(SendTakeOfferFeePayedTxId.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
PeerAddress peerAddress,
MessageFacade messageFacade,
String tradeId,
String takeOfferFeeTxId,
BigInteger tradeAmount,
String pubKeyAsHexString)
{
TakeOfferFeePayedMessage msg = new TakeOfferFeePayedMessage(tradeId,
takeOfferFeeTxId,
tradeAmount,
pubKeyAsHexString);
messageFacade.sendTradeMessage(peerAddress, msg, new OutgoingTradeMessageListener()
{
@Override
public void onResult()
{
log.trace("TakeOfferFeePayedMessage successfully arrived at peer");
resultHandler.onResult();
}
@Override
public void onFailed()
{
log.error("TakeOfferFeePayedMessage faultHandler.onFault to arrive at peer");
faultHandler.onFault(new Exception("TakeOfferFeePayedMessage faultHandler.onFault to arrive at peer"));
}
});
}
}

View File

@ -0,0 +1,68 @@
package io.bitsquare.trade.protocol.tasks.taker;
import com.google.bitcoin.core.Transaction;
import com.google.bitcoin.core.Utils;
import com.google.common.util.concurrent.FutureCallback;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import java.math.BigInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishPayoutTx
{
private static final Logger log = LoggerFactory.getLogger(SignAndPublishPayoutTx.class);
public static void run(ResultHandler resultHandler,
FaultHandler faultHandler,
WalletFacade walletFacade,
Trade trade,
String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererPayoutAddress
)
{
try
{
walletFacade.takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress,
trade.getId(),
new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.debug("takerSignsAndSendsTx " + transaction);
String payoutTxAsHex = Utils.bytesToHexString(transaction.bitcoinSerialize());
resultHandler.onResult(transaction, payoutTxAsHex);
}
@Override
public void onFailure(Throwable t)
{
log.error("Exception at takerSignsAndSendsTx " + t);
faultHandler.onFault(t);
}
});
} catch (Exception e)
{
log.error("Exception at takerSignsAndSendsTx " + e);
faultHandler.onFault(e);
}
}
public interface ResultHandler
{
void onResult(Transaction transaction, String payoutTxAsHex);
}
}

View File

@ -0,0 +1,35 @@
package io.bitsquare.trade.protocol.tasks.taker;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.trade.protocol.tasks.FaultHandler;
import io.bitsquare.trade.protocol.tasks.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyOffererAccount
{
private static final Logger log = LoggerFactory.getLogger(VerifyOffererAccount.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, BlockChainFacade blockChainFacade, String peersAccountId, BankAccount peersBankAccount)
{
//TODO mocked yet
if (blockChainFacade.verifyAccountRegistration())
{
if (blockChainFacade.isAccountBlackListed(peersAccountId, peersBankAccount))
{
log.error("Offerer is blacklisted");
faultHandler.onFault(new Exception("Offerer is blacklisted"));
}
else
{
resultHandler.onResult();
}
}
else
{
log.error("Account Registration for peer faultHandler.onFault.");
faultHandler.onFault(new Exception("Account Registration for peer faultHandler.onFault."));
}
}
}

View File

@ -35,7 +35,7 @@ public class User implements Serializable
{
if (savedUser != null)
{
accountID = savedUser.getAccountID();
accountID = savedUser.getAccountId();
messagePubKeyAsHex = savedUser.getMessagePubKeyAsHex();
isOnline = savedUser.getIsOnline();
bankAccounts = savedUser.getBankAccounts();
@ -92,7 +92,7 @@ public class User implements Serializable
}
public String getAccountID()
public String getAccountId()
{
return accountID;
}

View File

@ -0,0 +1,34 @@
package io.bitsquare.util;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class Validator
{
private static final Logger log = LoggerFactory.getLogger(Validator.class);
public static String validString(String value)
{
checkNotNull(value);
checkArgument(value.length() > 0);
return value;
}
public static long validNonNegativeLong(long value)
{
checkArgument(value >= 0);
return value;
}
public static PeerAddress validPeerAddress(PeerAddress value)
{
checkNotNull(value);
return value;
}
}

View File

@ -1,37 +0,0 @@
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

@ -1,47 +0,0 @@
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

@ -1,34 +0,0 @@
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

@ -1,44 +0,0 @@
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

@ -1,15 +0,0 @@
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

@ -1,48 +0,0 @@
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

@ -1,54 +0,0 @@
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

@ -1,68 +0,0 @@
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

@ -1,13 +0,0 @@
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

@ -1,55 +0,0 @@
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

@ -1,19 +0,0 @@
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

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

View File

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

View File

@ -3,7 +3,6 @@ package io.bitsquare;
import io.bitsquare.btc.BtcValidatorTest;
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 +11,6 @@ import org.junit.runners.Suite;
BtcValidatorTest.class,
BitSquareConverterTest.class,
BitSquareValidatorTest.class,
SequenceSchedulerTest.class
})
public class BitSquareTestSuite

View File

@ -1,228 +0,0 @@
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

@ -1,29 +0,0 @@
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

@ -1,27 +0,0 @@
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();
}
}