Search in sources :

Example 46 with Address

use of org.bitcoinj.core.Address in project bisq-core by bisq-network.

the class BtcWalletService method completePreparedCompensationRequestTx.

// /////////////////////////////////////////////////////////////////////////////////////////
// Public Methods
// /////////////////////////////////////////////////////////////////////////////////////////
// /////////////////////////////////////////////////////////////////////////////////////////
// CompensationRequest tx
// /////////////////////////////////////////////////////////////////////////////////////////
public Transaction completePreparedCompensationRequestTx(Coin issuanceAmount, Address issuanceAddress, Transaction feeTx, byte[] opReturnData) throws TransactionVerificationException, WalletException, InsufficientMoneyException {
    // (BsqFee)tx has following structure:
    // inputs [1-n] BSQ inputs (fee)
    // outputs [0-1] BSQ request fee change output (>= 2730 Satoshi)
    // preparedCompensationRequestTx has following structure:
    // inputs [1-n] BSQ inputs for request fee
    // inputs [1-n] BTC inputs for BSQ issuance and miner fee
    // outputs [0-1] BSQ request fee change output (>= 2730 Satoshi)
    // outputs [1] Potentially BSQ issuance output (>= 2730 Satoshi)
    // outputs [0-1] BTC change output from issuance and miner fee inputs (>= 2730 Satoshi)
    // outputs [0-1] OP_RETURN with opReturnData and amount 0
    // mining fee: BTC mining fee + burned BSQ fee
    Transaction preparedTx = new Transaction(params);
    // Copy inputs from BSQ fee tx
    feeTx.getInputs().forEach(preparedTx::addInput);
    int indexOfBtcFirstInput = feeTx.getInputs().size();
    // Need to be first because issuance is not guaranteed to be valid and would otherwise burn change output!
    // BSQ change outputs from BSQ fee inputs.
    feeTx.getOutputs().forEach(preparedTx::addOutput);
    // BSQ issuance output
    preparedTx.addOutput(issuanceAmount, issuanceAddress);
    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    final int sigSizePerInput = 106;
    // typical size for a tx with 3 inputs
    int txSizeWithUnsignedInputs = 300;
    final Coin txFeePerByte = feeService.getTxFeePerByte();
    Address changeAddress = getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");
    final BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    final List<TransactionInput> preparedBsqTxInputs = preparedTx.getInputs();
    final List<TransactionOutput> preparedBsqTxOutputs = preparedTx.getOutputs();
    int numInputs = preparedBsqTxInputs.size();
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }
        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.stream().forEach(tx::addInput);
        preparedBsqTxOutputs.stream().forEach(tx::addOutput);
        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;
        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;
        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);
        resultTx = sendRequest.tx;
        // add OP_RETURN output
        resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));
        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    } while (isFeeOutsideTolerance);
    // Sign all BTC inputs
    for (int i = indexOfBtcFirstInput; i < resultTx.getInputs().size(); i++) {
        TransactionInput txIn = resultTx.getInputs().get(i);
        checkArgument(txIn.getConnectedOutput() != null && txIn.getConnectedOutput().isMine(wallet), "txIn.getConnectedOutput() is not in our wallet. That must not happen.");
        signTransactionInput(wallet, aesKey, resultTx, txIn, i);
        checkScriptSig(resultTx, txIn, i);
    }
    checkWalletConsistency(wallet);
    verifyTransaction(resultTx);
    printTx("BTC wallet: Signed tx", resultTx);
    return resultTx;
}
Also used : TransactionOutput(org.bitcoinj.core.TransactionOutput) SendRequest(org.bitcoinj.wallet.SendRequest) Address(org.bitcoinj.core.Address) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction)

Example 47 with Address

use of org.bitcoinj.core.Address in project bisq-core by bisq-network.

the class BtcWalletService method completePreparedVoteRevealTx.

