payment process cleanup

This commit is contained in:
Manfred Karrer 2014-05-19 02:19:21 +02:00
parent 5d9d7a9a3d
commit 1215f338ad
11 changed files with 407 additions and 604 deletions

View File

@ -1,231 +0,0 @@
package io.bitsquare.btc;
import com.google.bitcoin.core.*;
import com.google.bitcoin.script.Script;
import com.google.bitcoin.script.ScriptBuilder;
import com.google.bitcoin.store.UnreadableWalletException;
import com.google.bitcoin.store.WalletProtobufSerializer;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import io.bitsquare.BitSquare;
import io.bitsquare.util.Utilities;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN;
public class AccountRegistrationWallet extends Wallet implements WalletEventListener
{
// private static final Logger log = LoggerFactory.getLogger(AccountRegistrationWallet.class);
private final File walletFile;
private NetworkParameters params;
private BlockChain chain;
private PeerGroup peerGroup;
private List<WalletFacade.WalletListener> walletListeners = new ArrayList<>();
AccountRegistrationWallet(NetworkParameters params, BlockChain chain, PeerGroup peerGroup)
{
super(params);
this.params = params;
this.chain = chain;
this.peerGroup = peerGroup;
/* try
{
final InetAddress localHost = InetAddress.getLocalHost();
PeerAddress peerAddress = new PeerAddress(localHost, params.getPort());
peerGroup.addAddress(peerAddress);
} catch (UnknownHostException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
} */
walletFile = new File(Utilities.getRootDir() + BitSquare.ID + "_account_reg" + ".wallet");
if (walletFile.exists())
{
FileInputStream walletStream = null;
try
{
walletStream = new FileInputStream(walletFile);
} catch (FileNotFoundException e)
{
e.printStackTrace();
}
try
{
new WalletProtobufSerializer().readWallet(WalletProtobufSerializer.parseToProto(walletStream), this);
} catch (UnreadableWalletException | IOException e)
{
e.printStackTrace();
}
}
else
{
addKey(new ECKey());
}
chain.addWallet(this);
peerGroup.addWallet(this);
try
{
saveToFile(walletFile);
} catch (IOException e)
{
e.printStackTrace();
}
autosaveToFile(walletFile, 1, TimeUnit.SECONDS, null);
peerGroup.setMinBroadcastConnections(1);
allowSpendingUnconfirmedTransactions();
}
void shutDown()
{
try
{
saveToFile(walletFile);
} catch (IOException e)
{
e.printStackTrace();
}
}
Address getAddress()
{
return getKey().toAddress(params);
}
ECKey getKey()
{
return getKeys().get(0);
}
void addWalletListener(WalletFacade.WalletListener listener)
{
if (walletListeners.size() == 0)
addEventListener(this);
walletListeners.add(listener);
}
void removeWalletListener(WalletFacade.WalletListener listener)
{
walletListeners.remove(listener);
if (walletListeners.size() == 0)
removeEventListener(this);
}
void saveToBlockchain(byte[] dataToEmbed) throws InsufficientMoneyException
{
Script script = new ScriptBuilder()
.op(OP_RETURN)
.data(dataToEmbed)
.build();
Transaction transaction = new Transaction(params);
TransactionOutput dataOutput = new TransactionOutput(params,
transaction,
Transaction.MIN_NONDUST_OUTPUT,
script.getProgram());
transaction.addOutput(dataOutput);
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction);
// give fee to miners yet. Later it could be spent to other traders via lottery...
sendRequest.fee = Fees.ACCOUNT_REGISTRATION_FEE;
Wallet.SendResult sendResult = sendCoins(sendRequest);
//TODO
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction result)
{
//log.info("sendResult onSuccess:" + result.toString());
// Platform.runLater(overlayUi::done);
}
@Override
public void onFailure(Throwable t)
{
//log.warn("sendResult onFailure:" + t.toString());
// We died trying to empty the wallet.
// crashAlert(t);
}
});
//TODO
sendResult.tx.getConfidence().addEventListener((tx, reason) -> {
//if (reason == TransactionConfidence.Listener.ChangeReason.SEEN_PEERS)
//updateTitleForBroadcast();
});
}
@Override
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
{
for (WalletFacade.WalletListener walletListener : walletListeners)
walletListener.onCoinsReceived(newBalance);
// //log.info("onCoinsReceived");
}
@Override
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
{
for (WalletFacade.WalletListener walletListener : walletListeners)
walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(this));
// //log.info("onTransactionConfidenceChanged " + tx.getConfidence().toString());
}
@Override
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
{
//log.info("onCoinsSent");
}
@Override
public void onReorganize(Wallet wallet)
{
//log.info("onReorganize");
}
@Override
public void onWalletChanged(Wallet wallet)
{
// //log.info("onWalletChanged");
}
@Override
public void onKeysAdded(Wallet wallet, List<ECKey> keys)
{
//log.info("onKeysAdded");
}
@Override
public void onScriptsAdded(Wallet wallet, List<Script> scripts)
{
//log.info("onScriptsAdded");
}
int getConfNumBroadcastPeers()
{
Transaction transaction = WalletUtil.getTransaction(this);
return (transaction == null || transaction.getConfidence() == null) ? 0 : transaction.getConfidence().numBroadcastPeers();
}
}

View File

