Refactor tasks with taskrunner

This commit is contained in:
Manfred Karrer 2015-03-11 19:02:18 +01:00
parent 033709f288
commit ef4937bba1
33 changed files with 1417 additions and 719 deletions

View File

@ -167,6 +167,7 @@ class PendingTradesDataModel implements Activatable, DataModel {
}
void fiatPaymentReceived() {
getTrade().setState(Trade.State.FIAT_PAYMENT_RECEIVED);
tradeManager.fiatPaymentReceived(getTrade().getId());
}

View File

@ -230,11 +230,11 @@ class PendingTradesViewModel extends ActivatableWithDataModel<PendingTradesDataM
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_START_PAYMENT :
State.TAKER_SELLER_WAIT_PAYMENT_STARTED);
break;
case PAYMENT_STARTED:
case FIAT_PAYMENT_STARTED:
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_WAIT_CONFIRM_PAYMENT_RECEIVED :
State.TAKER_SELLER_CONFIRM_RECEIVE_PAYMENT);
break;
case COMPLETED:
case PAYOUT_PUBLISHED:
state.set(dataModel.isOfferer() ? State.OFFERER_BUYER_COMPLETED : State.TAKER_SELLER_COMPLETED);
break;
case FAILED:

View File

@ -162,12 +162,12 @@ class TakeOfferDataModel implements Activatable, DataModel {
" That should not happen and needs more investigation why it can happen.");
}
break;
case PAYMENT_STARTED:
case FIAT_PAYMENT_STARTED:
break;
case FAILED:
requestTakeOfferErrorMessage.set("An error occurred. Error: " + trade.getFault().getMessage());
break;
case COMPLETED:
case PAYOUT_PUBLISHED:
break;
default:
log.error("Unhandled trade state: " + newValue);

View File

@ -76,7 +76,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
@FXML InputTextField amountTextField;
@FXML AddressTextField addressTextField;
@FXML BalanceTextField balanceTextField;
@FXML ProgressIndicator takeOfferSpinner;
@FXML ProgressIndicator takeOfferSpinner, isOfferAvailableProgressIndicator;
@FXML InfoDisplay advancedInfoDisplay, fundsBoxInfoDisplay;
@FXML TitledGroupBg priceAmountPane, payFundsPane, showDetailsPane;
@FXML Button showPaymentInfoScreenButton, showAdvancedSettingsButton, takeOfferButton;
@ -88,7 +88,6 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
bankAccountTypeLabel, bankAccountCurrencyLabel, bankAccountCountyLabel, acceptedCountriesLabel,
acceptedLanguagesLabel, acceptedArbitratorsLabel, amountBtcLabel, priceDescriptionLabel,
volumeDescriptionLabel, takeOfferSpinnerInfoLabel;
@FXML ProgressIndicator isOfferAvailableProgressIndicator;
private ImageView expand;
private ImageView collapse;

View File

