mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Refactor trade transactions
This commit is contained in:
parent
2377db62fc
commit
6e3f83b8e0
@ -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<TransactionOutput> connectedOutputsForAllInputs = new ArrayList<>();
|
||||
for (TransactionInput input : dummyTX.getInputs()) {
|
||||
connectedOutputsForAllInputs.add(input.getConnectedOutput());
|
||||
}
|
||||
|
||||
// Only save offerer outputs, the MS output is ignored
|
||||
List<TransactionOutput> 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<TransactionOutput> offererConnectedOutputsForAllInputs,
|
||||
List<TransactionOutput> 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<TransactionInput> takerInputs = new ArrayList<>(depositTx.getInputs());
|
||||
List<TransactionOutput> 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<TransactionOutput> 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<Transaction> 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<TransactionOutput> takersConnectedOutputsForAllInputs,
|
||||
List<TransactionOutput> offererConnectedOutputsForAllInputs,
|
||||
byte[] offererPubKey,
|
||||
byte[] takerPubKey,
|
||||
byte[] arbitratorPubKey,
|
||||
FutureCallback<Transaction> 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<TransactionInput> 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<TransactionInput> 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<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
|
||||
ListenableFuture<Transaction> 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<TransactionOutput> connectedOutputsForAllInputs;
|
||||
private final List<TransactionOutput> outputs;
|
||||
private Transaction depositTx;
|
||||
|
||||
public TransactionDataResult(List<TransactionOutput> connectedOutputsForAllInputs, List<TransactionOutput> outputs) {
|
||||
this.connectedOutputsForAllInputs = connectedOutputsForAllInputs;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
public TransactionDataResult(Transaction depositTx, List<TransactionOutput> connectedOutputsForAllInputs, List<TransactionOutput> outputs) {
|
||||
this.depositTx = depositTx;
|
||||
this.connectedOutputsForAllInputs = connectedOutputsForAllInputs;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getOutputs() {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getConnectedOutputsForAllInputs() {
|
||||
return connectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public Transaction getDepositTx() {
|
||||
return depositTx;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ObservableDownloadListener extends DownloadListener {
|
||||
|
||||
private final Subject<Double, Double> subject = BehaviorSubject.create(0d);
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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<TransactionOutput> offererConnectedOutputsForAllInputs;
|
||||
private List<TransactionOutput> offererOutputs;
|
||||
private Transaction takerDepositTx;
|
||||
private List<TransactionOutput> takerConnectedOutputsForAllInputs;
|
||||
private List<TransactionOutput> 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<TransactionOutput> getOffererConnectedOutputsForAllInputs() {
|
||||
return offererConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public void setOffererConnectedOutputsForAllInputs(List<TransactionOutput> offererConnectedOutputsForAllInputs) {
|
||||
this.offererConnectedOutputsForAllInputs = offererConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getOffererOutputs() {
|
||||
return offererOutputs;
|
||||
}
|
||||
|
||||
public void setOffererOutputs(List<TransactionOutput> offererOutputs) {
|
||||
this.offererOutputs = offererOutputs;
|
||||
}
|
||||
|
||||
public void setTakerDepositTx(Transaction takerDepositTx) {
|
||||
this.takerDepositTx = takerDepositTx;
|
||||
}
|
||||
|
||||
public Transaction getTakerDepositTx() {
|
||||
return takerDepositTx;
|
||||
}
|
||||
|
||||
public void setTakerConnectedOutputsForAllInputs(List<TransactionOutput> takerConnectedOutputsForAllInputs) {
|
||||
this.takerConnectedOutputsForAllInputs = takerConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getTakerConnectedOutputsForAllInputs() {
|
||||
return takerConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public void setTakerOutputs(List<TransactionOutput> takerOutputs) {
|
||||
this.takerOutputs = takerOutputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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<TransactionOutput> offererConnectedOutputsForAllInputs;
|
||||
private final List<TransactionOutput> 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<TransactionOutput> offererConnectedOutputsForAllInputs,
|
||||
List<TransactionOutput> 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<TransactionOutput> getOffererConnectedOutputsForAllInputs() {
|
||||
return offererConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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<BuyerAsOffererModel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(PrepareDepositTx.class);
|
||||
public class GetOffererDepositTxInputs extends Task<BuyerAsOffererModel> {
|
||||
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);
|
||||
}
|
||||
}
|
@ -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<BuyerAsOffererModel> {
|
||||
@ -39,17 +39,16 @@ public class ProcessRequestOffererPublishDepositTxMessage extends Task<BuyerAsOf
|
||||
protected void doRun() {
|
||||
try {
|
||||
checkTradeId(model.getTrade().getId(), model.getTradeMessage());
|
||||
|
||||
RequestOffererPublishDepositTxMessage message = (RequestOffererPublishDepositTxMessage) model.getTradeMessage();
|
||||
model.setTakerPayoutAddress(nonEmptyStringOf(message.getTakerPayoutAddress()));
|
||||
model.setTakerAccountId(nonEmptyStringOf(message.getTakerAccountId()));
|
||||
|
||||
model.setTakerBankAccount(checkNotNull(message.getTakerBankAccount()));
|
||||
model.setTakerAccountId(nonEmptyStringOf(message.getTakerAccountId()));
|
||||
model.setTakerMessagePublicKey(checkNotNull(message.getTakerMessagePublicKey()));
|
||||
model.setTakerContractAsJson(nonEmptyStringOf(message.getTakerContractAsJson()));
|
||||
model.setTakersSignedDepositTx(checkNotNull(message.getTakersSignedDepositTx()));
|
||||
model.setTakersFromTx(checkNotNull(message.getTakersFromTx()));
|
||||
model.setTxScriptSig(checkNotNull(message.getTxScriptSig()));
|
||||
model.setTakerTxOutIndex(nonNegativeLongOf(message.getTakerTxOutIndex()));
|
||||
model.setTakerDepositTx(checkNotNull(message.getTakersDepositTx()));
|
||||
model.setTakerConnectedOutputsForAllInputs(checkNotNull(message.getTakersConnectedOutputsForAllInputs()));
|
||||
checkArgument(message.getTakersConnectedOutputsForAllInputs().size() > 0);
|
||||
model.setTakerOutputs(checkNotNull(message.getTakerOutputs()));
|
||||
|
||||
complete();
|
||||
} catch (Throwable t) {
|
||||
|
@ -36,9 +36,6 @@ public class ProcessRequestTakeOfferMessage extends Task<BuyerAsOffererModel> {
|
||||
@Override
|
||||
protected void doRun() {
|
||||
try {
|
||||
log.debug("######### " + model.getOffer().getId());
|
||||
log.debug("######### " + model.getTradeMessage().getTradeId());
|
||||
|
||||
checkTradeId(model.getOffer().getId(), model.getTradeMessage());
|
||||
|
||||
complete();
|
||||
|
@ -40,7 +40,6 @@ public class ProcessTakeOfferFeePayedMessage extends Task<BuyerAsOffererModel> {
|
||||
protected void doRun() {
|
||||
try {
|
||||
checkTradeId(model.getTrade().getId(), model.getTradeMessage());
|
||||
|
||||
Trade trade = model.getTrade();
|
||||
TakeOfferFeePayedMessage takeOfferFeePayedMessage = (TakeOfferFeePayedMessage) model.getTradeMessage();
|
||||
trade.setTakeOfferFeeTxID(nonEmptyStringOf(takeOfferFeePayedMessage.getTakeOfferFeeTxId()));
|
||||
|
@ -35,13 +35,14 @@ public class RequestDepositPayment extends Task<BuyerAsOffererModel> {
|
||||
|
||||
@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
|
||||
|
@ -39,7 +39,7 @@ public class RespondToTakeOfferRequest extends Task<BuyerAsOffererModel> {
|
||||
@Override
|
||||
protected void doRun() {
|
||||
offerIsAvailable = model.getOpenOffer().getState() == OpenOffer.State.OPEN;
|
||||
|
||||
|
||||
if (offerIsAvailable) {
|
||||
Trade trade = new Trade(model.getOpenOffer().getOffer());
|
||||
model.setTrade(trade);
|
||||
|
@ -37,7 +37,7 @@ public class SendBankTransferStartedMessage extends Task<BuyerAsOffererModel> {
|
||||
protected void doRun() {
|
||||
BankTransferStartedMessage tradeMessage = new BankTransferStartedMessage(
|
||||
model.getTrade().getId(),
|
||||
model.getDepositTx(),
|
||||
model.getPublishedDepositTx(),
|
||||
model.getOffererSignature().encodeToDER(),
|
||||
model.getOffererPaybackAmount(),
|
||||
model.getTakerPaybackAmount(),
|
||||
|
@ -35,8 +35,7 @@ public class SendDepositTxIdToTaker extends Task<BuyerAsOffererModel> {
|
||||
|
||||
@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
|
||||
|
@ -42,17 +42,18 @@ public class SignAndPublishDepositTx extends Task<BuyerAsOffererModel> {
|
||||
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<Transaction>() {
|
||||
@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);
|
||||
|
||||
|
@ -53,7 +53,7 @@ public class SignPayoutTx extends Task<BuyerAsOffererModel> {
|
||||
model.getTakerPayoutAddress(),
|
||||
model.getTrade().getId());
|
||||
|
||||
model.setDepositTx(result.getValue());
|
||||
model.setOffererPayoutTx(result.getValue());
|
||||
model.setOffererSignature(result.getKey());
|
||||
model.setOffererPaybackAmount(offererPaybackAmount);
|
||||
model.setTakerPaybackAmount(takerPaybackAmount);
|
||||
|
@ -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<TransactionOutput> offererConnectedOutputsForAllInputs;
|
||||
private List<TransactionOutput> offererOutputs;
|
||||
private List<TransactionOutput> takerConnectedOutputsForAllInputs;
|
||||
private List<TransactionOutput> 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<TransactionOutput> getOffererConnectedOutputsForAllInputs() {
|
||||
return offererConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public void setOffererConnectedOutputsForAllInputs(List<TransactionOutput> offererConnectedOutputsForAllInputs) {
|
||||
this.offererConnectedOutputsForAllInputs = offererConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getOffererOutputs() {
|
||||
return offererOutputs;
|
||||
}
|
||||
|
||||
public void setOffererOutputs(List<TransactionOutput> 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<TransactionOutput> takerConnectedOutputsForAllInputs) {
|
||||
this.takerConnectedOutputsForAllInputs = takerConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> getTakerConnectedOutputsForAllInputs() {
|
||||
return takerConnectedOutputsForAllInputs;
|
||||
}
|
||||
|
||||
public void setTakerOutputs(List<TransactionOutput> takerOutputs) {
|
||||
this.takerOutputs = takerOutputs;
|
||||
}
|
||||
|
||||
public List<TransactionOutput> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<TransactionOutput> takersConnectedOutputsForAllInputs;
|
||||
private List<TransactionOutput> 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<TransactionOutput> takersConnectedOutputsForAllInputs,
|
||||
List<TransactionOutput> 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<TransactionOutput> 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<TransactionOutput> getTakersConnectedOutputsForAllInputs() {
|
||||
return takersConnectedOutputsForAllInputs;
|
||||
}
|
||||
}
|
||||
|
@ -43,13 +43,12 @@ public class ProcessBankTransferStartedMessage extends Task<SellerAsTakerModel>
|
||||
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);
|
||||
|
@ -41,7 +41,7 @@ public class ProcessDepositTxPublishedMessage extends Task<SellerAsTakerModel> {
|
||||
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) {
|
||||
|
@ -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<SellerAsTakerModel> {
|
||||
@ -40,11 +40,13 @@ public class ProcessRequestDepositPaymentMessage extends Task<SellerAsTakerModel
|
||||
try {
|
||||
checkTradeId(model.getTrade().getId(), model.getTradeMessage());
|
||||
RequestDepositPaymentMessage message = (RequestDepositPaymentMessage) model.getTradeMessage();
|
||||
model.setTakerAccountId(nonEmptyStringOf(message.getAccountId()));
|
||||
|
||||
model.setOffererConnectedOutputsForAllInputs(checkNotNull(message.getOffererConnectedOutputsForAllInputs()));
|
||||
checkArgument(message.getOffererConnectedOutputsForAllInputs().size() > 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) {
|
||||
|
@ -36,7 +36,6 @@ public class RequestTakeOffer extends Task<SellerAsTakerModel> {
|
||||
|
||||
@Override
|
||||
protected void doRun() {
|
||||
log.debug("######### " + model.getTrade().getId());
|
||||
model.getTradeMessageService().sendMessage(model.getOfferer(), new RequestTakeOfferMessage(model.getTrade().getId()),
|
||||
new SendMessageListener() {
|
||||
@Override
|
||||
|
@ -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<SellerAsTakerModel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(SendSignedTakerDepositTxAsHex.class);
|
||||
public class SendSignedTakerDepositTx extends Task<SellerAsTakerModel> {
|
||||
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
|
@ -43,7 +43,7 @@ public class SignAndPublishPayoutTx extends Task<SellerAsTakerModel> {
|
||||
@Override
|
||||
protected void doRun() {
|
||||
try {
|
||||
model.getWalletService().takerSignsAndSendsTx(model.getDepositTx(),
|
||||
model.getWalletService().takerSignsAndSendsTx(model.getPublishedDepositTx(),
|
||||
model.getOffererSignature(),
|
||||
model.getOffererPaybackAmount(),
|
||||
model.getTakerPaybackAmount(),
|
||||
|
@ -37,7 +37,7 @@ public class TakerCommitDepositTx extends Task<SellerAsTakerModel> {
|
||||
@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);
|
||||
|
||||
|
@ -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<SellerAsTakerModel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(PayDeposit.class);
|
||||
public class TakerCreatesAndSignsDepositTx extends Task<SellerAsTakerModel> {
|
||||
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);
|
||||
}
|
||||
}
|
@ -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).
|
||||
* <p/>
|
||||
* 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);
|
||||
|
@ -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<T extends SharedModel> {
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected void doRun();
|
||||
abstract protected void doRun() throws WalletException, TransactionVerificationException, SigningException;
|
||||
|
||||
abstract protected void updateStateOnFault();
|
||||
|
||||
|
@ -38,16 +38,17 @@
|
||||
<logger name="net.tomp2p" level="ERROR"/>
|
||||
<logger name="com.vinumeris.updatefx" level="OFF"/>
|
||||
<logger name="io.netty" level="OFF"/>
|
||||
<logger name="org.bitcoinj.core.BitcoinSerializer" level="ERROR"/>
|
||||
<logger name="org.bitcoinj.core.Peer" level="ERROR"/>
|
||||
|
||||
|
||||
<!-- <logger name="net.tomp2p.message.Encoder" level="WARN"/>
|
||||
<logger name="net.tomp2p.message.Decoder" level="WARN"/>
|
||||
<logger name="net.tomp2p.message.MessageHeaderCodec" level="WARN"/>
|
||||
|
||||
|
||||
<logger name="io.netty.util" level="WARN"/>
|
||||
<logger name="io.netty.channel" level="WARN"/>
|
||||
<logger name="io.netty.buffer" level="WARN"/>-->
|
||||
<!-- <logger name="net.tomp2p.message.Encoder" level="WARN"/>
|
||||
<logger name="net.tomp2p.message.Decoder" level="WARN"/>
|
||||
<logger name="net.tomp2p.message.MessageHeaderCodec" level="WARN"/>
|
||||
|
||||
|
||||
<logger name="io.netty.util" level="WARN"/>
|
||||
<logger name="io.netty.channel" level="WARN"/>
|
||||
<logger name="io.netty.buffer" level="WARN"/>-->
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user