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 // 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 MS_TX_FEE = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE;
public static BigInteger ACCOUNT_REGISTRATION_FEE = Utils.toNanoCoins("0.01"); public static BigInteger ACCOUNT_REGISTRATION_FEE = Utils.toNanoCoins("0.0002");
public static BigInteger OFFER_CREATION_FEE = Utils.toNanoCoins("0.001"); public static BigInteger OFFER_CREATION_FEE = Utils.toNanoCoins("0.0002");
public static BigInteger OFFER_TAKER_FEE = OFFER_CREATION_FEE; 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 // 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. // 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.startAsync();
walletAppKit.awaitRunning(); walletAppKit.awaitRunning();
@ -102,11 +102,12 @@ public class WalletFacade
wallet.addKey(tradingKey); wallet.addKey(tradingKey);
wallet.allowSpendingUnconfirmedTransactions(); wallet.allowSpendingUnconfirmedTransactions();
// walletAppKit.peerGroup().setMaxConnections(10); //walletAppKit.peerGroup().setMaxConnections(11);
//TODO for regtest set 1
walletAppKit.peerGroup().setMinBroadcastConnections(1);
//wallet.addWatchedAddress(tradingKey.toAddress(params)); if (params == RegTestParams.get())
walletAppKit.peerGroup().setMinBroadcastConnections(1);
/* else
walletAppKit.peerGroup().setMinBroadcastConnections(2); */
} }
public void shutDown() public void shutDown()
@ -178,24 +179,27 @@ public class WalletFacade
public void publishRegistrationTxWithExtraData(String stringifiedBankAccounts) throws InsufficientMoneyException public void publishRegistrationTxWithExtraData(String stringifiedBankAccounts) throws InsufficientMoneyException
{ {
log.debug("publishRegistrationTxWithExtraData");
log.trace("inputs: ");
log.trace("stringifiedBankAccounts " + stringifiedBankAccounts);
byte[] dataToEmbed = cryptoFacade.getEmbeddedAccountRegistrationData(registrationKey, stringifiedBankAccounts); byte[] dataToEmbed = cryptoFacade.getEmbeddedAccountRegistrationData(registrationKey, stringifiedBankAccounts);
Script script = new ScriptBuilder().op(OP_RETURN).data(dataToEmbed).build(); Script script = new ScriptBuilder().op(OP_RETURN).data(dataToEmbed).build();
Transaction transaction = new Transaction(params); Transaction tx = new Transaction(params);
TransactionOutput dataOutput = new TransactionOutput(params, transaction, Transaction.MIN_NONDUST_OUTPUT, script.getProgram()); TransactionOutput dataOutput = new TransactionOutput(params, tx, Transaction.MIN_NONDUST_OUTPUT, script.getProgram());
transaction.addOutput(dataOutput); tx.addOutput(dataOutput);
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(transaction); Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
// give fee to miners yet. Later it could be spent to other traders via lottery... // 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); Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
log.debug("Registration transaction: " + transaction.toString()); log.debug("Registration transaction: " + tx.toString());
//TODO printInputs("publishRegistrationTxWithExtraData", tx);
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>()
{ {
@Override @Override
public void onSuccess(Transaction result) public void onSuccess(Transaction result)
{ {
log.debug("sendResult onSuccess:" + result.toString()); log.debug("sendResult onSuccess");
} }
@Override @Override
@ -223,6 +227,8 @@ public class WalletFacade
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest); Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback); Futures.addCallback(sendResult.broadcastComplete, callback);
log.debug("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("payOfferFee", tx);
log.debug("tx=" + tx.toString()); log.debug("tx=" + tx.toString());
return tx.getHashAsString(); return tx.getHashAsString();
} }
@ -235,78 +241,113 @@ 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 public Transaction offererCreatesMSTxAndAddPayment(BigInteger offererInputAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
{ {
log.debug("offererCreatesMSTxAndAddPayment"); 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 pay the offererInputAmount to a temporary MS output which will be changed later to the correct value.
// we don't commit that tx to the wallet as it will be changed later and it's not signed yet // With the usage of completeTx() we get all the work done with fee calculation, validation and coin selection.
// btc ntx fee will be included by the completeTx call, so we don't add it manually // 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); Transaction tx = new Transaction(params);
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey); Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
tx.addOutput(offererInputAmount, multiSigOutputScript); tx.addOutput(offererInputAmount, multiSigOutputScript);
Wallet.SendRequest request = Wallet.SendRequest.forTx(tx); wallet.completeTx(Wallet.SendRequest.forTx(tx));
wallet.completeTx(request);
// 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.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[0] MS offererInputAmount
OUT[1] offerer change amount OUT[1] Change = input - offererInputAmount - fee
btc tx fee btc tx fee
*/ */
log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
printInputs("offererCreatesMSTxAndAddPayment", tx);
log.debug("tx = " + tx.toString()); log.debug("tx = " + tx.toString());
return tx; return tx;
} }
// Taker put his input in, change the MS amount to the correct value and sign his input // 2. step: deposit tx
public Transaction takerAddPaymentAndSign(BigInteger takerAmount, // Taker adds his input and change output, changes the multiSig amount to the correct value and sign his input
BigInteger msOutputAmount, public Transaction takerAddPaymentAndSignTx(BigInteger takerInputAmount,
String offererPubKey, BigInteger msOutputAmount,
String takerPubKey, String offererPubKey,
String arbitratorPubKey, String takerPubKey,
String offerersPartialDepositTxAsHex String arbitratorPubKey,
String offerersPartialDepositTxAsHex
) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException ) 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); 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: // The created tempTx looks like:
// 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 IN[0] any input taker > takerInputAmount + fee (signed)
// both traders pay 1 times a fee, so its equally split OUT[0] MS takerInputAmount
OUT[1] Change = input taker - takerInputAmount - fee
btc tx fee
*/
// 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 // Now we construct the real 2of3 multiSig tx from the serialized offerers tx
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(offerersPartialDepositTxAsHex)); Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(offerersPartialDepositTxAsHex));
log.trace("offerersPartialDepositTx=" + tx);
// it looks like that // The serialized offerers tx looks like:
/* /*
IN[0] offerer input IN[0] any input offerer > offererInputAmount + fee (unsigned)
OUT[0] MS offererInputAmount OUT[0] MS offererInputAmount
OUT[1] offerer change OUT[1] Change = input offerer - offererInputAmount - fee
btc tx fee btc tx fee
*/ */
// we add our inputs and outputs and change the MS amount to the correct one // Now we add the inputs and outputs from our temp tx and change the multiSig amount to the correct value
tx.addInput(dummyTx.getInput(0)); tx.addInput(tempTx.getInput(0));
tx.addOutput(dummyTx.getOutput(1)); if (tempTx.getOutputs().size() == 2)
tx.addOutput(tempTx.getOutput(1));
// we add the btc tx fee // 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); msOutputAmount = msOutputAmount.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
// and change the value of the MS output
tx.getOutput(0).setValue(msOutputAmount); tx.getOutput(0).setValue(msOutputAmount);
// Now we sign our input
TransactionInput input = tx.getInput(1); TransactionInput input = tx.getInput(1);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
@ -320,54 +361,92 @@ public class WalletFacade
else else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); 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); input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
log.debug("verify tx"); log.trace("verify tx");
tx.verify(); tx.verify();
// The resulting tx looks like:
/* /*
IN[0] offerer 0.1001 IN[0] any input offerer > offererInputAmount + fee (unsigned) e.g.: 0.1001
IN[1] taker signed 1.1001 IN[1] any input taker > takerInputAmount + fee (signed) e.g.: 1.1001
OUT[0] MS (include btc tx fee for payout tx) 1.2001 OUT[0] MS offererInputAmount e.g.: 1.2001
OUT[1] offerer change OUT[1] Change = input offerer - offererInputAmount - fee e.g.: 0 if input is matching correct value
OUT[2] taker change OUT[2] Change = input taker - takerInputAmount - fee e.g.: 0 if input is matching correct value
btc tx fee 0.0001 btc tx fee e.g.: 0.1001
*/ */
wallet.commitTx(tx); // We must not commit that tx to the wallet as we will get it over the network when the offerer
log.debug("tx" + tx.toString()); // 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; return tx;
} }
// deposit 3. offerer
public Transaction offererSignAndSendTx(String tx1AsHex, // 3. step: deposit tx
String tx2AsHex, // Offerer signs tx and publishes it
String tx2ConnOutAsHex, public Transaction offererSignAndPublishTx(String offerersFirstTxAsHex,
String tx2ScriptSigAsHex, String takersSignedTxAsHex,
FutureCallback<Transaction> callback) throws Exception 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 tx = new Transaction(params);
Transaction tx1 = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex)); // offerers first tx
Transaction tx1ConnOut = wallet.getTransaction(tx1.getInput(0).getOutpoint().getHash()); Transaction offerersFirstTx = new Transaction(params, Utils.parseAsHexOrBase58(offerersFirstTxAsHex));
TransactionOutPoint tx1OutPoint = new TransactionOutPoint(params, 1, 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)); printInputs("offerersFirstTx", offerersFirstTx);
Transaction tx2ConnOut = new Transaction(params, Utils.parseAsHexOrBase58(tx2ConnOutAsHex)); log.trace("offerersFirstTx = " + offerersFirstTx.toString());
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)); // add input
tx.addOutput(tx2.getOutput(1)); Transaction offerersFirstTxConnOut = wallet.getTransaction(offerersFirstTx.getInput(0).getOutpoint().getHash()); // pass that around!
tx.addOutput(tx2.getOutput(2)); 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); TransactionInput input = tx.getInput(0);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey(); Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
ECKey sigKey = input.getOutpoint().getConnectedKey(wallet); ECKey sigKey = input.getOutpoint().getConnectedKey(wallet);
@ -381,96 +460,113 @@ public class WalletFacade
else else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey); 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); input.getScriptSig().correctlySpends(tx, 0, scriptPubKey, false);
log.info("offererSignAndSendTx check correctlySpends input 1"); log.trace("check if it can be correctly spent for input 1");
input = tx.getInput(1); TransactionInput input1 = tx.getInput(1);
scriptPubKey = input.getConnectedOutput().getScriptPubKey(); scriptPubKey = input1.getConnectedOutput().getScriptPubKey();
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false); input1.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
/* /*
IN[0] offerer signed IN[0] offerer signed 0.1001
IN[1] taker signed IN[1] taker signed 1.1001
OUT[0] MS OUT[0] MS (include btc tx fee for payout tx) 1.2001
OUT[1] offerer change OUT[1] offerer change
OUT[2] taker change OUT[2] taker change
btc tx fee 0.0001
*/ */
log.info("offererSignAndSendTx broadcastTransaction verify ");
log.trace("verify ");
tx.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); ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
/*FutureCallback<Transaction> localCallback = new FutureCallback<Transaction>() log.trace("Wallet balance after broadcastTransaction: " + wallet.getBalance());
{ log.trace("Check if wallet is consistent: result=" + wallet.isConsistent());
@Override
public void onSuccess(Transaction transaction)
{
log.info("offererSignAndSendTx onSuccess" + transaction.getHashAsString());
}
@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); Futures.addCallback(broadcastComplete, callback);
printInputs("tx", tx);
log.debug("tx = " + tx.toString());
return tx; return tx;
} }
// payout 1. offerer // 4 step deposit tx: Offerer send deposit tx to taker
public Pair<ECKey.ECDSASignature, Transaction> offererCreateAndSignPayoutTx(String depositTxID, public void takerCommitDepositTx(String depositTxAsHex)
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
String takerAddress) throws InsufficientMoneyException, AddressFormatException
{ {
// offerer has published depositTx so he has it in wallet log.trace("takerCommitDepositTx");
Transaction depositTx = wallet.getTransaction(new Sha256Hash(depositTxID)); log.trace("inputs: ");
TransactionOutput multiSigOutput = depositTx.getOutput(0); log.trace("depositTxID=" + depositTxAsHex);
Script multiSigScript = multiSigOutput.getScriptPubKey(); Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex));
log.trace("depositTx=" + depositTx);
Transaction tx = new Transaction(params); wallet.commitTx(depositTx);
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);
return new Pair<>(signature, depositTx);
} }
// payout 2. taker // 5. step payout tx: Offerer creates payout tx and signs it
public Transaction takerSignAndSendTx(String depositTxAsHex, public Pair<ECKey.ECDSASignature, String> offererCreatesAndSignsPayoutTx(String depositTxID,
String offererSignatureR, BigInteger offererPaybackAmount,
String offererSignatureS, BigInteger takerPaybackAmount,
BigInteger offererPaybackAmount, String takerAddress) throws InsufficientMoneyException, AddressFormatException
BigInteger takerPaybackAmount,
String offererAddress,
FutureCallback<Transaction> callback) throws InsufficientMoneyException, AddressFormatException
{ {
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);
Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex)); // Offerer has published depositTx earlier, so he has it in his wallet
TransactionOutput multiSigOutput = depositTx.getOutput(0); Transaction depositTx = wallet.getTransaction(new Sha256Hash(depositTxID));
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(); 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); Sha256Hash sigHash = tx.hashForSignature(0, multiSigScript, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature offererSignature = tradingKey.sign(sigHash);
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);
}
// 6. step payout tx: Taker signs and publish tx
public Transaction takerSignsAndSendsTx(String depositTxAsHex,
String offererSignatureR,
String offererSignatureS,
BigInteger offererPaybackAmount,
BigInteger takerPaybackAmount,
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());
// 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();
Sha256Hash sigHash = tx.hashForSignature(0, multiSigScript, Transaction.SigHash.ALL, false);
log.trace("sigHash=" + sigHash.toString());
ECKey.ECDSASignature takerSignature = tradingKey.sign(sigHash); ECKey.ECDSASignature takerSignature = tradingKey.sign(sigHash);
TransactionSignature takerTxSig = new TransactionSignature(takerSignature, Transaction.SigHash.ALL, false); TransactionSignature takerTxSig = new TransactionSignature(takerSignature, Transaction.SigHash.ALL, false);
@ -481,59 +577,22 @@ public class WalletFacade
Script inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(offererTxSig, takerTxSig)); Script inputScript = ScriptBuilder.createMultiSigInputScript(ImmutableList.of(offererTxSig, takerTxSig));
tx.getInput(0).setScriptSig(inputScript); tx.getInput(0).setScriptSig(inputScript);
log.info("verify tx"); log.trace("verify tx");
tx.verify(); tx.verify();
try log.trace("check if it can be correctly spent for ms input");
{ tx.getInput(0).getScriptSig().correctlySpends(tx, 0, multiSigScript, false);
log.info("verify correctlySpends");
tx.getInput(0).getScriptSig().correctlySpends(tx, 0, multiSigScript, false);
} catch (Exception e)
{
log.error("verify correctlySpends 0:" + e.getMessage());
}
try log.trace("verify multiSigOutput");
{ tx.getInput(0).verify(multiSigOutput);
log.info("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); 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); 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; return tx;
} }
@ -552,114 +611,37 @@ public class WalletFacade
return ScriptBuilder.createMultiSigOutputScript(2, keys); return ScriptBuilder.createMultiSigOutputScript(2, keys);
} }
public Transaction createPayoutTx(String depositTxAsHex,
// TODO only temp. for testing trade process between offerer and taker BigInteger offererPaybackAmount,
// deposit BigInteger takerPaybackAmount,
private void testTradeProcessDepositTx() String offererAddress,
String takerAddress) throws AddressFormatException
{ {
try log.trace("createPayoutTx");
{ log.trace("inputs: ");
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex; 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"); Transaction depositTx = new Transaction(params, Utils.parseAsHexOrBase58(depositTxAsHex));
BigInteger takerAmount = Utils.toNanoCoins("0.02"); TransactionOutput multiSigOutput = depositTx.getOutput(0);
Transaction tx = new Transaction(params);
String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568"; tx.addInput(multiSigOutput);
String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7"; tx.addOutput(offererPaybackAmount, new Address(params, offererAddress));
String arbitratorPubKey = ""; tx.addOutput(takerPaybackAmount, new Address(params, takerAddress));
log.trace("tx=" + tx);
// 1 offerer creates MS TX and pay in return tx;
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.
}
} }
// payout private void printInputs(String tracePrefix, Transaction tx)
private void testTradeProcessPayOutTx()
{ {
String depositTxAsHex, offererSignatureR, offererSignatureS; for (TransactionInput input : tx.getInputs())
BigInteger offererPaybackAmount = Utils.toNanoCoins("0.029"); if (input.getConnectedOutput() != null)
BigInteger takerPaybackAmount = Utils.toNanoCoins("0.001"); log.trace(tracePrefix + ": " + Utils.bitcoinValueToFriendlyString(input.getConnectedOutput().getValue()));
else
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58("0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568")); log.trace(tracePrefix + ": " + "Transaction already has inputs but we don't have the connected outputs, so we don't know the value.");
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();
}
} }
@ -673,14 +655,24 @@ public class WalletFacade
protected void progress(double percent, int blocksSoFar, Date date) protected void progress(double percent, int blocksSoFar, Date date)
{ {
super.progress(percent, blocksSoFar, date); super.progress(percent, blocksSoFar, date);
for (DownloadListener downloadListener : downloadListeners) Platform.runLater(() -> onProgressInUserThread(percent, blocksSoFar, date));
downloadListener.progress(percent, blocksSoFar, date);
} }
@Override @Override
protected void doneDownload() protected void doneDownload()
{ {
super.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) for (DownloadListener downloadListener : downloadListeners)
downloadListener.doneDownload(); downloadListener.doneDownload();
} }
@ -693,11 +685,4 @@ public class WalletFacade
void doneDownload(); 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.Direction;
import io.bitsquare.trade.Trading; import io.bitsquare.trade.Trading;
import io.bitsquare.user.User; import io.bitsquare.user.User;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
@ -41,7 +40,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; 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); private static final Logger log = LoggerFactory.getLogger(MainController.class);
@ -97,7 +96,21 @@ public class MainController implements Initializable, NavigationController, Wall
messageFacade.init(); 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(); walletFacade.initWallet();
if (user.getAccountID() == null) 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 // Private methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -339,16 +333,19 @@ public class MainController implements Initializable, NavigationController, Wall
@Override @Override
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx) public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
{ {
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(walletFacade.getWallet().getBalance()));
} }
@Override @Override
public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance) public void onCoinsSent(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
{ {
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(newBalance));
} }
@Override @Override
public void onReorganize(Wallet wallet) public void onReorganize(Wallet wallet)
{ {
balanceTextField.setText(Utils.bitcoinValueToFriendlyString(walletFacade.getWallet().getBalance()));
} }
@Override @Override

View File

@ -94,10 +94,10 @@ public class CreateOfferController implements Initializable, ChildController
//TODO //TODO
amountTextField.setText("1"); amountTextField.setText("0,1");
minAmountTextField.setText("0,1"); minAmountTextField.setText("0,001");
priceTextField.setText("500"); priceTextField.setText("300");
collateralTextField.setText("10"); collateralTextField.setText("50");
updateVolume(); updateVolume();
amountTextField.textProperty().addListener(new ChangeListener<String>() amountTextField.textProperty().addListener(new ChangeListener<String>()
@ -132,7 +132,7 @@ public class CreateOfferController implements Initializable, ChildController
bankAccountCountyTextField.setText(user.getCurrentBankAccount().getCountryLocale().getDisplayCountry()); bankAccountCountyTextField.setText(user.getCurrentBankAccount().getCountryLocale().getDisplayCountry());
acceptedCountriesTextField.setText(Formatter.countryLocalesToString(settings.getAcceptedCountryLocales())); acceptedCountriesTextField.setText(Formatter.countryLocalesToString(settings.getAcceptedCountryLocales()));
acceptedLanguagesTextField.setText(Formatter.languageLocalesToString(settings.getAcceptedLanguageLocales())); 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.math.BigInteger;
import java.util.UUID; import java.util.UUID;
//TODO refactor
public class TradeMessage implements Serializable public class TradeMessage implements Serializable
{ {
private static final long serialVersionUID = 7916445031849763995L; private static final long serialVersionUID = 7916445031849763995L;
@ -21,6 +22,8 @@ public class TradeMessage implements Serializable
private String takerPayoutAddress; private String takerPayoutAddress;
private TradeMessageType type; private TradeMessageType type;
private String depositTxID; private String depositTxID;
private String depositTxAsHex;
private String offererSignatureR; private String offererSignatureR;
private String offererSignatureS; private String offererSignatureS;
private BigInteger offererPaybackAmount; private BigInteger offererPaybackAmount;
@ -94,11 +97,11 @@ public class TradeMessage implements Serializable
uid = UUID.randomUUID().toString(); uid = UUID.randomUUID().toString();
} }
public TradeMessage(TradeMessageType type, String offerUID, String depositTxID) public TradeMessage(TradeMessageType type, String offerUID, String depositTxAsHex)
{ {
this.offerUID = offerUID; this.offerUID = offerUID;
this.type = type; this.type = type;
this.depositTxID = depositTxID; this.depositTxAsHex = depositTxAsHex;
uid = UUID.randomUUID().toString(); uid = UUID.randomUUID().toString();
} }
@ -106,7 +109,7 @@ public class TradeMessage implements Serializable
// 3.10 // 3.10
public TradeMessage(TradeMessageType type, String offerUID, public TradeMessage(TradeMessageType type, String offerUID,
String depositTxID, String depositTxAsHex,
String offererSignatureR, String offererSignatureR,
String offererSignatureS, String offererSignatureS,
BigInteger offererPaybackAmount, BigInteger offererPaybackAmount,
@ -115,7 +118,7 @@ public class TradeMessage implements Serializable
{ {
this.offerUID = offerUID; this.offerUID = offerUID;
this.type = type; this.type = type;
this.depositTxID = depositTxID; this.depositTxAsHex = depositTxAsHex;
this.offererSignatureR = offererSignatureR; this.offererSignatureR = offererSignatureR;
this.offererSignatureS = offererSignatureS; this.offererSignatureS = offererSignatureS;
this.offererPaybackAmount = offererPaybackAmount; 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; import static com.google.common.base.Preconditions.checkNotNull;
//TODO refactor to process based pattern
public class OffererPaymentProtocol public class OffererPaymentProtocol
{ {
@ -407,7 +407,7 @@ public class OffererPaymentProtocol
try try
{ {
log.debug("3.4 offererSignAndSendTx"); log.debug("3.4 offererSignAndSendTx");
depositTransaction = walletFacade.offererSignAndSendTx(preparedOffererDepositTxAsHex, signedTakerDepositTxAsHex, txConnOutAsHex, txScriptSigAsHex, callback); depositTransaction = walletFacade.offererSignAndPublishTx(preparedOffererDepositTxAsHex, signedTakerDepositTxAsHex, txConnOutAsHex, txScriptSigAsHex, callback);
} catch (Exception e) } catch (Exception e)
{ {
log.error("3.4 error at walletFacade.offererSignAndSendTx: " + e.getMessage()); log.error("3.4 error at walletFacade.offererSignAndSendTx: " + e.getMessage());
@ -440,7 +440,7 @@ public class OffererPaymentProtocol
offererPaymentProtocolListener.onFailure("sendDepositTxAndDataForContract onSendTradingMessageFailed"); 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"); log.debug("3.5 sendTradingMessage");
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener); messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
@ -554,24 +554,23 @@ public class OffererPaymentProtocol
log.debug("takerPaybackAmount " + takerPaybackAmount.toString()); log.debug("takerPaybackAmount " + takerPaybackAmount.toString());
log.debug("depositTransaction.getHashAsString() " + depositTransaction.getHashAsString()); log.debug("depositTransaction.getHashAsString() " + depositTransaction.getHashAsString());
log.debug("takerPayoutAddress " + takerPayoutAddress); log.debug("takerPayoutAddress " + takerPayoutAddress);
log.debug("walletFacade.offererCreateAndSignPayoutTx"); log.debug("walletFacade.offererCreatesAndSignsPayoutTx");
Pair<ECKey.ECDSASignature, Transaction> result = walletFacade.offererCreateAndSignPayoutTx(depositTransaction.getHashAsString(), offererPaybackAmount, takerPaybackAmount, takerPayoutAddress); Pair<ECKey.ECDSASignature, String> result = walletFacade.offererCreatesAndSignsPayoutTx(depositTransaction.getHashAsString(), offererPaybackAmount, takerPaybackAmount, takerPayoutAddress);
ECKey.ECDSASignature offererSignature = result.getKey(); ECKey.ECDSASignature offererSignature = result.getKey();
String offererSignatureR = offererSignature.r.toString(); String offererSignatureR = offererSignature.r.toString();
String offererSignatureS = offererSignature.s.toString(); String offererSignatureS = offererSignature.s.toString();
Transaction depositTx = result.getValue(); String depositTxAsHex = result.getValue();
String depositTxID = Utils.bytesToHexString(depositTx.bitcoinSerialize());
String offererPayoutAddress = walletFacade.getTradingAddress(); String offererPayoutAddress = walletFacade.getTradingAddress();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getUid(), TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getUid(),
depositTxID, depositTxAsHex,
offererSignatureR, offererSignatureR,
offererSignatureS, offererSignatureS,
offererPaybackAmount, offererPaybackAmount,
takerPaybackAmount, takerPaybackAmount,
offererPayoutAddress); offererPayoutAddress);
log.debug("depositTxID " + depositTxID); log.debug("depositTxAsHex " + depositTxAsHex);
log.debug("offererSignatureR " + offererSignatureR); log.debug("offererSignatureR " + offererSignatureR);
log.debug("offererSignatureS " + offererSignatureS); log.debug("offererSignatureS " + offererSignatureS);
log.debug("offererPaybackAmount " + offererPaybackAmount.toString()); log.debug("offererPaybackAmount " + offererPaybackAmount.toString());
@ -583,10 +582,10 @@ public class OffererPaymentProtocol
} catch (InsufficientMoneyException e) } catch (InsufficientMoneyException e)
{ {
log.error("3.10 offererCreateAndSignPayoutTx onFailed InsufficientMoneyException " + e.getMessage()); log.error("3.10 offererCreatesAndSignsPayoutTx onFailed InsufficientMoneyException " + e.getMessage());
} catch (AddressFormatException e) } 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; import static com.google.common.base.Preconditions.checkNotNull;
//TODO refactor to process based pattern
public class TakerPaymentProtocol public class TakerPaymentProtocol
{ {
private static final Logger log = LoggerFactory.getLogger(TakerPaymentProtocol.class); private static final Logger log = LoggerFactory.getLogger(TakerPaymentProtocol.class);
@ -387,7 +387,7 @@ public class TakerPaymentProtocol
log.debug("preparedOffererDepositTxAsHex " + preparedOffererDepositTxAsHex); log.debug("preparedOffererDepositTxAsHex " + preparedOffererDepositTxAsHex);
try 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); log.debug("2.10 deposit tx created: " + signedTakerDepositTx);
sendSignedTakerDepositTxAsHex(signedTakerDepositTx); sendSignedTakerDepositTxAsHex(signedTakerDepositTx);
} catch (InterruptedException | AddressFormatException | ExecutionException | InsufficientMoneyException e) } catch (InterruptedException | AddressFormatException | ExecutionException | InsufficientMoneyException e)
@ -469,13 +469,11 @@ public class TakerPaymentProtocol
public void onDepositTxPublished(TradeMessage tradeMessage) public void onDepositTxPublished(TradeMessage tradeMessage)
{ {
log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxID()); log.debug("3.6 DepositTxID received: " + tradeMessage.getDepositTxAsHex());
//Transaction tx = walletFacade.getWallet().getTransaction(new Sha256Hash(tradeMessage.getDepositTxID()));
//walletFacade.getWallet().commitTx(tx);
walletFacade.takerCommitDepositTx(tradeMessage.getDepositTxAsHex());
takerPaymentProtocolListener.onProgress(getProgress()); takerPaymentProtocolListener.onProgress(getProgress());
takerPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxID()); takerPaymentProtocolListener.onDepositTxPublished(tradeMessage.getDepositTxAsHex());
} }
@ -511,8 +509,8 @@ public class TakerPaymentProtocol
@Override @Override
public void onSuccess(Transaction transaction) public void onSuccess(Transaction transaction)
{ {
System.out.println("######### 3.12 onSuccess walletFacade.takerSignAndSendTx " + transaction.toString()); System.out.println("######### 3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction.toString());
log.error("3.12 onSuccess walletFacade.takerSignAndSendTx " + transaction.toString()); log.debug("3.12 onSuccess walletFacade.takerSignsAndSendsTx " + transaction.toString());
takerPaymentProtocolListener.onTradeCompleted(transaction.getHashAsString()); takerPaymentProtocolListener.onTradeCompleted(transaction.getHashAsString());
sendPayoutTxToOfferer(transaction.getHashAsString()); sendPayoutTxToOfferer(transaction.getHashAsString());
@ -521,22 +519,22 @@ public class TakerPaymentProtocol
@Override @Override
public void onFailure(Throwable t) public void onFailure(Throwable t)
{ {
log.error("######### 3.12 onFailure walletFacade.takerSignAndSendTx"); log.error("######### 3.12 onFailure walletFacade.takerSignsAndSendsTx");
System.err.println("3.12 onFailure walletFacade.takerSignAndSendTx"); System.err.println("3.12 onFailure walletFacade.takerSignsAndSendsTx");
takerPaymentProtocolListener.onFailure("takerSignAndSendTx failed " + t.getMessage()); takerPaymentProtocolListener.onFailure("takerSignsAndSendsTx failed " + t.getMessage());
} }
}; };
try try
{ {
String depositTxID = tradeMessage.getDepositTxID(); String depositTxAsHex = tradeMessage.getDepositTxAsHex();
String offererSignatureR = tradeMessage.getOffererSignatureR(); String offererSignatureR = tradeMessage.getOffererSignatureR();
String offererSignatureS = tradeMessage.getOffererSignatureS(); String offererSignatureS = tradeMessage.getOffererSignatureS();
BigInteger offererPaybackAmount = tradeMessage.getOffererPaybackAmount(); BigInteger offererPaybackAmount = tradeMessage.getOffererPaybackAmount();
BigInteger takerPaybackAmount = tradeMessage.getTakerPaybackAmount(); BigInteger takerPaybackAmount = tradeMessage.getTakerPaybackAmount();
String offererPayoutAddress = tradeMessage.getOffererPayoutAddress(); String offererPayoutAddress = tradeMessage.getOffererPayoutAddress();
log.debug("3.12 walletFacade.takerSignAndSendTx"); log.debug("3.12 walletFacade.takerSignsAndSendsTx");
walletFacade.takerSignAndSendTx(depositTxID, walletFacade.takerSignsAndSendsTx(depositTxAsHex,
offererSignatureR, offererSignatureR,
offererSignatureS, offererSignatureS,
offererPaybackAmount, offererPaybackAmount,
@ -545,10 +543,10 @@ public class TakerPaymentProtocol
callback); callback);
} catch (InsufficientMoneyException e) } catch (InsufficientMoneyException e)
{ {
log.error("3.12 offererCreateAndSignPayoutTx onFailed InsufficientMoneyException " + e.getMessage()); log.error("3.12 offererCreatesAndSignsPayoutTx onFailed InsufficientMoneyException " + e.getMessage());
} catch (AddressFormatException e) } 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"/> <appender-ref ref="CONSOLE_APPENDER"/>
</root> </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="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.Peer" level="ERROR" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerGroup" 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"/> <logger name="com.google.bitcoin.core.PeerSocketHandler" level="OFF" additivity="false"/>