@ -18,6 +18,7 @@
package io.bitsquare.trade;
import io.bitsquare.offer.Offer;
import io.bitsquare.util.tasks.Task;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
@ -32,6 +33,24 @@ import javafx.beans.property.SimpleObjectProperty;
public class Trade implements Serializable {
private static final long serialVersionUID = -8275323072940974077L;
private Class<? extends Task> previousTask;
private Class<? extends Task> currentTask;
public void setCurrentTask(Class<? extends Task> currentTask) {
this.currentTask = currentTask;
}
public Class<? extends Task> getCurrentTask() {
return currentTask;
}
public void setPreviousTask(Class<? extends Task> previousTask) {
this.previousTask = previousTask;
}
public Class<? extends Task> getPreviousTask() {
return previousTask;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Enum
@ -43,9 +62,10 @@ public class Trade implements Serializable {
OFFERER_REJECTED, /* For taker only*/
DEPOSIT_PUBLISHED,
DEPOSIT_CONFIRMED,
PAYMENT_STARTED,
FAILED,
COMPLETED
FIAT_PAYMENT_STARTED,
FIAT_PAYMENT_RECEIVED,
PAYOUT_PUBLISHED,
FAILED
}
private final Offer offer;

View File

@ -31,7 +31,6 @@ import io.bitsquare.offer.OpenOffer;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.handlers.TransactionResultHandler;
import io.bitsquare.trade.listeners.BuyerAcceptsOfferProtocolListener;
import io.bitsquare.trade.listeners.SellerTakesOfferProtocolListener;
import io.bitsquare.trade.protocol.placeoffer.PlaceOfferProtocol;
import io.bitsquare.trade.protocol.trade.OfferMessage;
import io.bitsquare.trade.protocol.trade.TradeMessage;
@ -43,6 +42,7 @@ import io.bitsquare.trade.protocol.trade.offerer.messages.RespondToTakeOfferRequ
import io.bitsquare.trade.protocol.trade.offerer.messages.TakerDepositPaymentRequestMessage;
import io.bitsquare.trade.protocol.trade.offerer.tasks.IsOfferAvailableResponse;
import io.bitsquare.trade.protocol.trade.taker.RequestIsOfferAvailableProtocol;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferProtocol;
import io.bitsquare.trade.protocol.trade.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.taker.messages.RequestIsOfferAvailableMessage;
@ -137,7 +137,7 @@ public class TradeManager {
closedTrades.putAll((Map<String, Trade>) closedTradesObject);
}
tradeMessageService.addHandleNewMessageListener(this::handleNewMessage);
tradeMessageService.addMessageHandler(this::handleNewMessage);
}
@ -146,7 +146,7 @@ public class TradeManager {
///////////////////////////////////////////////////////////////////////////////////////////
public void cleanup() {
tradeMessageService.removeHandleNewMessageListener(this::handleNewMessage);
tradeMessageService.removeMessageHandler(this::handleNewMessage);
}
@ -239,6 +239,11 @@ public class TradeManager {
pendingTrades.remove(trade.getId());
persistPendingTrades();
if (takerAsSellerProtocolMap.containsKey(trade.getId()))
takerAsSellerProtocolMap.remove(trade.getId());
else if (offererAsBuyerProtocolMap.containsKey(trade.getId()))
offererAsBuyerProtocolMap.remove(trade.getId());
closedTrades.put(trade.getId(), trade);
persistClosedTrades();
}
@ -274,9 +279,9 @@ public class TradeManager {
public void onOfferAccepted(Offer offer) {
persistPendingTrades();
//TODO do that later
requestRemoveOpenOffer(offer.getId(),
/*requestRemoveOpenOffer(offer.getId(),
() -> log.debug("remove offer was successful"),
(message) -> log.error(message));
(message) -> log.error(message));*/
}
@Override
@ -295,7 +300,7 @@ public class TradeManager {
@Override
public void onPayoutTxPublished(Transaction payoutTx) {
trade.setPayoutTx(payoutTx);
trade.setState(Trade.State.COMPLETED);
trade.setState(Trade.State.PAYOUT_PUBLISHED);
// We close the trade when the user has withdrawn his trade funds (see #283)
//closeTrade(trade);
@ -363,76 +368,48 @@ public class TradeManager {
Trade trade = createTrade(offer);
trade.setTradeAmount(amount);
SellerTakesOfferProtocolListener listener = new SellerTakesOfferProtocolListener() {
@Override
public void onTakeOfferRequestAccepted() {
persistPendingTrades();
trade.stateProperty().addListener((ov, oldValue, newValue) -> {
log.debug("trade state = " + newValue);
switch (newValue) {
case OPEN:
break;
case OFFERER_ACCEPTED:
persistPendingTrades();
break;
case OFFERER_REJECTED:
removeFailedTrade(trade);
break;
case DEPOSIT_PUBLISHED:
persistPendingTrades();
break;
case DEPOSIT_CONFIRMED:
break;
case FIAT_PAYMENT_STARTED:
persistPendingTrades();
break;
case FAILED:
removeFailedTrade(trade);
break;
case PAYOUT_PUBLISHED:
persistPendingTrades();
break;
default:
log.error("Unhandled trade state: " + newValue);
break;
}
});
@Override
public void onTakeOfferRequestRejected() {
removeFailedTrade(trade);
}
@Override
public void onDepositTxPublished() {
persistPendingTrades();
}
@Override
public void onBankTransferInited(String tradeId) {
trade.setState(Trade.State.PAYMENT_STARTED);
persistPendingTrades();
}
@Override
public void onPayoutTxPublished(Trade trade, Transaction payoutTx) {
trade.setPayoutTx(payoutTx);
trade.setState(Trade.State.COMPLETED);
// We close the trade when the user has withdrawn his trade funds (see #283)
//closeTrade(trade);
persistPendingTrades();
}
@Override
public void onFault(Throwable throwable, SellerTakesOfferProtocol.State state) {
log.error("Error while executing trade process at state: " + state + " / " + throwable);
switch (state) {
case GetPeerAddress:
removeFailedTrade(trade);
break;
case RequestTakeOffer:
removeFailedTrade(trade);
break;
case ValidateRespondToTakeOfferRequestMessage:
// TODO might need further inspection. Removal could be used for sabotage.
//removeFailedTrade(trade);
break;
case PayTakeOfferFee:
removeFailedTrade(trade);
break;
case SendTakeOfferFeePayedMessage:
removeFailedTrade(trade);
break;
case ValidateTakerDepositPaymentRequestMessage:
removeFailedTrade(trade);
break;
}
}
};
SellerTakesOfferProtocol sellerTakesOfferProtocol = new SellerTakesOfferProtocol(
SellerTakesOfferModel model = new SellerTakesOfferModel(
trade,
listener,
tradeMessageService,
walletService,
blockChainService,
signatureService,
user);
SellerTakesOfferProtocol sellerTakesOfferProtocol = new SellerTakesOfferProtocol(model);
takerAsSellerProtocolMap.put(trade.getId(), sellerTakesOfferProtocol);
sellerTakesOfferProtocol.start();
return trade;
@ -444,7 +421,7 @@ public class TradeManager {
public void fiatPaymentStarted(String tradeId) {
if (offererAsBuyerProtocolMap.get(tradeId) != null) {
offererAsBuyerProtocolMap.get(tradeId).handleUIEventBankTransferStarted();
pendingTrades.get(tradeId).setState(Trade.State.PAYMENT_STARTED);
pendingTrades.get(tradeId).setState(Trade.State.FIAT_PAYMENT_STARTED);
persistPendingTrades();
}
else {
@ -455,7 +432,7 @@ public class TradeManager {
}
public void fiatPaymentReceived(String tradeId) {
takerAsSellerProtocolMap.get(tradeId).handleUIEventFiatReceived();
takerAsSellerProtocolMap.get(tradeId).handleFiatReceivedUIEvent();
}
public void requestIsOfferAvailable(Offer offer) {
@ -524,29 +501,19 @@ public class TradeManager {
createOffererAsBuyerProtocol(tradeId, sender);
}
else if (tradeMessage instanceof RespondToTakeOfferRequestMessage) {
takerAsSellerProtocolMap.get(tradeId).handleRespondToTakeOfferRequestMessage((RespondToTakeOfferRequestMessage) tradeMessage);
}
else if (tradeMessage instanceof TakeOfferFeePayedMessage) {
offererAsBuyerProtocolMap.get(tradeId).handleTakeOfferFeePayedMessage((TakeOfferFeePayedMessage) tradeMessage);
}
else if (tradeMessage instanceof TakerDepositPaymentRequestMessage) {
takerAsSellerProtocolMap.get(tradeId).handleTakerDepositPaymentRequestMessage((TakerDepositPaymentRequestMessage) tradeMessage);
}
else if (tradeMessage instanceof RequestOffererPublishDepositTxMessage) {
offererAsBuyerProtocolMap.get(tradeId).handleRequestOffererPublishDepositTxMessage((RequestOffererPublishDepositTxMessage) tradeMessage);
}
else if (tradeMessage instanceof DepositTxPublishedMessage) {
persistPendingTrades();
takerAsSellerProtocolMap.get(tradeId).handleDepositTxPublishedMessage((DepositTxPublishedMessage) tradeMessage);
// persistPendingTrades();
}
else if (tradeMessage instanceof BankTransferInitedMessage) {
// Here happened a null pointer. I assume the only possible reason was that we got a null for the
// tradeID
// as the takerAsSellerProtocolMap need to have that trade got added earlier.
// For getting better info we add a check. tradeId is checked above.
if (takerAsSellerProtocolMap.get(tradeId) == null)
log.error("takerAsSellerProtocolMap.get(tradeId) = null. That must not happen.");
takerAsSellerProtocolMap.get(tradeId).handleBankTransferInitedMessage((BankTransferInitedMessage) tradeMessage);
}
else if (tradeMessage instanceof PayoutTxPublishedMessage) {
offererAsBuyerProtocolMap.get(tradeId).handlePayoutTxPublishedMessage((PayoutTxPublishedMessage) tradeMessage);

View File

@ -21,7 +21,7 @@ import io.bitsquare.network.Message;
import io.bitsquare.network.MessageBroker;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.listeners.GetPeerAddressListener;
import io.bitsquare.trade.listeners.ProcessNewMessageListener;
import io.bitsquare.trade.listeners.MessageHandler;
import io.bitsquare.trade.listeners.SendMessageListener;
import java.security.PublicKey;
@ -34,9 +34,9 @@ public interface TradeMessageService extends MessageBroker {
void sendMessage(Peer peer, Message message, SendMessageListener listener);
void addHandleNewMessageListener(ProcessNewMessageListener listener);
void addMessageHandler(MessageHandler listener);
void removeHandleNewMessageListener(ProcessNewMessageListener listener);
void removeMessageHandler(MessageHandler listener);
void getPeerAddress(PublicKey messagePublicKey, GetPeerAddressListener getPeerAddressListener);
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade;
import io.bitsquare.trade.protocol.trade.TradeSharedModel;
import io.bitsquare.util.handlers.FaultHandler;
import io.bitsquare.util.handlers.ResultHandler;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TradeTaskRunner<T extends TradeSharedModel> extends TaskRunner<TradeSharedModel> {
private static final Logger log = LoggerFactory.getLogger(TradeTaskRunner.class);
public TradeTaskRunner(T sharedModel, ResultHandler resultHandler, FaultHandler faultHandler) {
super(sharedModel, resultHandler, faultHandler);
}
@Override
protected void setCurrentTask(Class<? extends Task> task) {
super.setCurrentTask(task);
sharedModel.getTrade().setCurrentTask(task);
}
@Override
protected void setPreviousTask(Class<? extends Task> task) {
super.setPreviousTask(task);
if (task != null)
sharedModel.getTrade().setPreviousTask(task);
}
@Override
public void handleFault(String message, @NotNull Throwable throwable) {
sharedModel.getTrade().setState(Trade.State.FAILED);
super.handleFault(message, throwable);
}
}

View File

@ -20,6 +20,6 @@ package io.bitsquare.trade.listeners;
import io.bitsquare.network.Message;
import io.bitsquare.network.Peer;
public interface ProcessNewMessageListener {
public interface MessageHandler {
void handleMessage(Message message, Peer sender);
}

View File

@ -15,24 +15,24 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.listeners;
package io.bitsquare.trade.protocol.trade;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferProtocol;
import io.bitsquare.util.tasks.SharedModel;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public interface SellerTakesOfferProtocolListener {
void onDepositTxPublished();
public class TradeSharedModel extends SharedModel {
private static final Logger log = LoggerFactory.getLogger(TradeSharedModel.class);
void onBankTransferInited(String tradeId);
public Trade getTrade() {
return trade;
}
void onPayoutTxPublished(Trade trade, Transaction payoutTx);
void onFault(Throwable throwable, SellerTakesOfferProtocol.State state);
void onTakeOfferRequestAccepted();
void onTakeOfferRequestRejected();
protected final Trade trade;
public TradeSharedModel(Trade trade) {
this.trade = trade;
}
}

View File

@ -0,0 +1,341 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.taker;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.network.Peer;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.protocol.trade.TradeMessage;
import io.bitsquare.trade.protocol.trade.TradeSharedModel;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Transaction;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SellerTakesOfferModel extends TradeSharedModel {
private static final Logger log = LoggerFactory.getLogger(SellerTakesOfferModel.class);
private Transaction payoutTx;
private String payoutTxAsHex;
public void setPayoutTx(Transaction payoutTx) {
this.payoutTx = payoutTx;
}
public Transaction getPayoutTx() {
return payoutTx;
}
public void setPayoutTxAsHex(String payoutTxAsHex) {
this.payoutTxAsHex = payoutTxAsHex;
}
public String getPayoutTxAsHex() {
return payoutTxAsHex;
}
public enum State {
Init,
GetPeerAddress,
RequestTakeOffer,
ValidateRespondToTakeOfferRequestMessage,
PayTakeOfferFee,
SendTakeOfferFeePayedMessage,
ValidateTakerDepositPaymentRequestMessage,
VerifyOffererAccount,
CreateAndSignContract,
PayDeposit,
SendSignedTakerDepositTxAsHex,
ValidateDepositTxPublishedMessage,
TakerCommitDepositTx,
handleBankTransferInitedMessage,
SignAndPublishPayoutTx,
SendPayoutTxToOfferer
}
// provided data
private final TradeMessageService tradeMessageService;
private final WalletService walletService;
private final BlockChainService blockChainService;
private final SignatureService signatureService;
// derived
private final Offer offer;
private final String tradeId;
private final BankAccount bankAccount;
private final String accountId;
private final PublicKey messagePublicKey;
private final Coin tradeAmount;
private final String tradePubKeyAsHex;
private final ECKey accountKey;
private final PublicKey offererMessagePublicKey;
private final Coin securityDeposit;
private final String arbitratorPubKey;
// written/read by task
private Peer peer;
// written by messages, read by tasks
private String peersAccountId;
private BankAccount peersBankAccount;
private String peersPubKey;
private String preparedPeersDepositTxAsHex;
private long peersTxOutIndex;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private Coin offererPaybackAmount;
private Coin takerPaybackAmount;
private String offererPayoutAddress;
private Transaction signedTakerDepositTx;
private TradeMessage tradeMessage;
// state
private State state;
public SellerTakesOfferModel(Trade trade,
TradeMessageService tradeMessageService,
WalletService walletService,
BlockChainService blockChainService,
SignatureService signatureService,
User user) {
super(trade);
this.tradeMessageService = tradeMessageService;
this.walletService = walletService;
this.blockChainService = blockChainService;
this.signatureService = signatureService;
offer = trade.getOffer();
tradeId = trade.getId();
tradeAmount = trade.getTradeAmount();
securityDeposit = trade.getSecurityDeposit();
//TODO use 1. for now
arbitratorPubKey = trade.getOffer().getArbitrators().get(0).getPubKeyAsHex();
offererMessagePublicKey = offer.getMessagePublicKey();
bankAccount = user.getCurrentBankAccount().get();
accountId = user.getAccountId();
messagePublicKey = user.getMessagePublicKey();
tradePubKeyAsHex = walletService.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
accountKey = walletService.getRegistrationAddressEntry().getKey();
state = State.Init;
}
// Setters
public void setPeer(Peer peer) {
this.peer = peer;
}
public void setPeersAccountId(String peersAccountId) {
this.peersAccountId = peersAccountId;
}
public void setPeersBankAccount(BankAccount peersBankAccount) {
this.peersBankAccount = peersBankAccount;
}
public void setPeersPubKey(String peersPubKey) {
this.peersPubKey = peersPubKey;
}
public void setPreparedPeersDepositTxAsHex(String preparedPeersDepositTxAsHex) {
this.preparedPeersDepositTxAsHex = preparedPeersDepositTxAsHex;
}
public void setPeersTxOutIndex(long peersTxOutIndex) {
this.peersTxOutIndex = peersTxOutIndex;
}
public void setDepositTxAsHex(String depositTxAsHex) {
this.depositTxAsHex = depositTxAsHex;
}
public void setOffererSignatureR(String offererSignatureR) {
this.offererSignatureR = offererSignatureR;
}
public void setOffererSignatureS(String offererSignatureS) {
this.offererSignatureS = offererSignatureS;
}
public void setOffererPaybackAmount(Coin offererPaybackAmount) {
this.offererPaybackAmount = offererPaybackAmount;
}
public void setTakerPaybackAmount(Coin takerPaybackAmount) {
this.takerPaybackAmount = takerPaybackAmount;
}
public void setOffererPayoutAddress(String offererPayoutAddress) {
this.offererPayoutAddress = offererPayoutAddress;
}
public void setState(State state) {
this.state = state;
}
public void setTradeMessage(TradeMessage tradeMessage) {
this.tradeMessage = tradeMessage;
}
public void setSignedTakerDepositTx(Transaction signedTakerDepositTx) {
this.signedTakerDepositTx = signedTakerDepositTx;
}
// Getters
public Trade getTrade() {
return trade;
}
public TradeMessageService getTradeMessageService() {
return tradeMessageService;
}
public WalletService getWalletService() {
return walletService;
}
public BlockChainService getBlockChainService() {
return blockChainService;
}
public SignatureService getSignatureService() {
return signatureService;
}
public Offer getOffer() {
return offer;
}
public String getTradeId() {
return tradeId;
}
public BankAccount getBankAccount() {
return bankAccount;
}
public String getAccountId() {
return accountId;
}
public PublicKey getMessagePublicKey() {
return messagePublicKey;
}
public Coin getTradeAmount() {
return tradeAmount;
}
public String getTradePubKeyAsHex() {
return tradePubKeyAsHex;
}
public ECKey getAccountKey() {
return accountKey;
}
public PublicKey getOffererMessagePublicKey() {
return offererMessagePublicKey;
}
public Coin getSecurityDeposit() {
return securityDeposit;
}
public String getArbitratorPubKey() {
return arbitratorPubKey;
}
public Peer getPeer() {
return peer;
}
public String getPeersAccountId() {
return peersAccountId;
}
public BankAccount getPeersBankAccount() {
return peersBankAccount;
}
public String getPeersPubKey() {
return peersPubKey;
}
public String getPreparedPeersDepositTxAsHex() {
return preparedPeersDepositTxAsHex;
}
public long getPeersTxOutIndex() {
return peersTxOutIndex;
}
public String getDepositTxAsHex() {
return depositTxAsHex;
}
public String getOffererSignatureR() {
return offererSignatureR;
}
public String getOffererSignatureS() {
return offererSignatureS;
}
public Coin getOffererPaybackAmount() {
return offererPaybackAmount;
}
public Coin getTakerPaybackAmount() {
return takerPaybackAmount;
}
public String getOffererPayoutAddress() {
return offererPayoutAddress;
}
public State getState() {
return state;
}
public TradeMessage getTradeMessage() {
return tradeMessage;
} public Transaction getSignedTakerDepositTx() {
return signedTakerDepositTx;
}
}

View File

@ -17,16 +17,11 @@
package io.bitsquare.trade.protocol.trade.taker;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.network.Message;
import io.bitsquare.network.Peer;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SellerTakesOfferProtocolListener;
import io.bitsquare.trade.TradeTaskRunner;
import io.bitsquare.trade.protocol.trade.TradeMessage;
import io.bitsquare.trade.protocol.trade.offerer.messages.BankTransferInitedMessage;
import io.bitsquare.trade.protocol.trade.offerer.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.offerer.messages.RespondToTakeOfferRequestMessage;
@ -41,21 +36,17 @@ import io.bitsquare.trade.protocol.trade.taker.tasks.SendSignedTakerDepositTxAsH
import io.bitsquare.trade.protocol.trade.taker.tasks.SendTakeOfferFeePayedMessage;
import io.bitsquare.trade.protocol.trade.taker.tasks.SignAndPublishPayoutTx;
import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCommitDepositTx;
import io.bitsquare.trade.protocol.trade.taker.tasks.ValidateBankTransferInitedMessage;
import io.bitsquare.trade.protocol.trade.taker.tasks.ValidateDepositTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.taker.tasks.ValidateRespondToTakeOfferRequestMessage;
import io.bitsquare.trade.protocol.trade.taker.tasks.ValidateTakerDepositPaymentRequestMessage;
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOfferFeePayment;
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOffererAccount;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Transaction;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.*;
import static io.bitsquare.util.Validator.*;
import static io.bitsquare.util.Validator.nonEmptyStringOf;
/**
@ -68,395 +59,160 @@ import static io.bitsquare.util.Validator.*;
public class SellerTakesOfferProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerTakesOfferProtocol.class);
public enum State {
Init,
GetPeerAddress,
RequestTakeOffer,
ValidateRespondToTakeOfferRequestMessage,
PayTakeOfferFee,
SendTakeOfferFeePayedMessage,
ValidateTakerDepositPaymentRequestMessage,
VerifyOffererAccount,
CreateAndSignContract,
PayDeposit,
SendSignedTakerDepositTxAsHex,
ValidateDepositTxPublishedMessage,
TakerCommitDepositTx,
handleBankTransferInitedMessage,
SignAndPublishPayoutTx,
SendPayoutTxToOfferer
}
// provided data
private final Trade trade;
private final SellerTakesOfferProtocolListener listener;
private final TradeMessageService tradeMessageService;
private final WalletService walletService;
private final BlockChainService blockChainService;
private final SignatureService signatureService;
// derived
private final Offer offer;
private final String tradeId;
private final BankAccount bankAccount;
private final String accountId;
private final PublicKey messagePublicKey;
private final Coin tradeAmount;
private final String tradePubKeyAsHex;
private final ECKey accountKey;
private final PublicKey offererMessagePublicKey;
private final Coin securityDeposit;
private final String arbitratorPubKey;
// written/read by task
private Peer peer;
// written by messages, read by tasks
private String peersAccountId;
private BankAccount peersBankAccount;
private String peersPubKey;
private String preparedPeersDepositTxAsHex;
private long peersTxOutIndex;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private Coin offererPaybackAmount;
private Coin takerPaybackAmount;
private String offererPayoutAddress;
// state
private State state;
private final SellerTakesOfferModel model;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public SellerTakesOfferProtocol(Trade trade,
SellerTakesOfferProtocolListener listener,
TradeMessageService tradeMessageService,
WalletService walletService,
BlockChainService blockChainService,
SignatureService signatureService,
User user) {
this.trade = trade;
this.listener = listener;
this.tradeMessageService = tradeMessageService;
this.walletService = walletService;
this.blockChainService = blockChainService;
this.signatureService = signatureService;
offer = trade.getOffer();
tradeId = trade.getId();
tradeAmount = trade.getTradeAmount();
securityDeposit = trade.getSecurityDeposit();
//TODO use 1. for now
arbitratorPubKey = trade.getOffer().getArbitrators().get(0).getPubKeyAsHex();
offererMessagePublicKey = offer.getMessagePublicKey();
bankAccount = user.getCurrentBankAccount().get();
accountId = user.getAccountId();
messagePublicKey = user.getMessagePublicKey();
tradePubKeyAsHex = walletService.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
accountKey = walletService.getRegistrationAddressEntry().getKey();
state = State.Init;
public SellerTakesOfferProtocol(SellerTakesOfferModel model) {
this.model = model;
}
public void start() {
getPeerAddress();
model.getTradeMessageService().addMessageHandler(this::handleMessage);
TradeTaskRunner<SellerTakesOfferModel> sequence1 = new TradeTaskRunner<>(model,
() -> {
log.debug("sequence1 completed");
},
(message, throwable) -> {
log.error(message);
}
);
sequence1.addTasks(
GetPeerAddress.class,
RequestTakeOffer.class
);
sequence1.run();
}
// 1. GetPeerAddress
private void getPeerAddress() {
log.debug("getPeerAddress called: state = " + state);
state = State.GetPeerAddress;
GetPeerAddress.run(this::handleGetPeerAddressResult, this::handleErrorMessage, tradeMessageService, offererMessagePublicKey);
public void cleanup() {
model.getTradeMessageService().removeMessageHandler(this::handleMessage);
}
// 2. RequestTakeOffer
private void handleGetPeerAddressResult(Peer peer) {
log.debug("handleGetPeerAddressResult called: state = " + state);
this.peer = peer;
state = State.RequestTakeOffer;
RequestTakeOffer.run(this::handleErrorMessage, tradeMessageService, peer, tradeId);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
// Incoming message handling
///////////////////////////////////////////////////////////////////////////////////////////
// 5. handleRespondToTakeOfferRequestMessage
public void handleRespondToTakeOfferRequestMessage(RespondToTakeOfferRequestMessage message) {
log.debug("handleRespondToTakeOfferRequestMessage called: state = " + state);
private void handleMessage(Message message, Peer sender) {
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName());
if (message instanceof TradeMessage) {
TradeMessage tradeMessage = (TradeMessage) message;
nonEmptyStringOf(tradeMessage.getTradeId());
try {
// validation
checkState(state == State.RequestTakeOffer);
state = State.ValidateRespondToTakeOfferRequestMessage;
checkTradeId(tradeId, message);
// apply new state
if (message.isTakeOfferRequestAccepted()) {
trade.setState(Trade.State.OFFERER_ACCEPTED);
listener.onTakeOfferRequestAccepted();
// next task
payTakeOfferFee();
if (tradeMessage instanceof RespondToTakeOfferRequestMessage) {
handleTradeMessage((RespondToTakeOfferRequestMessage) tradeMessage);
}
else if (tradeMessage instanceof TakerDepositPaymentRequestMessage) {
handleTradeMessage((TakerDepositPaymentRequestMessage) tradeMessage);
}
else if (tradeMessage instanceof DepositTxPublishedMessage) {
handleTradeMessage((DepositTxPublishedMessage) tradeMessage);
}
else if (tradeMessage instanceof BankTransferInitedMessage) {
handleTradeMessage((BankTransferInitedMessage) tradeMessage);
}
else {
// exit case
trade.setState(Trade.State.OFFERER_REJECTED);
listener.onTakeOfferRequestRejected();
log.error("Incoming message not supported. " + tradeMessage);
}
} catch (Throwable t) {
handleValidationFault(t);
}
}
// 6. PayTakeOfferFee
private void payTakeOfferFee() {
state = State.PayTakeOfferFee;
PayTakeOfferFee.run(this::handlePayTakeOfferFeeResult, this::handleFault, walletService, tradeId);
private void handleTradeMessage(RespondToTakeOfferRequestMessage tradeMessage) {
model.setTradeMessage(tradeMessage);
TradeTaskRunner<SellerTakesOfferModel> sequence2 = new TradeTaskRunner<>(model,
() -> {
log.debug("sequence2 completed");
},
(message, throwable) -> {
log.error(message);
}
);
sequence2.addTasks(
ValidateRespondToTakeOfferRequestMessage.class,
PayTakeOfferFee.class,
SendTakeOfferFeePayedMessage.class
);
sequence2.run();
}
// 7. SendTakeOfferFeePayedMessage
private void handlePayTakeOfferFeeResult(String takeOfferFeeTxId) {
log.debug("handlePayTakeOfferFeeResult called: state = " + state);
trade.setTakeOfferFeeTxID(takeOfferFeeTxId);
state = State.SendTakeOfferFeePayedMessage;
SendTakeOfferFeePayedMessage.run(this::handleErrorMessage, peer, tradeMessageService, tradeId, takeOfferFeeTxId, tradeAmount, tradePubKeyAsHex);
private void handleTradeMessage(TakerDepositPaymentRequestMessage tradeMessage) {
model.setTradeMessage(tradeMessage);
TradeTaskRunner<SellerTakesOfferModel> sequence3 = new TradeTaskRunner<>(model,
() -> {
log.debug("sequence3 completed");
},
(message, throwable) -> {
log.error(message);
}
);
sequence3.addTasks(
ValidateTakerDepositPaymentRequestMessage.class,
VerifyOffererAccount.class,
CreateAndSignContract.class,
PayDeposit.class,
SendSignedTakerDepositTxAsHex.class
);
sequence3.run();
}
private void handleTradeMessage(DepositTxPublishedMessage tradeMessage) {
model.setTradeMessage(tradeMessage);
TradeTaskRunner<SellerTakesOfferModel> sequence4 = new TradeTaskRunner<>(model,
() -> {
log.debug("sequence4 completed");
},
(message, throwable) -> {
log.error(message);
}
);
sequence4.addTasks(
ValidateDepositTxPublishedMessage.class,
TakerCommitDepositTx.class
);
sequence4.run();
}
private void handleTradeMessage(BankTransferInitedMessage tradeMessage) {
model.setTradeMessage(tradeMessage);
TradeTaskRunner<SellerTakesOfferModel> sequence5 = new TradeTaskRunner<>(model,
() -> {
log.debug("sequence5 completed");
model.getTrade().setState(Trade.State.FIAT_PAYMENT_STARTED);
},
(message, throwable) -> {
log.error(message);
}
);
sequence5.addTasks(ValidateBankTransferInitedMessage.class);
sequence5.run();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 11. VerifyOffererAccount
public void handleTakerDepositPaymentRequestMessage(TakerDepositPaymentRequestMessage message) {
log.debug("handleTakerDepositPaymentRequestMessage called: state = " + state);
try {
// validation
checkState(state == State.SendTakeOfferFeePayedMessage);
state = State.ValidateTakerDepositPaymentRequestMessage;
checkTradeId(tradeId, message);
String peersAccountId = nonEmptyStringOf(message.getAccountId());
BankAccount peersBankAccount = checkNotNull(message.getBankAccount());
String offererPubKey = nonEmptyStringOf(message.getOffererPubKey());
String preparedOffererDepositTxAsHex = nonEmptyStringOf(message.getPreparedOffererDepositTxAsHex());
long offererTxOutIndex = nonNegativeLongOf(message.getOffererTxOutIndex());
// apply new state
this.peersAccountId = peersAccountId;
this.peersBankAccount = peersBankAccount;
this.peersPubKey = offererPubKey;
this.preparedPeersDepositTxAsHex = preparedOffererDepositTxAsHex;
this.peersTxOutIndex = offererTxOutIndex;
// next task
verifyOffererAccount();
} catch (Throwable t) {
handleValidationFault(t);
}
}
// 12. VerifyOffererAccount
private void verifyOffererAccount() {
state = State.VerifyOffererAccount;
VerifyOffererAccount.run(this::handleVerifyOffererAccountResult, this::handleFault, blockChainService, peersAccountId, peersBankAccount);
}
// 13. CreateAndSignContract
private void handleVerifyOffererAccountResult() {
log.debug("handleVerifyOffererAccountResult called: state = " + state);
String takeOfferFeeTxId = trade.getTakeOfferFeeTxId();
state = State.CreateAndSignContract;
CreateAndSignContract.run(this::handleCreateAndSignContractResult,
this::handleFault,
signatureService,
offer,
tradeAmount,
takeOfferFeeTxId,
accountId,
bankAccount,
offererMessagePublicKey,
messagePublicKey,
peersAccountId,
peersBankAccount,
accountKey);
}
// 14. PayDeposit
private void handleCreateAndSignContractResult(Contract contract, String contractAsJson, String signature) {
log.debug("handleCreateAndSignContractResult called: state = " + state);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setTakerContractSignature(signature);
state = State.PayDeposit;
PayDeposit.run(this::handlePayDepositResult, this::handleFault, walletService, securityDeposit, tradeAmount, tradeId,
tradePubKeyAsHex, arbitratorPubKey, peersPubKey, preparedPeersDepositTxAsHex);
}
// 15. SendSignedTakerDepositTxAsHex
private void handlePayDepositResult(Transaction signedTakerDepositTx) {
log.debug("handlePayDepositResult called: state = " + state);
String contractAsJson = trade.getContractAsJson();
String takerContractSignature = trade.getTakerContractSignature();
state = State.SendSignedTakerDepositTxAsHex;
SendSignedTakerDepositTxAsHex.run(this::handleErrorMessage,
peer,
tradeMessageService,
walletService,
bankAccount,
accountId,
messagePublicKey,
tradeId,
contractAsJson,
takerContractSignature,
signedTakerDepositTx,
peersTxOutIndex);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 21. TakerCommitDepositTx
public void handleDepositTxPublishedMessage(DepositTxPublishedMessage message) {
log.debug("onDepositTxPublishedMessage called: state = " + state);
log.debug("state " + state);
try {
// validation
checkState(state == State.SendSignedTakerDepositTxAsHex);
state = State.ValidateDepositTxPublishedMessage;
checkTradeId(tradeId, message);
String depositTxAsHex = message.getDepositTxAsHex();
nonEmptyStringOf(depositTxAsHex);
// next task
takerCommitDepositTx(depositTxAsHex);
} catch (Throwable t) {
handleValidationFault(t);
}
}
// 22. TakerCommitDepositTx
private void takerCommitDepositTx(String depositTxAsHex) {
state = State.TakerCommitDepositTx;
TakerCommitDepositTx.run(this::handleTakerCommitDepositTxResult, this::handleFault, walletService, depositTxAsHex);
}
private void handleTakerCommitDepositTxResult(Transaction transaction) {
log.debug("handleTakerCommitDepositTxResult called: state = " + state);
trade.setDepositTx(transaction);
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
listener.onDepositTxPublished();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 25. handleBankTransferInitedMessage
public void handleBankTransferInitedMessage(BankTransferInitedMessage message) {
log.debug("handleBankTransferInitedMessage called: state = " + state);
try {
// validate
checkState(state == State.TakerCommitDepositTx);
checkTradeId(tradeId, message);
String depositTxAsHex = nonEmptyStringOf(message.getDepositTxAsHex());
String offererSignatureR = nonEmptyStringOf(message.getOffererSignatureR());
String offererSignatureS = nonEmptyStringOf(message.getOffererSignatureS());
Coin offererPaybackAmount = positiveCoinOf(nonZeroCoinOf(message.getOffererPaybackAmount()));
Coin takerPaybackAmount = positiveCoinOf(nonZeroCoinOf(message.getTakerPaybackAmount()));
String offererPayoutAddress = nonEmptyStringOf(message.getOffererPayoutAddress());
// apply state
state = State.handleBankTransferInitedMessage;
this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount;
this.takerPaybackAmount = takerPaybackAmount;
this.offererPayoutAddress = offererPayoutAddress;
listener.onBankTransferInited(message.getTradeId());
} catch (Throwable t) {
handleValidationFault(t);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Triggered UI event
// UI event handling
///////////////////////////////////////////////////////////////////////////////////////////
// User clicked the "bank transfer received" button, so we release the funds for pay out
// 26. SignAndPublishPayoutTx
public void handleUIEventFiatReceived() {
log.debug("handleUIEventFiatReceived called: state = " + state);
checkState(state == State.handleBankTransferInitedMessage);
state = State.SignAndPublishPayoutTx;
SignAndPublishPayoutTx.run(this::handleSignAndPublishPayoutTxResult,
this::handleFault,
walletService,
tradeId,
depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
verifyOfferFeePayment();
}
// 27a. SendPayoutTxToOfferer
private void handleSignAndPublishPayoutTxResult(Transaction transaction, String payoutTxAsHex) {
log.debug("handleSignAndPublishPayoutTxResult called: state = " + state);
listener.onPayoutTxPublished(trade, transaction);
state = State.SendPayoutTxToOfferer;
SendPayoutTxToOfferer.run(this::handleErrorMessage, peer, tradeMessageService, tradeId, payoutTxAsHex);
}
// 27b VerifyTakeOfferFeePayment
private void verifyOfferFeePayment() {
VerifyOfferFeePayment.run(this::handleFault, walletService, trade.getOffer().getOfferFeePaymentTxID());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private
///////////////////////////////////////////////////////////////////////////////////////////
private void handleFault(Throwable throwable) {
trade.setFault(throwable);
trade.setState(Trade.State.FAILED);
listener.onFault(throwable, state);
}
private void handleErrorMessage(String errorMessage) {
handleFault(new Exception(errorMessage));
}
private void handleValidationFault(Throwable throwable) {
throwable.printStackTrace();
log.error(throwable.getMessage());
handleErrorMessage("Validation of incoming message failed. Error message = " + throwable.getMessage());
public void handleFiatReceivedUIEvent() {
TradeTaskRunner<SellerTakesOfferModel> sequence6 = new TradeTaskRunner<>(model,
() -> {
log.debug("sequence6 completed");
},
(message, throwable) -> {
log.error(message);
}
);
sequence6.addTasks(
SignAndPublishPayoutTx.class,
VerifyOfferFeePayment.class,
SendPayoutTxToOfferer.class
);
sequence6.run();
}
}

View File

@ -17,53 +17,43 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.offer.Offer;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.Utilities;
import io.bitsquare.util.handlers.ExceptionHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import java.security.PublicKey;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CreateAndSignContract {
public class CreateAndSignContract extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(CreateAndSignContract.class);
public static void run(ResultHandler resultHandler,
ExceptionHandler exceptionHandler,
SignatureService signatureService,
Offer offer,
Coin tradeAmount,
String takeOfferFeeTxId,
String accountId,
BankAccount bankAccount,
PublicKey peersMessagePublicKey,
PublicKey messagePublicKey,
String peersAccountId,
BankAccount peersBankAccount,
ECKey registrationKey) {
log.trace("Run CreateAndSignContract task");
try {
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, peersAccountId, accountId,
peersBankAccount, bankAccount, peersMessagePublicKey, messagePublicKey);
String contractAsJson = Utilities.objectToJson(contract);
String signature = signatureService.signMessage(registrationKey, contractAsJson);
resultHandler.onResult(contract, contractAsJson, signature);
} catch (Throwable t) {
log.error("Exception at sign contract " + t);
exceptionHandler.handleException(t);
}
public CreateAndSignContract(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
public interface ResultHandler {
void onResult(Contract contract, String contractAsJson, String signature);
}
@Override
protected void run() {
Trade trade = model.getTrade();
Contract contract = new Contract(
model.getOffer(),
model.getTradeAmount(),
trade.getTakeOfferFeeTxId(),
model.getPeersAccountId(),
model.getAccountId(),
model.getPeersBankAccount(),
model.getBankAccount(),
model.getOffererMessagePublicKey(),
model.getMessagePublicKey());
String contractAsJson = Utilities.objectToJson(contract);
String signature = model.getSignatureService().signMessage(model.getAccountKey(), contractAsJson);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setTakerContractSignature(signature);
complete();
}
}

View File

@ -20,16 +20,41 @@ package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.GetPeerAddressListener;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class GetPeerAddress {
public class GetPeerAddress extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public GetPeerAddress(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
model.getTradeMessageService().getPeerAddress(model.getOffererMessagePublicKey(), new GetPeerAddressListener() {
@Override
public void onResult(Peer peer) {
log.trace("Found peer: " + peer.toString());
model.setPeer(peer);
complete();
}
@Override
public void onFailed() {
failed("DHT lookup for peer address failed.", null);
}
});
}
public static void run(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler,
TradeMessageService tradeMessageService, PublicKey messagePublicKey) {
log.trace("Run GetPeerAddress task");

View File

@ -17,8 +17,9 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.InsufficientMoneyException;
@ -27,41 +28,32 @@ import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayDeposit {
public class PayDeposit extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(PayDeposit.class);
public static void run(ResultHandler resultHandler,
ExceptionHandler exceptionHandler,
WalletService walletService,
Coin securityDeposit,
Coin tradeAmount,
String tradeId,
String pubKeyForThatTrade,
String arbitratorPubKey,
String offererPubKey,
String preparedOffererDepositTxAsHex) {
log.trace("Run PayDeposit task");
public PayDeposit(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
Coin amountToPay = tradeAmount.add(securityDeposit);
Coin msOutputAmount = amountToPay.add(securityDeposit);
Transaction signedTakerDepositTx = walletService.takerAddPaymentAndSignTx(amountToPay,
Coin amountToPay = model.getTradeAmount().add(model.getSecurityDeposit());
Coin msOutputAmount = amountToPay.add(model.getSecurityDeposit());
Transaction signedTakerDepositTx = model.getWalletService().takerAddPaymentAndSignTx(
amountToPay,
msOutputAmount,
offererPubKey,
pubKeyForThatTrade,
arbitratorPubKey,
preparedOffererDepositTxAsHex,
tradeId);
model.getPeersPubKey(),
model.getTradePubKeyAsHex(),
model.getArbitratorPubKey(),
model.getPreparedPeersDepositTxAsHex(),
model.getTradeId());
log.trace("signedTakerDepositTx: " + signedTakerDepositTx);
resultHandler.onResult(signedTakerDepositTx);
model.setSignedTakerDepositTx(signedTakerDepositTx);
complete();
} catch (InsufficientMoneyException e) {
log.error("Pay deposit failed due InsufficientMoneyException " + e);
exceptionHandler.handleException(e);
failed(e);
}
}
public interface ResultHandler {
void onResult(Transaction signedTakerDepositTx);
}
}

View File

@ -17,8 +17,9 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.bitcoinj.core.InsufficientMoneyException;
import org.bitcoinj.core.Transaction;
@ -30,34 +31,31 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayTakeOfferFee {
public class PayTakeOfferFee extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(PayTakeOfferFee.class);
public static void run(ResultHandler resultHandler, ExceptionHandler exceptionHandler, WalletService walletService,
String tradeId) {
log.trace("Run PayTakeOfferFee task");
public PayTakeOfferFee(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
walletService.payTakeOfferFee(tradeId, new FutureCallback<Transaction>() {
model.getWalletService().payTakeOfferFee(model.getTradeId(), new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction transaction) {
log.debug("Take offer fee paid successfully. Transaction ID = " + transaction.getHashAsString());
resultHandler.onResult(transaction.getHashAsString());
model.getTrade().setTakeOfferFeeTxID(transaction.getHashAsString());
complete();
}
@Override
public void onFailure(@NotNull Throwable t) {
log.error("Pay take offer fee caused an exception: " + t);
exceptionHandler.handleException(t);
failed("Pay take offer fee caused an exception: ", t);
}
});
} catch (InsufficientMoneyException e) {
log.error("Pay take offer fee caused an exception: " + e);
exceptionHandler.handleException(e);
failed("Pay take offer fee caused an exception: ", e);
}
}
public interface ResultHandler {
void onResult(String takeOfferFeeTxId);
}
}

View File

@ -17,32 +17,35 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.trade.protocol.trade.taker.messages.RequestTakeOfferMessage;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RequestTakeOffer {
public class RequestTakeOffer extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(RequestTakeOffer.class);
public static void run(ErrorMessageHandler errorMessageHandler,
TradeMessageService tradeMessageService, Peer peer, String tradeId) {
log.trace("Run RequestTakeOffer task");
tradeMessageService.sendMessage(peer, new RequestTakeOfferMessage(tradeId),
public RequestTakeOffer(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
model.getTradeMessageService().sendMessage(model.getPeer(), new RequestTakeOfferMessage(model.getTradeId()),
new SendMessageListener() {
@Override
public void handleResult() {
log.trace("Sending RequestTakeOfferMessage succeeded.");
complete();
}
@Override
public void handleFault() {
log.error("Sending RequestTakeOfferMessage failed.");
errorMessageHandler.handleErrorMessage("Sending RequestTakeOfferMessage failed.");
failed("Sending RequestTakeOfferMessage failed.");
}
});
}

View File

@ -17,33 +17,36 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.trade.protocol.trade.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendPayoutTxToOfferer {
public class SendPayoutTxToOfferer extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(SendPayoutTxToOfferer.class);
public static void run(ErrorMessageHandler errorMessageHandler, Peer peer, TradeMessageService tradeMessageService, String tradeId, String payoutTxAsHex) {
log.trace("Run SendPayoutTxToOfferer task");
PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(tradeId, payoutTxAsHex);
tradeMessageService.sendMessage(peer, tradeMessage, new SendMessageListener() {
public SendPayoutTxToOfferer(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
PayoutTxPublishedMessage tradeMessage = new PayoutTxPublishedMessage(model.getTradeId(), model.getPayoutTxAsHex());
model.getTradeMessageService().sendMessage(model.getPeer(), tradeMessage, new SendMessageListener() {
@Override
public void handleResult() {
log.trace("PayoutTxPublishedMessage successfully arrived at peer");
complete();
}
@Override
public void handleFault() {
errorMessageHandler.handleErrorMessage("Sending PayoutTxPublishedMessage failed.");
failed("Sending PayoutTxPublishedMessage failed.");
}
});
}
}

View File

@ -17,66 +17,55 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.WalletService;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.trade.protocol.trade.taker.messages.RequestOffererPublishDepositTxMessage;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Utils;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedTakerDepositTxAsHex {
public class SendSignedTakerDepositTxAsHex extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTxAsHex.class);
public static void run(ErrorMessageHandler errorMessageHandler,
Peer peer,
TradeMessageService tradeMessageService,
WalletService walletService,
BankAccount bankAccount,
String accountId,
PublicKey messagePublicKey,
String tradeId,
String contractAsJson,
String takerContractSignature,
Transaction signedTakerDepositTx,
long offererTxOutIndex) {
log.trace("Run SendSignedTakerDepositTxAsHex task");
long takerTxOutIndex = signedTakerDepositTx.getInput(1).getOutpoint().getIndex();
public SendSignedTakerDepositTxAsHex(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(tradeId,
bankAccount,
accountId,
messagePublicKey,
@Override
protected void run() {
Transaction signedTakerDepositTx = model.getSignedTakerDepositTx();
long takerTxOutIndex = model.getSignedTakerDepositTx().getInput(1).getOutpoint().getIndex();
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(
model.getTradeId(),
model.getBankAccount(),
model.getAccountId(),
model.getMessagePublicKey(),
Utils.HEX.encode(signedTakerDepositTx.bitcoinSerialize()),
Utils.HEX.encode(signedTakerDepositTx.getInput(1).getScriptBytes()),
Utils.HEX.encode(signedTakerDepositTx.getInput(1)
.getConnectedOutput()
.getParentTransaction()
.bitcoinSerialize()),
contractAsJson,
takerContractSignature,
walletService.getAddressInfoByTradeID(tradeId).getAddressString(),
Utils.HEX.encode(signedTakerDepositTx.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize()),
model.getTrade().getContractAsJson(),
model.getTrade().getTakerContractSignature(),
model.getWalletService().getAddressInfoByTradeID(model.getTradeId()).getAddressString(),
takerTxOutIndex,
offererTxOutIndex);
tradeMessageService.sendMessage(peer, tradeMessage, new SendMessageListener() {
model.getPeersTxOutIndex());
model.getTradeMessageService().sendMessage(model.getPeer(), tradeMessage, new SendMessageListener() {
@Override
public void handleResult() {
log.trace("RequestOffererDepositPublicationMessage successfully arrived at peer");
complete();
}
@Override
public void handleFault() {
log.error("RequestOffererDepositPublicationMessage did not arrive at peer");
errorMessageHandler.handleErrorMessage("RequestOffererDepositPublicationMessage did not arrive at peer");
failed("Sending RequestOffererDepositPublicationMessage failed");
}
});
}
}

View File

@ -17,42 +17,42 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.trade.protocol.trade.taker.messages.TakeOfferFeePayedMessage;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import org.bitcoinj.core.Coin;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendTakeOfferFeePayedMessage {
public class SendTakeOfferFeePayedMessage extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(SendTakeOfferFeePayedMessage.class);
public static void run(ErrorMessageHandler errorMessageHandler,
Peer peer,
TradeMessageService tradeMessageService,
String tradeId,
String takeOfferFeeTxId,
Coin tradeAmount,
String tradePubKeyAsHex) {
log.trace("Run SendTakeOfferFeePayedMessage task");
TakeOfferFeePayedMessage msg = new TakeOfferFeePayedMessage(tradeId, takeOfferFeeTxId, tradeAmount,
tradePubKeyAsHex);
public SendTakeOfferFeePayedMessage(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
tradeMessageService.sendMessage(peer, msg, new SendMessageListener() {
@Override
protected void run() {
TakeOfferFeePayedMessage msg = new TakeOfferFeePayedMessage(
model.getTradeId(),
model.getTrade().getTakeOfferFeeTxId(),
model.getTradeAmount(),
model.getTradePubKeyAsHex()
);
model.getTradeMessageService().sendMessage(model.getPeer(), msg, new SendMessageListener() {
@Override
public void handleResult() {
log.trace("TakeOfferFeePayedMessage succeeded.");
log.trace("Sending TakeOfferFeePayedMessage succeeded.");
complete();
}
@Override
public void handleFault() {
log.error("Sending TakeOfferFeePayedMessage failed.");
errorMessageHandler.handleErrorMessage("Sending TakeOfferFeePayedMessage failed.");
failed("Sending TakeOfferFeePayedMessage failed.");
}
});
}
}
}

View File

@ -17,10 +17,12 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Utils;
@ -31,48 +33,43 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignAndPublishPayoutTx {
public class SignAndPublishPayoutTx extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(SignAndPublishPayoutTx.class);
public static void run(ResultHandler resultHandler,
ExceptionHandler exceptionHandler,
WalletService walletService,
String tradeId,
String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
Coin offererPaybackAmount,
Coin takerPaybackAmount,
String offererPayoutAddress) {
log.trace("Run SignAndPublishPayoutTx task");
public SignAndPublishPayoutTx(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
walletService.takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress,
tradeId,
model.getWalletService().takerSignsAndSendsTx(model.getDepositTxAsHex(),
model.getOffererSignatureR(),
model.getOffererSignatureS(),
model.getOffererPaybackAmount(),
model.getTakerPaybackAmount(),
model.getOffererPayoutAddress(),
model.getTradeId(),
new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction transaction) {
log.debug("takerSignsAndSendsTx " + transaction);
String payoutTxAsHex = Utils.HEX.encode(transaction.bitcoinSerialize());
resultHandler.onResult(transaction, payoutTxAsHex);
model.setPayoutTx(transaction);
model.setPayoutTxAsHex(payoutTxAsHex);
model.getTrade().setState(Trade.State.PAYOUT_PUBLISHED);
complete();
}
@Override
public void onFailure(@NotNull Throwable t) {
exceptionHandler.handleException(t);
failed(t);
}
});
} catch (Exception e) {
exceptionHandler.handleException(e);
} catch (AddressFormatException e) {
failed(e);
}
}
public interface ResultHandler {
void onResult(Transaction transaction, String payoutTxAsHex);
}
}

View File

@ -17,29 +17,30 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TakerCommitDepositTx {
public class TakerCommitDepositTx extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(TakerCommitDepositTx.class);
public static void run(ResultHandler resultHandler, ExceptionHandler exceptionHandler, WalletService walletService, String depositTxAsHex) {
log.trace("Run PayDeposit task");
try {
Transaction transaction = walletService.takerCommitDepositTx(depositTxAsHex);
resultHandler.onResult(transaction);
} catch (Exception e) {
log.error("takerCommitDepositTx failed with exception " + e);
exceptionHandler.handleException(e);
}
public TakerCommitDepositTx(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
public interface ResultHandler {
void onResult(Transaction transaction);
@Override
protected void run() {
Transaction transaction = model.getWalletService().takerCommitDepositTx(model.getDepositTxAsHex());
model.getTrade().setDepositTx(transaction);
model.getTrade().setState(Trade.State.DEPOSIT_PUBLISHED);
complete();
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.trade.protocol.trade.offerer.messages.BankTransferInitedMessage;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkState;
import static io.bitsquare.util.Validator.*;
public class ValidateBankTransferInitedMessage extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(ValidateBankTransferInitedMessage.class);
public ValidateBankTransferInitedMessage(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
checkState(model.getTrade().getPreviousTask() == TakerCommitDepositTx.class);
checkTradeId(model.getTradeId(), model.getTradeMessage());
BankTransferInitedMessage message = (BankTransferInitedMessage) model.getTradeMessage();
model.setDepositTxAsHex(nonEmptyStringOf(message.getDepositTxAsHex()));
model.setOffererSignatureR(nonEmptyStringOf(message.getOffererSignatureR()));
model.setOffererSignatureS(nonEmptyStringOf(message.getOffererSignatureS()));
model.setOffererPaybackAmount(positiveCoinOf(nonZeroCoinOf(message.getOffererPaybackAmount())));
model.setTakerPaybackAmount(positiveCoinOf(nonZeroCoinOf(message.getTakerPaybackAmount())));
model.setOffererPayoutAddress(nonEmptyStringOf(message.getOffererPayoutAddress()));
// TODO listener.onBankTransferInited(message.getTradeId());
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View File

@ -0,0 +1,52 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.trade.protocol.trade.offerer.messages.DepositTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkState;
import static io.bitsquare.util.Validator.*;
public class ValidateDepositTxPublishedMessage extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(ValidateDepositTxPublishedMessage.class);
public ValidateDepositTxPublishedMessage(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
checkState(model.getTrade().getPreviousTask() == SendSignedTakerDepositTxAsHex.class);
checkTradeId(model.getTradeId(), model.getTradeMessage());
DepositTxPublishedMessage message = (DepositTxPublishedMessage) model.getTradeMessage();
model.setDepositTxAsHex(nonEmptyStringOf(message.getDepositTxAsHex()));
complete();
} catch (Throwable t) {
failed("Validation for DepositTxPublishedMessage failed.", t);
}
}
}

View File

@ -0,0 +1,57 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.offerer.messages.RespondToTakeOfferRequestMessage;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkState;
import static io.bitsquare.util.Validator.checkTradeId;
public class ValidateRespondToTakeOfferRequestMessage extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(ValidateRespondToTakeOfferRequestMessage.class);
public ValidateRespondToTakeOfferRequestMessage(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
checkState(model.getTrade().getPreviousTask() == RequestTakeOffer.class);
checkTradeId(model.getTradeId(), model.getTradeMessage());
if (((RespondToTakeOfferRequestMessage) model.getTradeMessage()).isTakeOfferRequestAccepted()) {
model.getTrade().setState(Trade.State.OFFERER_ACCEPTED);
complete();
}
else {
model.getTrade().setState(Trade.State.OFFERER_REJECTED);
failed("Requested offer rejected because it is not available anymore.");
}
} catch (Throwable t) {
failed("Validation for RespondToTakeOfferRequestMessage failed.", t);
}
}
}

View File

@ -0,0 +1,55 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.trade.protocol.trade.offerer.messages.TakerDepositPaymentRequestMessage;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.*;
import static io.bitsquare.util.Validator.*;
public class ValidateTakerDepositPaymentRequestMessage extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(ValidateTakerDepositPaymentRequestMessage.class);
public ValidateTakerDepositPaymentRequestMessage(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
try {
checkState(model.getTrade().getPreviousTask() == SendTakeOfferFeePayedMessage.class);
checkTradeId(model.getTradeId(), model.getTradeMessage());
TakerDepositPaymentRequestMessage message = (TakerDepositPaymentRequestMessage) model.getTradeMessage();
model.setPeersAccountId(nonEmptyStringOf(message.getAccountId()));
model.setPeersBankAccount(checkNotNull(message.getBankAccount()));
model.setPeersPubKey(nonEmptyStringOf(message.getOffererPubKey()));
model.setPreparedPeersDepositTxAsHex(nonEmptyStringOf(message.getPreparedOffererDepositTxAsHex()));
model.setPeersTxOutIndex(nonNegativeLongOf(message.getOffererTxOutIndex()));
complete();
} catch (Throwable t) {
failed("Validation for TakerDepositPaymentRequestMessage failed.", t);
}
}
}

View File

@ -17,23 +17,27 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyOfferFeePayment {
public class VerifyOfferFeePayment extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(VerifyOfferFeePayment.class);
public static void run(ExceptionHandler exceptionHandler, WalletService walletService,
String takeOfferFeeTxId) {
log.trace("Run VerifyOfferFeePayment task");
//TODO mocked yet, need a confidence listeners
int numOfPeersSeenTx = walletService.getNumOfPeersSeenTx(takeOfferFeeTxId);
public VerifyOfferFeePayment(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
//TODO impl. missing
int numOfPeersSeenTx = model.getWalletService().getNumOfPeersSeenTx(model.getTrade().getTakeOfferFeeTxId());
/* if (numOfPeersSeenTx > 2) {
resultHandler.handleResult();
}*/
complete();
}
}

View File

@ -17,21 +17,32 @@
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainService;
import io.bitsquare.trade.protocol.trade.shared.tasks.VerifyPeerAccount;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.util.handlers.ResultHandler;
import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferModel;
import io.bitsquare.util.tasks.Task;
import io.bitsquare.util.tasks.TaskRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyOffererAccount {
public class VerifyOffererAccount extends Task<SellerTakesOfferModel> {
private static final Logger log = LoggerFactory.getLogger(VerifyOffererAccount.class);
public static void run(ResultHandler resultHandler, ExceptionHandler exceptionHandler,
BlockChainService blockChainService, String peersAccountId, BankAccount peersBankAccount) {
log.trace("Run VerifyOffererAccount task");
VerifyPeerAccount.run(resultHandler, exceptionHandler, blockChainService, peersAccountId, peersBankAccount);
public VerifyOffererAccount(TaskRunner taskHandler, SellerTakesOfferModel model) {
super(taskHandler, model);
}
@Override
protected void run() {
if (model.getBlockChainService().verifyAccountRegistration()) {
if (model.getBlockChainService().isAccountBlackListed(model.getPeersAccountId(), model.getPeersBankAccount())) {
failed("Taker is blacklisted.");
}
else {
complete();
}
}
else {
failed("Account registration validation for peer faultHandler.onFault.");
}
}
}

View File

@ -23,7 +23,7 @@ import io.bitsquare.network.tomp2p.TomP2PNode;
import io.bitsquare.network.tomp2p.TomP2PPeer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.GetPeerAddressListener;
import io.bitsquare.trade.listeners.ProcessNewMessageListener;
import io.bitsquare.trade.listeners.MessageHandler;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.user.User;
@ -58,7 +58,7 @@ public class TomP2PTradeMessageService implements TradeMessageService {
private final TomP2PNode tomP2PNode;
private final User user;
private final List<ProcessNewMessageListener> processNewMessageListeners = new ArrayList<>();
private final List<MessageHandler> messageHandlers = new ArrayList<>();
private Executor executor;
@ -133,12 +133,12 @@ public class TomP2PTradeMessageService implements TradeMessageService {
// Event Listeners
///////////////////////////////////////////////////////////////////////////////////////////
public void addHandleNewMessageListener(ProcessNewMessageListener listener) {
processNewMessageListeners.add(listener);
public void addMessageHandler(MessageHandler listener) {
messageHandlers.add(listener);
}
public void removeHandleNewMessageListener(ProcessNewMessageListener listener) {
processNewMessageListeners.remove(listener);
public void removeMessageHandler(MessageHandler listener) {
messageHandlers.remove(listener);
}
@ -149,7 +149,7 @@ public class TomP2PTradeMessageService implements TradeMessageService {
@Override
public void handleMessage(Object message, Peer sender) {
if (message instanceof Message && sender instanceof TomP2PPeer) {
executor.execute(() -> processNewMessageListeners.stream().forEach(e ->
executor.execute(() -> messageHandlers.stream().forEach(e ->
e.handleMessage((Message) message, sender)));
}
}

View File

@ -0,0 +1,25 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.util.tasks;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SharedModel {
private static final Logger log = LoggerFactory.getLogger(SharedModel.class);
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.util.tasks;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Task<T extends SharedModel> {
private static final Logger log = LoggerFactory.getLogger(Task.class);
private final TaskRunner taskHandler;
protected final T model;
public Task(TaskRunner taskHandler, T model) {
this.taskHandler = taskHandler;
this.model = model;
}
protected void run() {
}
protected void complete() {
taskHandler.complete();
}
protected void failed(String message) {
taskHandler.handleFault(message);
}
protected void failed(String message, @NotNull Throwable throwable) {
log.error(message);
log.error(throwable.getMessage());
taskHandler.handleFault(message, throwable);
}
protected void failed(@NotNull Throwable throwable) {
taskHandler.handleFault(throwable.getMessage(), throwable);
}
}

View File

@ -0,0 +1,106 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.util.tasks;
import io.bitsquare.util.handlers.FaultHandler;
import io.bitsquare.util.handlers.ResultHandler;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TaskRunner<T extends SharedModel> {
private static final Logger log = LoggerFactory.getLogger(TaskRunner.class);
private final Queue<Class> tasks = new LinkedBlockingQueue<>();
protected final T sharedModel;
private final ResultHandler resultHandler;
private final FaultHandler faultHandler;
private boolean failed = false;
private Class<? extends Task> currentTask;
private Class<? extends Task> previousTask;
public TaskRunner(T sharedModel, ResultHandler resultHandler, FaultHandler faultHandler) {
this.sharedModel = sharedModel;
this.resultHandler = resultHandler;
this.faultHandler = faultHandler;
}
public void run() {
next();
}
public Class<? extends Task> getCurrentTask() {
return currentTask;
}
protected void next() {
if (!failed) {
if (tasks.size() > 0) {
try {
setCurrentTask(tasks.poll());
log.trace("Run task: " + currentTask.getSimpleName());
currentTask.getDeclaredConstructor(TaskRunner.class, sharedModel.getClass()).newInstance(this, sharedModel).run();
setPreviousTask(currentTask);
} catch (Throwable t) {
t.printStackTrace();
faultHandler.handleFault(t.getMessage(), t);
}
}
else {
resultHandler.handleResult();
}
}
}
protected void setPreviousTask(Class<? extends Task> task) {
previousTask = task;
}
protected void setCurrentTask(Class<? extends Task> task) {
currentTask = task;
}
public void addTask(Class<? extends Task> task) {
tasks.add(task);
}
public void addTasks(Class<? extends Task>... items) {
tasks.addAll(Arrays.asList(items));
}
public void complete() {
next();
}
public void handleFault(String message) {
handleFault(message, new Exception(message));
}
public void handleFault(String message, @NotNull Throwable throwable) {
log.debug(throwable.getMessage());
failed = true;
faultHandler.handleFault(message, throwable);
}
}

View File

@ -0,0 +1,136 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.placeoffer;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TaskRunner {
private static final Logger log = LoggerFactory.getLogger(TaskRunner.class);
private Queue<Class> tasks;
private SharedModel sharedModel = new SharedModel();
private FaultHandler faultHandler;
private ResultHandler taskCompleted;
private final boolean[] failed = {false};
@Test
public void test() {
// Task1.run();
//Task2.run();
tasks = new LinkedBlockingQueue<>();
tasks.add(Task1.class);
tasks.add(Task2.class);
faultHandler = (throwable) -> {
log.debug(throwable.getMessage());
failed[0] = true;
};
taskCompleted = () -> {
next();
};
next();
/* ResultHandler handleResult = () -> {
Class task = tasks.poll();
try {
if (!failed[0])
((Task) task.newInstance()).run(sharedModel, taskCompleted, faultHandler);
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
};*/
/* tasks.stream().forEach((e) -> {
try {
((Task) e.newInstance()).run(sharedModel, faultHandler);
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
});*/
}
private void next() {
Class task = tasks.poll();
if (task != null) {
try {
if (!failed[0])
((Task) task.newInstance()).run(sharedModel, taskCompleted, faultHandler);
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
}
}
}
interface ResultHandler {
void handleResult();
}
interface FaultHandler {
void handleFault(Throwable throwable);
}
class SharedModel {
public int data = 42;
}
class Task {
protected void run(SharedModel sharedModel, ResultHandler resultHandler, FaultHandler faultHandler) {
}
}
class Task1 extends Task {
private static final Logger log = LoggerFactory.getLogger(Task1.class);
@Override
public void run(SharedModel sharedModel, ResultHandler resultHandler, FaultHandler faultHandler) {
log.debug("run " + Task1.class);
log.debug("data " + sharedModel.data);
// faultHandler.handleFault(new Exception("test"));
sharedModel.data++;
resultHandler.handleResult();
}
}
class Task2 extends Task {
private static final Logger log = LoggerFactory.getLogger(Task2.class);
@Override
public void run(SharedModel sharedModel, ResultHandler resultHandler, FaultHandler faultHandler) {
log.debug("run " + Task2.class);
log.debug("data " + sharedModel.data);
resultHandler.handleResult();
}
}