mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 06:55:08 +01:00
pay out from MS fund
This commit is contained in:
parent
a4efa29bcd
commit
9a29004251
3 changed files with 348 additions and 200 deletions
|
@ -20,9 +20,9 @@ git clone --recursive git://github.com/bitsquare/bitsquare
|
||||||
* Setup with account registration and tx with OP_RETURN + embedded and blinded bank account data
|
* Setup with account registration and tx with OP_RETURN + embedded and blinded bank account data
|
||||||
* Offer fee payment with a OP_RETURN tx and fees to miners
|
* Offer fee payment with a OP_RETURN tx and fees to miners
|
||||||
* Pay in to MS fund
|
* Pay in to MS fund
|
||||||
|
* Payout from MS fund
|
||||||
|
|
||||||
### Next steps:
|
### Next steps:
|
||||||
* Payout from MS fund
|
|
||||||
* Arbitrator integration
|
* Arbitrator integration
|
||||||
* Messaging system
|
* Messaging system
|
||||||
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer)
|
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer)
|
||||||
|
|
5
TODO.txt
5
TODO.txt
|
@ -1,7 +1,6 @@
|
||||||
- pay out from MS in payment process
|
|
||||||
- arbitration integration
|
- arbitration integration
|
||||||
|
- Messaging!
|
||||||
Messaging!
|
- settings
|
||||||
|
|
||||||
low prio:
|
low prio:
|
||||||
- tx confirm. not working correct and reliable
|
- tx confirm. not working correct and reliable
|
||||||
|
|
|
@ -16,6 +16,7 @@ import com.google.inject.Inject;
|
||||||
import io.bitsquare.crypto.CryptoFacade;
|
import io.bitsquare.crypto.CryptoFacade;
|
||||||
import io.bitsquare.gui.util.Popups;
|
import io.bitsquare.gui.util.Popups;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.util.Pair;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -37,9 +38,9 @@ public class WalletFacade implements WalletEventListener
|
||||||
|
|
||||||
// for testing trade process between offerer and taker
|
// for testing trade process between offerer and taker
|
||||||
//public static final String WALLET_PREFIX = "offerer"; // offerer
|
//public static final String WALLET_PREFIX = "offerer"; // offerer
|
||||||
// public static final String WALLET_PREFIX = "taker"; // offerer
|
public static final String WALLET_PREFIX = "taker"; // offerer
|
||||||
|
|
||||||
public static final String WALLET_PREFIX = "bitsquare";
|
//public static final String WALLET_PREFIX = "bitsquare";
|
||||||
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
|
private static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
|
||||||
|
@ -112,200 +113,8 @@ public class WalletFacade implements WalletEventListener
|
||||||
|
|
||||||
log.info(wallet.toString());
|
log.info(wallet.toString());
|
||||||
|
|
||||||
// testTradeProcess();
|
// testTradeProcessDepositTx();
|
||||||
}
|
// testTradeProcessPayOutTx();
|
||||||
|
|
||||||
// TODO only temp. for testing trade process between offerer and taker
|
|
||||||
private void testTradeProcess()
|
|
||||||
{
|
|
||||||
//wallet.allowSpendingUnconfirmedTransactions();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex;
|
|
||||||
|
|
||||||
BigInteger offererAmount = Utils.toNanoCoins("0.01");
|
|
||||||
BigInteger takerAmount = Utils.toNanoCoins("0.02");
|
|
||||||
|
|
||||||
String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
|
|
||||||
String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7";
|
|
||||||
String arbitratorPubKey = "";
|
|
||||||
|
|
||||||
// 1 offerer creates MS TX and pay in
|
|
||||||
/* Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey);
|
|
||||||
tx1AsHex = Utils.bytesToHexString(tx1.bitcoinSerialize());
|
|
||||||
*/
|
|
||||||
|
|
||||||
tx1AsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffff0240420f00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac00000000";
|
|
||||||
|
|
||||||
// 2. taker pay in and sign
|
|
||||||
/* Transaction tx2 = takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, tx1AsHex);
|
|
||||||
tx2AsHex = Utils.bytesToHexString(tx2.bitcoinSerialize());
|
|
||||||
tx2ScriptSigAsHex = Utils.bytesToHexString(tx2.getInput(1).getScriptBytes());
|
|
||||||
tx2ConnOutAsHex = Utils.bytesToHexString(tx2.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
|
|
||||||
*/
|
|
||||||
|
|
||||||
tx2AsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
|
|
||||||
tx2ScriptSigAsHex = "47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
|
|
||||||
tx2ConnOutAsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0010000006a473044022011431387fc19b093b26a6d2371995c828179aae68e94ad5804e5d0986a6b471302206abc2b698375620e65fc9970b7781da0af2179d1bdc4ebc82a13e285359a3ce7012103c7b9e9ef657705522c85b8429bb2b42c04f0fd4a09e0605cd7dd62ffecb57944ffffffff02c0ce823e000000001976a9142d1b4347ae850805f3badbb4b2949674f46c4ccd88ac00e1f505000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
|
|
||||||
|
|
||||||
// 3. offerer sign and send
|
|
||||||
Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex);
|
|
||||||
|
|
||||||
log.info(tx3.toString());
|
|
||||||
|
|
||||||
} catch (AddressFormatException | InsufficientMoneyException | InterruptedException | ExecutionException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
|
||||||
} catch (Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. offerer
|
|
||||||
private Transaction offererCreatesMSTxAndAddPayment(BigInteger offererAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
|
|
||||||
{
|
|
||||||
// use that to use the convenient api for getting the best coin selection and fee calculation
|
|
||||||
// TODO should be constructed manually
|
|
||||||
Transaction tx = new Transaction(params);
|
|
||||||
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
|
|
||||||
tx.addOutput(offererAmount, multiSigOutputScript);
|
|
||||||
Wallet.SendRequest request = Wallet.SendRequest.forTx(tx);
|
|
||||||
wallet.completeTx(request);
|
|
||||||
// TODO remove sig or use SigHash.NONE
|
|
||||||
//tx.getInput(0).setScriptSig(null);
|
|
||||||
|
|
||||||
/*
|
|
||||||
IN[0] offerer
|
|
||||||
OUT[0] MS
|
|
||||||
OUT[1] offerer change
|
|
||||||
*/
|
|
||||||
return tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. taker
|
|
||||||
public Transaction takerAddPaymentAndSign(BigInteger takerAmount,
|
|
||||||
BigInteger msOutputAmount,
|
|
||||||
String offererPubKey,
|
|
||||||
String takerPubKey,
|
|
||||||
String arbitratorPubKey,
|
|
||||||
String tx1AsHex
|
|
||||||
) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException
|
|
||||||
{
|
|
||||||
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
|
|
||||||
|
|
||||||
// use that to use the convenient api for getting the best coin selection and fee calculation
|
|
||||||
// TODO should be constructed manually
|
|
||||||
Transaction dummyTx = new Transaction(params);
|
|
||||||
dummyTx.addOutput(takerAmount, multiSigOutputScript);
|
|
||||||
wallet.completeTx(Wallet.SendRequest.forTx(dummyTx));
|
|
||||||
|
|
||||||
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
|
|
||||||
tx.addInput(dummyTx.getInput(0));
|
|
||||||
tx.addOutput(dummyTx.getOutput(1));
|
|
||||||
tx.getOutput(0).setValue(msOutputAmount);
|
|
||||||
|
|
||||||
TransactionInput input = tx.getInput(1);
|
|
||||||
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
|
|
||||||
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
|
|
||||||
Sha256Hash hash = tx.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);
|
|
||||||
|
|
||||||
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
IN[0] offerer
|
|
||||||
IN[1] taker signed
|
|
||||||
OUT[0] MS
|
|
||||||
OUT[1] offerer change
|
|
||||||
OUT[2] taker change
|
|
||||||
*/
|
|
||||||
|
|
||||||
return tx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transaction offererSignAndSendTx(String tx1AsHex,
|
|
||||||
String tx2AsHex,
|
|
||||||
String tx2ConnOutAsHex,
|
|
||||||
String tx2ScriptSigAsHex) throws Exception
|
|
||||||
{
|
|
||||||
Transaction tx = new Transaction(params);
|
|
||||||
|
|
||||||
Transaction tx1 = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
|
|
||||||
Transaction tx1ConnOut = wallet.getTransaction(tx1.getInput(0).getOutpoint().getHash());
|
|
||||||
TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 0, tx1ConnOut);
|
|
||||||
TransactionInput tx1Input = new TransactionInput(params, tx, tx1.getInput(0).getScriptBytes(), tx1OutPoint);
|
|
||||||
tx1Input.setParent(tx);
|
|
||||||
tx.addInput(tx1Input);
|
|
||||||
|
|
||||||
Transaction tx2 = new Transaction(params, Utils.parseAsHexOrBase58(tx2AsHex));
|
|
||||||
Transaction tx2ConnOut = new Transaction(params, Utils.parseAsHexOrBase58(tx2ConnOutAsHex));
|
|
||||||
TransactionOutPoint tx2OutPoint = new TransactionOutPoint(params, 1, tx2ConnOut);
|
|
||||||
TransactionInput tx2Input = new TransactionInput(params, tx, Utils.parseAsHexOrBase58(tx2ScriptSigAsHex), tx2OutPoint);
|
|
||||||
tx2Input.setParent(tx);
|
|
||||||
tx.addInput(tx2Input);
|
|
||||||
|
|
||||||
tx.addOutput(tx2.getOutput(0));
|
|
||||||
tx.addOutput(tx2.getOutput(1));
|
|
||||||
tx.addOutput(tx2.getOutput(2));
|
|
||||||
|
|
||||||
TransactionInput input = tx.getInput(0);
|
|
||||||
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);
|
|
||||||
|
|
||||||
input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false);
|
|
||||||
|
|
||||||
input = tx.getInput(1);
|
|
||||||
scriptPubKey = input.getConnectedOutput().getScriptPubKey();
|
|
||||||
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
IN[0] offerer signed
|
|
||||||
IN[1] taker signed
|
|
||||||
OUT[0] MS
|
|
||||||
OUT[1] offerer change
|
|
||||||
OUT[2] taker change
|
|
||||||
*/
|
|
||||||
|
|
||||||
tx.verify();
|
|
||||||
|
|
||||||
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
|
|
||||||
|
|
||||||
FutureCallback callback = new FutureCallback<Transaction>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onSuccess(Transaction transaction)
|
|
||||||
{
|
|
||||||
log.info("sendResult onSuccess:" + transaction.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Throwable t)
|
|
||||||
{
|
|
||||||
log.warn("sendResult onFailure:" + t.toString());
|
|
||||||
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Futures.addCallback(broadcastComplete, callback);
|
|
||||||
|
|
||||||
return tx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutDown()
|
public void shutDown()
|
||||||
|
@ -501,6 +310,346 @@ public class WalletFacade implements WalletEventListener
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// testTradeProcess methods
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO only temp. for testing trade process between offerer and taker
|
||||||
|
// deposit
|
||||||
|
private void testTradeProcessDepositTx()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex;
|
||||||
|
|
||||||
|
BigInteger offererAmount = Utils.toNanoCoins("0.01");
|
||||||
|
BigInteger takerAmount = Utils.toNanoCoins("0.02");
|
||||||
|
|
||||||
|
String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
|
||||||
|
String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7";
|
||||||
|
String arbitratorPubKey = "";
|
||||||
|
|
||||||
|
// 1 offerer creates MS TX and pay in
|
||||||
|
/* Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey);
|
||||||
|
tx1AsHex = Utils.bytesToHexString(tx1.bitcoinSerialize());
|
||||||
|
*/
|
||||||
|
|
||||||
|
tx1AsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffff0240420f00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac00000000";
|
||||||
|
|
||||||
|
// 2. taker pay in and sign
|
||||||
|
/* Transaction tx2 = takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, tx1AsHex);
|
||||||
|
tx2AsHex = Utils.bytesToHexString(tx2.bitcoinSerialize());
|
||||||
|
tx2ScriptSigAsHex = Utils.bytesToHexString(tx2.getInput(1).getScriptBytes());
|
||||||
|
tx2ConnOutAsHex = Utils.bytesToHexString(tx2.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
|
||||||
|
*/
|
||||||
|
|
||||||
|
tx2AsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
|
||||||
|
tx2ScriptSigAsHex = "47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
|
||||||
|
tx2ConnOutAsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0010000006a473044022011431387fc19b093b26a6d2371995c828179aae68e94ad5804e5d0986a6b471302206abc2b698375620e65fc9970b7781da0af2179d1bdc4ebc82a13e285359a3ce7012103c7b9e9ef657705522c85b8429bb2b42c04f0fd4a09e0605cd7dd62ffecb57944ffffffff02c0ce823e000000001976a9142d1b4347ae850805f3badbb4b2949674f46c4ccd88ac00e1f505000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
|
||||||
|
|
||||||
|
// 3. offerer sign and send
|
||||||
|
Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex);
|
||||||
|
|
||||||
|
log.info(tx3.toString()); // tx has 453 Bytes
|
||||||
|
|
||||||
|
} catch (AddressFormatException | InsufficientMoneyException | InterruptedException | ExecutionException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||||
|
} catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// payout
|
||||||
|
private void testTradeProcessPayOutTx()
|
||||||
|
{
|
||||||
|
String depositTxAsHex, offererSignatureR, offererSignatureS;
|
||||||
|
BigInteger offererPaybackAmount = Utils.toNanoCoins("0.029");
|
||||||
|
BigInteger takerPaybackAmount = Utils.toNanoCoins("0.001");
|
||||||
|
|
||||||
|
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58("0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568"));
|
||||||
|
String takerAddress = takerKey.toAddress(params).toString();
|
||||||
|
|
||||||
|
ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58("0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7"));
|
||||||
|
String offererAddress = offererKey.toAddress(params).toString();
|
||||||
|
|
||||||
|
String depositTxID = "4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 1. offerer create and sign payout tx
|
||||||
|
/* Pair<ECKey.ECDSASignature, Transaction> result = offererCreateAndSignPayoutTx(depositTxID,
|
||||||
|
offererPaybackAmount,
|
||||||
|
takerPaybackAmount,
|
||||||
|
takerAddress);
|
||||||
|
|
||||||
|
ECKey.ECDSASignature offererSignature = result.getKey();
|
||||||
|
Transaction depositTx = result.getValue();
|
||||||
|
offererSignatureR = offererSignature.r.toString();
|
||||||
|
offererSignatureS = offererSignature.s.toString();
|
||||||
|
depositTxAsHex = Utils.bytesToHexString(depositTx.bitcoinSerialize()); */
|
||||||
|
|
||||||
|
|
||||||
|
depositTxAsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006a473044022008addf33a37f8f058e629020d73c3873953e1eca559fcb59d9db651f2c9b524f02203cbfd319b2675974adfbff2cb605fc9b89a92f2f3197ee634b39c892fb57d1be01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
|
||||||
|
offererSignatureR = "64562406184784382000330465585294975882418886289667123258365244289311131050102";
|
||||||
|
offererSignatureS = "9282449224979858852843663340827968850695102089943078462704407873774672151491";
|
||||||
|
|
||||||
|
// 2. taker sign and publish tx
|
||||||
|
Transaction takerTx = takerSignAndSendTx(depositTxAsHex,
|
||||||
|
offererSignatureR,
|
||||||
|
offererSignatureS,
|
||||||
|
offererPaybackAmount,
|
||||||
|
takerPaybackAmount,
|
||||||
|
offererAddress);
|
||||||
|
|
||||||
|
log.info(takerTx.toString()); // tx has 265 Bytes
|
||||||
|
|
||||||
|
/*
|
||||||
|
6606c366a487bff9e412d0b6c09c14916319932db5954bf5d8719f43f828a3ba: Unknown confidence level.
|
||||||
|
in [] [30450221008ebd06e53ce1ab7b599098b3117f2073b1d224baae21190ef7839b70a638207602201485ae19965f2d954398f691aa40fecb4d08d625ae96037a4fc5eecb7a4803c301] [30440220521c485045a46fbb61c634ebf456c470f9c2f7307a743e3fce10b50f7a69115d0220249ff5f9796a9fcd12984afcfd3f7d16d2f8c49ddcef245db7c7b02b909af9a601]
|
||||||
|
outpoint:4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9:0 hash160:[exception: Script not in the standard scriptPubKey form]
|
||||||
|
out DUP HASH160 [9fc3d8e0371b6eab89a8c3c015839f9e493ccf65] EQUALVERIFY CHECKSIG 0.0289 BTC
|
||||||
|
out DUP HASH160 [e5175c1f71c28218306d4a27c8cec0269dddbbde] EQUALVERIFY CHECKSIG 0.0009 BTC
|
||||||
|
|
||||||
|
2 [0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7] [0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568] [] 3 CHECKMULTISIG
|
||||||
|
*/
|
||||||
|
|
||||||
|
} catch (InsufficientMoneyException | AddressFormatException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// deposit 1. offerer
|
||||||
|
private Transaction offererCreatesMSTxAndAddPayment(BigInteger offererAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
|
||||||
|
{
|
||||||
|
// use that to use the convenient api for getting the best coin selection and fee calculation
|
||||||
|
// TODO should be constructed manually
|
||||||
|
Transaction tx = new Transaction(params);
|
||||||
|
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
|
||||||
|
tx.addOutput(offererAmount, multiSigOutputScript);
|
||||||
|
Wallet.SendRequest request = Wallet.SendRequest.forTx(tx);
|
||||||
|
wallet.completeTx(request);
|
||||||
|
// TODO remove sig or use SigHash.NONE
|
||||||
|
//tx.getInput(0).setScriptSig(null);
|
||||||
|
|
||||||
|
/*
|
||||||
|
IN[0] offerer
|
||||||
|
OUT[0] MS
|
||||||
|
OUT[1] offerer change
|
||||||
|
*/
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deposit 2. taker
|
||||||
|
public Transaction takerAddPaymentAndSign(BigInteger takerAmount,
|
||||||
|
BigInteger msOutputAmount,
|
||||||
|
String offererPubKey,
|
||||||
|
String takerPubKey,
|
||||||
|
String arbitratorPubKey,
|
||||||
|
String tx1AsHex
|
||||||
|
) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException
|
||||||
|
{
|
||||||
|
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
|
||||||
|
|
||||||
|
// use that to use the convenient api for getting the best coin selection and fee calculation
|
||||||
|
// TODO should be constructed manually
|
||||||
|
Transaction dummyTx = new Transaction(params);
|
||||||
|
dummyTx.addOutput(takerAmount, multiSigOutputScript);
|
||||||
|
wallet.completeTx(Wallet.SendRequest.forTx(dummyTx));
|
||||||
|
|
||||||
|
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
|
||||||
|
tx.addInput(dummyTx.getInput(0));
|
||||||
|
tx.addOutput(dummyTx.getOutput(1));
|
||||||
|
tx.getOutput(0).setValue(msOutputAmount);
|
||||||
|
|
||||||
|
TransactionInput input = tx.getInput(1);
|
||||||
|
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
|
||||||
|
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
|
||||||
|
Sha256Hash hash = tx.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);
|
||||||
|
|
||||||
|
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
IN[0] offerer
|
||||||
|
IN[1] taker signed
|
||||||
|
OUT[0] MS
|
||||||
|
OUT[1] offerer change
|
||||||
|
OUT[2] taker change
|
||||||
|
*/
|
||||||
|
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deposit 3. offerer
|
||||||
|
public Transaction offererSignAndSendTx(String tx1AsHex,
|
||||||
|
String tx2AsHex,
|
||||||
|
String tx2ConnOutAsHex,
|
||||||
|
String tx2ScriptSigAsHex) throws Exception
|
||||||
|
{
|
||||||
|
Transaction tx = new Transaction(params);
|
||||||
|
|
||||||
|
Transaction tx1 = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
|
||||||
|
Transaction tx1ConnOut = wallet.getTransaction(tx1.getInput(0).getOutpoint().getHash());
|
||||||
|
TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 0, tx1ConnOut);
|
||||||
|
TransactionInput tx1Input = new TransactionInput(params, tx, tx1.getInput(0).getScriptBytes(), tx1OutPoint);
|
||||||
|
tx1Input.setParent(tx);
|
||||||
|
tx.addInput(tx1Input);
|
||||||
|
|
||||||
|
Transaction tx2 = new Transaction(params, Utils.parseAsHexOrBase58(tx2AsHex));
|
||||||
|
Transaction tx2ConnOut = new Transaction(params, Utils.parseAsHexOrBase58(tx2ConnOutAsHex));
|
||||||
|
TransactionOutPoint tx2OutPoint = new TransactionOutPoint(params, 1, tx2ConnOut);
|
||||||
|
TransactionInput tx2Input = new TransactionInput(params, tx, Utils.parseAsHexOrBase58(tx2ScriptSigAsHex), tx2OutPoint);
|
||||||
|
tx2Input.setParent(tx);
|
||||||
|
tx.addInput(tx2Input);
|
||||||
|
|
||||||
|
tx.addOutput(tx2.getOutput(0));
|
||||||
|
tx.addOutput(tx2.getOutput(1));
|
||||||
|
tx.addOutput(tx2.getOutput(2));
|
||||||
|
|
||||||
|
TransactionInput input = tx.getInput(0);
|
||||||
|
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);
|
||||||
|
|
||||||
|
input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false);
|
||||||
|
|
||||||
|
input = tx.getInput(1);
|
||||||
|
scriptPubKey = input.getConnectedOutput().getScriptPubKey();
|
||||||
|
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
IN[0] offerer signed
|
||||||
|
IN[1] taker signed
|
||||||
|
OUT[0] MS
|
||||||
|
OUT[1] offerer change
|
||||||
|
OUT[2] taker change
|
||||||
|
*/
|
||||||
|
|
||||||
|
tx.verify();
|
||||||
|
|
||||||
|
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
|
||||||
|
|
||||||
|
FutureCallback callback = new FutureCallback<Transaction>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Transaction transaction)
|
||||||
|
{
|
||||||
|
log.info("sendResult onSuccess:" + transaction.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t)
|
||||||
|
{
|
||||||
|
log.warn("sendResult onFailure:" + t.toString());
|
||||||
|
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Futures.addCallback(broadcastComplete, callback);
|
||||||
|
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// payout 1. offerer
|
||||||
|
private Pair<ECKey.ECDSASignature, Transaction> offererCreateAndSignPayoutTx(String depositTxID,
|
||||||
|
BigInteger offererPaybackAmount,
|
||||||
|
BigInteger takerPaybackAmount,
|
||||||
|
String takerAddress) throws InsufficientMoneyException, AddressFormatException
|
||||||
|
{
|
||||||
|
ECKey key = wallet.getKeys().get(0);
|
||||||
|
// offerer has published depositTx so he has it in wallet
|
||||||
|
Transaction depositTx = wallet.getTransaction(new Sha256Hash(depositTxID));
|
||||||
|
TransactionOutput multiSigOutput = depositTx.getOutput(0);
|
||||||
|
Script multiSigScript = multiSigOutput.getScriptPubKey();
|
||||||
|
|
||||||
|
Transaction tx = new Transaction(params);
|
||||||
|
tx.addInput(multiSigOutput);
|
||||||
|
//TODO fee calculation
|
||||||
|
tx.addOutput(offererPaybackAmount.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), key.toAddress(params));
|
||||||
|
tx.addOutput(takerPaybackAmount.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), new Address(params, takerAddress));
|
||||||
|
|
||||||
|
Sha256Hash sigHash = tx.hashForSignature(0, multiSigScript, Transaction.SigHash.ALL, false);
|
||||||
|
ECKey.ECDSASignature signature = key.sign(sigHash);
|
||||||
|
|
||||||
|
return new Pair<>(signature, depositTx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// payout 2. taker
|
||||||
|
private Transaction takerSignAndSendTx(String depositTxAsHex,
|
||||||
|
String offererSignatureR,
|
||||||
|
String offererSignatureS,
|
||||||
|
BigInteger offererPaybackAmount,
|
||||||
|
BigInteger takerPaybackAmount,
|
||||||
|
String offererAddress) throws InsufficientMoneyException, AddressFormatException
|
||||||
|
{
|
||||||
|
ECKey key = wallet.getKeys().get(0);
|
||||||
|
|
||||||
|
Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex));
|
||||||
|
TransactionOutput multiSigOutput = depositTx.getOutput(0);
|
||||||
|
Script multiSigScript = multiSigOutput.getScriptPubKey();
|
||||||
|
|
||||||
|
Transaction tx = new Transaction(params);
|
||||||
|
tx.addInput(multiSigOutput);
|
||||||
|
//TODO fee calculation
|
||||||
|
tx.addOutput(offererPaybackAmount.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), new Address(params, offererAddress));
|
||||||
|
tx.addOutput(takerPaybackAmount.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), key.toAddress(params));
|
||||||
|
|
||||||
|
Sha256Hash sigHash = tx.hashForSignature(0, multiSigScript, Transaction.SigHash.ALL, false);
|
||||||
|
|
||||||
|
ECKey.ECDSASignature takerSignature = key.sign(sigHash);
|
||||||
|
TransactionSignature takerTxSig = new TransactionSignature(takerSignature, Transaction.SigHash.ALL, false);
|
||||||
|
|
||||||
|
ECKey.ECDSASignature offererSignature = new ECKey.ECDSASignature(new BigInteger(offererSignatureR), new BigInteger(offererSignatureS));
|
||||||
|
TransactionSignature offererTxSig = new TransactionSignature(offererSignature, Transaction.SigHash.ALL, false);
|
||||||
|
|
||||||
|
Script inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(offererTxSig, takerTxSig));
|
||||||
|
tx.getInput(0).setScriptSig(inputScript);
|
||||||
|
|
||||||
|
tx.verify();
|
||||||
|
tx.getInput(0).getScriptSig().correctlySpends(tx, 0, multiSigScript, false);
|
||||||
|
tx.getInput(0).verify(multiSigOutput);
|
||||||
|
|
||||||
|
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
|
||||||
|
|
||||||
|
FutureCallback callback = new FutureCallback<Transaction>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Transaction transaction)
|
||||||
|
{
|
||||||
|
log.info("sendResult onSuccess:" + transaction.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t)
|
||||||
|
{
|
||||||
|
log.warn("sendResult onFailure:" + t.toString());
|
||||||
|
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Futures.addCallback(broadcastComplete, callback);
|
||||||
|
|
||||||
|
return tx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Inner classes
|
// Inner classes
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Add table
Reference in a new issue