@ -0,0 +1,23 @@
package io.bitsquare.btc;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.Wallet;
import com.google.bitcoin.crypto.KeyCrypter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class BitSquareWallet extends Wallet
{
private static final Logger log = LoggerFactory.getLogger(BitSquareWallet.class);
public BitSquareWallet(NetworkParameters params)
{
super(params);
}
public BitSquareWallet(NetworkParameters params, KeyCrypter keyCrypter)
{
super(params, keyCrypter);
}
}

View File

@ -9,7 +9,7 @@ public class Fees
{
// min dust value lead to exception at for non standard to address pay scripts, so we use a value >= 7860 instead
public static BigInteger MS_TX_FEE = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
public static BigInteger ACCOUNT_REGISTRATION_FEE = Utils.toNanoCoins("0.01");
public static BigInteger OFFER_CREATION_FEE = Utils.toNanoCoins("0.001");
public static BigInteger ACCOUNT_REGISTRATION_FEE = Utils.toNanoCoins("0.0002");
public static BigInteger OFFER_CREATION_FEE = Utils.toNanoCoins("0.0002");
public static BigInteger OFFER_TAKER_FEE = OFFER_CREATION_FEE;
}

View File

@ -0,0 +1,21 @@
package io.bitsquare.btc;
import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.kits.WalletAppKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class WalletConfig extends WalletAppKit
{
private static final Logger log = LoggerFactory.getLogger(WalletConfig.class);
public WalletConfig(NetworkParameters params, File directory, String filePrefix)
{
super(params, directory, filePrefix);
}
}

View File

