Search in sources :

Example 91 with Transaction

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

the class TxBroadcaster method broadcastTx.

public static void broadcastTx(Wallet wallet, PeerGroup peerGroup, Transaction tx, Callback callback, int delayInSec) {
    Timer timeoutTimer;
    final String txId = tx.getHashAsString();
    if (!broadcastTimerMap.containsKey(txId)) {
        timeoutTimer = UserThread.runAfter(() -> {
            log.warn("Broadcast of tx {} not completed after {} sec.", txId, delayInSec);
            stopAndRemoveTimer(txId);
            UserThread.execute(() -> callback.onTimeout(new TxBroadcastTimeoutException(tx, delayInSec, wallet)));
        }, delayInSec);
        broadcastTimerMap.put(txId, timeoutTimer);
    } else {
        // Would be due a wrong way how to use the API (calling 2 times a broadcast with same tx).
        stopAndRemoveTimer(txId);
        UserThread.execute(() -> callback.onFailure(new TxBroadcastException("We got broadcastTx called with a tx " + "which has an open timeoutTimer. txId=" + txId, txId)));
    }
    Futures.addCallback(peerGroup.broadcastTransaction(tx).future(), new FutureCallback<Transaction>() {

        @Override
        public void onSuccess(@Nullable Transaction result) {
            if (result != null) {
                if (txId.equals(result.getHashAsString())) {
                    // We expect that there is still a timeout in our map, otherwise the timeout got triggered
                    if (broadcastTimerMap.containsKey(txId)) {
                        wallet.maybeCommitTx(tx);
                        stopAndRemoveTimer(txId);
                        // At regtest we get called immediately back but we want to make sure that the handler is not called
                        // before the caller is finished.
                        UserThread.execute(() -> callback.onSuccess(tx));
                    } else {
                        stopAndRemoveTimer(txId);
                        log.warn("We got an onSuccess callback for a broadcast which already triggered the timeout.", txId);
                    }
                } else {
                    stopAndRemoveTimer(txId);
                    UserThread.execute(() -> callback.onTxMalleability(new TxMalleabilityException(tx, result)));
                }
            } else {
                stopAndRemoveTimer(txId);
                UserThread.execute(() -> callback.onFailure(new TxBroadcastException("Transaction returned from the " + "broadcastTransaction call back is null.", txId)));
            }
        }

        @Override
        public void onFailure(@NotNull Throwable throwable) {
            stopAndRemoveTimer(txId);
            UserThread.execute(() -> callback.onFailure(new TxBroadcastException("We got an onFailure from " + "the peerGroup.broadcastTransaction callback.", throwable)));
        }
    });
}
Also used : Timer(bisq.common.Timer) Transaction(org.bitcoinj.core.Transaction)

Example 92 with Transaction

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

the class DisputeManager method onDisputedPayoutTxMessage.

// Losing trader or in case of 50/50 the seller gets the tx sent from the winner or buyer
private void onDisputedPayoutTxMessage(PeerPublishedDisputePayoutTxMessage peerPublishedDisputePayoutTxMessage) {
    final String uid = peerPublishedDisputePayoutTxMessage.getUid();
    final String tradeId = peerPublishedDisputePayoutTxMessage.getTradeId();
    Optional<Dispute> disputeOptional = findOwnDispute(tradeId);
    if (!disputeOptional.isPresent()) {
        log.debug("We got a peerPublishedPayoutTxMessage but we don't have a matching dispute. TradeId = " + tradeId);
        if (!delayMsgMap.containsKey(uid)) {
            // We delay 3 sec. to be sure the close msg gets added first
            Timer timer = UserThread.runAfter(() -> onDisputedPayoutTxMessage(peerPublishedDisputePayoutTxMessage), 3);
            delayMsgMap.put(uid, timer);
        } else {
            log.warn("We got a peerPublishedPayoutTxMessage after we already repeated to apply the message after a delay. " + "That should never happen. TradeId = " + tradeId);
        }
        return;
    }
    Dispute dispute = disputeOptional.get();
    final Contract contract = dispute.getContract();
    PubKeyRing ownPubKeyRing = keyRing.getPubKeyRing();
    boolean isBuyer = ownPubKeyRing.equals(contract.getBuyerPubKeyRing());
    PubKeyRing peersPubKeyRing = isBuyer ? contract.getSellerPubKeyRing() : contract.getBuyerPubKeyRing();
    cleanupRetryMap(uid);
    Transaction walletTx = tradeWalletService.addTxToWallet(peerPublishedDisputePayoutTxMessage.getTransaction());
    dispute.setDisputePayoutTxId(walletTx.getHashAsString());
    BtcWalletService.printTx("Disputed payoutTx received from peer", walletTx);
    // We can only send the ack msg if we have the peersPubKeyRing which requires the dispute
    sendAckMessage(peerPublishedDisputePayoutTxMessage, peersPubKeyRing, true, null);
}
Also used : Timer(bisq.common.Timer) Transaction(org.bitcoinj.core.Transaction) PubKeyRing(bisq.common.crypto.PubKeyRing) Contract(bisq.core.trade.Contract)