// /////////////////////////////////////////////////////////////////////////////////////////
// MyVote reveal tx
// /////////////////////////////////////////////////////////////////////////////////////////
// TODO is same as blind vote tx
public Transaction completePreparedVoteRevealTx(Transaction feeTx, byte[] opReturnData) throws TransactionVerificationException, WalletException, InsufficientMoneyException {
    // (BsqFee)tx has following structure:
    // inputs [1] BSQ inputs (stake)
    // inputs [1-n] BSQ inputs (fee)
    // outputs [1] BSQ unlocked stake
    // outputs [0-1] BSQ change output (>= 2730 Satoshi)
    // preparedVoteTx has following structure:
    // inputs [1] BSQ inputs (stake)
    // inputs [1-n] BSQ inputs (fee)
    // inputs [1-n] BTC inputs for miner fee
    // outputs [1] BSQ unlocked stake
    // outputs [0-1] BSQ change output (>= 2730 Satoshi)
    // outputs [0-1] BTC change output from miner fee inputs (>= 2730 Satoshi)
    // outputs [0-1] OP_RETURN with opReturnData and amount 0
    // mining fee: BTC mining fee + burned BSQ fee
    Transaction preparedTx = new Transaction(params);
    // Copy inputs from BSQ fee tx
    feeTx.getInputs().forEach(preparedTx::addInput);
    int indexOfBtcFirstInput = feeTx.getInputs().size();
    // BSQ change outputs from BSQ fee inputs.
    feeTx.getOutputs().forEach(preparedTx::addOutput);
    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    final int sigSizePerInput = 106;
    // typical size for a tx with 3 inputs
    int txSizeWithUnsignedInputs = 300;
    final Coin txFeePerByte = feeService.getTxFeePerByte();
    Address changeAddress = getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");
    final BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    final List<TransactionInput> preparedBsqTxInputs = preparedTx.getInputs();
    final List<TransactionOutput> preparedBsqTxOutputs = preparedTx.getOutputs();
    int numInputs = preparedBsqTxInputs.size();
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }
        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.forEach(tx::addInput);
        preparedBsqTxOutputs.forEach(tx::addOutput);
        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;
        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;
        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);
        resultTx = sendRequest.tx;
        // add OP_RETURN output
        resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));
        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    } while (isFeeOutsideTolerance);
    // Sign all BTC inputs
    for (int i = indexOfBtcFirstInput; i < resultTx.getInputs().size(); i++) {
        TransactionInput txIn = resultTx.getInputs().get(i);
        checkArgument(txIn.getConnectedOutput() != null && txIn.getConnectedOutput().isMine(wallet), "txIn.getConnectedOutput() is not in our wallet. That must not happen.");
        signTransactionInput(wallet, aesKey, resultTx, txIn, i);
        checkScriptSig(resultTx, txIn, i);
    }
    checkWalletConsistency(wallet);
    verifyTransaction(resultTx);
    printTx("BTC wallet: Signed tx", resultTx);
    return resultTx;
}
Also used : TransactionOutput(org.bitcoinj.core.TransactionOutput) SendRequest(org.bitcoinj.wallet.SendRequest) Address(org.bitcoinj.core.Address) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction)

Example 48 with Address

use of org.bitcoinj.core.Address in project bisq-core by bisq-network.

the class BtcWalletService method doubleSpendTransaction.