@ -91,7 +91,7 @@ public class WalletFacade
// Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen
// or progress widget to keep the user engaged whilst we initialise, but we don't.
walletAppKit.setDownloadListener(new BlockChainDownloadListener()).setBlockingStartup(false).setUserAgent("BitSquare", "0.1");
walletAppKit.setDownloadListener(new BlockChainDownloadListener()).setBlockingStartup(false).setUserAgent(BitSquare.ID, "0.1");
walletAppKit.startAsync();
walletAppKit.awaitRunning();
@ -102,11 +102,12 @@ public class WalletFacade
wallet.addKey(tradingKey);
wallet.allowSpendingUnconfirmedTransactions();
// walletAppKit.peerGroup().setMaxConnections(10);
//TODO for regtest set 1
walletAppKit.peerGroup().setMinBroadcastConnections(1);
//walletAppKit.peerGroup().setMaxConnections(11);
//wallet.addWatchedAddress(tradingKey.toAddress(params));
if (params == RegTestParams.get())
walletAppKit.peerGroup().setMinBroadcastConnections(1);
/* else
walletAppKit.peerGroup().setMinBroadcastConnections(2); */
}
public void shutDown()
@ -178,24 +179,27 @@ public class WalletFacade
public void publishRegistrationTxWithExtraData(String stringifiedBankAccounts) throws InsufficientMoneyException
{
log.debug("publishRegistrationTxWithExtraData");
log.trace("inputs: ");
log.trace("stringifiedBankAccounts " + stringifiedBankAccounts);
byte[] dataToEmbed = cryptoFacade.getEmbeddedAccountRegistrationData(registrationKey, stringifiedBankAccounts);
Script script = new ScriptBuilder().op(OP_RETURN).data(dataToEmbed).build();
Transaction transaction = new Transaction(params);
TransactionOutput dataOutput = new TransactionOutput(params, transaction, Transaction.MIN_NONDUST_OUTPUT, script.getProgram());
transaction.addOutput(dataOutput);
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction);
Transaction tx = new Transaction(params);
TransactionOutput dataOutput = new TransactionOutput(params, tx, Transaction.MIN_NONDUST_OUTPUT, script.getProgram());
tx.addOutput(dataOutput);
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
// give fee to miners yet. Later it could be spent to other traders via lottery...
sendRequest.fee = Fees.ACCOUNT_REGISTRATION_FEE;
sendRequest.fee = Fees.ACCOUNT_REGISTRATION_FEE.subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
log.debug("Registration transaction: " + transaction.toString());
//TODO
log.debug("Registration transaction: " + tx.toString());
printInputs("publishRegistrationTxWithExtraData", tx);
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction result)
{
log.debug("sendResult onSuccess:" + result.toString());
log.debug("sendResult onSuccess");
}
@Override
@ -223,6 +227,8 @@ public class WalletFacade
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback);
log.debug("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("payOfferFee", tx);
log.debug("tx=" + tx.toString());
return tx.getHashAsString();
}
@ -235,37 +241,52 @@ public class WalletFacade
}
// Offerer creates deposit tx with his input and change output, the MS has just a dummy value at the moment
// 1. step: deposit tx
// Offerer creates the 2of3 multiSig deposit tx with his unsigned input and change output
public Transaction offererCreatesMSTxAndAddPayment(BigInteger offererInputAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
{
log.debug("offererCreatesMSTxAndAddPayment");
log.trace("inputs: ");
log.trace("offererInputAmount=" + Utils.bitcoinValueToFriendlyString(offererInputAmount));
log.trace("offererPubKey=" + offererPubKey);
log.trace("takerPubKey=" + takerPubKey);
log.trace("arbitratorPubKey=" + arbitratorPubKey);
// we pay the offererInputAmount to a dummy MS output, that way we get the best coin selection and fee calculation form the wallet and the correct change amount
// we don't commit that tx to the wallet as it will be changed later and it's not signed yet
// btc ntx fee will be included by the completeTx call, so we don't add it manually
// 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(offererInputAmount, multiSigOutputScript);
Wallet.SendRequest request = Wallet.SendRequest.forTx(tx);
wallet.completeTx(request);
wallet.completeTx(Wallet.SendRequest.forTx(tx));
// we remove the signature to make sure the tx is invalid to publish
// 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
tx.getInput(0).setScriptSig(new Script(new byte[]{}));
// tx looks like:
log.trace("verify tx");
tx.verify();
// The created tx looks like:
/*
IN[0]
IN[0] any input > offererInputAmount + fee (unsigned)
OUT[0] MS offererInputAmount
OUT[1] offerer change amount
OUT[1] Change = input - offererInputAmount - fee
btc tx fee
*/
log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("offererCreatesMSTxAndAddPayment", tx);
log.debug("tx = " + tx.toString());
return tx;
}
// Taker put his input in, change the MS amount to the correct value and sign his input
public Transaction takerAddPaymentAndSign(BigInteger takerAmount,
// 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(BigInteger takerInputAmount,
BigInteger msOutputAmount,
String offererPubKey,
String takerPubKey,
@ -273,40 +294,60 @@ public class WalletFacade
String offerersPartialDepositTxAsHex
) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException
{
log.debug("takerAddPaymentAndSign");
log.debug("takerAddPaymentAndSignTx");
log.trace("inputs: ");
log.trace("takerInputAmount=" + Utils.bitcoinValueToFriendlyString(takerInputAmount));
log.trace("msOutputAmount=" + Utils.bitcoinValueToFriendlyString(msOutputAmount));
log.trace("offererPubKey=" + offererPubKey);
log.trace("takerPubKey=" + takerPubKey);
log.trace("arbitratorPubKey=" + arbitratorPubKey);
log.trace("offerersPartialDepositTxAsHex=" + offerersPartialDepositTxAsHex);
// 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
// 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);
wallet.completeTx(Wallet.SendRequest.forTx(tempTx));
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.
// we pay the btc tx fee 2 times directly in the deposit tx:
// 1. will be spent to miners when publishing the deposit tx (sum inputs - sum outputs = fee)
// 2. will be as added to the MS amount, so when spending the payout tx the fee is already there
// both traders pay 1 times a fee, so its equally split
// we construct a dummy tx to get the best coin selection and fee calculation form the wallet and the correct change amount
// btc ntx fee will be included by the completeTx call, so we don't add it manually
Transaction dummyTx = new Transaction(params);
dummyTx.addOutput(takerAmount, multiSigOutputScript);
wallet.completeTx(Wallet.SendRequest.forTx(dummyTx)); // it is signed but we don't care as it will not be used further
// Now we construct the real MS tx from the passed tx from the offerer
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(offerersPartialDepositTxAsHex));
// it looks like that
// The created tempTx looks like:
/*
IN[0] offerer input
OUT[0] MS offererInputAmount
OUT[1] offerer change
IN[0] any input taker > takerInputAmount + fee (signed)
OUT[0] MS takerInputAmount
OUT[1] Change = input taker - takerInputAmount - fee
btc tx fee
*/
// we add our inputs and outputs and change the MS amount to the correct one
tx.addInput(dummyTx.getInput(0));
tx.addOutput(dummyTx.getOutput(1));
// we add the btc tx fee
// Now we construct the real 2of3 multiSig tx from the serialized offerers tx
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(offerersPartialDepositTxAsHex));
log.trace("offerersPartialDepositTx=" + 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
tx.addInput(tempTx.getInput(0));
if (tempTx.getOutputs().size() == 2)
tx.addOutput(tempTx.getOutput(1));
// We add the btc tx fee to the msOutputAmount and apply the change to the multiSig output
msOutputAmount = msOutputAmount.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
// and change the value of the MS output
tx.getOutput(0).setValue(msOutputAmount);
// Now we sign our input
TransactionInput input = tx.getInput(1);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
@ -320,54 +361,92 @@ public class WalletFacade
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
log.debug("check if correctly spends");
log.trace("check if it can be correctly spent for input 1");
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
log.debug("verify tx");
log.trace("verify tx");
tx.verify();
// The resulting tx looks like:
/*
IN[0] offerer 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
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
*/
wallet.commitTx(tx);
log.debug("tx" + tx.toString());
// 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.
log.trace("Check if wallet is consistent before commit: result=" + wallet.isConsistent());
printInputs("takerAddPaymentAndSignTx", tx);
log.debug("tx = " + tx.toString());
return tx;
}
// deposit 3. offerer
public Transaction offererSignAndSendTx(String tx1AsHex,
String tx2AsHex,
String tx2ConnOutAsHex,
String tx2ScriptSigAsHex,
// 3. step: deposit tx
// Offerer signs tx and publishes it
public Transaction offererSignAndPublishTx(String offerersFirstTxAsHex,
String takersSignedTxAsHex,
String takersSignedConnOutAsHex,
String takersSignedScriptSigAsHex,
FutureCallback<Transaction> callback) throws Exception
{
log.info("offererSignAndSendTx start");
log.debug("offererSignAndPublishTx");
log.trace("inputs: ");
log.trace("offerersFirstTxAsHex=" + offerersFirstTxAsHex);
log.trace("takersSignedTxAsHex=" + takersSignedTxAsHex);
log.trace("takersSignedConnOutAsHex=" + takersSignedConnOutAsHex);
log.trace("takersSignedScriptSigAsHex=" + takersSignedScriptSigAsHex);
log.trace("callback=" + callback.toString());
log.trace("Wallet balance initial: " + wallet.getBalance());
// 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);
Transaction tx1 = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
Transaction tx1ConnOut = wallet.getTransaction(tx1.getInput(0).getOutpoint().getHash());
TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 1, tx1ConnOut);
TransactionInput tx1Input = new TransactionInput(params, tx, tx1.getInput(0).getScriptBytes(), tx1OutPoint);
tx1Input.setParent(tx);
tx.addInput(tx1Input);
// offerers first tx
Transaction offerersFirstTx = new Transaction(params, Utils.parseAsHexOrBase58(offerersFirstTxAsHex));
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);
printInputs("offerersFirstTx", offerersFirstTx);
log.trace("offerersFirstTx = " + offerersFirstTx.toString());
tx.addOutput(tx2.getOutput(0));
tx.addOutput(tx2.getOutput(1));
tx.addOutput(tx2.getOutput(2));
// add input
Transaction offerersFirstTxConnOut = wallet.getTransaction(offerersFirstTx.getInput(0).getOutpoint().getHash()); // pass that around!
TransactionOutPoint offerersFirstTxOutPoint = new TransactionOutPoint(params, 1, offerersFirstTxConnOut);
//TransactionInput offerersFirstTxInput = new TransactionInput(params, tx, offerersFirstTx.getInput(0).getScriptBytes(), offerersFirstTxOutPoint); // pass that around! getScriptBytes = empty bytes aray
TransactionInput offerersFirstTxInput = new TransactionInput(params, tx, new byte[]{}, offerersFirstTxOutPoint); // pass that around! getScriptBytes = empty bytes aray
offerersFirstTxInput.setParent(tx);
tx.addInput(offerersFirstTxInput);
// takers signed tx
Transaction takersSignedTx = new Transaction(params, Utils.parseAsHexOrBase58(takersSignedTxAsHex));
printInputs("takersSignedTxInput", takersSignedTx);
log.trace("takersSignedTx = " + takersSignedTx.toString());
// add input
Transaction takersSignedTxConnOut = new Transaction(params, Utils.parseAsHexOrBase58(takersSignedConnOutAsHex));
TransactionOutPoint takersSignedTxOutPoint = new TransactionOutPoint(params, 1, takersSignedTxConnOut);
TransactionInput takersSignedTxInput = new TransactionInput(params, tx, Utils.parseAsHexOrBase58(takersSignedScriptSigAsHex), takersSignedTxOutPoint);
takersSignedTxInput.setParent(tx);
tx.addInput(takersSignedTxInput);
//TODO handle non change output cases
// add outputs from takers tx, they are already correct
for (int i = 0; i < takersSignedTx.getOutputs().size(); i++)
{
tx.addOutput(takersSignedTx.getOutput(i));
}
printInputs("tx", tx);
log.trace("tx = " + tx.toString());
log.trace("Wallet balance before signing: " + wallet.getBalance());
// sign the input
TransactionInput input = tx.getInput(0);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
@ -381,72 +460,88 @@ public class WalletFacade
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
log.info("offererSignAndSendTx check correctlySpends input 0");
log.trace("check if it can be correctly spent for input 0");
input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false);
log.info("offererSignAndSendTx check correctlySpends input 1");
input = tx.getInput(1);
scriptPubKey = input.getConnectedOutput().getScriptPubKey();
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
log.trace("check if it can be correctly spent for input 1");
TransactionInput input1 = tx.getInput(1);
scriptPubKey = input1.getConnectedOutput().getScriptPubKey();
input1.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
/*
IN[0] offerer signed
IN[1] taker signed
OUT[0] MS
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.info("offererSignAndSendTx broadcastTransaction verify ");
log.trace("verify ");
tx.verify();
log.info("offererSignAndSendTx broadcastTransaction");
printInputs("tx", tx);
log.debug("tx = " + tx.toString());
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);
/*FutureCallback<Transaction> localCallback = new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
log.info("offererSignAndSendTx onSuccess" + transaction.getHashAsString());
}
log.trace("Wallet balance after broadcastTransaction: " + wallet.getBalance());
log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
@Override
public void onFailure(Throwable t)
{
log.info("offererSignAndSendTx onFailure" + t.toString());
Popups.openErrorPopup("Fee payment failed", "Fee payment failed. " + t.toString());
}
};
Futures.addCallback(broadcastComplete, localCallback); */
Futures.addCallback(broadcastComplete, callback);
printInputs("tx", tx);
log.debug("tx = " + tx.toString());
return tx;
}
// payout 1. offerer
public Pair<ECKey.ECDSASignature, Transaction> offererCreateAndSignPayoutTx(String depositTxID,
// 4 step deposit tx: Offerer send deposit tx to taker
public void takerCommitDepositTx(String depositTxAsHex)
{
log.trace("takerCommitDepositTx");
log.trace("inputs: ");
log.trace("depositTxID=" + depositTxAsHex);
Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex));
log.trace("depositTx=" + depositTx);
wallet.commitTx(depositTx);
}
// 5. step payout tx: Offerer creates payout tx and signs it
public Pair<ECKey.ECDSASignature, String> offererCreatesAndSignsPayoutTx(String depositTxID,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String takerAddress) throws InsufficientMoneyException, AddressFormatException
{
// offerer has published depositTx so he has it in wallet
log.debug("offererCreatesAndSignsPayoutTx");
log.trace("inputs: ");
log.trace("depositTxID=" + depositTxID);
log.trace("offererPaybackAmount=" + Utils.bitcoinValueToFriendlyString(offererPaybackAmount));
log.trace("takerPaybackAmount=" + Utils.bitcoinValueToFriendlyString(takerPaybackAmount));
log.trace("takerAddress=" + takerAddress);
// Offerer has published depositTx earlier, so he has it in his wallet
Transaction depositTx = wallet.getTransaction(new Sha256Hash(depositTxID));
TransactionOutput multiSigOutput = depositTx.getOutput(0);
String depositTxAsHex = Utils.bytesToHexString(depositTx.bitcoinSerialize());
// We create the payout tx
Transaction tx = createPayoutTx(depositTxAsHex, offererPaybackAmount, takerPaybackAmount, getTradingAddress(), takerAddress);
// We create the signature for that tx
TransactionOutput multiSigOutput = tx.getInput(0).getConnectedOutput();
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), tradingKey.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 = tradingKey.sign(sigHash);
ECKey.ECDSASignature offererSignature = tradingKey.sign(sigHash);
return new Pair<>(signature, depositTx);
TransactionSignature offererTxSig = new TransactionSignature(offererSignature, Transaction.SigHash.ALL, false);
Script inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(offererTxSig));
tx.getInput(0).setScriptSig(inputScript);
log.trace("sigHash=" + sigHash.toString());
return new Pair<>(offererSignature, depositTxAsHex);
}
// payout 2. taker
public Transaction takerSignAndSendTx(String depositTxAsHex,
// 6. step payout tx: Taker signs and publish tx
public Transaction takerSignsAndSendsTx(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
@ -454,23 +549,24 @@ public class WalletFacade
String offererAddress,
FutureCallback<Transaction> callback) throws InsufficientMoneyException, AddressFormatException
{
log.debug("takerSignsAndSendsTx");
log.trace("inputs: ");
log.trace("depositTxAsHex=" + depositTxAsHex);
log.trace("offererSignatureR=" + offererSignatureR);
log.trace("offererSignatureS=" + offererSignatureS);
log.trace("offererPaybackAmount=" + Utils.bitcoinValueToFriendlyString(offererPaybackAmount));
log.trace("takerPaybackAmount=" + Utils.bitcoinValueToFriendlyString(takerPaybackAmount));
log.trace("offererAddress=" + offererAddress);
log.trace("callback=" + callback.toString());
Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex));
TransactionOutput multiSigOutput = depositTx.getOutput(0);
// We create the payout tx
Transaction tx = createPayoutTx(depositTxAsHex, offererPaybackAmount, takerPaybackAmount, offererAddress, getTradingAddress());
// We sign that tx with our key and apply the signature form the offerer
TransactionOutput multiSigOutput = tx.getInput(0).getConnectedOutput();
Script multiSigScript = multiSigOutput.getScriptPubKey();
Transaction tx = new Transaction(params);
tx.addInput(multiSigOutput);
//TODO fee calculation
if (offererPaybackAmount.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) <= 0)
log.error("Cannot use such low value");
if (takerPaybackAmount.compareTo(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE) <= 0)
log.error("Cannot use such low value");
tx.addOutput(offererPaybackAmount.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), new Address(params, offererAddress));
tx.addOutput(takerPaybackAmount.subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE), tradingKey.toAddress(params));
Sha256Hash sigHash = tx.hashForSignature(0, multiSigScript, Transaction.SigHash.ALL, false);
log.trace("sigHash=" + sigHash.toString());
ECKey.ECDSASignature takerSignature = tradingKey.sign(sigHash);
TransactionSignature takerTxSig = new TransactionSignature(takerSignature, Transaction.SigHash.ALL, false);
@ -481,59 +577,22 @@ public class WalletFacade
Script inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(offererTxSig, takerTxSig));
tx.getInput(0).setScriptSig(inputScript);
log.info("verify tx");
log.trace("verify tx");
tx.verify();
try
{
log.info("verify correctlySpends");
log.trace("check if it can be correctly spent for ms input");
tx.getInput(0).getScriptSig().correctlySpends(tx, 0, multiSigScript, false);
} catch (Exception e)
{
log.error("verify correctlySpends 0:" + e.getMessage());
}
try
{
log.info("verify multiSigOutput");
log.trace("verify multiSigOutput");
tx.getInput(0).verify(multiSigOutput);
} catch (Exception e)
{
log.error("verify multiSigOutput 1:" + e.getMessage());
}
log.info("broadcastTransaction:" + tx.toString());
Object t = wallet.getTransactions(true);
log.info("pre broadcastTransaction broadcastTransaction:" + wallet.getTransactions(true).size());
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
FutureCallback<Transaction> localCallback = new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction transaction)
{
Object t = wallet.getTransactions(true);
log.info("onSuccess broadcastTransaction:" + wallet.getTransactions(true).size());
log.info("sendResult onSuccess:" + transaction.toString());
log.info("is in wallet?" + wallet.getTransaction(transaction.getHash()).getHashAsString());
/*if (wallet.getTransaction(transaction.getHash()) == null)
wallet.commitTx(transaction); */
log.info("now must be in wallet?" + wallet.getTransaction(transaction.getHash()).getHashAsString());
log.info("##################### now must be in wallet?" + wallet.getTransaction(transaction.getHash()).getHashAsString());
}
@Override
public void onFailure(Throwable t)
{
log.warn("sendResult onFailure:" + t.toString());
}
};
Futures.addCallback(broadcastComplete, localCallback);
Futures.addCallback(broadcastComplete, callback);
log.trace("getTransactions.size=" + wallet.getTransactions(true).size());
log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("takerSignsAndSendsTx", tx);
log.debug("tx = " + tx.toString());
return tx;
}
@ -552,114 +611,37 @@ public class WalletFacade
return ScriptBuilder.createMultiSigOutputScript(2, keys);
}
// TODO only temp. for testing trade process between offerer and taker
// deposit
private void testTradeProcessDepositTx()
public Transaction createPayoutTx(String depositTxAsHex,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String offererAddress,
String takerAddress) throws AddressFormatException
{
try
{
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex;
log.trace("createPayoutTx");
log.trace("inputs: ");
log.trace("depositTxAsHex=" + depositTxAsHex);
log.trace("offererPaybackAmount=" + Utils.bitcoinValueToFriendlyString(offererPaybackAmount));
log.trace("takerPaybackAmount=" + Utils.bitcoinValueToFriendlyString(takerPaybackAmount));
log.trace("offererAddress=" + offererAddress);
log.trace("takerAddress=" + takerAddress);
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, null);
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.
}
Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex));
TransactionOutput multiSigOutput = depositTx.getOutput(0);
Transaction tx = new Transaction(params);
tx.addInput(multiSigOutput);
tx.addOutput(offererPaybackAmount, new Address(params, offererAddress));
tx.addOutput(takerPaybackAmount, new Address(params, takerAddress));
log.trace("tx=" + tx);
return tx;
}
// payout
private void testTradeProcessPayOutTx()
private void printInputs(String tracePrefix, Transaction tx)
{
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, null);
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();
}
for (TransactionInput input : tx.getInputs())
if (input.getConnectedOutput() != null)
log.trace(tracePrefix + ": " + Utils.bitcoinValueToFriendlyString(input.getConnectedOutput().getValue()));
else
log.trace(tracePrefix + ": " + "Transaction already has inputs but we don't have the connected outputs, so we don't know the value.");
}
@ -673,14 +655,24 @@ public class WalletFacade
protected void progress(double percent, int blocksSoFar, Date date)
{
super.progress(percent, blocksSoFar, date);
for (DownloadListener downloadListener : downloadListeners)
downloadListener.progress(percent, blocksSoFar, date);
Platform.runLater(() -> onProgressInUserThread(percent, blocksSoFar, date));
}
@Override
protected void doneDownload()
{
super.doneDownload();
Platform.runLater(() -> onDoneDownloadInUserThread());
}
private void onProgressInUserThread(double percent, int blocksSoFar, final Date date)
{
for (DownloadListener downloadListener : downloadListeners)
downloadListener.progress(percent, blocksSoFar, date);
}
private void onDoneDownloadInUserThread()
{
for (DownloadListener downloadListener : downloadListeners)
downloadListener.doneDownload();
}
@ -693,11 +685,4 @@ public class WalletFacade
void doneDownload();
}
public static interface WalletListener
{
void onConfidenceChanged(int numBroadcastPeers, int depthInBlocks);
void onCoinsReceived(BigInteger newBalance);
}
}

