From 6e3f83b8e073a3d38df51a2eac216835fc994119 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Sun, 15 Mar 2015 23:46:46 +0100 Subject: [PATCH] Refactor trade transactions --- .../java/io/bitsquare/btc/WalletService.java | 521 ++++++++++-------- .../btc/exceptions/SigningException.java | 34 ++ .../TransactionVerificationException.java | 29 + .../btc/exceptions/WalletException.java | 29 + .../bitsquare/gui/main/debug/DebugView.java | 12 +- .../protocol/trade/OfferSharedModel.java | 26 + .../trade/offerer/BuyerAsOffererModel.java | 147 ++--- .../trade/offerer/BuyerAsOffererProtocol.java | 4 +- .../RequestDepositPaymentMessage.java | 49 +- ...Tx.java => GetOffererDepositTxInputs.java} | 30 +- ...RequestOffererPublishDepositTxMessage.java | 15 +- .../tasks/ProcessRequestTakeOfferMessage.java | 3 - .../ProcessTakeOfferFeePayedMessage.java | 1 - .../offerer/tasks/RequestDepositPayment.java | 9 +- .../tasks/RespondToTakeOfferRequest.java | 2 +- .../tasks/SendBankTransferStartedMessage.java | 2 +- .../offerer/tasks/SendDepositTxIdToTaker.java | 3 +- .../tasks/SignAndPublishDepositTx.java | 13 +- .../trade/offerer/tasks/SignPayoutTx.java | 2 +- .../trade/taker/SellerAsTakerModel.java | 64 ++- .../trade/taker/SellerAsTakerProtocol.java | 8 +- ...RequestOffererPublishDepositTxMessage.java | 72 ++- .../ProcessBankTransferStartedMessage.java | 3 +- .../ProcessDepositTxPublishedMessage.java | 2 +- .../ProcessRequestDepositPaymentMessage.java | 12 +- .../trade/taker/tasks/RequestTakeOffer.java | 1 - ...Hex.java => SendSignedTakerDepositTx.java} | 21 +- .../taker/tasks/SignAndPublishPayoutTx.java | 2 +- .../taker/tasks/TakerCommitDepositTx.java | 2 +- ...ava => TakerCreatesAndSignsDepositTx.java} | 30 +- .../tomp2p/TomP2PTradeMessageService.java | 1 - .../io/bitsquare/util/taskrunner/Task.java | 6 +- core/src/main/resources/logback.xml | 19 +- 33 files changed, 685 insertions(+), 489 deletions(-) create mode 100644 core/src/main/java/io/bitsquare/btc/exceptions/SigningException.java create mode 100644 core/src/main/java/io/bitsquare/btc/exceptions/TransactionVerificationException.java create mode 100644 core/src/main/java/io/bitsquare/btc/exceptions/WalletException.java rename core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/{PrepareDepositTx.java => GetOffererDepositTxInputs.java} (56%) rename core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/{SendSignedTakerDepositTxAsHex.java => SendSignedTakerDepositTx.java} (72%) rename core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/{PayDeposit.java => TakerCreatesAndSignsDepositTx.java} (60%) diff --git a/core/src/main/java/io/bitsquare/btc/WalletService.java b/core/src/main/java/io/bitsquare/btc/WalletService.java index 5d19a0e537..889071c398 100644 --- a/core/src/main/java/io/bitsquare/btc/WalletService.java +++ b/core/src/main/java/io/bitsquare/btc/WalletService.java @@ -17,6 +17,9 @@ package io.bitsquare.btc; +import io.bitsquare.btc.exceptions.SigningException; +import io.bitsquare.btc.exceptions.TransactionVerificationException; +import io.bitsquare.btc.exceptions.WalletException; import io.bitsquare.btc.listeners.AddressConfidenceListener; import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.TxConfidenceListener; @@ -31,7 +34,6 @@ import org.bitcoinj.core.DownloadListener; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.InsufficientMoneyException; import org.bitcoinj.core.NetworkParameters; -import org.bitcoinj.core.ScriptException; import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Transaction; import org.bitcoinj.core.TransactionConfidence; @@ -87,6 +89,7 @@ import rx.Observable; import rx.subjects.BehaviorSubject; import rx.subjects.Subject; +import static com.google.inject.internal.util.$Preconditions.checkState; import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN; public class WalletService { @@ -597,299 +600,219 @@ public class WalletService { // Trade process /////////////////////////////////////////////////////////////////////////////////////////// - // 1. step: deposit tx - // Offerer creates the temporary 2of3 multiSig deposit tx with his unsigned input and change output - public Transaction offererPreparesDepositTx(Coin offererInputAmount, - String tradeId, - byte[] offererPubKey, - byte[] takerPubKey, - byte[] arbitratorPubKey) throws InsufficientMoneyException { - log.debug("offererCreatesMSTxAndAddPayment"); - log.trace("inputs: "); - log.trace("offererInputAmount=" + offererInputAmount.toFriendlyString()); - log.trace("offererPubKey=" + offererPubKey); - log.trace("takerPubKey=" + takerPubKey); - log.trace("arbitratorPubKey=" + arbitratorPubKey); - log.trace("tradeId=" + tradeId); + // 1. step: Define offerers inputs and outputs for the deposit tx + public TransactionDataResult offererCreatesDepositTxInputs(Coin inputAmount, AddressEntry addressInfo) throws InsufficientMoneyException, + TransactionVerificationException, WalletException { - // We need to subtract the fee as it will go to the miners - Coin offererInput = offererInputAmount.subtract(FeePolicy.TX_FEE); - log.trace("amountToPay=" + offererInput.toFriendlyString()); + // We pay the tx fee 2 times to the deposit tx: + // 1. Will be spent when publishing the deposit tx (paid by offerer) + // 2. Will be added to the MS amount, so when publishing the payout tx the fee is already there and the outputs are not changed by fee reduction + // The fee for the payout will be paid by the taker. - // We pay the offererInputAmount to a temporary MS output which will be changed later to the correct value. - // With the usage of completeTx() we get all the work done with fee calculation, validation and coin selection. - // Later with more customized coin selection we can use a custom CoinSelector implementation. - // We don't commit that tx to the wallet as it will be changed later and it's not signed yet. - // So it will not change the wallet balance. - // The btc tx fee will be included by the completeTx() call, so we don't need to add it manually. - Transaction tx = new Transaction(params); - Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey); - tx.addOutput(offererInput, multiSigOutputScript); // that output is just a dummy for input calculation + // inputAmount includes the tx fee. So we subtract the fee to get the dummyOutputAmount. + Coin dummyOutputAmount = inputAmount.subtract(FeePolicy.TX_FEE); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx); - sendRequest.shuffleOutputs = false; - AddressEntry addressEntry = getAddressInfo(tradeId); - // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to - // wait for 1 confirmation) - sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry, true); - sendRequest.changeAddress = addressEntry.getAddress(); - wallet.completeTx(sendRequest); + Transaction dummyTX = new Transaction(params); + // The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything. + // We don't care about fee calculation differences between the real tx and that dummy tx as we use a static tx fee. + TransactionOutput msOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, new ECKey()); + dummyTX.addOutput(msOutput); - // The completeTx() call signs the input, but we don't want to pass over a signed tx so we remove the - // signature to make sure the tx is invalid for publishing - // We have exactly 1 input as our spending transaction output is from the create offer fee payment and has only 1 output - tx.getInputs().get(0).setScriptSig(new Script(new byte[]{})); + // Fin the needed inputs to pay the output, optional add change output. + // Normally only 1 input and no change output is used, but we support multiple inputs and outputs. Our spending transaction output is from the create + // offer fee payment. In future changes (in case of no offer fee) multiple inputs might become used. + addAvailableInputsAndChangeOutputs(dummyTX, addressInfo); - log.trace("verify tx"); - tx.verify(); + // The completeTx() call signs the input, but we don't want to pass over signed tx inputs + // But to be safe and to support future changes (in case of no offer fee) we handle potential multiple inputs + removeSignatures(dummyTX); + + verifyTransaction(dummyTX); + checkWalletConsistency(); // The created tx looks like: /* - IN[0] any input > offererInputAmount + fee (unsigned) - OUT[0] MS offererInputAmount - OUT[1] Optional Change = input - offererInputAmount - fee btc tx fee + IN[0] any input > inputAmount (including tx fee) (unsigned) + IN[1...n] optional inputs supported, but currently there is just 1 input (unsigned) + OUT[0] dummyOutputAmount (inputAmount - tx fee) + OUT[1] Optional Change = inputAmount - dummyOutputAmount - tx fee + OUT[2...n] optional more outputs are supported, but currently there is just max. 1 optional change output */ - log.trace("Check if wallet is consistent: result=" + wallet.isConsistent()); - printInputs("offererCreatesMSTxAndAddPayment", tx); - log.debug("tx = " + tx); + printInputs("dummyTX", dummyTX); + log.debug("dummyTX created: " + dummyTX); - return tx; + List connectedOutputsForAllInputs = new ArrayList<>(); + for (TransactionInput input : dummyTX.getInputs()) { + connectedOutputsForAllInputs.add(input.getConnectedOutput()); + } + + // Only save offerer outputs, the MS output is ignored + List outputs = new ArrayList<>(); + for (TransactionOutput output : dummyTX.getOutputs()) { + if (output.equals(msOutput)) + continue; + outputs.add(output); + } + + return new TransactionDataResult(connectedOutputsForAllInputs, outputs); } - // 2. step: deposit tx - // Taker adds his input and change output, changes the multiSig amount to the correct value and sign his input - public Transaction takerAddPaymentAndSignTx(Coin takerInputAmount, - Coin msOutputAmount, - Transaction preparedDepositTx, - String tradeId, - byte[] offererPubKey, - byte[] takerPubKey, - byte[] arbitratorPubKey) throws InsufficientMoneyException { - log.debug("takerAddPaymentAndSignTx"); - log.trace("inputs: "); - log.trace("takerInputAmount=" + takerInputAmount.toFriendlyString()); - log.trace("msOutputAmount=" + msOutputAmount.toFriendlyString()); - log.trace("offererPubKey=" + offererPubKey); - log.trace("takerPubKey=" + takerPubKey); - log.trace("arbitratorPubKey=" + arbitratorPubKey); - log.trace("preparedDepositTransaction=" + preparedDepositTx); - log.trace("tradeId=" + tradeId); + // 2. step: Taker creates a deposit tx and signs his inputs + public TransactionDataResult takerCreatesAndSignsDepositTx(Coin inputAmount, + Coin msOutputAmount, + List offererConnectedOutputsForAllInputs, + List offererOutputs, + AddressEntry addressInfo, + byte[] offererPubKey, + byte[] takerPubKey, + byte[] arbitratorPubKey) throws InsufficientMoneyException, SigningException, + TransactionVerificationException, WalletException { - // We pay the btc tx fee 2 times to the deposit tx: - // 1. will be spent to miners when publishing the deposit tx - // 2. will be as added to the MS amount, so when spending the payout tx the fee is already there and the - // outputs are not changed by fee reduction - // Both traders pay 1 times a fee, so it is equally split between them + // TODO verify amounts, addresses, MS + + Transaction depositTx = new Transaction(params); + Script multiSigOutputScript = getMultiSigOutputScript(offererPubKey, takerPubKey, arbitratorPubKey); + // We use temporary inputAmount as the value for the output amount to get the correct inputs from the takers side. + // Later when we add the offerer inputs we replace the output amount with the real msOutputAmount + // Tx fee for deposit tx will be paid by offerer. + TransactionOutput msOutput = new TransactionOutput(params, depositTx, inputAmount, multiSigOutputScript.getProgram()); + depositTx.addOutput(msOutput); - // We do exactly the same as in the 1. step but with the takers input. - Transaction tempTx = new Transaction(params); - Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey); - tempTx.addOutput(takerInputAmount, multiSigOutputScript); + // Not lets find the inputs to satisfy that output and add an optional change output + addAvailableInputsAndChangeOutputs(depositTx, addressInfo); - Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tempTx); - sendRequest.shuffleOutputs = false; - AddressEntry addressEntry = getAddressInfo(tradeId); - // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to - // wait for 1 confirmation) - sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry, true); - sendRequest.changeAddress = addressEntry.getAddress(); - wallet.completeTx(sendRequest); + // Now as we have the takers inputs and outputs we replace the temporary output amount with the real msOutputAmount + msOutput.setValue(msOutputAmount); - printInputs("tempTx", tempTx); - log.trace("tempTx=" + tempTx); - // That tx has signed input, but we don't need to remove it as we don't send that tx out, - // it is just used temporary. - - // The created tempTx looks like: - /* - IN[0] any input taker > takerInputAmount + fee (signed) - OUT[0] MS takerInputAmount - OUT[1] Optional change = input taker - takerInputAmount - fee btc tx fee - */ - - // Now we construct the real 2of3 multiSig tx from the serialized offerers tx - - // The serialized offerers tx looks like: - /* - IN[0] any input offerer > offererInputAmount + fee (unsigned) - OUT[0] MS offererInputAmount - OUT[1] Change = input offerer - offererInputAmount - fee - btc tx fee - */ - - // Now we add the inputs and outputs from our temp tx and change the multiSig amount to the correct value - for (TransactionInput input : tempTx.getInputs()) { - preparedDepositTx.addInput(input); - } - // handle optional change output - if (tempTx.getOutputs().size() == 2) { - preparedDepositTx.addOutput(tempTx.getOutput(1)); + // Save reference to inputs for signing, before we add offerer inputs + List takerInputs = new ArrayList<>(depositTx.getInputs()); + List connectedOutputsForAllTakerInputs = new ArrayList<>(); + for (TransactionInput input : takerInputs) { + connectedOutputsForAllTakerInputs.add(input.getConnectedOutput()); } - preparedDepositTx.getOutput(0).setValue(msOutputAmount); - - // Now we sign our input (index 1) - TransactionInput input = preparedDepositTx.getInput(1); - if (input == null || input.getConnectedOutput() == null) - log.error("Must not happen - input or input.getConnectedOutput() is null: " + input); - - Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); - ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); - Sha256Hash hash = preparedDepositTx.hashForSignature(1, scriptPubKey, Transaction.SigHash.ALL, false); - ECKey.ECDSASignature ecSig = sigKey.sign(hash); - TransactionSignature txSig = new TransactionSignature(ecSig, Transaction.SigHash.ALL, false); - if (scriptPubKey.isSentToRawPubKey()) { - input.setScriptSig(ScriptBuilder.createInputScript(txSig)); - } - else if (scriptPubKey.isSentToAddress()) { - input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); - } - else { - throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); + // Lets save the takerOutputs for passing later to the result, the MS output is ignored + List takerOutputs = new ArrayList<>(); + for (TransactionOutput output : depositTx.getOutputs()) { + if (output.equals(msOutput)) + continue; + takerOutputs.add(output); } - log.trace("check if it can be correctly spent for input 1"); - input.getScriptSig().correctlySpends(preparedDepositTx, 1, scriptPubKey); + // Add all inputs from offerer (normally its just 1 input) + for (TransactionOutput connectedOutputForInput : offererConnectedOutputsForAllInputs) { + TransactionOutPoint outPoint = new TransactionOutPoint(params, connectedOutputForInput.getIndex(), connectedOutputForInput.getParentTransaction()); + TransactionInput transactionInput = new TransactionInput(params, depositTx, new byte[]{}, outPoint); + depositTx.addInput(transactionInput); + } - log.trace("verify tx"); - preparedDepositTx.verify(); + // Add optional outputs + for (TransactionOutput output : offererOutputs) { + depositTx.addOutput(output); + } - // The resulting tx looks like: - /* - IN[0] any input offerer > offererInputAmount + fee (unsigned) e.g.: 0.1001 - IN[1] any input taker > takerInputAmount + fee (signed) e.g.: 1.1001 - OUT[0] MS offererInputAmount e.g.: 1.2001 - OUT[1] Change = input offerer - offererInputAmount - fee e.g.: 0 if input is matching correct value - OUT[2] Change = input taker - takerInputAmount - fee e.g.: 0 if input is matching correct value btc tx fee e.g.: 0.1001 - */ + printInputs("depositTx", depositTx); + log.debug("depositTx = " + depositTx); - // We must not commit that tx to the wallet as we will get it over the network when the offerer - // publishes it and it will have a different tx hash, so it would invalidate our wallet. + // Sign taker inputs + // Taker inputs are the first inputs (0 -n), so the index of takerInputs and depositTx.getInputs() matches for the number of takerInputs. + int index = 0; + for (TransactionInput input : takerInputs) { + log.debug("signInput input "+input.toString()); + log.debug("signInput index "+index); + signInput(depositTx, input, index); + checkScriptSig(depositTx, input, index); + index++; + } - log.trace("Check if wallet is consistent before commit: result=" + wallet.isConsistent()); - printInputs("takerAddPaymentAndSignTx", preparedDepositTx); - log.debug("tx = " + preparedDepositTx); - return preparedDepositTx; + verifyTransaction(depositTx); + checkWalletConsistency(); + + printInputs("depositTx", depositTx); + log.debug("depositTx = " + depositTx); + + return new TransactionDataResult(depositTx, connectedOutputsForAllTakerInputs, takerOutputs); } - // 3. step: deposit tx // Offerer signs tx and publishes it - public void offererSignAndPublishTx(Transaction preparedDepositTx, - Transaction takersSignedDepositTx, - Transaction takersFromTx, - byte[] takersSignedScriptSig, - long offererTxOutIndex, - long takerTxOutIndex, - FutureCallback callback) { - log.debug("offererSignAndPublishTx"); - log.trace("inputs: "); - log.trace("preparedDepositTx=" + preparedDepositTx); - log.trace("takersSignedTx=" + takersSignedDepositTx); - log.trace("takersFromTx=" + takersFromTx); - log.trace("takersSignedScriptSig=" + takersSignedScriptSig); - log.trace("callback=" + callback); + public void offererSignAndPublishTx(Transaction takersDepositTx, + List takersConnectedOutputsForAllInputs, + List offererConnectedOutputsForAllInputs, + byte[] offererPubKey, + byte[] takerPubKey, + byte[] arbitratorPubKey, + FutureCallback callback) throws SigningException, TransactionVerificationException, WalletException { - // We create an empty tx (did not find a way to manipulate a tx input, otherwise the takers tx could be used - // directly and add the offerers input and output) - Transaction tx = new Transaction(params); + // TODO verify amounts, addresses, MS + + // The outpoints are not available from the serialized takersDepositTx, so we cannot use that tx directly, but we use it to construct a new depositTx + Transaction depositTx = new Transaction(params); - printInputs("preparedDepositTx", preparedDepositTx); - log.trace("preparedDepositTx = " + preparedDepositTx); + // We save offererInputs for later signing when tx is fully constructed + List offererInputs = new ArrayList<>(); - // add input - Transaction offerersFirstTxConnOut = wallet.getTransaction(preparedDepositTx.getInput(0).getOutpoint().getHash()); - TransactionOutPoint offerersFirstTxOutPoint = new TransactionOutPoint(params, offererTxOutIndex, offerersFirstTxConnOut); - TransactionInput offerersFirstTxInput = new TransactionInput(params, tx, new byte[]{}, offerersFirstTxOutPoint); - offerersFirstTxInput.setParent(tx); - tx.addInput(offerersFirstTxInput); - - printInputs("takersSignedTxInput", takersSignedDepositTx); - log.trace("takersSignedTx = " + takersSignedDepositTx); - - // add input - TransactionOutPoint takersSignedTxOutPoint = new TransactionOutPoint(params, takerTxOutIndex, takersFromTx); - TransactionInput takersSignedTxInput = new TransactionInput( - params, tx, takersSignedScriptSig, takersSignedTxOutPoint); - takersSignedTxInput.setParent(tx); - tx.addInput(takersSignedTxInput); - - // add outputs from takers tx, they are already correct - tx.addOutput(takersSignedDepositTx.getOutput(0)); - if (takersSignedDepositTx.getOutputs().size() > 1) { - tx.addOutput(takersSignedDepositTx.getOutput(1)); - } - if (takersSignedDepositTx.getOutputs().size() == 3) { - tx.addOutput(takersSignedDepositTx.getOutput(2)); + // Add all inputs from offerer (normally its just 1 input) + for (TransactionOutput connectedOutputForInput : offererConnectedOutputsForAllInputs) { + TransactionOutPoint outPoint = new TransactionOutPoint(params, connectedOutputForInput.getIndex(), connectedOutputForInput.getParentTransaction()); + TransactionInput input = new TransactionInput(params, depositTx, new byte[]{}, outPoint); + offererInputs.add(input); + depositTx.addInput(input); } - printInputs("tx", tx); - log.trace("tx = " + tx); - log.trace("Wallet balance before signing: " + wallet.getBalance()); + // Add all inputs from taker and apply signature + for (TransactionOutput connectedOutputForInput : takersConnectedOutputsForAllInputs) { + TransactionOutPoint outPoint = new TransactionOutPoint(params, connectedOutputForInput.getIndex(), connectedOutputForInput.getParentTransaction()); - // sign the input - TransactionInput input = tx.getInput(0); - if (input == null || input.getConnectedOutput() == null) { - log.error("input or input.getConnectedOutput() is null: " + input); + // We grab the signatures from the takersDepositTx and apply it to the new tx input + Optional result = takersDepositTx.getInputs().stream() + .filter(e -> e.getConnectedOutput().hashCode() == connectedOutputForInput.hashCode()).findAny(); + if (result.isPresent()) { + TransactionInput signedInput = result.get(); + Script script = signedInput.getScriptSig(); + + TransactionInput transactionInput = new TransactionInput(params, depositTx, script.getProgram(), outPoint); + depositTx.addInput(transactionInput); + } } - Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); - ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); - Sha256Hash hash = tx.hashForSignature(0, scriptPubKey, Transaction.SigHash.ALL, false); - ECKey.ECDSASignature ecSig = sigKey.sign(hash); - TransactionSignature txSig = new TransactionSignature(ecSig, Transaction.SigHash.ALL, false); - if (scriptPubKey.isSentToRawPubKey()) { - input.setScriptSig(ScriptBuilder.createInputScript(txSig)); - } - else if (scriptPubKey.isSentToAddress()) { - input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); - } - else { - throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); + // Add all outputs from takersDepositTx to depositTx + takersDepositTx.getOutputs().forEach(depositTx::addOutput); + + printInputs("depositTx", depositTx); + log.debug("depositTx = " + depositTx); + + // Offerer inputs are the first inputs (0 -n), so the index of offererInputs and depositTx.getInputs() matches for the number of offererInputs. + int index = 0; + for (TransactionInput input : offererInputs) { + signInput(depositTx, input, index); + checkScriptSig(depositTx, input, index); + index++; } - input.getScriptSig().correctlySpends(tx, 0, scriptPubKey); - log.trace("check if it can be correctly spent for input 0 OK"); - - TransactionInput input1 = tx.getInput(1); - scriptPubKey = input1.getConnectedOutput().getScriptPubKey(); - input1.getScriptSig().correctlySpends(tx, 1, scriptPubKey); - log.trace("check if it can be correctly spent for input 1 OK"); - - /* - IN[0] offerer signed 0.1001 - IN[1] taker signed 1.1001 - OUT[0] MS (include btc tx fee for payout tx) 1.2001 - OUT[1] offerer change - OUT[2] taker change - btc tx fee 0.0001 - */ - - log.trace("verify "); - tx.verify(); - - printInputs("tx", tx); - log.debug("tx = " + tx); + // TODO verify MS, amounts + //Script multiSigOutputScript = getMultiSigOutputScript(offererPubKey, takerPubKey, arbitratorPubKey); + verifyTransaction(depositTx); + checkWalletConsistency(); + + // Broadcast depositTx log.trace("Wallet balance before broadcastTransaction: " + wallet.getBalance()); log.trace("Check if wallet is consistent before broadcastTransaction: result=" + wallet.isConsistent()); - ListenableFuture broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx); + ListenableFuture broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(depositTx); log.trace("Wallet balance after broadcastTransaction: " + wallet.getBalance()); - log.trace("Check if wallet is consistent: result=" + wallet.isConsistent()); - + log.trace("Check if wallet is consistent after broadcastTransaction: result=" + wallet.isConsistent()); Futures.addCallback(broadcastComplete, callback); - printInputs("tx", tx); - log.debug("tx = " + tx); } // 4 step deposit tx: Offerer send deposit tx to taker - public Transaction takerCommitDepositTx(Transaction depositTx) { + public Transaction takerCommitDepositTx(Transaction depositTx) throws WalletException { log.trace("takerCommitDepositTx"); log.trace("inputs: "); log.trace("depositTx=" + depositTx); // If not recreate the tx we get a null pointer at receivePending - depositTx = new Transaction(params, depositTx.bitcoinSerialize()); + //depositTx = new Transaction(params, depositTx.bitcoinSerialize()); log.trace("depositTx=" + depositTx); // boolean isAlreadyInWallet = wallet.maybeCommitTx(depositTx); //log.trace("isAlreadyInWallet=" + isAlreadyInWallet); @@ -900,6 +823,8 @@ public class WalletService { wallet.receivePending(depositTx, null, true); } catch (Throwable t) { log.error(t.getMessage()); + t.printStackTrace(); + throw new WalletException(t); } return depositTx; @@ -1008,7 +933,7 @@ public class WalletService { } } - private Script getMultiSigScript(byte[] offererPubKey, byte[] takerPubKey, byte[] arbitratorPubKey) { + private Script getMultiSigOutputScript(byte[] offererPubKey, byte[] takerPubKey, byte[] arbitratorPubKey) { ECKey offererKey = ECKey.fromPublicOnly(offererPubKey); ECKey takerKey = ECKey.fromPublicOnly(takerPubKey); ECKey arbitratorKey = ECKey.fromPublicOnly(arbitratorPubKey); @@ -1049,12 +974,120 @@ public class WalletService { } } + private void checkWalletConsistency() throws WalletException { + try { + log.trace("Check if wallet is consistent before commit."); + checkState(wallet.isConsistent()); + } catch (Throwable t) { + t.printStackTrace(); + log.error(t.getMessage()); + throw new WalletException(t); + } + } + + private void verifyTransaction(Transaction transaction) throws TransactionVerificationException { + try { + log.trace("Verify transaction"); + transaction.verify(); + } catch (Throwable t) { + t.printStackTrace(); + log.error(t.getMessage()); + throw new TransactionVerificationException(t); + } + } + + private void signInput(Transaction transaction, TransactionInput input, int inputIndex) throws SigningException, TransactionVerificationException { + Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); + ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); + Sha256Hash hash = transaction.hashForSignature(inputIndex, scriptPubKey, Transaction.SigHash.ALL, false); + ECKey.ECDSASignature signature = sigKey.sign(hash); + TransactionSignature txSig = new TransactionSignature(signature, Transaction.SigHash.ALL, false); + if (scriptPubKey.isSentToRawPubKey()) { + input.setScriptSig(ScriptBuilder.createInputScript(txSig)); + } + else if (scriptPubKey.isSentToAddress()) { + input.setScriptSig(ScriptBuilder.createInputScript(txSig, sigKey)); + } + else { + throw new SigningException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); + } + } + + private void checkScriptSig(Transaction transaction, TransactionInput input, int inputIndex) throws TransactionVerificationException { + try { + log.trace("Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey."); + input.getScriptSig().correctlySpends(transaction, inputIndex, input.getConnectedOutput().getScriptPubKey()); + inputIndex++; + } catch (Throwable t) { + t.printStackTrace(); + log.error(t.getMessage()); + throw new TransactionVerificationException(t); + } + } + + /* private void checkScriptSigForAllInputs(Transaction transaction) throws TransactionVerificationException { + int inputIndex = 0; + for (TransactionInput input : transaction.getInputs()) { + checkScriptSig(transaction, input, inputIndex); + } + }*/ + + private void removeSignatures(Transaction transaction) throws InsufficientMoneyException { + for (TransactionInput input : transaction.getInputs()) { + input.setScriptSig(new Script(new byte[]{})); + } + } + + private void addAvailableInputsAndChangeOutputs(Transaction transaction, AddressEntry addressEntry) throws InsufficientMoneyException { + // Lets let the framework do the work to find the right inputs + Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction); + sendRequest.shuffleOutputs = false; + // we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to wait for 1 confirmation) + sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry, true); + sendRequest.changeAddress = addressEntry.getAddress(); + // With the usage of completeTx() we get all the work done with fee calculation, validation and coin selection. + // We don't commit that tx to the wallet as it will be changed later and it's not signed yet. + // So it will not change the wallet balance. + wallet.completeTx(sendRequest); + + printInputs("transaction", transaction); + log.trace("transaction=" + transaction); + } /////////////////////////////////////////////////////////////////////////////////////////// // Inner classes /////////////////////////////////////////////////////////////////////////////////////////// + public class TransactionDataResult { + private final List connectedOutputsForAllInputs; + private final List outputs; + private Transaction depositTx; + + public TransactionDataResult(List connectedOutputsForAllInputs, List outputs) { + this.connectedOutputsForAllInputs = connectedOutputsForAllInputs; + this.outputs = outputs; + } + + public TransactionDataResult(Transaction depositTx, List connectedOutputsForAllInputs, List outputs) { + this.depositTx = depositTx; + this.connectedOutputsForAllInputs = connectedOutputsForAllInputs; + this.outputs = outputs; + } + + public List getOutputs() { + return outputs; + } + + public List getConnectedOutputsForAllInputs() { + return connectedOutputsForAllInputs; + } + + public Transaction getDepositTx() { + return depositTx; + } + } + private static class ObservableDownloadListener extends DownloadListener { private final Subject subject = BehaviorSubject.create(0d); diff --git a/core/src/main/java/io/bitsquare/btc/exceptions/SigningException.java b/core/src/main/java/io/bitsquare/btc/exceptions/SigningException.java new file mode 100644 index 0000000000..2d53c2a089 --- /dev/null +++ b/core/src/main/java/io/bitsquare/btc/exceptions/SigningException.java @@ -0,0 +1,34 @@ +/* + * 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 . + */ + +package io.bitsquare.btc.exceptions; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SigningException extends Exception { + private static final Logger log = LoggerFactory.getLogger(SigningException.class); + + public SigningException(String message) { + super(message); + } + + public SigningException(Throwable t) { + super(t); + + } +} diff --git a/core/src/main/java/io/bitsquare/btc/exceptions/TransactionVerificationException.java b/core/src/main/java/io/bitsquare/btc/exceptions/TransactionVerificationException.java new file mode 100644 index 0000000000..33d35120d5 --- /dev/null +++ b/core/src/main/java/io/bitsquare/btc/exceptions/TransactionVerificationException.java @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +package io.bitsquare.btc.exceptions; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TransactionVerificationException extends Exception { + private static final Logger log = LoggerFactory.getLogger(TransactionVerificationException.class); + + public TransactionVerificationException(Throwable t) { + super(t); + } +} diff --git a/core/src/main/java/io/bitsquare/btc/exceptions/WalletException.java b/core/src/main/java/io/bitsquare/btc/exceptions/WalletException.java new file mode 100644 index 0000000000..aa1777acda --- /dev/null +++ b/core/src/main/java/io/bitsquare/btc/exceptions/WalletException.java @@ -0,0 +1,29 @@ +/* + * 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 . + */ + +package io.bitsquare.btc.exceptions; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WalletException extends Exception { + private static final Logger log = LoggerFactory.getLogger(WalletException.class); + + public WalletException(Throwable t) { + super(t); + } +} diff --git a/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java b/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java index 27bf8b5d0d..26c312f3b1 100644 --- a/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java +++ b/core/src/main/java/io/bitsquare/gui/main/debug/DebugView.java @@ -26,7 +26,7 @@ import io.bitsquare.trade.protocol.placeoffer.tasks.BroadcastCreateOfferFeeTx; import io.bitsquare.trade.protocol.placeoffer.tasks.CreateOfferFeeTx; import io.bitsquare.trade.protocol.placeoffer.tasks.ValidateOffer; import io.bitsquare.trade.protocol.trade.offerer.BuyerAsOffererProtocol; -import io.bitsquare.trade.protocol.trade.offerer.tasks.PrepareDepositTx; +import io.bitsquare.trade.protocol.trade.offerer.tasks.GetOffererDepositTxInputs; import io.bitsquare.trade.protocol.trade.offerer.tasks.ProcessPayoutTxPublishedMessage; import io.bitsquare.trade.protocol.trade.offerer.tasks.ProcessRequestOffererPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.offerer.tasks.ProcessRequestTakeOfferMessage; @@ -43,7 +43,7 @@ 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.SellerAsTakerProtocol; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateAndSignContract; -import io.bitsquare.trade.protocol.trade.taker.tasks.PayDeposit; +import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCreatesAndSignsDepositTx; import io.bitsquare.trade.protocol.trade.taker.tasks.PayTakeOfferFee; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessBankTransferStartedMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessDepositTxPublishedMessage; @@ -51,7 +51,7 @@ import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessRespondToTakeOfferRe import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessRequestDepositPaymentMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.RequestTakeOffer; import io.bitsquare.trade.protocol.trade.taker.tasks.SendPayoutTxToOfferer; -import io.bitsquare.trade.protocol.trade.taker.tasks.SendSignedTakerDepositTxAsHex; +import io.bitsquare.trade.protocol.trade.taker.tasks.SendSignedTakerDepositTx; 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; @@ -110,7 +110,7 @@ public class DebugView extends InitializableView { RespondToTakeOfferRequest.class, ProcessTakeOfferFeePayedMessage.class, - PrepareDepositTx.class, + GetOffererDepositTxInputs.class, RequestDepositPayment.class, ProcessRequestOffererPublishDepositTxMessage.class, @@ -140,8 +140,8 @@ public class DebugView extends InitializableView { ProcessRequestDepositPaymentMessage.class, VerifyOffererAccount.class, CreateAndSignContract.class, - PayDeposit.class, - SendSignedTakerDepositTxAsHex.class, + TakerCreatesAndSignsDepositTx.class, + SendSignedTakerDepositTx.class, ProcessDepositTxPublishedMessage.class, TakerCommitDepositTx.class, diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/OfferSharedModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/OfferSharedModel.java index 2dd60a4bbb..3859ee7958 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/OfferSharedModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/OfferSharedModel.java @@ -18,6 +18,7 @@ package io.bitsquare.trade.protocol.trade; import io.bitsquare.bank.BankAccount; +import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.BlockChainService; import io.bitsquare.btc.WalletService; import io.bitsquare.crypto.SignatureService; @@ -50,6 +51,9 @@ public class OfferSharedModel extends SharedModel { protected final ECKey accountKey; protected final byte[] arbitratorPubKey; + // lazy initialized at first read access, as we don't want to create an entry before it is really needed + protected AddressEntry addressInfo; + // data written/read by tasks protected TradeMessage tradeMessage; protected byte[] takerPubKey; @@ -77,6 +81,28 @@ public class OfferSharedModel extends SharedModel { } // getter/setter + public AddressEntry getAddressInfo() { + if (addressInfo == null) + addressInfo = getWalletService().getAddressInfo(offer.getId()); + + return addressInfo; + } + + public String getPeersAccountId() { + return peersAccountId; + } + + public void setPeersAccountId(String peersAccountId) { + this.peersAccountId = peersAccountId; + } + + public BankAccount getPeersBankAccount() { + return peersBankAccount; + } + + public void setPeersBankAccount(BankAccount peersBankAccount) { + this.peersBankAccount = peersBankAccount; + } public TradeMessageService getTradeMessageService() { return tradeMessageService; diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererModel.java index f135fbf884..b8180dda3a 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererModel.java @@ -31,9 +31,12 @@ import io.bitsquare.user.User; import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutput; import java.security.PublicKey; +import java.util.List; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,28 +55,25 @@ public class BuyerAsOffererModel extends OfferSharedModel { private Trade trade; private Peer taker; - private Transaction preparedDepositTx; - private Transaction depositTx; - private String takerAccountId; private BankAccount takerBankAccount; private PublicKey takerMessagePublicKey; private String takerContractAsJson; - private Transaction takersSignedDepositTx; - - private Transaction takersFromTx; - private byte[] txScriptSig; - - private long takerTxOutIndex; private Coin takerPaybackAmount; private String takeOfferFeeTxId; - private String takerPayoutAddress; - private long offererTxOutIndex; private byte[] offererPubKey; private ECKey.ECDSASignature offererSignature; private Coin offererPaybackAmount; + private List offererConnectedOutputsForAllInputs; + private List offererOutputs; + private Transaction takerDepositTx; + private List takerConnectedOutputsForAllInputs; + private List takerOutputs; + private String takerPayoutAddress; + private Transaction offererPayoutTx; + private Transaction publishedDepositTx; /////////////////////////////////////////////////////////////////////////////////////////// @@ -110,21 +110,6 @@ public class BuyerAsOffererModel extends OfferSharedModel { return offererPaybackAddress; } - public Transaction getPreparedDepositTx() { - return preparedDepositTx; - } - - public void setPreparedDepositTx(Transaction preparedDepositTx) { - this.preparedDepositTx = preparedDepositTx; - } - - public long getOffererTxOutIndex() { - return offererTxOutIndex; - } - - public void setOffererTxOutIndex(long offererTxOutIndex) { - this.offererTxOutIndex = offererTxOutIndex; - } public String getTakeOfferFeeTxId() { return takeOfferFeeTxId; @@ -134,14 +119,6 @@ public class BuyerAsOffererModel extends OfferSharedModel { this.takeOfferFeeTxId = takeOfferFeeTxId; } - public String getTakerPayoutAddress() { - return takerPayoutAddress; - } - - public void setTakerPayoutAddress(String takerPayoutAddress) { - this.takerPayoutAddress = takerPayoutAddress; - } - @Override public String getTakerAccountId() { return takerAccountId; @@ -178,37 +155,6 @@ public class BuyerAsOffererModel extends OfferSharedModel { this.takerContractAsJson = takerContractAsJson; } - public Transaction getTakersSignedDepositTx() { - return takersSignedDepositTx; - } - - public void setTakersSignedDepositTx(Transaction takersSignedDepositTx) { - this.takersSignedDepositTx = takersSignedDepositTx; - } - - public Transaction getTakersFromTx() { - return takersFromTx; - } - - public void setTakersFromTx(Transaction takersFromTx) { - this.takersFromTx = takersFromTx; - } - - public byte[] getTxScriptSig() { - return txScriptSig; - } - - public void setTxScriptSig(byte[] txScriptSig) { - this.txScriptSig = txScriptSig; - } - - public long getTakerTxOutIndex() { - return takerTxOutIndex; - } - - public void setTakerTxOutIndex(long takerTxOutIndex) { - this.takerTxOutIndex = takerTxOutIndex; - } public byte[] getOffererPubKey() { return offererPubKey; @@ -218,13 +164,6 @@ public class BuyerAsOffererModel extends OfferSharedModel { this.offererPubKey = offererPubKey; } - public Transaction getDepositTx() { - return depositTx; - } - - public void setDepositTx(Transaction depositTx) { - this.depositTx = depositTx; - } public ECKey.ECDSASignature getOffererSignature() { return offererSignature; @@ -261,4 +200,68 @@ public class BuyerAsOffererModel extends OfferSharedModel { public void setTaker(Peer taker) { this.taker = taker; } + + public List getOffererConnectedOutputsForAllInputs() { + return offererConnectedOutputsForAllInputs; + } + + public void setOffererConnectedOutputsForAllInputs(List offererConnectedOutputsForAllInputs) { + this.offererConnectedOutputsForAllInputs = offererConnectedOutputsForAllInputs; + } + + public List getOffererOutputs() { + return offererOutputs; + } + + public void setOffererOutputs(List offererOutputs) { + this.offererOutputs = offererOutputs; + } + + public void setTakerDepositTx(Transaction takerDepositTx) { + this.takerDepositTx = takerDepositTx; + } + + public Transaction getTakerDepositTx() { + return takerDepositTx; + } + + public void setTakerConnectedOutputsForAllInputs(List takerConnectedOutputsForAllInputs) { + this.takerConnectedOutputsForAllInputs = takerConnectedOutputsForAllInputs; + } + + public List getTakerConnectedOutputsForAllInputs() { + return takerConnectedOutputsForAllInputs; + } + + public void setTakerOutputs(List takerOutputs) { + this.takerOutputs = takerOutputs; + } + + public List getTakerOutputs() { + return takerOutputs; + } + + public String getTakerPayoutAddress() { + return takerPayoutAddress; + } + + public void setTakerPayoutAddress(String takerPayoutAddress) { + this.takerPayoutAddress = takerPayoutAddress; + } + + public void setOffererPayoutTx(Transaction offererPayoutTx) { + this.offererPayoutTx = offererPayoutTx; + } + + public Transaction getOffererPayoutTx() { + return offererPayoutTx; + } + + public void setPublishedDepositTx(Transaction publishedDepositTx) { + this.publishedDepositTx = publishedDepositTx; + } + + public Transaction getPublishedDepositTx() { + return publishedDepositTx; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererProtocol.java index 78dfe0bbee..144141bd1f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/BuyerAsOffererProtocol.java @@ -21,7 +21,7 @@ import io.bitsquare.network.Message; import io.bitsquare.network.Peer; import io.bitsquare.trade.handlers.MessageHandler; import io.bitsquare.trade.protocol.trade.TradeMessage; -import io.bitsquare.trade.protocol.trade.offerer.tasks.PrepareDepositTx; +import io.bitsquare.trade.protocol.trade.offerer.tasks.GetOffererDepositTxInputs; import io.bitsquare.trade.protocol.trade.offerer.tasks.ProcessPayoutTxPublishedMessage; import io.bitsquare.trade.protocol.trade.offerer.tasks.ProcessRequestOffererPublishDepositTxMessage; import io.bitsquare.trade.protocol.trade.offerer.tasks.ProcessRequestTakeOfferMessage; @@ -110,7 +110,7 @@ public class BuyerAsOffererProtocol { ); taskRunner.addTasks( ProcessTakeOfferFeePayedMessage.class, - PrepareDepositTx.class, + GetOffererDepositTxInputs.class, RequestDepositPayment.class ); taskRunner.run(); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/messages/RequestDepositPaymentMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/messages/RequestDepositPaymentMessage.java index b3dac96891..f78a9760a3 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/messages/RequestDepositPaymentMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/messages/RequestDepositPaymentMessage.java @@ -20,29 +20,34 @@ package io.bitsquare.trade.protocol.trade.offerer.messages; import io.bitsquare.bank.BankAccount; import io.bitsquare.trade.protocol.trade.TradeMessage; -import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutput; import java.io.Serializable; +import java.util.List; + public class RequestDepositPaymentMessage implements Serializable, TradeMessage { private static final long serialVersionUID = -3988720410493712913L; private final String tradeId; + private final List offererConnectedOutputsForAllInputs; + private final List offererOutputs; + private final byte[] offererPubKey; private final BankAccount bankAccount; private final String accountID; - private final byte[] offererPubKey; - private final Transaction preparedDepositTx; - private final long offererTxOutIndex; - public RequestDepositPaymentMessage(String tradeId, BankAccount bankAccount, String accountID, - byte[] offererPubKey, Transaction preparedDepositTx, - long offererTxOutIndex) { + public RequestDepositPaymentMessage(String tradeId, + List offererConnectedOutputsForAllInputs, + List offererOutputs, + byte[] offererPubKey, + BankAccount bankAccount, + String accountID) { this.tradeId = tradeId; + this.offererConnectedOutputsForAllInputs = offererConnectedOutputsForAllInputs; + this.offererOutputs = offererOutputs; + this.offererPubKey = offererPubKey; this.bankAccount = bankAccount; this.accountID = accountID; - this.offererPubKey = offererPubKey; - this.preparedDepositTx = preparedDepositTx; - this.offererTxOutIndex = offererTxOutIndex; } @Override @@ -50,6 +55,18 @@ public class RequestDepositPaymentMessage implements Serializable, TradeMessage return tradeId; } + public List getOffererConnectedOutputsForAllInputs() { + return offererConnectedOutputsForAllInputs; + } + + public List getOffererOutputs() { + return offererOutputs; + } + + public byte[] getOffererPubKey() { + return offererPubKey; + } + public BankAccount getBankAccount() { return bankAccount; } @@ -57,16 +74,4 @@ public class RequestDepositPaymentMessage implements Serializable, TradeMessage public String getAccountId() { return accountID; } - - public byte[] getOffererPubKey() { - return offererPubKey; - } - - public Transaction getPreparedDepositTx() { - return preparedDepositTx; - } - - public long getOffererTxOutIndex() { - return offererTxOutIndex; - } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/PrepareDepositTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/GetOffererDepositTxInputs.java similarity index 56% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/PrepareDepositTx.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/GetOffererDepositTxInputs.java index 24a0a3131e..9a6f0fdcc4 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/PrepareDepositTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/GetOffererDepositTxInputs.java @@ -17,45 +17,37 @@ package io.bitsquare.trade.protocol.trade.offerer.tasks; +import io.bitsquare.btc.AddressEntry; import io.bitsquare.btc.FeePolicy; +import io.bitsquare.btc.WalletService; import io.bitsquare.trade.protocol.trade.offerer.BuyerAsOffererModel; import io.bitsquare.util.taskrunner.Task; import io.bitsquare.util.taskrunner.TaskRunner; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PrepareDepositTx extends Task { - private static final Logger log = LoggerFactory.getLogger(PrepareDepositTx.class); +public class GetOffererDepositTxInputs extends Task { + private static final Logger log = LoggerFactory.getLogger(GetOffererDepositTxInputs.class); - public PrepareDepositTx(TaskRunner taskHandler, BuyerAsOffererModel model) { + public GetOffererDepositTxInputs(TaskRunner taskHandler, BuyerAsOffererModel model) { super(taskHandler, model); } @Override protected void doRun() { try { - byte[] offererPubKey = model.getWalletService().getAddressInfo(model.getTrade().getId()).getPubKey(); Coin offererInputAmount = model.getTrade().getSecurityDeposit().add(FeePolicy.TX_FEE); - Transaction transaction = model.getWalletService().offererPreparesDepositTx( - offererInputAmount, - model.getTrade().getId(), - offererPubKey, - model.getTakerPubKey(), - model.getArbitratorPubKey()); - - long offererTxOutIndex = transaction.getInput(0).getOutpoint().getIndex(); - - model.setOffererPubKey(offererPubKey); - model.setPreparedDepositTx(transaction); - model.setOffererTxOutIndex(offererTxOutIndex); + AddressEntry addressInfo = model.getWalletService().getAddressInfo(model.getTrade().getId()); + WalletService.TransactionDataResult result = model.getWalletService().offererCreatesDepositTxInputs(offererInputAmount, addressInfo); + + model.setOffererConnectedOutputsForAllInputs(result.getConnectedOutputsForAllInputs()); + model.setOffererOutputs(result.getOutputs()); complete(); - } catch (InsufficientMoneyException e) { + } catch (Throwable e) { failed(e); } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java index ce827d467f..5764007564 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestOffererPublishDepositTxMessage.java @@ -25,7 +25,7 @@ import io.bitsquare.util.taskrunner.TaskRunner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.*; import static io.bitsquare.util.Validator.*; public class ProcessRequestOffererPublishDepositTxMessage extends Task { @@ -39,17 +39,16 @@ public class ProcessRequestOffererPublishDepositTxMessage extends Task 0); + model.setTakerOutputs(checkNotNull(message.getTakerOutputs())); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestTakeOfferMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestTakeOfferMessage.java index c036319354..dbe5a98b1a 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestTakeOfferMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessRequestTakeOfferMessage.java @@ -36,9 +36,6 @@ public class ProcessRequestTakeOfferMessage extends Task { @Override protected void doRun() { try { - log.debug("######### " + model.getOffer().getId()); - log.debug("######### " + model.getTradeMessage().getTradeId()); - checkTradeId(model.getOffer().getId(), model.getTradeMessage()); complete(); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessTakeOfferFeePayedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessTakeOfferFeePayedMessage.java index 023aa62d97..142decd672 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessTakeOfferFeePayedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/ProcessTakeOfferFeePayedMessage.java @@ -40,7 +40,6 @@ public class ProcessTakeOfferFeePayedMessage extends Task { protected void doRun() { try { checkTradeId(model.getTrade().getId(), model.getTradeMessage()); - Trade trade = model.getTrade(); TakeOfferFeePayedMessage takeOfferFeePayedMessage = (TakeOfferFeePayedMessage) model.getTradeMessage(); trade.setTakeOfferFeeTxID(nonEmptyStringOf(takeOfferFeePayedMessage.getTakeOfferFeeTxId())); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RequestDepositPayment.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RequestDepositPayment.java index 5fed79e497..36e8665bda 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RequestDepositPayment.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RequestDepositPayment.java @@ -35,13 +35,14 @@ public class RequestDepositPayment extends Task { @Override protected void doRun() { + byte[] offererPubKey = model.getWalletService().getAddressInfo(model.getTrade().getId()).getPubKey(); RequestDepositPaymentMessage tradeMessage = new RequestDepositPaymentMessage( model.getTrade().getId(), + model.getOffererConnectedOutputsForAllInputs(), + model.getOffererOutputs(), + offererPubKey, model.getBankAccount(), - model.getAccountId(), - model.getOffererPubKey(), - model.getPreparedDepositTx(), - model.getOffererTxOutIndex()); + model.getAccountId()); model.getTradeMessageService().sendMessage(model.getTaker(), tradeMessage, new SendMessageListener() { @Override diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RespondToTakeOfferRequest.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RespondToTakeOfferRequest.java index 6fa6566660..4d43648fdf 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RespondToTakeOfferRequest.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/RespondToTakeOfferRequest.java @@ -39,7 +39,7 @@ public class RespondToTakeOfferRequest extends Task { @Override protected void doRun() { offerIsAvailable = model.getOpenOffer().getState() == OpenOffer.State.OPEN; - + if (offerIsAvailable) { Trade trade = new Trade(model.getOpenOffer().getOffer()); model.setTrade(trade); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendBankTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendBankTransferStartedMessage.java index cc823859b4..c05cea2f28 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendBankTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendBankTransferStartedMessage.java @@ -37,7 +37,7 @@ public class SendBankTransferStartedMessage extends Task { protected void doRun() { BankTransferStartedMessage tradeMessage = new BankTransferStartedMessage( model.getTrade().getId(), - model.getDepositTx(), + model.getPublishedDepositTx(), model.getOffererSignature().encodeToDER(), model.getOffererPaybackAmount(), model.getTakerPaybackAmount(), diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendDepositTxIdToTaker.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendDepositTxIdToTaker.java index 11cbdea0cc..84a965a3ec 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendDepositTxIdToTaker.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SendDepositTxIdToTaker.java @@ -35,8 +35,7 @@ public class SendDepositTxIdToTaker extends Task { @Override protected void doRun() { - DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(model.getTrade().getId(), - model.getTrade().getDepositTx()); + DepositTxPublishedMessage tradeMessage = new DepositTxPublishedMessage(model.getTrade().getId(), model.getTrade().getDepositTx()); model.getTradeMessageService().sendMessage(model.getTaker(), tradeMessage, new SendMessageListener() { @Override diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignAndPublishDepositTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignAndPublishDepositTx.java index 8d4ff72df2..4a5b68601c 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignAndPublishDepositTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignAndPublishDepositTx.java @@ -42,17 +42,18 @@ public class SignAndPublishDepositTx extends Task { protected void doRun() { try { model.getWalletService().offererSignAndPublishTx( - model.getPreparedDepositTx(), - model.getTakersSignedDepositTx(), - model.getTakersFromTx(), - model.getTxScriptSig(), - model.getOffererTxOutIndex(), - model.getTakerTxOutIndex(), + model.getTakerDepositTx(), + model.getTakerConnectedOutputsForAllInputs(), + model.getOffererConnectedOutputsForAllInputs(), + model.getOffererPubKey(), + model.getTakerPubKey(), + model.getArbitratorPubKey(), new FutureCallback() { @Override public void onSuccess(Transaction transaction) { log.trace("offererSignAndPublishTx succeeded " + transaction); + model.setPublishedDepositTx(transaction); model.getTrade().setDepositTx(transaction); model.getTrade().setState(Trade.State.DEPOSIT_PUBLISHED); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignPayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignPayoutTx.java index c190079d4f..feff1837f6 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignPayoutTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/offerer/tasks/SignPayoutTx.java @@ -53,7 +53,7 @@ public class SignPayoutTx extends Task { model.getTakerPayoutAddress(), model.getTrade().getId()); - model.setDepositTx(result.getValue()); + model.setOffererPayoutTx(result.getValue()); model.setOffererSignature(result.getKey()); model.setOffererPaybackAmount(offererPaybackAmount); model.setTakerPaybackAmount(takerPaybackAmount); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerModel.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerModel.java index a760c585dc..172e632655 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerModel.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerModel.java @@ -29,6 +29,9 @@ import io.bitsquare.user.User; import org.bitcoinj.core.Coin; import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutput; + +import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,6 +41,7 @@ public class SellerAsTakerModel extends OfferSharedModel { // provided private final Trade trade; + // written/read by task private Peer offerer; @@ -52,6 +56,12 @@ public class SellerAsTakerModel extends OfferSharedModel { private ECKey.ECDSASignature offererSignature; private Coin offererPaybackAmount; private String offererPayoutAddress; + private List offererConnectedOutputsForAllInputs; + private List offererOutputs; + private List takerConnectedOutputsForAllInputs; + private List takerOutputs; + private Transaction takerDepositTx; + private Transaction publishedDepositTx; public SellerAsTakerModel(Trade trade, TradeMessageService tradeMessageService, @@ -68,9 +78,29 @@ public class SellerAsTakerModel extends OfferSharedModel { this.trade = trade; takerPubKey = walletService.getAddressInfo(trade.getId()).getPubKey(); + } // getter/setter + public void setOffererPubKey(byte[] offererPubKey) { + this.offererPubKey = offererPubKey; + } + + public List getOffererConnectedOutputsForAllInputs() { + return offererConnectedOutputsForAllInputs; + } + + public void setOffererConnectedOutputsForAllInputs(List offererConnectedOutputsForAllInputs) { + this.offererConnectedOutputsForAllInputs = offererConnectedOutputsForAllInputs; + } + + public List getOffererOutputs() { + return offererOutputs; + } + + public void setOffererOutputs(List offererOutputs) { + this.offererOutputs = offererOutputs; + } public Trade getTrade() { return trade; } @@ -103,9 +133,6 @@ public class SellerAsTakerModel extends OfferSharedModel { return offererPubKey; } - public void setOffererPubKeyAsHex(byte[] offererPubKey) { - this.offererPubKey = offererPubKey; - } public Transaction getPreparedDepositTx() { return preparedDepositTx; @@ -172,4 +199,35 @@ public class SellerAsTakerModel extends OfferSharedModel { } + public void setTakerConnectedOutputsForAllInputs(List takerConnectedOutputsForAllInputs) { + this.takerConnectedOutputsForAllInputs = takerConnectedOutputsForAllInputs; + } + + public List getTakerConnectedOutputsForAllInputs() { + return takerConnectedOutputsForAllInputs; + } + + public void setTakerOutputs(List takerOutputs) { + this.takerOutputs = takerOutputs; + } + + public List getTakerOutputs() { + return takerOutputs; + } + + public void setTakerDepositTx(Transaction takerDepositTx) { + this.takerDepositTx = takerDepositTx; + } + + public Transaction getTakerDepositTx() { + return takerDepositTx; + } + + public void setPublishedDepositTx(Transaction publishedDepositTx) { + this.publishedDepositTx = publishedDepositTx; + } + + public Transaction getPublishedDepositTx() { + return publishedDepositTx; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java index 184a397195..dbf2f6035f 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/SellerAsTakerProtocol.java @@ -28,7 +28,7 @@ import io.bitsquare.trade.protocol.trade.offerer.messages.RequestDepositPaymentM import io.bitsquare.trade.protocol.trade.offerer.messages.RespondToTakeOfferRequestMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.CreateAndSignContract; import io.bitsquare.trade.protocol.trade.taker.tasks.GetPeerAddress; -import io.bitsquare.trade.protocol.trade.taker.tasks.PayDeposit; +import io.bitsquare.trade.protocol.trade.taker.tasks.TakerCreatesAndSignsDepositTx; import io.bitsquare.trade.protocol.trade.taker.tasks.PayTakeOfferFee; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessBankTransferStartedMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessDepositTxPublishedMessage; @@ -36,7 +36,7 @@ import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessRequestDepositPaymen import io.bitsquare.trade.protocol.trade.taker.tasks.ProcessRespondToTakeOfferRequestMessage; import io.bitsquare.trade.protocol.trade.taker.tasks.RequestTakeOffer; import io.bitsquare.trade.protocol.trade.taker.tasks.SendPayoutTxToOfferer; -import io.bitsquare.trade.protocol.trade.taker.tasks.SendSignedTakerDepositTxAsHex; +import io.bitsquare.trade.protocol.trade.taker.tasks.SendSignedTakerDepositTx; 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; @@ -129,8 +129,8 @@ public class SellerAsTakerProtocol { ProcessRequestDepositPaymentMessage.class, VerifyOffererAccount.class, CreateAndSignContract.class, - PayDeposit.class, - SendSignedTakerDepositTxAsHex.class + TakerCreatesAndSignsDepositTx.class, + SendSignedTakerDepositTx.class ); taskRunner.run(); } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/messages/RequestOffererPublishDepositTxMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/messages/RequestOffererPublishDepositTxMessage.java index 337812c328..49487293a3 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/messages/RequestOffererPublishDepositTxMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/messages/RequestOffererPublishDepositTxMessage.java @@ -21,53 +21,44 @@ import io.bitsquare.bank.BankAccount; import io.bitsquare.trade.protocol.trade.TradeMessage; import org.bitcoinj.core.Transaction; +import org.bitcoinj.core.TransactionOutput; import java.io.Serializable; import java.security.PublicKey; +import java.util.List; + public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage { private static final long serialVersionUID = 2179683654379803071L; private final String tradeId; private final BankAccount bankAccount; private final String accountID; private final PublicKey takerMessagePublicKey; - private final Transaction takersSignedDepositTx; - private final byte[] txScriptSig; - private final Transaction takersFromTx; private final String contractAsJson; private final String takerContractSignature; - private final String takerPayoutAddress; - private final long takerTxOutIndex; - - - private final long offererTxOutIndex; + private Transaction takersDepositTx; + private List takersConnectedOutputsForAllInputs; + private List takerOutputs; public RequestOffererPublishDepositTxMessage(String tradeId, BankAccount bankAccount, String accountID, PublicKey takerMessagePublicKey, - Transaction takersSignedDepositTx, - byte[] txScriptSig, - Transaction takersFromTx, String contractAsJson, String takerContractSignature, - String takerPayoutAddress, - long takerTxOutIndex, - long offererTxOutIndex) { - + Transaction takersDepositTx, + List takersConnectedOutputsForAllInputs, + List takerOutputs) { this.tradeId = tradeId; this.bankAccount = bankAccount; this.accountID = accountID; this.takerMessagePublicKey = takerMessagePublicKey; - this.takersSignedDepositTx = takersSignedDepositTx; - this.txScriptSig = txScriptSig; - this.takersFromTx = takersFromTx; this.contractAsJson = contractAsJson; this.takerContractSignature = takerContractSignature; - this.takerPayoutAddress = takerPayoutAddress; - this.takerTxOutIndex = takerTxOutIndex; - this.offererTxOutIndex = offererTxOutIndex; + this.takersDepositTx = takersDepositTx; + this.takersConnectedOutputsForAllInputs = takersConnectedOutputsForAllInputs; + this.takerOutputs = takerOutputs; } @@ -76,10 +67,6 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad return tradeId; } - public long getOffererTxOutIndex() { - return offererTxOutIndex; - } - public BankAccount getTakerBankAccount() { return bankAccount; } @@ -92,18 +79,6 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad return takerMessagePublicKey; } - public Transaction getTakersSignedDepositTx() { - return takersSignedDepositTx; - } - - public byte[] getTxScriptSig() { - return txScriptSig; - } - - public Transaction getTakersFromTx() { - return takersFromTx; - } - public String getTakerContractAsJson() { return contractAsJson; } @@ -112,12 +87,27 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad return takerContractSignature; } - public String getTakerPayoutAddress() { - return takerPayoutAddress; + public List getTakerOutputs() { + return takerOutputs; } - public long getTakerTxOutIndex() { - return takerTxOutIndex; + public BankAccount getBankAccount() { + return bankAccount; } + public String getAccountID() { + return accountID; + } + + public String getContractAsJson() { + return contractAsJson; + } + + public Transaction getTakersDepositTx() { + return takersDepositTx; + } + + public List getTakersConnectedOutputsForAllInputs() { + return takersConnectedOutputsForAllInputs; + } } diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java index 1745f55c14..c8c11934f3 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessBankTransferStartedMessage.java @@ -43,13 +43,12 @@ public class ProcessBankTransferStartedMessage extends Task checkTradeId(model.getTrade().getId(), model.getTradeMessage()); BankTransferStartedMessage message = (BankTransferStartedMessage) model.getTradeMessage(); - model.setDepositTx(checkNotNull(message.getDepositTx())); + //model.setDepositTx(checkNotNull(message.getDepositTx())); model.setOffererSignature(checkNotNull(ECKey.ECDSASignature.decodeFromDER(message.getOffererSignature()))); model.setOffererPaybackAmount(positiveCoinOf(nonZeroCoinOf(message.getOffererPaybackAmount()))); model.setTakerPaybackAmount(positiveCoinOf(nonZeroCoinOf(message.getTakerPaybackAmount()))); model.setOffererPayoutAddress(nonEmptyStringOf(message.getOffererPayoutAddress())); - // TODO listener.onBankTransferInited(message.getTrade().getId()); complete(); } catch (Throwable t) { failed(t); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessDepositTxPublishedMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessDepositTxPublishedMessage.java index d180436d8a..b30438b3a5 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessDepositTxPublishedMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessDepositTxPublishedMessage.java @@ -41,7 +41,7 @@ public class ProcessDepositTxPublishedMessage extends Task { checkTradeId(model.getTrade().getId(), model.getTradeMessage()); DepositTxPublishedMessage message = (DepositTxPublishedMessage) model.getTradeMessage(); - model.setDepositTx(checkNotNull(message.getDepositTx())); + model.setPublishedDepositTx(checkNotNull(message.getDepositTx())); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessRequestDepositPaymentMessage.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessRequestDepositPaymentMessage.java index c9a32a57b6..780d4a1488 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessRequestDepositPaymentMessage.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/ProcessRequestDepositPaymentMessage.java @@ -25,7 +25,7 @@ import io.bitsquare.util.taskrunner.TaskRunner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.*; import static io.bitsquare.util.Validator.*; public class ProcessRequestDepositPaymentMessage extends Task { @@ -40,11 +40,13 @@ public class ProcessRequestDepositPaymentMessage extends Task 0); + model.setOffererOutputs(checkNotNull(message.getOffererOutputs())); + model.setOffererPubKey(checkNotNull(message.getOffererPubKey())); model.setTakerBankAccount(checkNotNull(message.getBankAccount())); - model.setOffererPubKeyAsHex(checkNotNull(message.getOffererPubKey())); - model.setPreparedDepositTx(checkNotNull(message.getPreparedDepositTx())); - model.setOffererTxOutIndex(nonNegativeLongOf(message.getOffererTxOutIndex())); + model.setTakerAccountId(nonEmptyStringOf(message.getAccountId())); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/RequestTakeOffer.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/RequestTakeOffer.java index 1d681e04d2..7dba7adeef 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/RequestTakeOffer.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/RequestTakeOffer.java @@ -36,7 +36,6 @@ public class RequestTakeOffer extends Task { @Override protected void doRun() { - log.debug("######### " + model.getTrade().getId()); model.getTradeMessageService().sendMessage(model.getOfferer(), new RequestTakeOfferMessage(model.getTrade().getId()), new SendMessageListener() { @Override diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTxAsHex.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java similarity index 72% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTxAsHex.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java index bc28849462..2ab1be93fa 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTxAsHex.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SendSignedTakerDepositTx.java @@ -23,36 +23,29 @@ import io.bitsquare.trade.protocol.trade.taker.messages.RequestOffererPublishDep import io.bitsquare.util.taskrunner.Task; import io.bitsquare.util.taskrunner.TaskRunner; -import org.bitcoinj.core.Transaction; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class SendSignedTakerDepositTxAsHex extends Task { - private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTxAsHex.class); +public class SendSignedTakerDepositTx extends Task { + private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTx.class); - public SendSignedTakerDepositTxAsHex(TaskRunner taskHandler, SellerAsTakerModel model) { + public SendSignedTakerDepositTx(TaskRunner taskHandler, SellerAsTakerModel model) { super(taskHandler, model); } @Override protected void doRun() { - Transaction takersSignedDepositTx = model.getSignedTakerDepositTx(); - long takerTxOutIndex = model.getSignedTakerDepositTx().getInput(1).getOutpoint().getIndex(); - RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage( model.getTrade().getId(), model.getBankAccount(), model.getAccountId(), model.getMessagePublicKey(), - takersSignedDepositTx, - takersSignedDepositTx.getInput(1).getScriptBytes(), - takersSignedDepositTx.getInput(1).getConnectedOutput().getParentTransaction(), model.getTrade().getContractAsJson(), model.getTrade().getTakerContractSignature(), - model.getWalletService().getAddressInfo(model.getTrade().getId()).getAddressString(), - takerTxOutIndex, - model.getOffererTxOutIndex()); + model.getTakerDepositTx(), + model.getTakerConnectedOutputsForAllInputs(), + model.getTakerOutputs() + ); model.getTradeMessageService().sendMessage(model.getOfferer(), tradeMessage, new SendMessageListener() { @Override diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SignAndPublishPayoutTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SignAndPublishPayoutTx.java index 1c98f704b9..706a0a8240 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SignAndPublishPayoutTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/SignAndPublishPayoutTx.java @@ -43,7 +43,7 @@ public class SignAndPublishPayoutTx extends Task { @Override protected void doRun() { try { - model.getWalletService().takerSignsAndSendsTx(model.getDepositTx(), + model.getWalletService().takerSignsAndSendsTx(model.getPublishedDepositTx(), model.getOffererSignature(), model.getOffererPaybackAmount(), model.getTakerPaybackAmount(), diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCommitDepositTx.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCommitDepositTx.java index d5a22253c0..9ce3f27a77 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCommitDepositTx.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCommitDepositTx.java @@ -37,7 +37,7 @@ public class TakerCommitDepositTx extends Task { @Override protected void doRun() { try { - Transaction transaction = model.getWalletService().takerCommitDepositTx(model.getDepositTx()); + Transaction transaction = model.getWalletService().takerCommitDepositTx(model.getPublishedDepositTx()); model.getTrade().setDepositTx(transaction); model.getTrade().setState(Trade.State.DEPOSIT_PUBLISHED); diff --git a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/PayDeposit.java b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCreatesAndSignsDepositTx.java similarity index 60% rename from core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/PayDeposit.java rename to core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCreatesAndSignsDepositTx.java index 0e4b83a2ee..72f7d6214e 100644 --- a/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/PayDeposit.java +++ b/core/src/main/java/io/bitsquare/trade/protocol/trade/taker/tasks/TakerCreatesAndSignsDepositTx.java @@ -18,42 +18,46 @@ package io.bitsquare.trade.protocol.trade.taker.tasks; import io.bitsquare.btc.FeePolicy; +import io.bitsquare.btc.WalletService; import io.bitsquare.trade.protocol.trade.taker.SellerAsTakerModel; import io.bitsquare.util.taskrunner.Task; import io.bitsquare.util.taskrunner.TaskRunner; import org.bitcoinj.core.Coin; -import org.bitcoinj.core.InsufficientMoneyException; -import org.bitcoinj.core.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class PayDeposit extends Task { - private static final Logger log = LoggerFactory.getLogger(PayDeposit.class); +public class TakerCreatesAndSignsDepositTx extends Task { + private static final Logger log = LoggerFactory.getLogger(TakerCreatesAndSignsDepositTx.class); - public PayDeposit(TaskRunner taskHandler, SellerAsTakerModel model) { + public TakerCreatesAndSignsDepositTx(TaskRunner taskHandler, SellerAsTakerModel model) { super(taskHandler, model); } @Override protected void doRun() { try { - Coin amountToPay = model.getTrade().getTradeAmount().add(model.getTrade().getSecurityDeposit()); - Coin msOutputAmount = amountToPay.add(model.getTrade().getSecurityDeposit()).add(FeePolicy.TX_FEE); - Transaction signedTakerDepositTx = model.getWalletService().takerAddPaymentAndSignTx( - amountToPay, + Coin inputAmount = model.getTrade().getTradeAmount().add(model.getTrade().getSecurityDeposit()); + Coin msOutputAmount = inputAmount.add(model.getTrade().getSecurityDeposit()).add(FeePolicy.TX_FEE); + + WalletService.TransactionDataResult result = model.getWalletService().takerCreatesAndSignsDepositTx( + inputAmount, msOutputAmount, - model.getPreparedDepositTx(), - model.getTrade().getId(), + model.getOffererConnectedOutputsForAllInputs(), + model.getOffererOutputs(), + model.getAddressInfo(), model.getOffererPubKey(), model.getTakerPubKey(), model.getArbitratorPubKey()); - model.setSignedTakerDepositTx(signedTakerDepositTx); + + model.setTakerConnectedOutputsForAllInputs(result.getConnectedOutputsForAllInputs()); + model.setTakerOutputs(result.getOutputs()); + model.setTakerDepositTx(result.getDepositTx()); complete(); - } catch (InsufficientMoneyException e) { + } catch (Exception e) { failed(e); } } diff --git a/core/src/main/java/io/bitsquare/trade/tomp2p/TomP2PTradeMessageService.java b/core/src/main/java/io/bitsquare/trade/tomp2p/TomP2PTradeMessageService.java index 57f78c59c8..7b9d96c44c 100644 --- a/core/src/main/java/io/bitsquare/trade/tomp2p/TomP2PTradeMessageService.java +++ b/core/src/main/java/io/bitsquare/trade/tomp2p/TomP2PTradeMessageService.java @@ -51,7 +51,6 @@ import org.slf4j.LoggerFactory; * The TomP2P library codebase shall not be used outside that service. * That way we limit the dependency of the TomP2P library only to that class (and it's sub components). *

- * TODO: improve callbacks that executor.execute is not necessary. We call usually that methods form teh UI thread. */ public class TomP2PTradeMessageService implements TradeMessageService { private static final Logger log = LoggerFactory.getLogger(TomP2PTradeMessageService.class); diff --git a/core/src/main/java/io/bitsquare/util/taskrunner/Task.java b/core/src/main/java/io/bitsquare/util/taskrunner/Task.java index 214b97b131..cac5fdc713 100644 --- a/core/src/main/java/io/bitsquare/util/taskrunner/Task.java +++ b/core/src/main/java/io/bitsquare/util/taskrunner/Task.java @@ -17,6 +17,10 @@ package io.bitsquare.util.taskrunner; +import io.bitsquare.btc.exceptions.SigningException; +import io.bitsquare.btc.exceptions.TransactionVerificationException; +import io.bitsquare.btc.exceptions.WalletException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +49,7 @@ public abstract class Task { } } - abstract protected void doRun(); + abstract protected void doRun() throws WalletException, TransactionVerificationException, SigningException; abstract protected void updateStateOnFault(); diff --git a/core/src/main/resources/logback.xml b/core/src/main/resources/logback.xml index 2620228851..3aa274749b 100644 --- a/core/src/main/resources/logback.xml +++ b/core/src/main/resources/logback.xml @@ -38,16 +38,17 @@ + + - - +