Search in sources :

Example 6 with TransactionVerificationException

use of bisq.core.btc.exceptions.TransactionVerificationException in project bisq-desktop by bisq-network.

the class MakeProposalView method publishProposal.

private void publishProposal(ProposalType type) {
    try {
        Proposal proposal = createProposal(type);
        Transaction tx = Objects.requireNonNull(proposal).getTx();
        Coin miningFee = Objects.requireNonNull(tx).getFee();
        int txSize = tx.bitcoinSerialize().length;
        validateInputs();
        new Popup<>().headLine(Res.get("dao.proposal.create.confirm")).confirmation(Res.get("dao.proposal.create.confirm.info", bsqFormatter.formatCoinWithCode(ProposalConsensus.getCreateCompensationRequestFee(readableBsqBlockChain)), btcFormatter.formatCoinWithCode(miningFee), CoinUtil.getFeePerByte(miningFee, txSize), (txSize / 1000d))).actionButtonText(Res.get("shared.yes")).onAction(() -> {
            proposalCollectionsService.publishProposal(proposal, new FutureCallback<Transaction>() {

                @Override
                public void onSuccess(@Nullable Transaction transaction) {
                    proposalDisplay.clearForm();
                    proposalTypeComboBox.getSelectionModel().clearSelection();
                    new Popup<>().confirmation(Res.get("dao.tx.published.success")).show();
                }

                @Override
                public void onFailure(@NotNull Throwable t) {
                    log.error(t.toString());
                    new Popup<>().warning(t.toString()).show();
                }
            });
        }).closeButtonText(Res.get("shared.cancel")).show();
    } catch (InsufficientMoneyException e) {
        BSFormatter formatter = e instanceof InsufficientBsqException ? bsqFormatter : btcFormatter;
        new Popup<>().warning(Res.get("dao.proposal.create.missingFunds", formatter.formatCoinWithCode(e.missing))).show();
    } catch (CompensationAmountException e) {
        new Popup<>().warning(Res.get("validation.bsq.amountBelowMinAmount", bsqFormatter.formatCoinWithCode(e.required))).show();
    } catch (TransactionVerificationException | WalletException e) {
        log.error(e.toString());
        e.printStackTrace();
        new Popup<>().warning(e.toString()).show();
    } catch (ChangeBelowDustException e) {
        // TODO
        e.printStackTrace();
    }
}
Also used : WalletException(bisq.core.btc.exceptions.WalletException) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) InsufficientMoneyException(org.bitcoinj.core.InsufficientMoneyException) BSFormatter(bisq.desktop.util.BSFormatter) CompensationAmountException(bisq.core.dao.proposal.compensation.CompensationAmountException) ChangeBelowDustException(bisq.core.btc.wallet.ChangeBelowDustException) Coin(org.bitcoinj.core.Coin) Transaction(org.bitcoinj.core.Transaction) InsufficientBsqException(bisq.core.btc.wallet.InsufficientBsqException) Popup(bisq.desktop.main.overlays.popups.Popup) Proposal(bisq.core.dao.proposal.Proposal)

Example 7 with TransactionVerificationException

use of bisq.core.btc.exceptions.TransactionVerificationException 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)

Example 8 with TransactionVerificationException

use of bisq.core.btc.exceptions.TransactionVerificationException 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, FutureCallback<Transaction> 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)

Example 9 with TransactionVerificationException

use of bisq.core.btc.exceptions.TransactionVerificationException in project bisq-core by bisq-network.

the class WalletService method checkScriptSig.

public static void checkScriptSig(Transaction transaction, TransactionInput input, int inputIndex) throws TransactionVerificationException {
    try {
        log.trace("Verifies that this script (interpreted as a scriptSig) correctly spends the given scriptPubKey. Check input at index: " + inputIndex);
        checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null");
        input.getScriptSig().correctlySpends(transaction, inputIndex, input.getConnectedOutput().getScriptPubKey(), Script.ALL_VERIFY_FLAGS);
    } catch (Throwable t) {
        t.printStackTrace();
        log.error(t.getMessage());
        throw new TransactionVerificationException(t);
    }
}
Also used : TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException)

Example 10 with TransactionVerificationException

use of bisq.core.btc.exceptions.TransactionVerificationException in project bisq-desktop by bisq-network.

the class DisputeSummaryWindow method addButtons.