View File

@ -17,7 +17,6 @@ import io.bitsquare.msg.TradeMessage;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Trading;
import io.bitsquare.user.User;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
@ -41,7 +40,7 @@ import java.util.Date;
import java.util.List;
import java.util.ResourceBundle;
public class MainController implements Initializable, NavigationController, WalletFacade.DownloadListener
public class MainController implements Initializable, NavigationController
{
private static final Logger log = LoggerFactory.getLogger(MainController.class);
@ -97,7 +96,21 @@ public class MainController implements Initializable, NavigationController, Wall
messageFacade.init();
walletFacade.addDownloadListener(this);
walletFacade.addDownloadListener(new WalletFacade.DownloadListener()
{
@Override
public void progress(double percent, int blocksSoFar, Date date)
{
networkSyncPane.setProgress(percent);
}
@Override
public void doneDownload()
{
networkSyncPane.doneDownload();
}
});
walletFacade.initWallet();
if (user.getAccountID() == null)
@ -183,25 +196,6 @@ public class MainController implements Initializable, NavigationController, Wall
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: WalletFacade.DownloadListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void progress(double percent, int blocksSoFar, Date date)
{
if (networkSyncPane != null)
Platform.runLater(() -> networkSyncPane.setProgress(percent));
}
@Override
public void doneDownload()
{
if (networkSyncPane != null)
Platform.runLater(networkSyncPane::doneDownload);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
@ -339,16 +333,19 @@ public class MainController implements Initializable, NavigationController, Wall
@Override
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
{
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(walletFacade.getWallet().getBalance()));
}
@Override
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
{
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(newBalance));
}
@Override
public void onReorganize(Wallet wallet)
{
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(walletFacade.getWallet().getBalance()));
}
@Override

