Search in sources :

Example 81 with Transaction

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();
}
Also used : Transaction(org.bitcoinj.core.Transaction) TxOutputKey(bisq.core.dao.state.blockchain.TxOutputKey)

Example 82 with Transaction

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;
}
Also used : SendRequest(org.bitcoinj.wallet.SendRequest) Transaction(org.bitcoinj.core.Transaction)

Example 83 with Transaction

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;
}
Also used : Script(org.bitcoinj.script.Script) TransactionOutput(org.bitcoinj.core.TransactionOutput) Transaction(org.bitcoinj.core.Transaction) Sha256Hash(org.bitcoinj.core.Sha256Hash) ECKey(org.bitcoinj.core.ECKey) TransactionSignature(org.bitcoinj.crypto.TransactionSignature) RawTransactionInput(bisq.core.btc.data.RawTransactionInput) TransactionInput(org.bitcoinj.core.TransactionInput)

Example 84 with Transaction

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;
    }
}
Also used : SendRequest(org.bitcoinj.wallet.SendRequest) Transaction(org.bitcoinj.core.Transaction)

Example 85 with Transaction

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;
}
Also used : Script(org.bitcoinj.script.Script) TransactionOutput(org.bitcoinj.core.TransactionOutput) Transaction(org.bitcoinj.core.Transaction) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) RawTransactionInput(bisq.core.btc.data.RawTransactionInput) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) RawTransactionInput(bisq.core.btc.data.RawTransactionInput) TransactionInput(org.bitcoinj.core.TransactionInput)

Aggregations

Transaction (org.bitcoinj.core.Transaction)214 Coin (org.bitcoinj.core.Coin)71 TransactionInput (org.bitcoinj.core.TransactionInput)48 TransactionOutput (org.bitcoinj.core.TransactionOutput)42 TransactionOutPoint (org.bitcoinj.core.TransactionOutPoint)38 Address (org.bitcoinj.core.Address)35 ECKey (org.bitcoinj.core.ECKey)32 Script (org.bitcoinj.script.Script)32 ArrayList (java.util.ArrayList)31 HashMap (java.util.HashMap)29 SendRequest (org.bitcoinj.wallet.SendRequest)25 Wallet (org.bitcoinj.wallet.Wallet)25 IOException (java.io.IOException)24 List (java.util.List)24 InsufficientMoneyException (org.bitcoinj.core.InsufficientMoneyException)20 MyTransactionOutPoint (com.samourai.wallet.send.MyTransactionOutPoint)19 AddressFormatException (org.bitcoinj.core.AddressFormatException)19 Sha256Hash (org.bitcoinj.core.Sha256Hash)19 UTXO (com.samourai.wallet.send.UTXO)17 Nullable (javax.annotation.Nullable)17