Example 93 with Transaction

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

the class DisputeManager method onDisputeResultMessage.

// We get that message at both peers. The dispute object is in context of the trader
private void onDisputeResultMessage(DisputeResultMessage disputeResultMessage) {
    String errorMessage = null;
    boolean success = false;
    PubKeyRing arbitratorsPubKeyRing = null;
    DisputeResult disputeResult = disputeResultMessage.getDisputeResult();
    if (isArbitrator(disputeResult)) {
        log.error("Arbitrator received disputeResultMessage. That must never happen.");
        return;
    }
    final String tradeId = disputeResult.getTradeId();
    Optional<Dispute> disputeOptional = findDispute(tradeId, disputeResult.getTraderId());
    final String uid = disputeResultMessage.getUid();
    if (!disputeOptional.isPresent()) {
        log.debug("We got a dispute result msg but we don't have a matching dispute. " + "That might happen when we get the disputeResultMessage before the dispute was created. " + "We try again after 2 sec. to apply the disputeResultMessage. TradeId = " + tradeId);
        if (!delayMsgMap.containsKey(uid)) {
            // We delay2 sec. to be sure the comm. msg gets added first
            Timer timer = UserThread.runAfter(() -> onDisputeResultMessage(disputeResultMessage), 2);
            delayMsgMap.put(uid, timer);
        } else {
            log.warn("We got a dispute result msg after we already repeated to apply the message after a delay. " + "That should never happen. TradeId = " + tradeId);
        }
        return;
    }
    try {
        cleanupRetryMap(uid);
        Dispute dispute = disputeOptional.get();
        arbitratorsPubKeyRing = dispute.getArbitratorPubKeyRing();
        DisputeCommunicationMessage disputeCommunicationMessage = disputeResult.getDisputeCommunicationMessage();
        if (!dispute.getDisputeCommunicationMessages().contains(disputeCommunicationMessage))
            dispute.addDisputeCommunicationMessage(disputeCommunicationMessage);
        else if (disputeCommunicationMessage != null)
            log.warn("We got a dispute mail msg what we have already stored. TradeId = " + disputeCommunicationMessage.getTradeId());
        dispute.setIsClosed(true);
        if (dispute.disputeResultProperty().get() != null)
            log.warn("We got already a dispute result. That should only happen if a dispute needs to be closed " + "again because the first close did not succeed. TradeId = " + tradeId);
        dispute.setDisputeResult(disputeResult);
        // We need to avoid publishing the tx from both traders as it would create problems with zero confirmation withdrawals
        // There would be different transactions if both sign and publish (signers: once buyer+arb, once seller+arb)
        // The tx publisher is the winner or in case both get 50% the buyer, as the buyer has more inventive to publish the tx as he receives
        // more BTC as he has deposited
        final Contract contract = dispute.getContract();
        boolean isBuyer = keyRing.getPubKeyRing().equals(contract.getBuyerPubKeyRing());
        DisputeResult.Winner publisher = disputeResult.getWinner();
        // Default isLoserPublisher is set to false
        if (disputeResult.isLoserPublisher()) {
            // we invert the logic
            if (publisher == DisputeResult.Winner.BUYER)
                publisher = DisputeResult.Winner.SELLER;
            else if (publisher == DisputeResult.Winner.SELLER)
                publisher = DisputeResult.Winner.BUYER;
        }
        if ((isBuyer && publisher == DisputeResult.Winner.BUYER) || (!isBuyer && publisher == DisputeResult.Winner.SELLER)) {
            final Optional<Trade> tradeOptional = tradeManager.getTradeById(tradeId);
            Transaction payoutTx = null;
            if (tradeOptional.isPresent()) {
                payoutTx = tradeOptional.get().getPayoutTx();
            } else {
                final Optional<Tradable> tradableOptional = closedTradableManager.getTradableById(tradeId);
                if (tradableOptional.isPresent() && tradableOptional.get() instanceof Trade) {
                    payoutTx = ((Trade) tradableOptional.get()).getPayoutTx();
                }
            }
            if (payoutTx == null) {
                if (dispute.getDepositTxSerialized() != null) {
                    byte[] multiSigPubKey = isBuyer ? contract.getBuyerMultiSigPubKey() : contract.getSellerMultiSigPubKey();
                    DeterministicKey multiSigKeyPair = walletService.getMultiSigKeyPair(dispute.getTradeId(), multiSigPubKey);
                    Transaction signedDisputedPayoutTx = tradeWalletService.traderSignAndFinalizeDisputedPayoutTx(dispute.getDepositTxSerialized(), disputeResult.getArbitratorSignature(), disputeResult.getBuyerPayoutAmount(), disputeResult.getSellerPayoutAmount(), contract.getBuyerPayoutAddressString(), contract.getSellerPayoutAddressString(), multiSigKeyPair, contract.getBuyerMultiSigPubKey(), contract.getSellerMultiSigPubKey(), disputeResult.getArbitratorPubKey());
                    Transaction committedDisputedPayoutTx = tradeWalletService.addTxToWallet(signedDisputedPayoutTx);
                    tradeWalletService.broadcastTx(committedDisputedPayoutTx, new TxBroadcaster.Callback() {

                        @Override
                        public void onSuccess(Transaction transaction) {
                            // after successful publish we send peer the tx
                            dispute.setDisputePayoutTxId(transaction.getHashAsString());
                            sendPeerPublishedPayoutTxMessage(transaction, dispute, contract);
                            // set state after payout as we call swapTradeEntryToAvailableEntry
                            if (tradeManager.getTradeById(dispute.getTradeId()).isPresent())
                                tradeManager.closeDisputedTrade(dispute.getTradeId());
                            else {
                                Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(dispute.getTradeId());
                                openOfferOptional.ifPresent(openOffer -> openOfferManager.closeOpenOffer(openOffer.getOffer()));
                            }
                        }

                        @Override
                        public void onFailure(TxBroadcastException exception) {
                            log.error(exception.getMessage());
                        }
                    }, 15);
                    success = true;
                } else {
                    errorMessage = "DepositTx is null. TradeId = " + tradeId;
                    log.warn(errorMessage);
                    success = false;
                }
            } else {
                log.warn("We got already a payout tx. That might be the case if the other peer did not get the " + "payout tx and opened a dispute. TradeId = " + tradeId);
                dispute.setDisputePayoutTxId(payoutTx.getHashAsString());
                sendPeerPublishedPayoutTxMessage(payoutTx, dispute, contract);
                success = true;
            }
        } else {
            log.trace("We don't publish the tx as we are not the winning party.");
            // Clean up tangling trades
            if (dispute.disputeResultProperty().get() != null && dispute.isClosed() && tradeManager.getTradeById(dispute.getTradeId()).isPresent()) {
                tradeManager.closeDisputedTrade(dispute.getTradeId());
            }
            success = true;
        }
    } catch (AddressFormatException | WalletException | TransactionVerificationException e) {
        e.printStackTrace();
        errorMessage = "Error at traderSignAndFinalizeDisputedPayoutTx " + e.toString();
        log.error(errorMessage);
        success = false;
        throw new RuntimeException(errorMessage);
    } finally {
        if (arbitratorsPubKeyRing != null) {
            // We use the disputeCommunicationMessage as we only persist those not the disputeResultMessage.
            // If we would use the disputeResultMessage we could not lookup for the msg when we receive the AckMessage.
            DisputeCommunicationMessage disputeCommunicationMessage = disputeResultMessage.getDisputeResult().getDisputeCommunicationMessage();
            sendAckMessage(disputeCommunicationMessage, arbitratorsPubKeyRing, success, errorMessage);
        }
    }
}
Also used : DisputeMessage(bisq.core.arbitration.messages.DisputeMessage) PubKeyRing(bisq.common.crypto.PubKeyRing) Arrays(java.util.Arrays) Transaction(org.bitcoinj.core.Transaction) OpenOffer(bisq.core.offer.OpenOffer) AckMessageSourceType(bisq.network.p2p.AckMessageSourceType) Inject(com.google.inject.Inject) LoggerFactory(org.slf4j.LoggerFactory) Contract(bisq.core.trade.Contract) ListChangeListener(javafx.collections.ListChangeListener) Res(bisq.core.locale.Res) BootstrapListener(bisq.network.p2p.BootstrapListener) SimpleIntegerProperty(javafx.beans.property.SimpleIntegerProperty) Map(java.util.Map) DeterministicKey(org.bitcoinj.crypto.DeterministicKey) AddressFormatException(org.bitcoinj.core.AddressFormatException) WalletException(bisq.core.btc.exceptions.WalletException) TxBroadcastException(bisq.core.btc.wallet.TxBroadcastException) ClosedTradableManager(bisq.core.trade.closed.ClosedTradableManager) NetworkEnvelope(bisq.common.proto.network.NetworkEnvelope) P2PService(bisq.network.p2p.P2PService) PersistedDataHost(bisq.common.proto.persistable.PersistedDataHost) Subscription(org.fxmisc.easybind.Subscription) UUID(java.util.UUID) CopyOnWriteArraySet(java.util.concurrent.CopyOnWriteArraySet) Collectors(java.util.stream.Collectors) AckMessage(bisq.network.p2p.AckMessage) OpenNewDisputeMessage(bisq.core.arbitration.messages.OpenNewDisputeMessage) List(java.util.List) DisputeResultMessage(bisq.core.arbitration.messages.DisputeResultMessage) Stream(java.util.stream.Stream) TradeWalletService(bisq.core.btc.wallet.TradeWalletService) WalletsSetup(bisq.core.btc.wallet.WalletsSetup) TradeManager(bisq.core.trade.TradeManager) NodeAddress(bisq.network.p2p.NodeAddress) UserThread(bisq.common.UserThread) Optional(java.util.Optional) ObservableList(javafx.collections.ObservableList) BtcWalletService(bisq.core.btc.wallet.BtcWalletService) Getter(lombok.Getter) Timer(bisq.common.Timer) HashMap(java.util.HashMap) Tradable(bisq.core.trade.Tradable) TxBroadcaster(bisq.core.btc.wallet.TxBroadcaster) IntegerProperty(javafx.beans.property.IntegerProperty) PersistenceProtoResolver(bisq.common.proto.persistable.PersistenceProtoResolver) ArrayList(java.util.ArrayList) Tuple2(bisq.common.util.Tuple2) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) Named(javax.inject.Named) Nullable(javax.annotation.Nullable) PeerOpenedDisputeMessage(bisq.core.arbitration.messages.PeerOpenedDisputeMessage) PeerPublishedDisputePayoutTxMessage(bisq.core.arbitration.messages.PeerPublishedDisputePayoutTxMessage) Logger(org.slf4j.Logger) Trade(bisq.core.trade.Trade) FaultHandler(bisq.common.handlers.FaultHandler) ResultHandler(bisq.common.handlers.ResultHandler) OpenOfferManager(bisq.core.offer.OpenOfferManager) SendMailboxMessageListener(bisq.network.p2p.SendMailboxMessageListener) File(java.io.File) DisputeCommunicationMessage(bisq.core.arbitration.messages.DisputeCommunicationMessage) EasyBind(org.fxmisc.easybind.EasyBind) Storage(bisq.common.storage.Storage) KeyRing(bisq.common.crypto.KeyRing) DecryptedMessageWithPubKey(bisq.network.p2p.DecryptedMessageWithPubKey) Tradable(bisq.core.trade.Tradable) Trade(bisq.core.trade.Trade) PubKeyRing(bisq.common.crypto.PubKeyRing) DisputeCommunicationMessage(bisq.core.arbitration.messages.DisputeCommunicationMessage) WalletException(bisq.core.btc.exceptions.WalletException) AddressFormatException(org.bitcoinj.core.AddressFormatException) Optional(java.util.Optional) TransactionVerificationException(bisq.core.btc.exceptions.TransactionVerificationException) TxBroadcastException(bisq.core.btc.wallet.TxBroadcastException) TxBroadcaster(bisq.core.btc.wallet.TxBroadcaster) Timer(bisq.common.Timer) Transaction(org.bitcoinj.core.Transaction) Contract(bisq.core.trade.Contract) DeterministicKey(org.bitcoinj.crypto.DeterministicKey)