View File

@ -94,10 +94,10 @@ public class CreateOfferController implements Initializable, ChildController
//TODO
amountTextField.setText("1");
minAmountTextField.setText("0,1");
priceTextField.setText("500");
collateralTextField.setText("10");
amountTextField.setText("0,1");
minAmountTextField.setText("0,001");
priceTextField.setText("300");
collateralTextField.setText("50");
updateVolume();
amountTextField.textProperty().addListener(new ChangeListener<String>()
@ -132,7 +132,7 @@ public class CreateOfferController implements Initializable, ChildController
bankAccountCountyTextField.setText(user.getCurrentBankAccount().getCountryLocale().getDisplayCountry());
acceptedCountriesTextField.setText(Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()));
acceptedLanguagesTextField.setText(Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales()));
feeLabel.setText(Utils.bitcoinValueToFriendlyString(Fees.OFFER_CREATION_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE)));
feeLabel.setText(Utils.bitcoinValueToFriendlyString(Fees.OFFER_CREATION_FEE));
}

View File

@ -6,6 +6,7 @@ import java.io.Serializable;
import java.math.BigInteger;
import java.util.UUID;
//TODO refactor
public class TradeMessage implements Serializable
{
private static final long serialVersionUID = 7916445031849763995L;
@ -21,6 +22,8 @@ public class TradeMessage implements Serializable
private String takerPayoutAddress;
private TradeMessageType type;
private String depositTxID;
private String depositTxAsHex;
private String offererSignatureR;
private String offererSignatureS;
private BigInteger offererPaybackAmount;
@ -94,11 +97,11 @@ public class TradeMessage implements Serializable
uid = UUID.randomUUID().toString();
}
public TradeMessage(TradeMessageType type, String offerUID, String depositTxID)
public TradeMessage(TradeMessageType type, String offerUID, String depositTxAsHex)
{
this.offerUID = offerUID;
this.type = type;
this.depositTxID = depositTxID;
this.depositTxAsHex = depositTxAsHex;
uid = UUID.randomUUID().toString();
}
@ -106,7 +109,7 @@ public class TradeMessage implements Serializable
// 3.10
public TradeMessage(TradeMessageType type, String offerUID,
String depositTxID,
String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
@ -115,7 +118,7 @@ public class TradeMessage implements Serializable
{
this.offerUID = offerUID;
this.type = type;
this.depositTxID = depositTxID;
this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount;
@ -252,4 +255,8 @@ public class TradeMessage implements Serializable
}
public String getDepositTxAsHex()
{
return depositTxAsHex;
}
}