// /////////////////////////////////////////////////////////////////////////////////////////
// Double spend unconfirmed transaction (unlock in case we got into a tx with a too low mining fee)
// /////////////////////////////////////////////////////////////////////////////////////////
public void doubleSpendTransaction(String txId, Runnable resultHandler, ErrorMessageHandler errorMessageHandler) throws InsufficientFundsException {
    AddressEntry addressEntry = getOrCreateUnusedAddressEntry(AddressEntry.Context.AVAILABLE);
    checkNotNull(addressEntry.getAddress(), "addressEntry.getAddress() must not be null");
    Optional<Transaction> transactionOptional = wallet.getTransactions(true).stream().filter(t -> t.getHashAsString().equals(txId)).findAny();
    if (transactionOptional.isPresent()) {
        Transaction txToDoubleSpend = transactionOptional.get();
        Address toAddress = addressEntry.getAddress();
        final TransactionConfidence.ConfidenceType confidenceType = txToDoubleSpend.getConfidence().getConfidenceType();
        if (confidenceType == TransactionConfidence.ConfidenceType.PENDING) {
            log.debug("txToDoubleSpend no. of inputs " + txToDoubleSpend.getInputs().size());
            Transaction newTransaction = new Transaction(params);
            txToDoubleSpend.getInputs().stream().forEach(input -> {
                final TransactionOutput connectedOutput = input.getConnectedOutput();
                if (connectedOutput != null && connectedOutput.isMine(wallet) && connectedOutput.getParentTransaction() != null && connectedOutput.getParentTransaction().getConfidence() != null && input.getValue() != null) {
                    // if (connectedOutput.getParentTransaction().getConfidence().getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
                    newTransaction.addInput(new TransactionInput(params, newTransaction, new byte[] {}, new TransactionOutPoint(params, input.getOutpoint().getIndex(), new Transaction(params, connectedOutput.getParentTransaction().bitcoinSerialize())), Coin.valueOf(input.getValue().value)));
                /* } else {
                                    log.warn("Confidence of parent tx is not of type BUILDING: ConfidenceType=" +
                                            connectedOutput.getParentTransaction().getConfidence().getConfidenceType());
                                }*/
                }
            });
            log.info("newTransaction no. of inputs " + newTransaction.getInputs().size());
            log.info("newTransaction size in kB " + newTransaction.bitcoinSerialize().length / 1024);
            if (!newTransaction.getInputs().isEmpty()) {
                Coin amount = Coin.valueOf(newTransaction.getInputs().stream().mapToLong(input -> input.getValue() != null ? input.getValue().value : 0).sum());
                newTransaction.addOutput(amount, toAddress);
                try {
                    Coin fee;
                    int counter = 0;
                    int txSize = 0;
                    Transaction tx;
                    SendRequest sendRequest;
                    Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
                    do {
                        counter++;
                        fee = txFeeForWithdrawalPerByte.multiply(txSize);
                        newTransaction.clearOutputs();
                        newTransaction.addOutput(amount.subtract(fee), toAddress);
                        sendRequest = SendRequest.forTx(newTransaction);
                        sendRequest.fee = fee;
                        sendRequest.feePerKb = Coin.ZERO;
                        sendRequest.ensureMinRequiredFee = false;
                        sendRequest.aesKey = aesKey;
                        sendRequest.coinSelector = new BtcCoinSelector(toAddress);
                        sendRequest.changeAddress = toAddress;
                        wallet.completeTx(sendRequest);
                        tx = sendRequest.tx;
                        txSize = tx.bitcoinSerialize().length;
                        printTx("FeeEstimationTransaction", tx);
                        sendRequest.tx.getOutputs().forEach(o -> log.debug("Output value " + o.getValue().toFriendlyString()));
                    } while (feeEstimationNotSatisfied(counter, tx));
                    if (counter == 10)
                        log.error("Could not calculate the fee. Tx=" + tx);
                    Wallet.SendResult sendResult = null;
                    try {
                        sendRequest = SendRequest.forTx(newTransaction);
                        sendRequest.fee = fee;
                        sendRequest.feePerKb = Coin.ZERO;
                        sendRequest.ensureMinRequiredFee = false;
                        sendRequest.aesKey = aesKey;
                        sendRequest.coinSelector = new BtcCoinSelector(toAddress);
                        sendRequest.changeAddress = toAddress;
                        sendResult = wallet.sendCoins(sendRequest);
                    } catch (InsufficientMoneyException e) {
                        // in some cases getFee did not calculate correctly and we still get an InsufficientMoneyException
                        log.warn("We still have a missing fee " + (e.missing != null ? e.missing.toFriendlyString() : ""));
                        amount = amount.subtract(e.missing);
                        newTransaction.clearOutputs();
                        newTransaction.addOutput(amount, toAddress);
                        sendRequest = SendRequest.forTx(newTransaction);
                        sendRequest.fee = fee;
                        sendRequest.feePerKb = Coin.ZERO;
                        sendRequest.ensureMinRequiredFee = false;
                        sendRequest.aesKey = aesKey;
                        sendRequest.coinSelector = new BtcCoinSelector(toAddress, false);
                        sendRequest.changeAddress = toAddress;
                        try {
                            sendResult = wallet.sendCoins(sendRequest);
                            printTx("FeeEstimationTransaction", newTransaction);
                        } catch (InsufficientMoneyException e2) {
                            errorMessageHandler.handleErrorMessage("We did not get the correct fee calculated. " + (e2.missing != null ? e2.missing.toFriendlyString() : ""));
                        }
                    }
                    if (sendResult != null) {
                        log.info("Broadcasting double spending transaction. " + sendResult.tx);
                        Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {

                            @Override
                            public void onSuccess(Transaction result) {
                                log.info("Double spending transaction published. " + result);
                                resultHandler.run();
                            }

                            @Override
                            public void onFailure(@NotNull Throwable t) {
                                log.error("Broadcasting double spending transaction failed. " + t.getMessage());
                                errorMessageHandler.handleErrorMessage(t.getMessage());
                            }
                        });
                    }
                } catch (InsufficientMoneyException e) {
                    throw new InsufficientFundsException("The fees for that transaction exceed the available funds " + "or the resulting output value is below the min. dust value:\n" + "Missing " + (e.missing != null ? e.missing.toFriendlyString() : "null"));
                }
            } else {
                String errorMessage = "We could not find inputs we control in the transaction we want to double spend.";
                log.warn(errorMessage);
                errorMessageHandler.handleErrorMessage(errorMessage);
            }
        } else if (confidenceType == TransactionConfidence.ConfidenceType.BUILDING) {
            errorMessageHandler.handleErrorMessage("That transaction is already in the blockchain so we cannot double spend it.");
        } else if (confidenceType == TransactionConfidence.ConfidenceType.DEAD) {
            errorMessageHandler.handleErrorMessage("One of the inputs of that transaction has been already double spent.");
        }
    }
}
Also used : Arrays(java.util.Arrays) Transaction(org.bitcoinj.core.Transaction) TransactionConfidence(org.bitcoinj.core.TransactionConfidence) Coin(org.bitcoinj.core.Coin) Wallet(org.bitcoinj.wallet.Wallet) LoggerFactory(org.slf4j.LoggerFactory) ArrayList(java.util.ArrayList) Inject(javax.inject.Inject) Preconditions.checkArgument(com.google.common.base.Preconditions.checkArgument) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) ImmutableList(com.google.common.collect.ImmutableList) AddressEntryList(bisq.core.btc.AddressEntryList) SendRequest(org.bitcoinj.wallet.SendRequest) ErrorMessageHandler(bisq.common.handlers.ErrorMessageHandler) KeyCrypterScrypt(org.bitcoinj.crypto.KeyCrypterScrypt) KeyParameter(org.spongycastle.crypto.params.KeyParameter) DeterministicKey(org.bitcoinj.crypto.DeterministicKey) Nullable(javax.annotation.Nullable) ScriptBuilder(org.bitcoinj.script.ScriptBuilder) AddressFormatException(org.bitcoinj.core.AddressFormatException) AddressEntryException(bisq.core.btc.AddressEntryException) WalletException(bisq.core.btc.exceptions.WalletException) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) Logger(org.slf4j.Logger) InsufficientFundsException(bisq.core.btc.InsufficientFundsException) Preconditions.checkNotNull(com.google.common.base.Preconditions.checkNotNull) Set(java.util.Set) InsufficientMoneyException(org.bitcoinj.core.InsufficientMoneyException) Collectors(java.util.stream.Collectors) FutureCallback(com.google.common.util.concurrent.FutureCallback) Futures(com.google.common.util.concurrent.Futures) List(java.util.List) AddressEntry(bisq.core.btc.AddressEntry) TransactionInput(org.bitcoinj.core.TransactionInput) Preferences(bisq.core.user.Preferences) TransactionOutput(org.bitcoinj.core.TransactionOutput) Optional(java.util.Optional) FeeService(bisq.core.provider.fee.FeeService) Address(org.bitcoinj.core.Address) Preconditions(com.google.common.base.Preconditions) NotNull(org.jetbrains.annotations.NotNull) Restrictions(bisq.core.btc.Restrictions) TransactionOutput(org.bitcoinj.core.TransactionOutput) SendRequest(org.bitcoinj.wallet.SendRequest) Address(org.bitcoinj.core.Address) AddressEntry(bisq.core.btc.AddressEntry) Wallet(org.bitcoinj.wallet.Wallet) InsufficientMoneyException(org.bitcoinj.core.InsufficientMoneyException) TransactionInput(org.bitcoinj.core.TransactionInput) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction) InsufficientFundsException(bisq.core.btc.InsufficientFundsException) TransactionConfidence(org.bitcoinj.core.TransactionConfidence) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint)