Example 94 with Transaction

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

the class BsqWalletService method getPreparedUnlockTx.

// /////////////////////////////////////////////////////////////////////////////////////////
// Unlock bond tx
// /////////////////////////////////////////////////////////////////////////////////////////
public Transaction getPreparedUnlockTx(TxOutput lockedTxOutput) throws AddressFormatException {
    Transaction tx = new Transaction(params);
    // Unlocking means spending the full value of the locked txOutput to another txOutput with the same value
    Coin amountToUnlock = Coin.valueOf(lockedTxOutput.getValue());
    checkArgument(Restrictions.isAboveDust(amountToUnlock), "The amount is too low (dust limit).");
    Transaction lockupTx = getTransaction(lockedTxOutput.getTxId());
    checkNotNull(lockupTx, "lockupTx must not be null");
    TransactionOutPoint outPoint = new TransactionOutPoint(params, lockedTxOutput.getIndex(), lockupTx);
    // Input is not signed yet so we use new byte[]{}
    tx.addInput(new TransactionInput(params, tx, new byte[] {}, outPoint, amountToUnlock));
    tx.addOutput(new TransactionOutput(params, tx, amountToUnlock, getUnusedAddress()));
    printTx("prepareUnlockTx", tx);
    return tx;
}
Also used : Coin(org.bitcoinj.core.Coin) TransactionOutput(org.bitcoinj.core.TransactionOutput) Transaction(org.bitcoinj.core.Transaction) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) TransactionInput(org.bitcoinj.core.TransactionInput)