View File

@ -25,7 +25,7 @@ import java.math.BigInteger;
import static com.google.common.base.Preconditions.checkNotNull;
//TODO refactor to process based pattern
public class OffererPaymentProtocol
{
@ -407,7 +407,7 @@ public class OffererPaymentProtocol
try
{
log.debug("3.4 offererSignAndSendTx");
depositTransaction = walletFacade.offererSignAndSendTx(preparedOffererDepositTxAsHex, signedTakerDepositTxAsHex, txConnOutAsHex, txScriptSigAsHex, callback);
depositTransaction = walletFacade.offererSignAndPublishTx(preparedOffererDepositTxAsHex, signedTakerDepositTxAsHex, txConnOutAsHex, txScriptSigAsHex, callback);
} catch (Exception e)
{
log.error("3.4 error at walletFacade.offererSignAndSendTx: " + e.getMessage());
@ -440,7 +440,7 @@ public class OffererPaymentProtocol
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed");
}
};
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getUid(), transaction.getHashAsString());
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.DEPOSIT_TX_PUBLISHED, trade.getUid(), Utils.bytesToHexString(transaction.bitcoinSerialize()));
log.debug("3.5 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
@ -554,24 +554,23 @@ public class OffererPaymentProtocol
log.debug("takerPaybackAmount " + takerPaybackAmount.toString());
log.debug("depositTransaction.getHashAsString() " + depositTransaction.getHashAsString());
log.debug("takerPayoutAddress " + takerPayoutAddress);
log.debug("walletFacade.offererCreateAndSignPayoutTx");
Pair<ECKey.ECDSASignature, Transaction> result = walletFacade.offererCreateAndSignPayoutTx(depositTransaction.getHashAsString(), offererPaybackAmount, takerPaybackAmount, takerPayoutAddress);
log.debug("walletFacade.offererCreatesAndSignsPayoutTx");
Pair<ECKey.ECDSASignature, String> result = walletFacade.offererCreatesAndSignsPayoutTx(depositTransaction.getHashAsString(), offererPaybackAmount, takerPaybackAmount, takerPayoutAddress);
ECKey.ECDSASignature offererSignature = result.getKey();
String offererSignatureR = offererSignature.r.toString();
String offererSignatureS = offererSignature.s.toString();
Transaction depositTx = result.getValue();
String depositTxID = Utils.bytesToHexString(depositTx.bitcoinSerialize());
String depositTxAsHex = result.getValue();
String offererPayoutAddress = walletFacade.getTradingAddress();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getUid(),
depositTxID,
depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererPayoutAddress);
log.debug("depositTxID " + depositTxID);
log.debug("depositTxAsHex " + depositTxAsHex);
log.debug("offererSignatureR " + offererSignatureR);
log.debug("offererSignatureS " + offererSignatureS);
log.debug("offererPaybackAmount " + offererPaybackAmount.toString());
@ -583,10 +582,10 @@ public class OffererPaymentProtocol
} catch (InsufficientMoneyException e)
{
log.error("3.10 offererCreateAndSignPayoutTx onFailed InsufficientMoneyException " + e.getMessage());
log.error("3.10 offererCreatesAndSignsPayoutTx onFailed InsufficientMoneyException " + e.getMessage());
} catch (AddressFormatException e)
{
log.error("3.10 offererCreateAndSignPayoutTx onFailed AddressFormatException " + e.getMessage());
log.error("3.10 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage());
}
}

