Move fee verification task to end

This commit is contained in:
Manfred Karrer 2015-03-10 23:29:28 +01:00
parent aee5addacf
commit 033709f288
14 changed files with 398 additions and 265 deletions

View file

@ -611,7 +611,6 @@ public class WalletService {
// 1. step: deposit tx
// Offerer creates the 2of3 multiSig deposit tx with his unsigned input and change output
public Transaction offererCreatesMSTxAndAddPayment(Coin offererInputAmount,
String offererPubKey,
String takerPubKey,

View file

@ -281,12 +281,10 @@ public class TradeManager {
@Override
public void onDepositTxPublished(Transaction depositTx) {
trade.setDepositTx(depositTx);
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
persistPendingTrades();
log.trace("trading onDepositTxPublishedMessage " + depositTx.getHashAsString());
}
// TODO should be removed
@Override
public void onDepositTxConfirmedInBlockchain() {
log.trace("trading onDepositTxConfirmedInBlockchain");
@ -311,6 +309,36 @@ public class TradeManager {
case RespondToTakeOfferRequest:
removeFailedTrade(trade);
break;
case ValidateTakeOfferFeePayedMessage:
removeFailedTrade(trade);
break;
case CreateDepositTx:
removeFailedTrade(trade);
break;
case SendTakerDepositPaymentRequest:
removeFailedTrade(trade);
break;
case ValidateRequestOffererPublishDepositTxMessage:
removeFailedTrade(trade);
break;
case VerifyTakerAccount:
removeFailedTrade(trade);
break;
case VerifyAndSignContract:
removeFailedTrade(trade);
break;
case SignAndPublishDepositTx:
removeFailedTrade(trade);
break;
case SignAndPublishDepositTxResulted:
removeFailedTrade(trade);
break;
case SendSignedPayoutTx:
removeFailedTrade(trade);
break;
default:
log.error("Unhandled state: " + state);
break;
}
}
});
@ -347,9 +375,7 @@ public class TradeManager {
}
@Override
public void onDepositTxPublished(Transaction depositTx) {
trade.setDepositTx(depositTx);
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
public void onDepositTxPublished() {
persistPendingTrades();
}
@ -378,12 +404,19 @@ public class TradeManager {
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;
}
@ -410,7 +443,7 @@ public class TradeManager {
// Also we don't support yet offline messaging (mail box)
public void fiatPaymentStarted(String tradeId) {
if (offererAsBuyerProtocolMap.get(tradeId) != null) {
offererAsBuyerProtocolMap.get(tradeId).handleUIEventBankTransferInited();
offererAsBuyerProtocolMap.get(tradeId).handleUIEventBankTransferStarted();
pendingTrades.get(tradeId).setState(Trade.State.PAYMENT_STARTED);
persistPendingTrades();
}

View file

@ -23,7 +23,7 @@ import io.bitsquare.trade.protocol.trade.taker.SellerTakesOfferProtocol;
import org.bitcoinj.core.Transaction;
public interface SellerTakesOfferProtocolListener {
void onDepositTxPublished(Transaction depositTx);
void onDepositTxPublished();
void onBankTransferInited(String tradeId);

View file

@ -19,7 +19,6 @@ package io.bitsquare.trade.protocol.trade.offerer;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.btc.BlockChainService;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletService;
import io.bitsquare.crypto.SignatureService;
import io.bitsquare.network.Peer;
@ -30,12 +29,14 @@ import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.BuyerAcceptsOfferProtocolListener;
import io.bitsquare.trade.protocol.trade.offerer.tasks.CreateDepositTx;
import io.bitsquare.trade.protocol.trade.offerer.tasks.RespondToTakeOfferRequest;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SendBankTransferInitedMessage;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SendDepositTxIdToTaker;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SendSignedPayoutTx;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SendTakerDepositPaymentRequest;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SetupListenerForBlockChainConfirmation;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SignAndPublishDepositTx;
import io.bitsquare.trade.protocol.trade.offerer.tasks.SignPayoutTx;
import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyAndSignContract;
import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakeOfferFeePayment;
import io.bitsquare.trade.protocol.trade.offerer.tasks.VerifyTakerAccount;
import io.bitsquare.trade.protocol.trade.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.trade.protocol.trade.taker.messages.RequestOffererPublishDepositTxMessage;
@ -49,8 +50,6 @@ import org.bitcoinj.core.Utils;
import java.security.PublicKey;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -74,22 +73,19 @@ public class BuyerAcceptsOfferProtocol {
Init,
RespondToTakeOfferRequest,
handleTakeOfferFeePayedMessage,
/* VerifyTakeOfferFeePayment,*/
/* VerifyTakeOfferFeePayment,*/
ValidateTakeOfferFeePayedMessage,
CreateDepositTx,
RequestTakerDepositPayment,
SendTakerDepositPaymentRequest,
handleRequestOffererPublishDepositTxMessage,
ValidateRequestOffererPublishDepositTxMessage,
VerifyTakerAccount,
VerifyAndSignContract,
SignAndPublishDepositTx,
SendDepositTxIdToTaker,
SetupListenerForBlockChainConfirmation,
SignAndPublishDepositTxResulted,
handleUIEventBankTransferInited,
SendSignedPayoutTx,
handlePayoutTxPublishedMessage
SignPayoutTx,
SendSignedPayoutTx
}
// provided
@ -203,46 +199,46 @@ public class BuyerAcceptsOfferProtocol {
///////////////////////////////////////////////////////////////////////////////////////////
// 8. handleTakeOfferFeePayedMessage
public void handleTakeOfferFeePayedMessage(@NotNull TakeOfferFeePayedMessage message) {
public void handleTakeOfferFeePayedMessage(TakeOfferFeePayedMessage message) {
log.debug("handleTakeOfferFeePayedMessage called: state = " + state);
// validation
checkState(state == State.RespondToTakeOfferRequest);
checkTradeId(tradeId, message);
String takeOfferFeeTxId = nonEmptyStringOf(message.getTakeOfferFeeTxId());
Coin tradeAmount = positiveCoinOf(nonZeroCoinOf(message.getTradeAmount()));
String tradePubKeyAsHex = nonEmptyStringOf(message.getTakerPubKeyAsHex());
try {
checkState(state == State.RespondToTakeOfferRequest);
state = State.ValidateTakeOfferFeePayedMessage;
checkTradeId(tradeId, message);
String takeOfferFeeTxId = nonEmptyStringOf(message.getTakeOfferFeeTxId());
Coin tradeAmount = positiveCoinOf(nonZeroCoinOf(message.getTradeAmount()));
String tradePubKeyAsHex = nonEmptyStringOf(message.getTakerPubKeyAsHex());
// apply new state
state = State.handleTakeOfferFeePayedMessage;
this.takeOfferFeeTxId = takeOfferFeeTxId;
this.tradePubKeyAsHex = tradePubKeyAsHex;
trade.setTakeOfferFeeTxID(takeOfferFeeTxId);
trade.setTradeAmount(tradeAmount);
// apply new state
this.takeOfferFeeTxId = takeOfferFeeTxId;
this.tradePubKeyAsHex = tradePubKeyAsHex;
trade.setTakeOfferFeeTxID(takeOfferFeeTxId);
trade.setTradeAmount(tradeAmount);
// next task
createDepositTx();
// next task
createDepositTx();
} catch (Throwable t) {
handleValidationFault(t);
}
}
// 9. CreateDepositTx
private void createDepositTx() {
log.debug("handleVerifyTakeOfferFeePaymentResult called: state = " + state);
checkState(state == State.handleTakeOfferFeePayedMessage);
Coin offererInputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
state = State.CreateDepositTx;
CreateDepositTx.run(this::handleCreateDepositTxResult, this::handleFault, walletService, tradeId, offererInputAmount,
tradePubKeyAsHex, arbitratorPubKey);
CreateDepositTx.run(this::handleCreateDepositTxResult, this::handleFault, walletService, trade, tradePubKeyAsHex, arbitratorPubKey);
}
// 4. RequestTakerDepositPayment
// 10. RequestTakerDepositPayment
private void handleCreateDepositTxResult(String offererPubKey, String preparedOffererDepositTxAsHex, long offererTxOutIndex) {
log.debug("handleCreateDepositTxResult called: state = " + state);
checkState(state == State.CreateDepositTx);
this.preparedOffererDepositTxAsHex = preparedOffererDepositTxAsHex;
this.offererTxOutIndex = offererTxOutIndex;
state = State.RequestTakerDepositPayment;
state = State.SendTakerDepositPaymentRequest;
SendTakerDepositPaymentRequest.run(this::handleErrorMessage,
peer,
tradeMessageService,
@ -259,43 +255,52 @@ public class BuyerAcceptsOfferProtocol {
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 5. VerifyTakerAccount
// 16. VerifyTakerAccount
public void handleRequestOffererPublishDepositTxMessage(RequestOffererPublishDepositTxMessage message) {
log.debug("handleRequestOffererPublishDepositTxMessage called: state = " + state);
log.debug("state " + state);
// validation
checkState(state == State.RequestTakerDepositPayment);
checkTradeId(tradeId, message);
String peersPayoutAddress = nonEmptyStringOf(message.getTakerPayoutAddress());
String peersAccountId = nonEmptyStringOf(message.getTakerAccountId());
BankAccount peersBankAccount = checkNotNull(message.getTakerBankAccount());
PublicKey peersMessagePublicKey = checkNotNull(message.getTakerMessagePublicKey());
String peersContractAsJson = nonEmptyStringOf(message.getTakerContractAsJson());
String signedTakerDepositTxAsHex = nonEmptyStringOf(message.getSignedTakerDepositTxAsHex());
String txConnOutAsHex = nonEmptyStringOf(message.getTxConnOutAsHex());
String txScriptSigAsHex = nonEmptyStringOf(message.getTxScriptSigAsHex());
long takerTxOutIndex = nonNegativeLongOf(message.getTakerTxOutIndex());
// apply new state
state = State.handleRequestOffererPublishDepositTxMessage;
this.peersPayoutAddress = peersPayoutAddress;
this.peersAccountId = peersAccountId;
this.peersBankAccount = peersBankAccount;
this.peersMessagePublicKey = peersMessagePublicKey;
this.peersContractAsJson = peersContractAsJson;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txConnOutAsHex = txConnOutAsHex;
this.txScriptSigAsHex = txScriptSigAsHex;
this.takerTxOutIndex = takerTxOutIndex;
try {
// validation
checkState(state == State.SendTakerDepositPaymentRequest);
state = State.ValidateRequestOffererPublishDepositTxMessage;
checkTradeId(tradeId, message);
String peersPayoutAddress = nonEmptyStringOf(message.getTakerPayoutAddress());
String peersAccountId = nonEmptyStringOf(message.getTakerAccountId());
BankAccount peersBankAccount = checkNotNull(message.getTakerBankAccount());
PublicKey peersMessagePublicKey = checkNotNull(message.getTakerMessagePublicKey());
String peersContractAsJson = nonEmptyStringOf(message.getTakerContractAsJson());
String signedTakerDepositTxAsHex = nonEmptyStringOf(message.getSignedTakerDepositTxAsHex());
String txConnOutAsHex = nonEmptyStringOf(message.getTxConnOutAsHex());
String txScriptSigAsHex = nonEmptyStringOf(message.getTxScriptSigAsHex());
long takerTxOutIndex = nonNegativeLongOf(message.getTakerTxOutIndex());
// next task
// apply new state
this.peersPayoutAddress = peersPayoutAddress;
this.peersAccountId = peersAccountId;
this.peersBankAccount = peersBankAccount;
this.peersMessagePublicKey = peersMessagePublicKey;
this.peersContractAsJson = peersContractAsJson;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txConnOutAsHex = txConnOutAsHex;
this.txScriptSigAsHex = txScriptSigAsHex;
this.takerTxOutIndex = takerTxOutIndex;
// next task
verifyTakerAccount();
} catch (Throwable t) {
handleValidationFault(t);
}
}
// 17. VerifyTakerAccount
private void verifyTakerAccount() {
state = State.VerifyTakerAccount;
VerifyTakerAccount.run(this::handleVerifyTakerAccountResult, this::handleFault, blockChainService,
this.peersAccountId, this.peersBankAccount);
}
// 6. VerifyAndSignContract
// 18. VerifyAndSignContract
private void handleVerifyTakerAccountResult() {
log.debug("handleVerifyTakerAccountResult called: state = " + state);
@ -317,7 +322,7 @@ public class BuyerAcceptsOfferProtocol {
accountKey);
}
// 7. SignAndPublishDepositTx
// 19. SignAndPublishDepositTx
private void handleVerifyAndSignContractResult(Contract contract, String contractAsJson, String signature) {
log.debug("handleVerifyAndSignContractResult called: state = " + state);
@ -336,66 +341,47 @@ public class BuyerAcceptsOfferProtocol {
takerTxOutIndex);
}
// 8. SendDepositTxIdToTaker
private void handleSignAndPublishDepositTxResult(Transaction depositTransaction) {
log.debug("handleSignAndPublishDepositTxResult called: state = " + state);
state = State.SignAndPublishDepositTxResulted;
trade.setDepositTx(depositTransaction);
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
listener.onDepositTxPublished(depositTransaction);
state = State.SendDepositTxIdToTaker;
SendDepositTxIdToTaker.run(this::handleSendDepositTxIdToTakerResult, this::handleErrorMessage, peer, tradeMessageService,
tradeId, depositTransaction);
sendDepositTxIdToTaker(depositTransaction);
setupListenerForBlockChainConfirmation();
}
private void handleSendDepositTxIdToTakerResult() {
log.debug("handleSendDepositTxIdToTakerResult called: state = " + state);
// 20a. SendDepositTxIdToTaker
private void sendDepositTxIdToTaker(Transaction depositTransaction) {
log.debug("sendDepositTxIdToTaker called: state = " + state);
SendDepositTxIdToTaker.run(this::handleErrorMessage, peer, tradeMessageService, tradeId, depositTransaction);
}
state = State.SetupListenerForBlockChainConfirmation;
// TODO remove
// 20b. SetupListenerForBlockChainConfirmation
private void setupListenerForBlockChainConfirmation() {
log.debug("setupListenerForBlockChainConfirmation called: state = " + state);
SetupListenerForBlockChainConfirmation.run(trade.getDepositTx(), listener);
}
/*
// 9. VerifyTakeOfferFeePayment
private void verifyTakeOfferFeePayment() {
state = State.VerifyTakeOfferFeePayment;
VerifyTakeOfferFeePayment.run(this::handleVerifyTakeOfferFeePaymentResult, this::handleFault, walletService, this.takeOfferFeeTxId);
}
// 10. CreateDepositTx
private void handleVerifyTakeOfferFeePaymentResult() {
log.debug("handleVerifyTakeOfferFeePaymentResult called: state = " + state);
checkState(state == State.VerifyTakeOfferFeePayment);
Coin offererInputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
state = State.CreateDepositTx;
CreateDepositTx.run(this::handleCreateDepositTxResult, this::handleFault, walletService, tradeId, offererInputAmount,
tradePubKeyAsHex, arbitratorPubKey);
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Triggered UI event
///////////////////////////////////////////////////////////////////////////////////////////
// Triggered from UI event: Button click "Bank transfer inited"
// 9. SendSignedPayoutTx
public void handleUIEventBankTransferInited() {
log.debug("onUIEventBankTransferInited called: state = " + state);
log.debug("state " + state);
// 23. SignPayoutTx
public void handleUIEventBankTransferStarted() {
log.debug("handleUIEventBankTransferStarted called: state = " + state);
// validation
checkState(state.ordinal() >= State.SignAndPublishDepositTx.ordinal() &&
state.ordinal() <= State.SetupListenerForBlockChainConfirmation.ordinal());
state = State.handleUIEventBankTransferInited;
// next task
String depositTransactionId = trade.getDepositTx().getHashAsString();
Coin tradeAmount = trade.getTradeAmount();
Coin securityDeposit = trade.getSecurityDeposit();
state = State.SendSignedPayoutTx;
SendSignedPayoutTx.run(this::handleFault,
peer,
tradeMessageService,
Coin tradeAmount = trade.getTradeAmount();
state = State.SignPayoutTx;
SignPayoutTx.run(this::handleSignPayoutTxResult,
this::handleFault,
walletService,
tradeId,
peersPayoutAddress,
@ -405,24 +391,55 @@ public class BuyerAcceptsOfferProtocol {
tradeAmount);
}
// 24a. SendBankTransferInitedMessage
private void handleSignPayoutTxResult(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
Coin offererPaybackAmount,
Coin takerPaybackAmount,
String offererPayoutAddress) {
log.debug("handleSignPayoutTxResult called: state = " + state);
state = State.SendSignedPayoutTx;
SendBankTransferInitedMessage.run(this::handleFault,
peer,
tradeMessageService,
tradeId,
depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
verifyTakeOfferFeePayment();
}
// 24b VerifyTakeOfferFeePayment
private void verifyTakeOfferFeePayment() {
VerifyTakeOfferFeePayment.run(this::handleFault, walletService, this.takeOfferFeeTxId);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 10. handlePayoutTxPublishedMessage
// 28. handlePayoutTxPublishedMessage
public void handlePayoutTxPublishedMessage(PayoutTxPublishedMessage message) {
log.debug("onPayoutTxPublishedMessage called: state = " + state);
// validation
checkState(state == State.SendSignedPayoutTx);
checkTradeId(tradeId, message);
String payoutTxAsHex = nonEmptyStringOf(message.getPayoutTxAsHex());
try {
// validation
checkState(state == State.SendSignedPayoutTx);
checkTradeId(tradeId, message);
String payoutTxAsHex = nonEmptyStringOf(message.getPayoutTxAsHex());
// apply new state
state = State.handlePayoutTxPublishedMessage;
Transaction payoutTx = new Transaction(walletService.getWallet().getParams(), Utils.parseAsHexOrBase58(payoutTxAsHex));
listener.onPayoutTxPublished(payoutTx);
// apply new state
Transaction payoutTx = new Transaction(walletService.getWallet().getParams(), Utils.parseAsHexOrBase58(payoutTxAsHex));
listener.onPayoutTxPublished(payoutTx);
} catch (Throwable t) {
handleValidationFault(t);
}
}
@ -430,7 +447,6 @@ public class BuyerAcceptsOfferProtocol {
// Private
///////////////////////////////////////////////////////////////////////////////////////////
// generic fault handler
private void handleFault(Throwable throwable) {
trade.setFault(throwable);
trade.setState(Trade.State.FAILED);
@ -441,4 +457,9 @@ public class BuyerAcceptsOfferProtocol {
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());
}
}

View file

@ -17,7 +17,9 @@
package io.bitsquare.trade.protocol.trade.offerer.tasks;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletService;
import io.bitsquare.trade.Trade;
import io.bitsquare.util.handlers.ExceptionHandler;
import org.bitcoinj.core.Coin;
@ -34,15 +36,15 @@ public class CreateDepositTx {
public static void run(ResultHandler resultHandler,
ExceptionHandler exceptionHandler,
WalletService walletService,
String tradeId,
Coin offererInputAmount,
Trade trade,
String takerMultiSigPubKey,
String arbitratorPubKeyAsHex) {
log.trace("Run CreateDepositTx task");
try {
String offererPubKey = walletService.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
String offererPubKey = walletService.getAddressInfoByTradeID(trade.getId()).getPubKeyAsHexString();
Coin offererInputAmount = trade.getSecurityDeposit().add(FeePolicy.TX_FEE);
Transaction transaction = walletService.offererCreatesMSTxAndAddPayment(offererInputAmount, offererPubKey, takerMultiSigPubKey,
arbitratorPubKeyAsHex, tradeId);
arbitratorPubKeyAsHex, trade.getId());
String preparedOffererDepositTxAsHex = Utils.HEX.encode(transaction.bitcoinSerialize());
long offererTxOutIndex = transaction.getInput(0).getOutpoint().getIndex();

View file

@ -17,7 +17,6 @@
package io.bitsquare.trade.protocol.trade.offerer.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.network.Peer;
import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
@ -25,39 +24,25 @@ import io.bitsquare.trade.protocol.trade.offerer.messages.BankTransferInitedMess
import io.bitsquare.util.handlers.ExceptionHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendSignedPayoutTx {
private static final Logger log = LoggerFactory.getLogger(SendSignedPayoutTx.class);
public class SendBankTransferInitedMessage {
private static final Logger log = LoggerFactory.getLogger(SendBankTransferInitedMessage.class);
public static void run(ExceptionHandler exceptionHandler,
Peer peer,
TradeMessageService tradeMessageService,
WalletService walletService,
String tradeId,
String takerPayoutAddress,
String offererPayoutAddress,
String depositTransactionId,
Coin securityDeposit,
Coin tradeAmount) {
log.trace("Run task");
String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
Coin offererPaybackAmount,
Coin takerPaybackAmount,
String offererPayoutAddress) {
log.trace("Run SendSignedPayoutTx task");
try {
Coin offererPaybackAmount = tradeAmount.add(securityDeposit);
@SuppressWarnings("UnnecessaryLocalVariable") Coin takerPaybackAmount = securityDeposit;
Pair<ECKey.ECDSASignature, String> result = walletService.offererCreatesAndSignsPayoutTx(
depositTransactionId, offererPaybackAmount, takerPaybackAmount, takerPayoutAddress, tradeId);
ECKey.ECDSASignature offererSignature = result.getKey();
String offererSignatureR = offererSignature.r.toString();
String offererSignatureS = offererSignature.s.toString();
String depositTxAsHex = result.getValue();
BankTransferInitedMessage tradeMessage = new BankTransferInitedMessage(tradeId,
depositTxAsHex,
offererSignatureR,
@ -65,22 +50,19 @@ public class SendSignedPayoutTx {
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
tradeMessageService.sendMessage(peer, tradeMessage, new SendMessageListener() {
@Override
public void handleResult() {
log.trace("BankTransferInitedMessage successfully arrived at peer");
log.trace("Sending BankTransferInitedMessage succeeded.");
}
@Override
public void handleFault() {
log.error("BankTransferInitedMessage did not arrive at peer");
exceptionHandler.handleException(new Exception("BankTransferInitedMessage did not arrive at peer"));
exceptionHandler.handleException(new Exception("Sending BankTransferInitedMessage failed."));
}
});
} catch (Exception e) {
log.error("Exception at OffererCreatesAndSignsPayoutTx " + e);
exceptionHandler.handleException(e);
}
}

View file

@ -22,7 +22,6 @@ import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.trade.protocol.trade.offerer.messages.DepositTxPublishedMessage;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import io.bitsquare.util.handlers.ResultHandler;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.Utils;
@ -33,7 +32,7 @@ import org.slf4j.LoggerFactory;
public class SendDepositTxIdToTaker {
private static final Logger log = LoggerFactory.getLogger(SendDepositTxIdToTaker.class);
public static void run(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler, Peer peer,
public static void run( ErrorMessageHandler errorMessageHandler, Peer peer,
TradeMessageService tradeMessageService, String tradeId, Transaction depositTransaction) {
log.trace("Run task");
DepositTxPublishedMessage tradeMessage =
@ -43,7 +42,6 @@ public class SendDepositTxIdToTaker {
@Override
public void handleResult() {
log.trace("DepositTxPublishedMessage successfully arrived at peer");
resultHandler.handleResult();
}
@Override

View file

@ -25,6 +25,7 @@ import org.bitcoinj.core.TransactionConfidence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// TODO should be removed
public class SetupListenerForBlockChainConfirmation {
private static final Logger log = LoggerFactory.getLogger(SetupListenerForBlockChainConfirmation.class);

View file

@ -0,0 +1,72 @@
/*
* 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.offerer.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.ECKey;
import javafx.util.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SignPayoutTx {
private static final Logger log = LoggerFactory.getLogger(SignPayoutTx.class);
public static void run(ResultHandler resultHandler,
ExceptionHandler exceptionHandler,
WalletService walletService,
String tradeId,
String takerPayoutAddress,
String offererPayoutAddress,
String depositTransactionId,
Coin securityDeposit,
Coin tradeAmount) {
log.trace("Run SignPayoutTx task");
try {
Coin offererPaybackAmount = tradeAmount.add(securityDeposit);
@SuppressWarnings("UnnecessaryLocalVariable") Coin takerPaybackAmount = securityDeposit;
Pair<ECKey.ECDSASignature, String> result = walletService.offererCreatesAndSignsPayoutTx(
depositTransactionId, offererPaybackAmount, takerPaybackAmount, takerPayoutAddress, tradeId);
ECKey.ECDSASignature offererSignature = result.getKey();
String offererSignatureR = offererSignature.r.toString();
String offererSignatureS = offererSignature.s.toString();
String depositTxAsHex = result.getValue();
resultHandler.handleResult(depositTxAsHex, offererSignatureR, offererSignatureS, offererPaybackAmount, takerPaybackAmount, offererPayoutAddress);
} catch (Exception e) {
exceptionHandler.handleException(e);
}
}
public interface ResultHandler {
void handleResult(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
Coin offererPaybackAmount,
Coin takerPaybackAmount,
String offererPayoutAddress);
}
}

View file

@ -19,7 +19,6 @@ package io.bitsquare.trade.protocol.trade.offerer.tasks;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import io.bitsquare.util.handlers.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -27,14 +26,14 @@ import org.slf4j.LoggerFactory;
public class VerifyTakeOfferFeePayment {
private static final Logger log = LoggerFactory.getLogger(VerifyTakeOfferFeePayment.class);
public static void run(ResultHandler resultHandler, ExceptionHandler exceptionHandler, WalletService walletService,
public static void run(ExceptionHandler exceptionHandler, WalletService walletService,
String takeOfferFeeTxId) {
log.trace("Run VerifyTakeOfferFeePayment task");
//TODO mocked yet, need a confidence listeners
int numOfPeersSeenTx = walletService.getNumOfPeersSeenTx(takeOfferFeeTxId);
if (numOfPeersSeenTx > 2) {
/* if (numOfPeersSeenTx > 2) {
resultHandler.handleResult();
}
}*/
}
}

View file

@ -41,6 +41,7 @@ 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.VerifyOfferFeePayment;
import io.bitsquare.trade.protocol.trade.taker.tasks.VerifyOffererAccount;
import io.bitsquare.user.User;
@ -73,17 +74,17 @@ public class SellerTakesOfferProtocol {
GetPeerAddress,
RequestTakeOffer,
handleRespondToTakeOfferRequestMessage,
ValidateRespondToTakeOfferRequestMessage,
PayTakeOfferFee,
SendTakeOfferFeePayedMessage,
handleTakerDepositPaymentRequestMessage,
ValidateTakerDepositPaymentRequestMessage,
VerifyOffererAccount,
CreateAndSignContract,
PayDeposit,
SendSignedTakerDepositTxAsHex,
handleDepositTxPublishedMessage,
ValidateDepositTxPublishedMessage,
TakerCommitDepositTx,
handleBankTransferInitedMessage,
SignAndPublishPayoutTx,
@ -197,23 +198,27 @@ public class SellerTakesOfferProtocol {
public void handleRespondToTakeOfferRequestMessage(RespondToTakeOfferRequestMessage message) {
log.debug("handleRespondToTakeOfferRequestMessage called: state = " + state);
// validation
checkState(state == State.RequestTakeOffer);
checkTradeId(tradeId, message);
try {
// validation
checkState(state == State.RequestTakeOffer);
state = State.ValidateRespondToTakeOfferRequestMessage;
checkTradeId(tradeId, message);
// apply new state
state = State.handleRespondToTakeOfferRequestMessage;
if (message.isTakeOfferRequestAccepted()) {
trade.setState(Trade.State.OFFERER_ACCEPTED);
listener.onTakeOfferRequestAccepted();
// apply new state
if (message.isTakeOfferRequestAccepted()) {
trade.setState(Trade.State.OFFERER_ACCEPTED);
listener.onTakeOfferRequestAccepted();
// next task
payTakeOfferFee();
}
else {
// exit case
trade.setState(Trade.State.OFFERER_REJECTED);
listener.onTakeOfferRequestRejected();
// next task
payTakeOfferFee();
}
else {
// exit case
trade.setState(Trade.State.OFFERER_REJECTED);
listener.onTakeOfferRequestRejected();
}
} catch (Throwable t) {
handleValidationFault(t);
}
}
@ -228,8 +233,7 @@ public class SellerTakesOfferProtocol {
log.debug("handlePayTakeOfferFeeResult called: state = " + state);
trade.setTakeOfferFeeTxID(takeOfferFeeTxId);
state = State.SendTakeOfferFeePayedMessage;
SendTakeOfferFeePayedMessage.run(this::handleErrorMessage, peer,
tradeMessageService, tradeId, takeOfferFeeTxId, tradeAmount, tradePubKeyAsHex);
SendTakeOfferFeePayedMessage.run(this::handleErrorMessage, peer, tradeMessageService, tradeId, takeOfferFeeTxId, tradeAmount, tradePubKeyAsHex);
}
@ -237,36 +241,44 @@ public class SellerTakesOfferProtocol {
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 5. VerifyOffererAccount
// 11. VerifyOffererAccount
public void handleTakerDepositPaymentRequestMessage(TakerDepositPaymentRequestMessage message) {
log.debug("handleTakerDepositPaymentRequestMessage called: state = " + state);
// validation
checkState(state == State.SendTakeOfferFeePayedMessage);
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());
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
state = State.handleTakerDepositPaymentRequestMessage;
this.peersAccountId = peersAccountId;
this.peersBankAccount = peersBankAccount;
this.peersPubKey = offererPubKey;
this.preparedPeersDepositTxAsHex = preparedOffererDepositTxAsHex;
this.peersTxOutIndex = offererTxOutIndex;
// apply new state
this.peersAccountId = peersAccountId;
this.peersBankAccount = peersBankAccount;
this.peersPubKey = offererPubKey;
this.preparedPeersDepositTxAsHex = preparedOffererDepositTxAsHex;
this.peersTxOutIndex = offererTxOutIndex;
// next task
// 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);
}
// 6. CreateAndSignContract
// 13. CreateAndSignContract
private void handleVerifyOffererAccountResult() {
log.debug("handleVerifyOffererAccountResult called: state = " + state);
checkState(state == State.VerifyOffererAccount);
String takeOfferFeeTxId = trade.getTakeOfferFeeTxId();
state = State.CreateAndSignContract;
CreateAndSignContract.run(this::handleCreateAndSignContractResult,
@ -284,10 +296,9 @@ public class SellerTakesOfferProtocol {
accountKey);
}
// 7. PayDeposit
// 14. PayDeposit
private void handleCreateAndSignContractResult(Contract contract, String contractAsJson, String signature) {
log.debug("handleCreateAndSignContractResult called: state = " + state);
checkState(state == State.CreateAndSignContract);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setTakerContractSignature(signature);
@ -296,10 +307,9 @@ public class SellerTakesOfferProtocol {
tradePubKeyAsHex, arbitratorPubKey, peersPubKey, preparedPeersDepositTxAsHex);
}
// 8. SendSignedTakerDepositTxAsHex
// 15. SendSignedTakerDepositTxAsHex
private void handlePayDepositResult(Transaction signedTakerDepositTx) {
log.debug("handlePayDepositResult called: state = " + state);
checkState(state == State.PayDeposit);
String contractAsJson = trade.getContractAsJson();
String takerContractSignature = trade.getTakerContractSignature();
state = State.SendSignedTakerDepositTxAsHex;
@ -322,29 +332,37 @@ public class SellerTakesOfferProtocol {
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 9.a TakerCommitDepositTx
// 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);
// validation
checkState(state.ordinal() >= State.SendSignedTakerDepositTxAsHex.ordinal());
checkTradeId(tradeId, message);
nonEmptyStringOf(message.getDepositTxAsHex());
// next task
takerCommitDepositTx(depositTxAsHex);
} catch (Throwable t) {
handleValidationFault(t);
}
}
// apply new state
state = State.handleDepositTxPublishedMessage;
String depositTxAsHex = message.getDepositTxAsHex();
// next task
// 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);
listener.onDepositTxPublished(transaction);
trade.setDepositTx(transaction);
trade.setState(Trade.State.DEPOSIT_PUBLISHED);
listener.onDepositTxPublished();
}
@ -352,32 +370,34 @@ public class SellerTakesOfferProtocol {
// Incoming message from peer
///////////////////////////////////////////////////////////////////////////////////////////
// 9.b. handleBankTransferInitedMessage
// 25. handleBankTransferInitedMessage
public void handleBankTransferInitedMessage(BankTransferInitedMessage message) {
log.debug("onBankTransferInitedMessage called: state = " + state);
log.debug("state " + state);
log.debug("handleBankTransferInitedMessage called: state = " + state);
// validate
checkState(state.ordinal() >= State.SendSignedTakerDepositTxAsHex.ordinal() &&
state.ordinal() < State.SignAndPublishPayoutTx.ordinal());
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());
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;
// 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());
listener.onBankTransferInited(message.getTradeId());
} catch (Throwable t) {
handleValidationFault(t);
}
}
@ -386,11 +406,10 @@ public class SellerTakesOfferProtocol {
///////////////////////////////////////////////////////////////////////////////////////////
// User clicked the "bank transfer received" button, so we release the funds for pay out
// 10. SignAndPublishPayoutTx
// 26. SignAndPublishPayoutTx
public void handleUIEventFiatReceived() {
log.debug("onUIEventFiatReceived called: state = " + state);
log.debug("state " + state);
checkState(state == State.handleBankTransferInitedMessage || state == State.TakerCommitDepositTx);
log.debug("handleUIEventFiatReceived called: state = " + state);
checkState(state == State.handleBankTransferInitedMessage);
state = State.SignAndPublishPayoutTx;
SignAndPublishPayoutTx.run(this::handleSignAndPublishPayoutTxResult,
@ -403,19 +422,21 @@ public class SellerTakesOfferProtocol {
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
verifyOfferFeePayment();
}
// 11. SendPayoutTxToOfferer
// 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::handleSendPayoutTxToOffererResult, this::handleErrorMessage, peer, tradeMessageService,
tradeId, payoutTxAsHex);
SendPayoutTxToOfferer.run(this::handleErrorMessage, peer, tradeMessageService, tradeId, payoutTxAsHex);
}
private void handleSendPayoutTxToOffererResult() {
log.debug("onResultSendPayoutTxToOfferer called: state = " + state);
// 27b VerifyTakeOfferFeePayment
private void verifyOfferFeePayment() {
VerifyOfferFeePayment.run(this::handleFault, walletService, trade.getOffer().getOfferFeePaymentTxID());
}
@ -423,7 +444,6 @@ public class SellerTakesOfferProtocol {
// Private
///////////////////////////////////////////////////////////////////////////////////////////
// generic fault handler
private void handleFault(Throwable throwable) {
trade.setFault(throwable);
trade.setState(Trade.State.FAILED);
@ -434,4 +454,9 @@ public class SellerTakesOfferProtocol {
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());
}
}

View file

@ -22,7 +22,6 @@ import io.bitsquare.trade.TradeMessageService;
import io.bitsquare.trade.listeners.SendMessageListener;
import io.bitsquare.trade.protocol.trade.taker.messages.PayoutTxPublishedMessage;
import io.bitsquare.util.handlers.ErrorMessageHandler;
import io.bitsquare.util.handlers.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -30,21 +29,18 @@ import org.slf4j.LoggerFactory;
public class SendPayoutTxToOfferer {
private static final Logger log = LoggerFactory.getLogger(SendPayoutTxToOfferer.class);
public static void run(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler, Peer peer,
TradeMessageService tradeMessageService, String tradeId, String payoutTxAsHex) {
log.trace("Run task");
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() {
@Override
public void handleResult() {
log.trace("PayoutTxPublishedMessage successfully arrived at peer");
resultHandler.handleResult();
}
@Override
public void handleFault() {
log.error("PayoutTxPublishedMessage did not arrive at peer");
errorMessageHandler.handleErrorMessage("PayoutTxPublishedMessage did not arrive at peer");
errorMessageHandler.handleErrorMessage("Sending PayoutTxPublishedMessage failed.");
}
});

View file

@ -44,9 +44,8 @@ public class SignAndPublishPayoutTx {
Coin offererPaybackAmount,
Coin takerPaybackAmount,
String offererPayoutAddress) {
log.trace("Run task");
log.trace("Run SignAndPublishPayoutTx task");
try {
walletService.takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
@ -64,12 +63,10 @@ public class SignAndPublishPayoutTx {
@Override
public void onFailure(@NotNull Throwable t) {
log.error("Exception at takerSignsAndSendsTx " + t);
exceptionHandler.handleException(t);
}
});
} catch (Exception e) {
log.error("Exception at takerSignsAndSendsTx " + e);
exceptionHandler.handleException(e);
}
}

View file

@ -15,17 +15,25 @@
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade;
package io.bitsquare.trade.protocol.trade.taker.tasks;
import io.bitsquare.network.Message;
import io.bitsquare.btc.WalletService;
import io.bitsquare.util.handlers.ExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TradeMessage implements Message {
private static final Logger log = LoggerFactory.getLogger(TradeMessage.class);
public TradeMessage() {
public class VerifyOfferFeePayment {
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);
/* if (numOfPeersSeenTx > 2) {
resultHandler.handleResult();
}*/
}
}