Example 95 with Transaction

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

the class BsqWalletService method getPreparedVoteRevealTx.

// /////////////////////////////////////////////////////////////////////////////////////////
// MyVote reveal tx
// /////////////////////////////////////////////////////////////////////////////////////////
public Transaction getPreparedVoteRevealTx(TxOutput stakeTxOutput) {
    Transaction tx = new Transaction(params);
    final Coin stake = Coin.valueOf(stakeTxOutput.getValue());
    Transaction blindVoteTx = getTransaction(stakeTxOutput.getTxId());
    checkNotNull(blindVoteTx, "blindVoteTx must not be null");
    TransactionOutPoint outPoint = new TransactionOutPoint(params, stakeTxOutput.getIndex(), blindVoteTx);
    // Input is not signed yet so we use new byte[]{}
    tx.addInput(new TransactionInput(params, tx, new byte[] {}, outPoint, stake));
    tx.addOutput(new TransactionOutput(params, tx, stake, getUnusedAddress()));
    // printTx("getPreparedVoteRevealTx", tx);
    return tx;
}
Also used : Coin(org.bitcoinj.core.Coin) TransactionOutput(org.bitcoinj.core.TransactionOutput) Transaction(org.bitcoinj.core.Transaction) TransactionOutPoint(org.bitcoinj.core.TransactionOutPoint) 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