Example 49 with Address

use of org.bitcoinj.core.Address in project bisq-core by bisq-network.

the class BtcWalletService method completePreparedBsqTx.

public Transaction completePreparedBsqTx(Transaction preparedBsqTx, boolean useCustomTxFee, @Nullable byte[] opReturnData) throws TransactionVerificationException, WalletException, InsufficientMoneyException {
    // preparedBsqTx has following structure:
    // inputs [1-n] BSQ inputs
    // outputs [0-1] BSQ receivers output
    // outputs [0-1] BSQ change output
    // mining fee: optional burned BSQ fee (only if opReturnData != null)
    // We add BTC mining fee. Result tx looks like:
    // inputs [1-n] BSQ inputs
    // inputs [1-n] BTC inputs
    // outputs [0-1] BSQ receivers output
    // outputs [0-1] BSQ change output
    // outputs [0-1] BTC change output
    // outputs [0-1] OP_RETURN with opReturnData (only if opReturnData != null)
    // mining fee: BTC mining fee + optional burned BSQ fee (only if opReturnData != null)
    // In case of txs for burned BSQ fees we have no receiver output and it might be that there is no change outputs
    // We need to guarantee that min. 1 valid output is added (OP_RETURN does not count). So we use a higher input
    // for BTC to force an additional change output.
    // safety check counter to avoid endless loops
    int counter = 0;
    // estimated size of input sig
    final int sigSizePerInput = 106;
    // typical size for a tx with 2 inputs
    int txSizeWithUnsignedInputs = 203;
    // If useCustomTxFee we allow overriding the estimated fee from preferences
    final Coin txFeePerByte = useCustomTxFee ? getTxFeeForWithdrawalPerByte() : feeService.getTxFeePerByte();
    // In case there are no change outputs we force a change by adding min dust to the BTC input
    Coin forcedChangeValue = Coin.ZERO;
    Address changeAddress = getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress();
    checkNotNull(changeAddress, "changeAddress must not be null");
    final BtcCoinSelector coinSelector = new BtcCoinSelector(walletsSetup.getAddressesByContext(AddressEntry.Context.AVAILABLE));
    final List<TransactionInput> preparedBsqTxInputs = preparedBsqTx.getInputs();
    final List<TransactionOutput> preparedBsqTxOutputs = preparedBsqTx.getOutputs();
    // We add 1 for the BTC fee input
    int numInputs = preparedBsqTxInputs.size() + 1;
    Transaction resultTx = null;
    boolean isFeeOutsideTolerance;
    boolean opReturnIsOnlyOutput;
    do {
        counter++;
        if (counter >= 10) {
            checkNotNull(resultTx, "resultTx must not be null");
            log.error("Could not calculate the fee. Tx=" + resultTx);
            break;
        }
        Transaction tx = new Transaction(params);
        preparedBsqTxInputs.stream().forEach(tx::addInput);
        if (forcedChangeValue.isZero()) {
            preparedBsqTxOutputs.stream().forEach(tx::addOutput);
        } else {
            // TODO test that case
            checkArgument(preparedBsqTxOutputs.size() == 0, "preparedBsqTxOutputs.size must be null in that code branch");
            tx.addOutput(forcedChangeValue, changeAddress);
        }
        SendRequest sendRequest = SendRequest.forTx(tx);
        sendRequest.shuffleOutputs = false;
        sendRequest.aesKey = aesKey;
        // signInputs needs to be false as it would try to sign all inputs (BSQ inputs are not in this wallet)
        sendRequest.signInputs = false;
        sendRequest.fee = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs);
        sendRequest.feePerKb = Coin.ZERO;
        sendRequest.ensureMinRequiredFee = false;
        sendRequest.coinSelector = coinSelector;
        sendRequest.changeAddress = changeAddress;
        wallet.completeTx(sendRequest);
        resultTx = sendRequest.tx;
        // We might have the rare case that both inputs matched the required fees, so both did not require
        // a change output.
        // In such cases we need to add artificially a change output (OP_RETURN is not allowed as only output)
        opReturnIsOnlyOutput = resultTx.getOutputs().size() == 0;
        forcedChangeValue = opReturnIsOnlyOutput ? Restrictions.getMinNonDustOutput() : Coin.ZERO;
        // add OP_RETURN output
        if (opReturnData != null)
            resultTx.addOutput(new TransactionOutput(params, resultTx, Coin.ZERO, ScriptBuilder.createOpReturnScript(opReturnData).getProgram()));
        numInputs = resultTx.getInputs().size();
        txSizeWithUnsignedInputs = resultTx.bitcoinSerialize().length;
        final long estimatedFeeAsLong = txFeePerByte.multiply(txSizeWithUnsignedInputs + sigSizePerInput * numInputs).value;
        // calculated fee must be inside of a tolerance range with tx fee
        isFeeOutsideTolerance = Math.abs(resultTx.getFee().value - estimatedFeeAsLong) > 1000;
    } while (opReturnIsOnlyOutput || isFeeOutsideTolerance || resultTx.getFee().value < txFeePerByte.multiply(resultTx.bitcoinSerialize().length).value);
    // Sign all BTC inputs
    for (int i = preparedBsqTxInputs.size(); i < resultTx.getInputs().size(); i++) {
        TransactionInput txIn = resultTx.getInputs().get(i);
        checkArgument(txIn.getConnectedOutput() != null && txIn.getConnectedOutput().isMine(wallet), "txIn.getConnectedOutput() is not in our wallet. That must not happen.");
        signTransactionInput(wallet, aesKey, resultTx, txIn, i);
        checkScriptSig(resultTx, txIn, i);
    }
    checkWalletConsistency(wallet);
    verifyTransaction(resultTx);
    printTx("BTC wallet: Signed tx", resultTx);
    return resultTx;
}
Also used : TransactionOutput(org.bitcoinj.core.TransactionOutput) SendRequest(org.bitcoinj.wallet.SendRequest) Address(org.bitcoinj.core.Address) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction)

