use of org.bitcoinj.core.Transaction in project bisq-core by bisq-network.
the class NonBsqCoinSelector method isTxOutputSpendable.
@Override
protected boolean isTxOutputSpendable(TransactionOutput output) {
// output.getParentTransaction() cannot be null as it is checked in calling method
Transaction parentTransaction = output.getParentTransaction();
if (parentTransaction == null)
return false;
if (parentTransaction.getConfidence().getConfidenceType() != TransactionConfidence.ConfidenceType.BUILDING)
return false;
TxOutputKey key = new TxOutputKey(parentTransaction.getHashAsString(), output.getIndex());
// It might be that we received BTC in a non-BSQ tx so that will not be stored in out state and not found.
// So we consider any txOutput which is not in the state as BTC output.
boolean outputIsNotInBsqState = !bsqStateService.existsTxOutput(key);
return outputIsNotInBsqState || bsqStateService.getBtcTxOutput(key).isPresent();
}
use of org.bitcoinj.core.Transaction in project bisq-core by bisq-network.
the class TradeWalletService method estimateBtcTradingFeeTxSize.
public Transaction estimateBtcTradingFeeTxSize(Address fundingAddress, Address reservedForTradeAddress, Address changeAddress, Coin reservedFundsForOffer, boolean useSavingsWallet, Coin tradingFee, Coin txFee, String feeReceiverAddresses) throws InsufficientMoneyException, AddressFormatException {
Transaction tradingFeeTx = new Transaction(params);
tradingFeeTx.addOutput(tradingFee, Address.fromBase58(params, feeReceiverAddresses));
tradingFeeTx.addOutput(reservedFundsForOffer, reservedForTradeAddress);
SendRequest sendRequest = SendRequest.forTx(tradingFeeTx);
sendRequest.shuffleOutputs = false;
sendRequest.aesKey = aesKey;
if (useSavingsWallet)
sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
else
sendRequest.coinSelector = new BtcCoinSelector(fundingAddress);
sendRequest.fee = txFee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
sendRequest.changeAddress = changeAddress;
checkNotNull(wallet, "Wallet must not be null");
log.info("estimateBtcTradingFeeTxSize");
wallet.completeTx(sendRequest);
return tradingFeeTx;
}
use of org.bitcoinj.core.Transaction in project bisq-core by bisq-network.
the class TradeWalletService method traderSignAndFinalizeDisputedPayoutTx.
/**
* A trader who got the signed tx from the arbitrator finalizes the payout tx
*
* @param depositTxSerialized Serialized deposit tx
* @param arbitratorSignature DER encoded canonical signature of arbitrator
* @param buyerPayoutAmount Payout amount of the buyer
* @param sellerPayoutAmount Payout amount of the seller
* @param buyerAddressString The address of the buyer.
* @param sellerAddressString The address of the seller.
* @param tradersMultiSigKeyPair The keypair for the MultiSig of the trader who calls that method
* @param buyerPubKey The public key of the buyer.
* @param sellerPubKey The public key of the seller.
* @param arbitratorPubKey The public key of the arbitrator.
* @return The completed payout tx
* @throws AddressFormatException
* @throws TransactionVerificationException
* @throws WalletException
*/
public Transaction traderSignAndFinalizeDisputedPayoutTx(byte[] depositTxSerialized, byte[] arbitratorSignature, Coin buyerPayoutAmount, Coin sellerPayoutAmount, String buyerAddressString, String sellerAddressString, DeterministicKey tradersMultiSigKeyPair, byte[] buyerPubKey, byte[] sellerPubKey, byte[] arbitratorPubKey) throws AddressFormatException, TransactionVerificationException, WalletException {
Transaction depositTx = new Transaction(params, depositTxSerialized);
log.trace("signAndFinalizeDisputedPayoutTx called");
log.trace("depositTx " + depositTx);
log.trace("arbitratorSignature r " + ECKey.ECDSASignature.decodeFromDER(arbitratorSignature).r.toString());
log.trace("arbitratorSignature s " + ECKey.ECDSASignature.decodeFromDER(arbitratorSignature).s.toString());
log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString());
log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString());
log.trace("buyerAddressString " + buyerAddressString);
log.trace("sellerAddressString " + sellerAddressString);
log.trace("tradersMultiSigKeyPair (not displayed for security reasons)");
log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0);
Transaction payoutTx = new Transaction(params);
payoutTx.addInput(p2SHMultiSigOutput);
if (buyerPayoutAmount.isGreaterThan(Coin.ZERO))
payoutTx.addOutput(buyerPayoutAmount, Address.fromBase58(params, buyerAddressString));
if (sellerPayoutAmount.isGreaterThan(Coin.ZERO))
payoutTx.addOutput(sellerPayoutAmount, Address.fromBase58(params, sellerAddressString));
// take care of sorting!
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
Sha256Hash sigHash = payoutTx.hashForSignature(0, redeemScript, Transaction.SigHash.ALL, false);
checkNotNull(tradersMultiSigKeyPair, "tradersMultiSigKeyPair must not be null");
if (tradersMultiSigKeyPair.isEncrypted())
checkNotNull(aesKey);
ECKey.ECDSASignature tradersSignature = tradersMultiSigKeyPair.sign(sigHash, aesKey).toCanonicalised();
TransactionSignature tradersTxSig = new TransactionSignature(tradersSignature, Transaction.SigHash.ALL, false);
TransactionSignature arbitratorTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(arbitratorSignature), Transaction.SigHash.ALL, false);
// Take care of order of signatures. See comment below at getMultiSigRedeemScript (sort order needed here: arbitrator, seller, buyer)
Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(arbitratorTxSig, tradersTxSig), redeemScript);
TransactionInput input = payoutTx.getInput(0);
input.setScriptSig(inputScript);
WalletService.printTx("disputed payoutTx", payoutTx);
WalletService.verifyTransaction(payoutTx);
WalletService.checkWalletConsistency(wallet);
WalletService.checkScriptSig(payoutTx, input, 0);
checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null");
input.verify(input.getConnectedOutput());
return payoutTx;
}
use of org.bitcoinj.core.Transaction in project bisq-core by bisq-network.
the class TradeWalletService method createBtcTradingFeeTx.
// /////////////////////////////////////////////////////////////////////////////////////////
// Trade fee
// /////////////////////////////////////////////////////////////////////////////////////////
/**
* @param reservedForTradeAddress From where we want to spend the transaction fee. Used also as change reservedForTradeAddress.
* @param useSavingsWallet
* @param tradingFee The amount of the trading fee.
* @param feeReceiverAddresses The reservedForTradeAddress of the receiver of the trading fee (arbitrator). @return The broadcasted transaction
* @throws InsufficientMoneyException
* @throws AddressFormatException
*/
public Transaction createBtcTradingFeeTx(Address fundingAddress, Address reservedForTradeAddress, Address changeAddress, Coin reservedFundsForOffer, boolean useSavingsWallet, Coin tradingFee, Coin txFee, String feeReceiverAddresses, TxBroadcaster.Callback callback) throws InsufficientMoneyException, AddressFormatException {
log.debug("fundingAddress " + fundingAddress.toString());
log.debug("reservedForTradeAddress " + reservedForTradeAddress.toString());
log.debug("changeAddress " + changeAddress.toString());
log.info("reservedFundsForOffer " + reservedFundsForOffer.toPlainString());
log.debug("useSavingsWallet " + useSavingsWallet);
log.info("tradingFee " + tradingFee.toPlainString());
log.info("txFee " + txFee.toPlainString());
log.debug("feeReceiverAddresses " + feeReceiverAddresses);
Transaction tradingFeeTx = new Transaction(params);
SendRequest sendRequest = null;
try {
tradingFeeTx.addOutput(tradingFee, Address.fromBase58(params, feeReceiverAddresses));
// the reserved amount we need for the trade we send to our trade reservedForTradeAddress
tradingFeeTx.addOutput(reservedFundsForOffer, reservedForTradeAddress);
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
// wait for 1 confirmation)
// In case of double spend we will detect later in the trade process and use a ban score to penalize bad behaviour (not impl. yet)
sendRequest = SendRequest.forTx(tradingFeeTx);
sendRequest.shuffleOutputs = false;
sendRequest.aesKey = aesKey;
if (useSavingsWallet)
sendRequest.coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
else
sendRequest.coinSelector = new BtcCoinSelector(fundingAddress);
// We use a fixed fee
sendRequest.fee = txFee;
sendRequest.feePerKb = Coin.ZERO;
sendRequest.ensureMinRequiredFee = false;
// Change is optional in case of overpay or use of funds from savings wallet
sendRequest.changeAddress = changeAddress;
checkNotNull(wallet, "Wallet must not be null");
wallet.completeTx(sendRequest);
WalletService.printTx("tradingFeeTx", tradingFeeTx);
broadcastTx(tradingFeeTx, callback);
return tradingFeeTx;
} catch (Throwable t) {
if (wallet != null && sendRequest != null && sendRequest.coinSelector != null)
log.warn("Balance = {}; CoinSelector = {}", wallet.getBalance(sendRequest.coinSelector), sendRequest.coinSelector);
log.warn("createBtcTradingFeeTx failed: tradingFeeTx={}, txOutputs={}", tradingFeeTx.toString(), tradingFeeTx.getOutputs());
throw t;
}
}
use of org.bitcoinj.core.Transaction in project bisq-core by bisq-network.
the class TradeWalletService method takerSignsAndPublishesDepositTx.
/**
* The taker signs the deposit transaction he received from the maker and publishes it.
*
* @param takerIsSeller The flag indicating if we are in the taker as seller role or the opposite.
* @param contractHash The hash of the contract to be added to the OP_RETURN output.
* @param makersDepositTxSerialized The prepared deposit transaction signed by the maker.
* @param buyerInputs The connected outputs for all inputs of the buyer.
* @param sellerInputs The connected outputs for all inputs of the seller.
* @param buyerPubKey The public key of the buyer.
* @param sellerPubKey The public key of the seller.
* @param arbitratorPubKey The public key of the arbitrator.
* @param callback Callback when transaction is broadcasted.
* @throws SigningException
* @throws TransactionVerificationException
* @throws WalletException
*/
public Transaction takerSignsAndPublishesDepositTx(boolean takerIsSeller, byte[] contractHash, byte[] makersDepositTxSerialized, List<RawTransactionInput> buyerInputs, List<RawTransactionInput> sellerInputs, byte[] buyerPubKey, byte[] sellerPubKey, byte[] arbitratorPubKey, TxBroadcaster.Callback callback) throws SigningException, TransactionVerificationException, WalletException {
Transaction makersDepositTx = new Transaction(params, makersDepositTxSerialized);
log.debug("signAndPublishDepositTx called");
log.debug("takerIsSeller " + takerIsSeller);
log.debug("makersDepositTx " + makersDepositTx.toString());
log.debug("buyerConnectedOutputsForAllInputs " + buyerInputs.toString());
log.debug("sellerConnectedOutputsForAllInputs " + sellerInputs.toString());
log.debug("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
log.debug("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
log.debug("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
checkArgument(!buyerInputs.isEmpty());
checkArgument(!sellerInputs.isEmpty());
// Check if maker's Multisig script is identical to the takers
Script p2SHMultiSigOutputScript = getP2SHMultiSigOutputScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
if (!makersDepositTx.getOutput(0).getScriptPubKey().equals(p2SHMultiSigOutputScript))
throw new TransactionVerificationException("Maker's p2SHMultiSigOutputScript does not match to takers p2SHMultiSigOutputScript");
// The outpoints are not available from the serialized makersDepositTx, so we cannot use that tx directly, but we use it to construct a new
// depositTx
Transaction depositTx = new Transaction(params);
if (takerIsSeller) {
// We grab the signature from the makersDepositTx and apply it to the new tx input
for (int i = 0; i < buyerInputs.size(); i++) depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(makersDepositTx, i), buyerInputs.get(i)));
// Add seller inputs
for (RawTransactionInput rawTransactionInput : sellerInputs) depositTx.addInput(getTransactionInput(depositTx, new byte[] {}, rawTransactionInput));
} else {
// Add buyer inputs and apply signature
for (RawTransactionInput rawTransactionInput : buyerInputs) depositTx.addInput(getTransactionInput(depositTx, new byte[] {}, rawTransactionInput));
// We grab the signature from the makersDepositTx and apply it to the new tx input
for (int i = buyerInputs.size(), k = 0; i < makersDepositTx.getInputs().size(); i++, k++) depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(makersDepositTx, i), sellerInputs.get(k)));
}
// Check if OP_RETURN output with contract hash matches the one from the maker
TransactionOutput contractHashOutput = new TransactionOutput(params, makersDepositTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(contractHash).getProgram());
log.debug("contractHashOutput " + contractHashOutput);
TransactionOutput makersContractHashOutput = makersDepositTx.getOutputs().get(1);
log.debug("makersContractHashOutput " + makersContractHashOutput);
if (!makersContractHashOutput.getScriptPubKey().equals(contractHashOutput.getScriptPubKey()))
throw new TransactionVerificationException("Maker's transaction output for the contract hash is not matching takers version.");
// Add all outputs from makersDepositTx to depositTx
makersDepositTx.getOutputs().forEach(depositTx::addOutput);
// WalletService.printTx("makersDepositTx", makersDepositTx);
// Sign inputs
int start = takerIsSeller ? buyerInputs.size() : 0;
int end = takerIsSeller ? depositTx.getInputs().size() : buyerInputs.size();
for (int i = start; i < end; i++) {
TransactionInput input = depositTx.getInput(i);
signInput(depositTx, input, i);
WalletService.checkScriptSig(depositTx, input, i);
}
WalletService.printTx("depositTx", depositTx);
WalletService.verifyTransaction(depositTx);
WalletService.checkWalletConsistency(wallet);
broadcastTx(depositTx, callback);
return depositTx;
}
Aggregations