View File

@ -29,7 +29,7 @@ import java.util.concurrent.ExecutionException;
import static com.google.common.base.Preconditions.checkNotNull;
//TODO refactor to process based pattern
public class TakerPaymentProtocol
{
private static final Logger log = LoggerFactory.getLogger(TakerPaymentProtocol.class);
@ -387,7 +387,7 @@ public class TakerPaymentProtocol
log.debug("preparedOffererDepositTxAsHex " + preparedOffererDepositTxAsHex);
try
{
Transaction signedTakerDepositTx = walletFacade.takerAddPaymentAndSign(takerInputAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, preparedOffererDepositTxAsHex);
Transaction signedTakerDepositTx = walletFacade.takerAddPaymentAndSignTx(takerInputAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, preparedOffererDepositTxAsHex);
log.debug("2.10 deposit tx created: " + signedTakerDepositTx);
sendSignedTakerDepositTxAsHex(signedTakerDepositTx);
} catch (InterruptedException | AddressFormatException | ExecutionException | InsufficientMoneyException e)
@ -469,13 +469,11 @@ public class TakerPaymentProtocol
public void onDepositTxPublished(TradeMessage tradeMessage)
{
log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxID());
//Transaction tx = walletFacade.getWallet().getTransaction(new Sha256Hash(tradeMessage.getDepositTxID()));
//walletFacade.getWallet().commitTx(tx);
log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxAsHex());
walletFacade.takerCommitDepositTx(tradeMessage.getDepositTxAsHex());
takerPaymentProtocolListener.onProgress(getProgress());
takerPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxID());
takerPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxAsHex());
}
@ -511,8 +509,8 @@ public class TakerPaymentProtocol
@Override
public void onSuccess(Transaction transaction)
{
System.out.println("######### 3.12 onSuccess walletFacade.takerSignAndSendTx " + transaction.toString());
log.error("3.12 onSuccess walletFacade.takerSignAndSendTx " + transaction.toString());
System.out.println("######### 3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction.toString());
log.debug("3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction.toString());
takerPaymentProtocolListener.onTradeCompleted(transaction.getHashAsString());
sendPayoutTxToOfferer(transaction.getHashAsString());
@ -521,22 +519,22 @@ public class TakerPaymentProtocol
@Override
public void onFailure(Throwable t)
{
log.error("######### 3.12 onFailure walletFacade.takerSignAndSendTx");
System.err.println("3.12 onFailure walletFacade.takerSignAndSendTx");
takerPaymentProtocolListener.onFailure("takerSignAndSendTx failed " + t.getMessage());
log.error("######### 3.12 onFailure walletFacade.takerSignsAndSendsTx");
System.err.println("3.12 onFailure walletFacade.takerSignsAndSendsTx");
takerPaymentProtocolListener.onFailure("takerSignsAndSendsTx failed " + t.getMessage());
}
};
try
{
String depositTxID = tradeMessage.getDepositTxID();
String depositTxAsHex = tradeMessage.getDepositTxAsHex();
String offererSignatureR = tradeMessage.getOffererSignatureR();
String offererSignatureS = tradeMessage.getOffererSignatureS();
BigInteger offererPaybackAmount = tradeMessage.getOffererPaybackAmount();
BigInteger takerPaybackAmount = tradeMessage.getTakerPaybackAmount();
String offererPayoutAddress = tradeMessage.getOffererPayoutAddress();
log.debug("3.12 walletFacade.takerSignAndSendTx");
walletFacade.takerSignAndSendTx(depositTxID,
log.debug("3.12 walletFacade.takerSignsAndSendsTx");
walletFacade.takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
@ -545,10 +543,10 @@ public class TakerPaymentProtocol
callback);
} catch (InsufficientMoneyException e)
{
log.error("3.12 offererCreateAndSignPayoutTx onFailed InsufficientMoneyException " + e.getMessage());
log.error("3.12 offererCreatesAndSignsPayoutTx onFailed InsufficientMoneyException " + e.getMessage());
} catch (AddressFormatException e)
{
log.error("3.12 offererCreateAndSignPayoutTx onFailed AddressFormatException " + e.getMessage());
log.error("3.12 offererCreatesAndSignsPayoutTx onFailed AddressFormatException " + e.getMessage());
}
}

View File

@ -13,13 +13,17 @@
<appender-ref ref="CONSOLE_APPENDER"/>
</root>
<logger name="io.bitsquare" level="DEBUG"/>
<logger name="io.bitsquare" level="TRACE"/>
<logger name="com.google.bitcoin" level="INFO"/>
<logger name="com.google.bitcoin" level="TRACE"/>
<logger name="net.tomp2p" level="WARN"/>
<!--
-->
<logger name="com.google.bitcoin.core.DownloadListener" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.TransactionOutput" level="INFO" additivity="false"/>
<logger name="com.google.bitcoin.core.BitcoinSerializer" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.Peer" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerGroup" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerSocketHandler" level="OFF" additivity="false"/>