Example 50 with Address

use of org.bitcoinj.core.Address in project bisq-core by bisq-network.

the class TradeWalletService method takerCreatesDepositsTxInputs.

// /////////////////////////////////////////////////////////////////////////////////////////
// Trade
// /////////////////////////////////////////////////////////////////////////////////////////
// We construct the deposit transaction in the way that the buyer is always the first entry (inputs, outputs, MS keys) and then the seller.
// In the creation of the deposit tx the taker/maker roles are the determining roles instead of buyer/seller.
// In the payout tx is is the buyer/seller role. We keep the buyer/seller ordering over all transactions to not get confusion with ordering,
// which is important to follow correctly specially for the order of the MS keys.
/**
 * The taker creates a dummy transaction to get the input(s) and optional change output for the amount and the takersAddress for that trade.
 * That will be used to send to the maker for creating the deposit transaction.
 *
 * @param inputAmount   Amount of takers input
 * @param txFee         Mining fee
 * @param takersAddress Address of taker
 * @return A data container holding the inputs, the output value and address
 * @throws TransactionVerificationException
 * @throws WalletException
 */
public InputsAndChangeOutput takerCreatesDepositsTxInputs(Coin inputAmount, Coin txFee, Address takersAddress, Address takersChangeAddress) throws TransactionVerificationException, WalletException {
    log.debug("takerCreatesDepositsTxInputs called");
    log.debug("inputAmount " + inputAmount.toFriendlyString());
    log.debug("txFee " + txFee.toFriendlyString());
    log.debug("takersAddress " + takersAddress.toString());
    // We add the mining fee 2 times to the deposit tx:
    // 1. Will be spent when publishing the deposit tx (paid by buyer)
    // 2. Will be added to the MS amount, so when publishing the payout tx the fee is already there and the outputs are not changed by fee reduction
    // The fee for the payout will be paid by the seller.
    /*
         The tx we create has that structure:

         IN[0]  any input > inputAmount (including tx fee) (unsigned)
         IN[1...n] optional inputs supported, but normally there is just 1 input (unsigned)
         OUT[0] dummyOutputAmount (inputAmount - tx fee)
         OUT[1] Optional Change = inputAmount - dummyOutputAmount - tx fee

         We are only interested in the inputs and the optional change output.
         */
    // inputAmount includes the tx fee. So we subtract the fee to get the dummyOutputAmount.
    Coin dummyOutputAmount = inputAmount.subtract(txFee);
    Transaction dummyTX = new Transaction(params);
    // The output is just used to get the right inputs and change outputs, so we use an anonymous ECKey, as it will never be used for anything.
    // We don't care about fee calculation differences between the real tx and that dummy tx as we use a static tx fee.
    TransactionOutput dummyOutput = new TransactionOutput(params, dummyTX, dummyOutputAmount, new ECKey().toAddress(params));
    dummyTX.addOutput(dummyOutput);
    // Find the needed inputs to pay the output, optionally add 1 change output.
    // Normally only 1 input and no change output is used, but we support multiple inputs and 1 change output.
    // Our spending transaction output is from the create offer fee payment.
    addAvailableInputsAndChangeOutputs(dummyTX, takersAddress, takersChangeAddress, txFee);
    // The completeTx() call signs the input, but we don't want to pass over signed tx inputs so we remove the signature
    WalletService.removeSignatures(dummyTX);
    WalletService.verifyTransaction(dummyTX);
    // WalletService.printTx("dummyTX", dummyTX);
    List<RawTransactionInput> rawTransactionInputList = dummyTX.getInputs().stream().map(e -> {
        checkNotNull(e.getConnectedOutput(), "e.getConnectedOutput() must not be null");
        checkNotNull(e.getConnectedOutput().getParentTransaction(), "e.getConnectedOutput().getParentTransaction() must not be null");
        checkNotNull(e.getValue(), "e.getValue() must not be null");
        return getRawInputFromTransactionInput(e);
    }).collect(Collectors.toList());
    // We don't support more then 1 change outputs, so there are max. 2 outputs
    checkArgument(dummyTX.getOutputs().size() < 3);
    // Only interested in optional change output, the dummy output at index 0 is ignored (that's why we use index 1)
    TransactionOutput changeOutput = dummyTX.getOutputs().size() == 2 ? dummyTX.getOutputs().get(1) : null;
    long changeOutputValue = 0L;
    String changeOutputAddress = null;
    if (changeOutput != null) {
        changeOutputValue = changeOutput.getValue().getValue();
        Address addressFromP2PKHScript = changeOutput.getAddressFromP2PKHScript(params);
        checkNotNull(addressFromP2PKHScript, "changeOutput.getAddressFromP2PKHScript(params) must not be null");
        changeOutputAddress = addressFromP2PKHScript.toString();
    }
    return new InputsAndChangeOutput(new ArrayList<>(rawTransactionInputList), changeOutputValue, changeOutputAddress);
}
Also used : Transaction(org.bitcoinj.core.Transaction) TransactionConfidence(org.bitcoinj.core.TransactionConfidence) Coin(org.bitcoinj.core.Coin) Wallet(org.bitcoinj.wallet.Wallet) LoggerFactory(org.slf4j.LoggerFactory) ArrayList(java.util.ArrayList) Inject(javax.inject.Inject) NetworkParameters(org.bitcoinj.core.NetworkParameters) Preconditions.checkArgument(com.google.common.base.Preconditions.checkArgument) TransactionSignature(org.bitcoinj.crypto.TransactionSignature) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) VerificationException(org.bitcoinj.core.VerificationException) ImmutableList(com.google.common.collect.ImmutableList) Res(bisq.core.locale.Res) SendRequest(org.bitcoinj.wallet.SendRequest) KeyParameter(org.spongycastle.crypto.params.KeyParameter) Sha256Hash(org.bitcoinj.core.Sha256Hash) DeterministicKey(org.bitcoinj.crypto.DeterministicKey) Context(org.bitcoinj.core.Context) Nullable(javax.annotation.Nullable) ScriptBuilder(org.bitcoinj.script.ScriptBuilder) PreparedDepositTxAndMakerInputs(bisq.core.btc.data.PreparedDepositTxAndMakerInputs) AddressFormatException(org.bitcoinj.core.AddressFormatException) WalletException(bisq.core.btc.exceptions.WalletException) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) RawTransactionInput(bisq.core.btc.data.RawTransactionInput) SigningException(bisq.core.btc.exceptions.SigningException) Logger(org.slf4j.Logger) Utils(org.bitcoinj.core.Utils) Preconditions.checkNotNull(com.google.common.base.Preconditions.checkNotNull) Log(bisq.common.app.Log) BisqEnvironment(bisq.core.app.BisqEnvironment) InputsAndChangeOutput(bisq.core.btc.data.InputsAndChangeOutput) InsufficientMoneyException(org.bitcoinj.core.InsufficientMoneyException) Collectors(java.util.stream.Collectors) FutureCallback(com.google.common.util.concurrent.FutureCallback) ECKey(org.bitcoinj.core.ECKey) List(java.util.List) Script(org.bitcoinj.script.Script) AddressEntry(bisq.core.btc.AddressEntry) TransactionInput(org.bitcoinj.core.TransactionInput) TransactionOutput(org.bitcoinj.core.TransactionOutput) Address(org.bitcoinj.core.Address) NotNull(org.jetbrains.annotations.NotNull) Coin(org.bitcoinj.core.Coin) TransactionOutput(org.bitcoinj.core.TransactionOutput) Transaction(org.bitcoinj.core.Transaction) Address(org.bitcoinj.core.Address) RawTransactionInput(bisq.core.btc.data.RawTransactionInput) InputsAndChangeOutput(bisq.core.btc.data.InputsAndChangeOutput) ECKey(org.bitcoinj.core.ECKey)

Aggregations

Address (org.bitcoinj.core.Address)78 Transaction (org.bitcoinj.core.Transaction)32 Coin (org.bitcoinj.core.Coin)28 TransactionOutput (org.bitcoinj.core.TransactionOutput)15 TransactionInput (org.bitcoinj.core.TransactionInput)13 ArrayList (java.util.ArrayList)12 AddressEntry (bisq.core.btc.AddressEntry)11 Wallet (org.bitcoinj.wallet.Wallet)11 BtcWalletService (bisq.core.btc.wallet.BtcWalletService)10 AddressFormatException (org.bitcoinj.core.AddressFormatException)10 TransactionConfidence (org.bitcoinj.core.TransactionConfidence)10 IOException (java.io.IOException)9 SendRequest (org.bitcoinj.wallet.SendRequest)9 WalletService (io.bitsquare.btc.WalletService)8 List (java.util.List)8 Collectors (java.util.stream.Collectors)8 Nullable (javax.annotation.Nullable)8 TransactionOutPoint (org.bitcoinj.core.TransactionOutPoint)8 Script (org.bitcoinj.script.Script)8 Intent (android.content.Intent)7