private void addButtons(Contract contract) {
    Tuple2<Button, Button> tuple = add2ButtonsAfterGroup(gridPane, ++rowIndex, Res.get("disputeSummaryWindow.close.button"), Res.get("shared.cancel"));
    Button closeTicketButton = tuple.first;
    closeTicketButton.disableProperty().bind(Bindings.createBooleanBinding(() -> tradeAmountToggleGroup.getSelectedToggle() == null || summaryNotesTextArea.getText() == null || summaryNotesTextArea.getText().length() == 0 || !isPayoutAmountValid(), tradeAmountToggleGroup.selectedToggleProperty(), summaryNotesTextArea.textProperty(), buyerPayoutAmountInputTextField.textProperty(), sellerPayoutAmountInputTextField.textProperty()));
    Button cancelButton = tuple.second;
    final Dispute finalPeersDispute = peersDisputeOptional.get();
    closeTicketButton.setOnAction(e -> {
        if (dispute.getDepositTxSerialized() != null) {
            try {
                AddressEntry arbitratorAddressEntry = walletService.getOrCreateAddressEntry(AddressEntry.Context.ARBITRATOR);
                disputeResult.setArbitratorPubKey(walletService.getOrCreateAddressEntry(AddressEntry.Context.ARBITRATOR).getPubKey());
                /* byte[] depositTxSerialized,
                    Coin buyerPayoutAmount,
                    Coin sellerPayoutAmount,
                    Coin arbitratorPayoutAmount,
                    String buyerAddressString,
                    String sellerAddressString,
                    AddressEntry arbitratorAddressEntry,
                    byte[] buyerPubKey,
                    byte[] sellerPubKey,
                    byte[] arbitratorPubKey)
                    */
                byte[] arbitratorSignature = tradeWalletService.arbitratorSignsDisputedPayoutTx(dispute.getDepositTxSerialized(), disputeResult.getBuyerPayoutAmount(), disputeResult.getSellerPayoutAmount(), contract.getBuyerPayoutAddressString(), contract.getSellerPayoutAddressString(), arbitratorAddressEntry.getKeyPair(), contract.getBuyerMultiSigPubKey(), contract.getSellerMultiSigPubKey(), arbitratorAddressEntry.getPubKey());
                disputeResult.setArbitratorSignature(arbitratorSignature);
                closeTicketButton.disableProperty().unbind();
                dispute.setDisputeResult(disputeResult);
                disputeResult.setLoserPublisher(isLoserPublisherCheckBox.isSelected());
                disputeResult.setCloseDate(new Date());
                String text = Res.get("disputeSummaryWindow.close.msg", formatter.formatDateTime(disputeResult.getCloseDate()), role, formatter.booleanToYesNo(disputeResult.tamperProofEvidenceProperty().get()), role, formatter.booleanToYesNo(disputeResult.idVerificationProperty().get()), role, formatter.booleanToYesNo(disputeResult.screenCastProperty().get()), formatter.formatCoinWithCode(disputeResult.getBuyerPayoutAmount()), formatter.formatCoinWithCode(disputeResult.getSellerPayoutAmount()), disputeResult.summaryNotesProperty().get());
                dispute.setIsClosed(true);
                disputeManager.sendDisputeResultMessage(disputeResult, dispute, text);
                if (!finalPeersDispute.isClosed())
                    UserThread.runAfter(() -> new Popup<>().attention(Res.get("disputeSummaryWindow.close.closePeer")).show(), Transitions.DEFAULT_DURATION, TimeUnit.MILLISECONDS);
                hide();
                finalizeDisputeHandlerOptional.ifPresent(Runnable::run);
            } catch (AddressFormatException | TransactionVerificationException e2) {
                e2.printStackTrace();
            }
        } else {
            log.warn("dispute.getDepositTxSerialized is null");
        }
    });
    cancelButton.setOnAction(e -> {
        dispute.setDisputeResult(disputeResult);
        hide();
    });
}
Also used : AddressFormatException(org.bitcoinj.core.AddressFormatException) Button(javafx.scene.control.Button) AutoTooltipRadioButton(bisq.desktop.components.AutoTooltipRadioButton) RadioButton(javafx.scene.control.RadioButton) AddressEntry(bisq.core.btc.AddressEntry) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) Popup(bisq.desktop.main.overlays.popups.Popup) Dispute(bisq.core.arbitration.Dispute) Date(java.util.Date)

Aggregations

TransactionVerificationException (bisq.core.btc.exceptions.TransactionVerificationException)10 Transaction (org.bitcoinj.core.Transaction)7 WalletException (bisq.core.btc.exceptions.WalletException)6 AddressFormatException (org.bitcoinj.core.AddressFormatException)4 Coin (org.bitcoinj.core.Coin)4 InsufficientMoneyException (org.bitcoinj.core.InsufficientMoneyException)4 ChangeBelowDustException (bisq.core.btc.wallet.ChangeBelowDustException)3 Popup (bisq.desktop.main.overlays.popups.Popup)3 FutureCallback (com.google.common.util.concurrent.FutureCallback)3 Nullable (javax.annotation.Nullable)3 CryptoException (bisq.common.crypto.CryptoException)2 BisqEnvironment (bisq.core.app.BisqEnvironment)2 AddressEntry (bisq.core.btc.AddressEntry)2 RawTransactionInput (bisq.core.btc.data.RawTransactionInput)2 InsufficientBsqException (bisq.core.btc.wallet.InsufficientBsqException)2 Proposal (bisq.core.dao.proposal.Proposal)2 InvalidProtocolBufferException (com.google.protobuf.InvalidProtocolBufferException)2 Optional (java.util.Optional)2 TransactionInput (org.bitcoinj.core.TransactionInput)2 TransactionOutPoint (org.bitcoinj.core.TransactionOutPoint)2