payment process cleanup

This commit is contained in:
Manfred Karrer 2014-05-17 11:08:22 +02:00
parent a508f95582
commit 5d9d7a9a3d
6 changed files with 261 additions and 471 deletions

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").subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
public static BigInteger OFFER_CREATION_FEE = Utils.toNanoCoins("0.001").subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(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 OFFER_TAKER_FEE = OFFER_CREATION_FEE;
}

View File

@ -28,12 +28,7 @@ import java.util.concurrent.ExecutionException;
import static com.google.bitcoin.script.ScriptOpCodes.OP_RETURN;
/**
* That facade delivers wallet functionality from the bitcoinJ library
* Code from BitcoinJ must not be used outside that facade.
* That way a change of the library will only affect that class.
*/
public class WalletFacade implements WalletEventListener
public class WalletFacade
{
public static final String MAIN_NET = "MAIN_NET";
public static final String TEST_NET = "TEST_NET";
@ -41,22 +36,13 @@ public class WalletFacade implements WalletEventListener
public static String WALLET_PREFIX = BitSquare.ID;
// for testing trade process between offerer and taker
//public static String WALLET_PREFIX = "offerer"; // offerer
//public static String WALLET_PREFIX = "taker"; // offerer
private static final Logger log = LoggerFactory.getLogger(WalletFacade.class);
private NetworkParameters params;
private WalletAppKit walletAppKit;
private CryptoFacade cryptoFacade;
// that wallet is used only for the registration process
//private AccountRegistrationWallet accountRegistrationWallet = null;
private List<DownloadListener> downloadListeners = new ArrayList<>();
private List<WalletListener> walletListeners = new ArrayList<>();
private Wallet wallet;
private ECKey registrationKey;
@ -115,30 +101,20 @@ public class WalletFacade implements WalletEventListener
tradingKey = new ECKey();
wallet.addKey(tradingKey);
log.info("getConnectedPeers " + walletAppKit.peerGroup().getConnectedPeers().size());
wallet.allowSpendingUnconfirmedTransactions();
// walletAppKit.peerGroup().setMaxConnections(10);
//TODO for regtest set 1
walletAppKit.peerGroup().setMinBroadcastConnections(1);
wallet.addEventListener(this);
wallet.addWatchedAddress(tradingKey.toAddress(params));
// testTradeProcessDepositTx();
// testTradeProcessPayOutTx();
//wallet.addWatchedAddress(tradingKey.toAddress(params));
}
public void shutDown()
{
/*if (accountRegistrationWallet != null)
accountRegistrationWallet.shutDown(); */
walletAppKit.stopAsync();
walletAppKit.awaitTerminated();
}
public void updateWallet()
{
}
///////////////////////////////////////////////////////////////////////////////////////////
// Listener
@ -154,26 +130,6 @@ public class WalletFacade implements WalletEventListener
downloadListeners.remove(listener);
}
/* public void addWalletListener(WalletListener listener)
{
walletListeners.add(listener);
}
public void removeWalletListener(WalletListener listener)
{
walletListeners.remove(listener);
}
public void addRegistrationWalletListener(WalletListener listener)
{
getAccountRegistrationWallet().addWalletListener(listener);
}
public void removeRegistrationWalletListener(WalletListener listener)
{
getAccountRegistrationWallet().removeWalletListener(listener);
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Trading wallet
@ -184,25 +140,70 @@ public class WalletFacade implements WalletEventListener
return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
}
public String getAddressAsString()
public String getTradingAddress()
{
return tradingKey.toAddress(params).toString();
}
public String payOfferFee(BigInteger fee, FutureCallback<Transaction> callback) throws InsufficientMoneyException
public Wallet getWallet()
{
Transaction tx = new Transaction(params);
//TransactionOutput output = new TransactionOutput(params, tx, Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript());
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
return wallet;
}
public String getPubKeyAsHex()
{
return Utils.bytesToHexString(tradingKey.getPubKey());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Account registration
///////////////////////////////////////////////////////////////////////////////////////////
public Address getRegistrationAddress()
{
return registrationKey.toAddress(params);
}
public ECKey getRegistrationKey()
{
return registrationKey;
}
//TODO assume for now that there is only reg address funding, so total wallet balance = reg. balance
public BigInteger getRegistrationBalance()
{
return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
}
public void publishRegistrationTxWithExtraData(String stringifiedBankAccounts) throws InsufficientMoneyException
{
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);
// give fee to miners yet. Later it could be spent to other traders via lottery...
sendRequest.fee = fee;
sendRequest.fee = Fees.ACCOUNT_REGISTRATION_FEE;
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback);
log.debug("Registration transaction: " + transaction.toString());
//TODO
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>()
{
@Override
public void onSuccess(Transaction result)
{
log.debug("sendResult onSuccess:" + result.toString());
}
return tx.getHashAsString();
@Override
public void onFailure(Throwable t)
{
log.error("sendResult onFailure:" + t.toString());
}
});
}
@ -210,6 +211,22 @@ public class WalletFacade implements WalletEventListener
// Trade process
///////////////////////////////////////////////////////////////////////////////////////////
public String payOfferFee(BigInteger fee, FutureCallback<Transaction> callback) throws InsufficientMoneyException
{
log.debug("payOfferFee fee=" + Utils.bitcoinValueToFriendlyString(fee));
Transaction tx = new Transaction(params);
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, WalletUtil.getEmptyOP_RETURNScript());
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
sendRequest.fee = fee.subtract(Transaction.MIN_NONDUST_OUTPUT).subtract(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
Futures.addCallback(sendResult.broadcastComplete, callback);
log.debug("tx=" + tx.toString());
return tx.getHashAsString();
}
public int getNumOfPeersSeenTx(String txID)
{
// TODO check from blockchain
@ -217,344 +234,78 @@ public class WalletFacade implements WalletEventListener
return 3;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Account registration
///////////////////////////////////////////////////////////////////////////////////////////
public Address getAccountRegistrationAddress()
{
return registrationKey.toAddress(params);
}
/* public String getAccountRegistrationPubKey()
{
return Utils.bytesToHexString(getAccountRegistrationKey().getPubKey());
} */
public BigInteger getAccountRegistrationBalance()
{
return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
// return getAccountRegistrationWallet().getBalance(Wallet.BalanceType.ESTIMATED);
}
public void sendRegistrationTx(String stringifiedBankAccounts) throws InsufficientMoneyException
{
saveToBlockchain(cryptoFacade.getEmbeddedAccountRegistrationData(registrationKey, stringifiedBankAccounts));
}
public ECKey getRegistrationKey()
{
return registrationKey;
}
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 = null;
sendResult = wallet.sendCoins(sendRequest);
log.info("Registration transaction: " + transaction.toString());
//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();
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter
///////////////////////////////////////////////////////////////////////////////////////////
/*public int getRegConfNumBroadcastPeers()
{
return getAccountRegistrationWallet().getConfNumBroadcastPeers();
}
public int getRegConfDepthInBlocks()
{
return WalletUtil.getConfDepthInBlocks(getAccountRegistrationWallet());
} */
public Wallet getWallet()
{
return wallet;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Interface implementation: WalletEventListener
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void onCoinsReceived(Wallet wallet, Transaction tx, BigInteger prevBalance, BigInteger newBalance)
{
for (WalletListener walletListener : walletListeners)
walletListener.onCoinsReceived(newBalance);
// log.info("onCoinsReceived");
}
@Override
public void onTransactionConfidenceChanged(Wallet wallet, Transaction tx)
{
for (WalletListener walletListener : walletListeners)
walletListener.onConfidenceChanged(tx.getConfidence().numBroadcastPeers(), WalletUtil.getConfDepthInBlocks(wallet));
// 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");
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
/* public AccountRegistrationWallet getAccountRegistrationWallet()
{
if (accountRegistrationWallet == null)
accountRegistrationWallet = new AccountRegistrationWallet(params, walletAppKit.chain(), walletAppKit.peerGroup());
return accountRegistrationWallet;
}*/
private Script getMultiSigScript(String offererPubKey, String takerPubKey, String arbitratorPubKey)
{
ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58(offererPubKey));
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58(takerPubKey));
ECKey arbitratorKey = new ECKey(null, Utils.parseAsHexOrBase58(arbitratorPubKey));
List<ECKey> keys = ImmutableList.of(offererKey, takerKey, arbitratorKey);
return ScriptBuilder.createMultiSigOutputScript(2, keys);
}
///////////////////////////////////////////////////////////////////////////////////////////
// testTradeProcess methods
///////////////////////////////////////////////////////////////////////////////////////////
// TODO only temp. for testing trade process between offerer and taker
// deposit
private void testTradeProcessDepositTx()
{
try
{
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex;
BigInteger offererAmount = Utils.toNanoCoins("0.01");
BigInteger takerAmount = Utils.toNanoCoins("0.02");
String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7";
String arbitratorPubKey = "";
// 1 offerer creates MS TX and pay in
Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey);
tx1AsHex = Utils.bytesToHexString(tx1.bitcoinSerialize());
tx1AsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffff0240420f00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac00000000";
// 2. taker pay in and sign
/* Transaction tx2 = takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, tx1AsHex);
tx2AsHex = Utils.bytesToHexString(tx2.bitcoinSerialize());
tx2ScriptSigAsHex = Utils.bytesToHexString(tx2.getInput(1).getScriptBytes());
tx2ConnOutAsHex = Utils.bytesToHexString(tx2.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
*/
tx2AsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
tx2ScriptSigAsHex = "47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
tx2ConnOutAsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0010000006a473044022011431387fc19b093b26a6d2371995c828179aae68e94ad5804e5d0986a6b471302206abc2b698375620e65fc9970b7781da0af2179d1bdc4ebc82a13e285359a3ce7012103c7b9e9ef657705522c85b8429bb2b42c04f0fd4a09e0605cd7dd62ffecb57944ffffffff02c0ce823e000000001976a9142d1b4347ae850805f3badbb4b2949674f46c4ccd88ac00e1f505000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
// 3. offerer sign and send
Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex, 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 testTradeProcessPayOutTx()
{
String depositTxAsHex, offererSignatureR, offererSignatureS;
BigInteger offererPaybackAmount = Utils.toNanoCoins("0.029");
BigInteger takerPaybackAmount = Utils.toNanoCoins("0.001");
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58("0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568"));
String takerAddress = takerKey.toAddress(params).toString();
ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58("0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7"));
String offererAddress = offererKey.toAddress(params).toString();
String depositTxID = "4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9";
try
{
// 1. offerer create and sign payout tx
/* Pair<ECKey.ECDSASignature, Transaction> result = offererCreateAndSignPayoutTx(depositTxID,
offererPaybackAmount,
takerPaybackAmount,
takerAddress);
ECKey.ECDSASignature offererSignature = result.getKey();
Transaction depositTx = result.getValue();
offererSignatureR = offererSignature.r.toString();
offererSignatureS = offererSignature.s.toString();
depositTxAsHex = Utils.bytesToHexString(depositTx.bitcoinSerialize()); */
depositTxAsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006a473044022008addf33a37f8f058e629020d73c3873953e1eca559fcb59d9db651f2c9b524f02203cbfd319b2675974adfbff2cb605fc9b89a92f2f3197ee634b39c892fb57d1be01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
offererSignatureR = "64562406184784382000330465585294975882418886289667123258365244289311131050102";
offererSignatureS = "9282449224979858852843663340827968850695102089943078462704407873774672151491";
// 2. taker sign and publish tx
Transaction takerTx = takerSignAndSendTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererAddress, 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();
}
}
public String getMultiSigPubKeyAsHex()
{
return Utils.bytesToHexString(tradingKey.getPubKey());
}
// deposit 1. offerer
// Offerer creates deposit tx with his input and change output, the MS has just a dummy value at the moment
public Transaction offererCreatesMSTxAndAddPayment(BigInteger offererInputAmount, String offererPubKey, String takerPubKey, String arbitratorPubKey) throws InsufficientMoneyException
{
log.debug("offererCreatesMSTxAndAddPayment");
// use that to use the convenient api for getting the best coin selection and fee calculation
// TODO should be constructed manually
// 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
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);
// TODO remove sig or use SigHash.NONE
//tx.getInput(0).setScriptSig(null);
// we remove the signature to make sure the tx is invalid to publish
tx.getInput(0).setScriptSig(new Script(new byte[]{}));
// tx looks like:
/*
IN[0] offerer
OUT[0] MS
OUT[1] offerer change
IN[0]
OUT[0] MS offererInputAmount
OUT[1] offerer change amount
btc tx fee
*/
log.debug("offererCreatesMSTxAndAddPayment tx = " + tx.toString());
log.debug("tx = " + tx.toString());
return tx;
}
// deposit 2. taker
// Taker put his input in, change the MS amount to the correct value and sign his input
public Transaction takerAddPaymentAndSign(BigInteger takerAmount,
BigInteger msOutputAmount,
String offererPubKey,
String takerPubKey,
String arbitratorPubKey,
String tx1AsHex
String offerersPartialDepositTxAsHex
) throws InsufficientMoneyException, ExecutionException, InterruptedException, AddressFormatException
{
log.debug("takerAddPaymentAndSign");
Script multiSigOutputScript = getMultiSigScript(offererPubKey, takerPubKey, arbitratorPubKey);
// use that to use the convenient api for getting the best coin selection and fee calculation
// TODO should be constructed manually
// 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));
wallet.completeTx(Wallet.SendRequest.forTx(dummyTx)); // it is signed but we don't care as it will not be used further
Transaction tx = new Transaction(params, Utils.parseAsHexOrBase58(tx1AsHex));
// 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
/*
IN[0] offerer input
OUT[0] MS offererInputAmount
OUT[1] offerer change
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));
tx.getOutput(0).setValue(msOutputAmount);
log.debug("tx" + tx.toString());
// we add the btc tx fee
msOutputAmount = msOutputAmount.add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
// and change the value of the MS output
tx.getOutput(0).setValue(msOutputAmount);
TransactionInput input = tx.getInput(1);
Script scriptPubKey = input.getConnectedOutput().getScriptPubKey();
@ -569,18 +320,23 @@ public class WalletFacade implements WalletEventListener
else
throw new ScriptException("Don't know how to sign for this kind of scriptPubKey: " + scriptPubKey);
log.debug("correctlySpends?");
log.debug("check if correctly spends");
input.getScriptSig().correctlySpends(tx, 1, scriptPubKey, false);
log.debug("verify tx");
tx.verify();
/*
IN[0] offerer
IN[1] taker signed
OUT[0] MS
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
*/
// wallet.commitTx(tx);
wallet.commitTx(tx);
log.debug("tx" + tx.toString());
return tx;
}
@ -751,43 +507,6 @@ public class WalletFacade implements WalletEventListener
log.info("pre broadcastTransaction broadcastTransaction:" + wallet.getTransactions(true).size());
ListenableFuture<Transaction> broadcastComplete = walletAppKit.peerGroup().broadcastTransaction(tx);
/* final TransactionBroadcast broadcast = new TransactionBroadcast(walletAppKit.peerGroup(), tx);
broadcast.setMinConnections(1);
Futures.addCallback(broadcast.future(), new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction transaction) {
// OK, now tell the wallet about the transaction. If the wallet created the transaction then
// it already knows and will ignore this. If it's a transaction we received from
// somebody else via a side channel and are now broadcasting, this will put it into the
// wallet now we know it's valid.
for (Wallet wallet : wallets) {
// Assumption here is there are no dependencies of the created transaction.
//
// We may end up with two threads trying to do this in parallel - the wallet will
// ignore whichever one loses the race.
try {
wallet.receivePending(transaction, null);
} catch (VerificationException e) {
throw new RuntimeException(e); // Cannot fail to verify a tx we created ourselves.
}
}
}
@Override
public void onFailure(Throwable throwable) {
// This can't happen with the current code, but just in case one day that changes ...
// runningBroadcasts.remove(broadcast);
throw new RuntimeException(throwable);
}
}); */
// Keep a reference to the TransactionBroadcast object. This is important because otherwise, the entire tree
// of objects we just created would become garbage if the user doens't hold on to the returned future, and
// eventually be collected. This in turn could result in the transaction not being committed to the wallet
// at all.
// runningBroadcasts.add(broadcast);
// broadcast.broadcast();
FutureCallback<Transaction> localCallback = new FutureCallback<Transaction>()
{
@Override
@ -819,48 +538,130 @@ public class WalletFacade implements WalletEventListener
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
///////////////////////////////////////////////////////////////////////////////////////////
private Script getMultiSigScript(String offererPubKey, String takerPubKey, String arbitratorPubKey)
{
ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58(offererPubKey));
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58(takerPubKey));
ECKey arbitratorKey = new ECKey(null, Utils.parseAsHexOrBase58(arbitratorPubKey));
List<ECKey> keys = ImmutableList.of(offererKey, takerKey, arbitratorKey);
return ScriptBuilder.createMultiSigOutputScript(2, keys);
}
// TODO only temp. for testing trade process between offerer and taker
// deposit
private void testTradeProcessDepositTx()
{
try
{
String tx1AsHex, tx2AsHex, tx2ScriptSigAsHex, tx2ConnOutAsHex;
BigInteger offererAmount = Utils.toNanoCoins("0.01");
BigInteger takerAmount = Utils.toNanoCoins("0.02");
String takerPubKey = "0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
String offererPubKey = "0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7";
String arbitratorPubKey = "";
// 1 offerer creates MS TX and pay in
Transaction tx1 = offererCreatesMSTxAndAddPayment(offererAmount, offererPubKey, takerPubKey, arbitratorPubKey);
tx1AsHex = Utils.bytesToHexString(tx1.bitcoinSerialize());
tx1AsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffff0240420f00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac00000000";
// 2. taker pay in and sign
/* Transaction tx2 = takerAddPaymentAndSign(takerAmount, msOutputAmount, offererPubKey, takerPubKey, arbitratorPubKey, tx1AsHex);
tx2AsHex = Utils.bytesToHexString(tx2.bitcoinSerialize());
tx2ScriptSigAsHex = Utils.bytesToHexString(tx2.getInput(1).getScriptBytes());
tx2ConnOutAsHex = Utils.bytesToHexString(tx2.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
*/
tx2AsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006b4830450221008e599dd7bb7223c7b036869198b14f08009f9bc117709d23c249d0bdd6b483be022047be181f467782ea277b36890feb2f6de3ceddcedf8730a9f505bac36b3b015b01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
tx2ScriptSigAsHex = "47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568";
tx2ConnOutAsHex = "01000000014378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0010000006a473044022011431387fc19b093b26a6d2371995c828179aae68e94ad5804e5d0986a6b471302206abc2b698375620e65fc9970b7781da0af2179d1bdc4ebc82a13e285359a3ce7012103c7b9e9ef657705522c85b8429bb2b42c04f0fd4a09e0605cd7dd62ffecb57944ffffffff02c0ce823e000000001976a9142d1b4347ae850805f3badbb4b2949674f46c4ccd88ac00e1f505000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
// 3. offerer sign and send
Transaction tx3 = offererSignAndSendTx(tx1AsHex, tx2AsHex, tx2ConnOutAsHex, tx2ScriptSigAsHex, 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 testTradeProcessPayOutTx()
{
String depositTxAsHex, offererSignatureR, offererSignatureS;
BigInteger offererPaybackAmount = Utils.toNanoCoins("0.029");
BigInteger takerPaybackAmount = Utils.toNanoCoins("0.001");
ECKey takerKey = new ECKey(null, Utils.parseAsHexOrBase58("0207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568"));
String takerAddress = takerKey.toAddress(params).toString();
ECKey offererKey = new ECKey(null, Utils.parseAsHexOrBase58("0352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7"));
String offererAddress = offererKey.toAddress(params).toString();
String depositTxID = "4142ee4877eb116abf955a7ec6ef2dc38133b793df762b76d75e3d7d4d8badc9";
try
{
// 1. offerer create and sign payout tx
/* Pair<ECKey.ECDSASignature, Transaction> result = offererCreateAndSignPayoutTx(depositTxID,
offererPaybackAmount,
takerPaybackAmount,
takerAddress);
ECKey.ECDSASignature offererSignature = result.getKey();
Transaction depositTx = result.getValue();
offererSignatureR = offererSignature.r.toString();
offererSignatureS = offererSignature.s.toString();
depositTxAsHex = Utils.bytesToHexString(depositTx.bitcoinSerialize()); */
depositTxAsHex = "01000000024378dfcd19add18eb6f118a1e35ced127ff23c9dc5034eee1cda5b9caeb814f0000000006a473044022008addf33a37f8f058e629020d73c3873953e1eca559fcb59d9db651f2c9b524f02203cbfd319b2675974adfbff2cb605fc9b89a92f2f3197ee634b39c892fb57d1be01210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7ffffffffa58b22a93a0fcf99ba48aa3b96d842284b2b3d24f72d045cc192ea8a6b89435c010000006a47304402207f4beeb1a86432be0b4c3d4f4db7416b52b66c84383d1980d39e21d547a1762f02200405d0d4b80d1094e3a08cb39ef6f1161be163026d417af08d54c5a1cfdbbbeb01210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b6568ffffffff03c0c62d00000000004852210352f2e34760514099f90b03aab91239466924c3b06047d3cf0e011f26ef96ceb7210207cf5fb65d6923d5d41db21ceac9567a0fc3eb92c6137f274018381ced7b65680053aeb077e605000000001976a9149fc3d8e0371b6eab89a8c3c015839f9e493ccf6588ac7035d705000000001976a914e5175c1f71c28218306d4a27c8cec0269dddbbde88ac00000000";
offererSignatureR = "64562406184784382000330465585294975882418886289667123258365244289311131050102";
offererSignatureS = "9282449224979858852843663340827968850695102089943078462704407873774672151491";
// 2. taker sign and publish tx
Transaction takerTx = takerSignAndSendTx(depositTxAsHex,
offererSignatureR,
offererSignatureS,
offererPaybackAmount,
takerPaybackAmount,
offererAddress, null);
log.info(takerTx.toString()); // tx has 265 Bytes
/*
public ListenableFuture<Transaction> broadcastTransaction(final Transaction tx, final int minConnections) {
final TransactionBroadcast broadcast = new TransactionBroadcast(walletAppKit.peerGroup(), tx);
broadcast.setMinConnections(1);
// Send the TX to the wallet once we have a successful broadcast.
Futures.addCallback(broadcast.future(), new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction transaction) {
// OK, now tell the wallet about the transaction. If the wallet created the transaction then
// it already knows and will ignore this. If it's a transaction we received from
// somebody else via a side channel and are now broadcasting, this will put it into the
// wallet now we know it's valid.
for (Wallet wallet : wallets) {
// Assumption here is there are no dependencies of the created transaction.
//
// We may end up with two threads trying to do this in parallel - the wallet will
// ignore whichever one loses the race.
try {
wallet.receivePending(transaction, null);
} catch (VerificationException e) {
throw new RuntimeException(e); // Cannot fail to verify a tx we created ourselves.
}
}
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();
}
@Override
public void onFailure(Throwable throwable) {
// This can't happen with the current code, but just in case one day that changes ...
runningBroadcasts.remove(broadcast);
throw new RuntimeException(throwable);
}
});
// Keep a reference to the TransactionBroadcast object. This is important because otherwise, the entire tree
// of objects we just created would become garbage if the user doens't hold on to the returned future, and
// eventually be collected. This in turn could result in the transaction not being committed to the wallet
// at all.
runningBroadcasts.add(broadcast);
broadcast.broadcast();
return broadcast.future();
} */
///////////////////////////////////////////////////////////////////////////////////////////
// Inner classes

View File

@ -44,7 +44,7 @@ public class FundsController implements Initializable, ChildController
@Override
public void initialize(URL url, ResourceBundle rb)
{
String tradingAccountAddress = walletFacade.getAddressAsString();
String tradingAccountAddress = walletFacade.getTradingAddress();
tradingAccountTextField.setText(tradingAccountAddress);
copyIcon.setId("copy-icon");

View File

@ -163,8 +163,8 @@ public class SetupController implements Initializable, ChildController
{
try
{
walletFacade.sendRegistrationTx(user.getStringifiedBankAccounts());
user.setAccountID(walletFacade.getAccountRegistrationAddress().toString());
walletFacade.publishRegistrationTxWithExtraData(user.getStringifiedBankAccounts());
user.setAccountID(walletFacade.getRegistrationAddress().toString());
user.setMessagePubKeyAsHex(DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey()));
storage.write(user.getClass().getName(), user);
@ -201,7 +201,7 @@ public class SetupController implements Initializable, ChildController
"Your trading account will be the source for your reputation in the trading platform.\n\n" +
"You need at least 1 confirmation for doing the registration payment.");
String registrationAddress = walletFacade.getAccountRegistrationAddress().toString();
String registrationAddress = walletFacade.getRegistrationAddress().toString();
registrationAddressTextField.setText(registrationAddress);
copyIcon.setId("copy-icon");
@ -215,8 +215,8 @@ public class SetupController implements Initializable, ChildController
});
confidenceDisplay = new ConfidenceDisplay(walletFacade.getWallet(), confirmationLabel, balanceTextField, progressIndicator);
paymentDoneButton.setDisable(walletFacade.getAccountRegistrationBalance().compareTo(BigInteger.ZERO) == 0);
log.debug("getAccountRegistrationBalance " + walletFacade.getAccountRegistrationBalance().toString());
paymentDoneButton.setDisable(walletFacade.getRegistrationBalance().compareTo(BigInteger.ZERO) == 0);
log.debug("getAccountRegistrationBalance " + walletFacade.getRegistrationBalance().toString());
walletFacade.getWallet().addEventListener(new WalletEventListener()
{
@Override
@ -336,13 +336,13 @@ public class SetupController implements Initializable, ChildController
accountSecondaryID.textProperty().addListener((ov, oldValue, newValue) -> checkCreateAccountButtonState());
//todo
/* bankAccountTypesComboBox.getSelectionModel().select(0);
bankAccountTypesComboBox.getSelectionModel().select(0);
currencyComboBox.getSelectionModel().select(0);
countryComboBox.getSelectionModel().select(0);
accountTitle.setText("Sepa EUR Account");
accountHolderName.setText("Alice");
accountPrimaryID.setText("123456");
accountSecondaryID.setText("7896541"); */
accountSecondaryID.setText("7896541");
}
private void setupSettingsScreen()

View File

@ -215,7 +215,7 @@ public class OffererPaymentProtocol
log.debug("2.5 createDepositTx");
BigInteger offererInputAmount = trade.getTradeAmount().multiply(BigInteger.valueOf(offer.getCollateral())).divide(BigInteger.valueOf(100));
String offererPubKey = walletFacade.getMultiSigPubKeyAsHex();
String offererPubKey = walletFacade.getPubKeyAsHex();
String takerPubKey = requestTradeMessage.getTakerMultiSigPubKey();
String arbitratorPubKey = offer.getArbitrator().getPubKey();
@ -369,14 +369,6 @@ public class OffererPaymentProtocol
else
{
log.error("3.3 verifyContract failed");
//TODO ignore that for now... fee inconsistency...
String signature = cryptoFacade.signContract(walletFacade.getRegistrationKey(), contractAsJson);
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setContractTakerSignature(signature);
log.debug("3.3 signature: " + signature);
signAndPublishDepositTx(requestTradeMessage);
}
}
@ -570,7 +562,7 @@ public class OffererPaymentProtocol
String offererSignatureS = offererSignature.s.toString();
Transaction depositTx = result.getValue();
String depositTxID = Utils.bytesToHexString(depositTx.bitcoinSerialize());
String offererPayoutAddress = walletFacade.getAddressAsString();
String offererPayoutAddress = walletFacade.getTradingAddress();
TradeMessage tradeMessage = new TradeMessage(TradeMessageType.BANK_TX_INITED, trade.getUid(),
depositTxID,
offererSignatureR,
@ -613,7 +605,6 @@ public class OffererPaymentProtocol
log.debug("3.14 onPayoutTxPublished");
log.debug("3.14 TRADE COMPLETE!!!!!!!!!!!");
walletFacade.updateWallet();
offererPaymentProtocolListener.onPayoutTxPublished(tradeMessage.getPayoutTxID());
}

View File

@ -264,7 +264,7 @@ public class TakerPaymentProtocol
trade.getUid(),
trade.getTradeAmount(),
takeOfferFeeTxID,
walletFacade.getMultiSigPubKeyAsHex());
walletFacade.getPubKeyAsHex());
messageFacade.sendTradeMessage(peerAddress, tradeMessage, listener);
}
@ -364,12 +364,10 @@ public class TakerPaymentProtocol
BigInteger collateralAmount = trade.getTradeAmount().multiply(BigInteger.valueOf(offer.getCollateral())).divide(BigInteger.valueOf(100));
BigInteger takerInputAmount = trade.getTradeAmount().add(collateralAmount);
// trade + 2 x coll + 1 x btc network fee
BigInteger msOutputAmount = trade.getTradeAmount().add(collateralAmount).add(collateralAmount).add(Transaction.REFERENCE_DEFAULT_MIN_TX_FEE);
BigInteger msOutputAmount = trade.getTradeAmount().add(collateralAmount).add(collateralAmount);
String offererPubKey = requestTradeMessage.getOffererPubKey();
String takerPubKey = walletFacade.getMultiSigPubKeyAsHex();
String takerPubKey = walletFacade.getPubKeyAsHex();
String arbitratorPubKey = offer.getArbitrator().getPubKey();
String preparedOffererDepositTxAsHex = requestTradeMessage.getPreparedOffererDepositTxAsHex();
@ -427,7 +425,7 @@ public class TakerPaymentProtocol
takerPaymentProtocolListener.onProgress(getProgress());
BankAccount bankAccount = user.getBankAccount(offer.getBankAccountUID());
BankAccount bankAccount = user.getCurrentBankAccount();
String accountID = user.getAccountID();
String messagePubKey = user.getMessagePubKeyAsHex();
String contractAsJson = trade.getContractAsJson();
@ -437,7 +435,7 @@ public class TakerPaymentProtocol
String txScriptSigAsHex = com.google.bitcoin.core.Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes());
String txConnOutAsHex = com.google.bitcoin.core.Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getConnectedOutput().getParentTransaction().bitcoinSerialize());
//TODO just 1 address supported yet
String payoutAddress = walletFacade.getAddressAsString();
String payoutAddress = walletFacade.getTradingAddress();
log.debug("2.10 deposit txAsHex: " + signedTakerDepositTxAsHex);
log.debug("2.10 txScriptSigAsHex: " + txScriptSigAsHex);
log.debug("2.10 txConnOutAsHex: " + txConnOutAsHex);