use of bisq.core.btc.wallet.TxBroadcastException 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);
}
}
}
use of bisq.core.btc.wallet.TxBroadcastException in project bisq-core by bisq-network.
the class SellerBroadcastPayoutTx method run.
@Override
protected void run() {
try {
runInterceptHook();
Transaction payoutTx = trade.getPayoutTx();
checkNotNull(payoutTx, "payoutTx must not be null");
TransactionConfidence.ConfidenceType confidenceType = payoutTx.getConfidence().getConfidenceType();
log.debug("payoutTx confidenceType:" + confidenceType);
if (confidenceType.equals(TransactionConfidence.ConfidenceType.BUILDING) || confidenceType.equals(TransactionConfidence.ConfidenceType.PENDING)) {
log.debug("payoutTx was already published. confidenceType:" + confidenceType);
trade.setState(Trade.State.SELLER_PUBLISHED_PAYOUT_TX);
complete();
} else {
processModel.getTradeWalletService().broadcastTx(payoutTx, new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
if (!completed) {
log.debug("BroadcastTx succeeded. Transaction:" + transaction);
trade.setState(Trade.State.SELLER_PUBLISHED_PAYOUT_TX);
complete();
} else {
log.warn("We got the onSuccess callback called after the timeout has been triggered a complete().");
}
}
@Override
public void onFailure(TxBroadcastException exception) {
if (!completed) {
log.error("BroadcastTx failed. Error:" + exception.getMessage());
failed(exception);
} else {
log.warn("We got the onFailure callback called after the timeout has been triggered a complete().");
}
}
});
}
} catch (Throwable t) {
failed(t);
}
}
use of bisq.core.btc.wallet.TxBroadcastException in project bisq-core by bisq-network.
the class SellerAsTakerSignAndPublishDepositTx method run.
@Override
protected void run() {
try {
runInterceptHook();
log.debug("\n\n------------------------------------------------------------\n" + "Contract as json\n" + trade.getContractAsJson() + "\n------------------------------------------------------------\n");
byte[] contractHash = Hash.getSha256Hash(trade.getContractAsJson());
trade.setContractHash(contractHash);
List<RawTransactionInput> sellerInputs = checkNotNull(processModel.getRawTransactionInputs(), "sellerInputs must not be null");
BtcWalletService walletService = processModel.getBtcWalletService();
String id = processModel.getOffer().getId();
Optional<AddressEntry> addressEntryOptional = walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG);
checkArgument(addressEntryOptional.isPresent(), "addressEntryOptional must be present");
AddressEntry sellerMultiSigAddressEntry = addressEntryOptional.get();
byte[] sellerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(sellerMultiSigPubKey, sellerMultiSigAddressEntry.getPubKey()), "sellerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
Coin sellerInput = Coin.valueOf(sellerInputs.stream().mapToLong(input -> input.value).sum());
sellerMultiSigAddressEntry.setCoinLockedInMultiSig(sellerInput.subtract(trade.getTxFee().multiply(2)));
walletService.saveAddressEntryList();
TradingPeer tradingPeer = processModel.getTradingPeer();
Transaction depositTx = processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(true, contractHash, processModel.getPreparedDepositTx(), tradingPeer.getRawTransactionInputs(), sellerInputs, tradingPeer.getMultiSigPubKey(), sellerMultiSigPubKey, trade.getArbitratorBtcPubKey(), new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
if (!completed) {
// We set the depositTx before we change the state as the state change triggers code
// which expected the tx to be available. That case will usually never happen as the
// callback is called after the method call has returned but in some test scenarios
// with regtest we run into such issues, thus fixing it to make it more stict seems
// reasonable.
trade.setDepositTx(transaction);
log.trace("takerSignsAndPublishesDepositTx succeeded " + transaction);
trade.setState(Trade.State.TAKER_PUBLISHED_DEPOSIT_TX);
walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.RESERVED_FOR_TRADE);
complete();
} else {
log.warn("We got the onSuccess callback called after the timeout has been triggered a complete().");
}
}
@Override
public void onFailure(TxBroadcastException exception) {
if (!completed) {
failed(exception);
} else {
log.warn("We got the onFailure callback called after the timeout has been triggered a complete().");
}
}
});
if (trade.getDepositTx() == null) {
// We set the deposit tx in case we get the onFailure called. We cannot set it in the onFailure
// callback as the tx is returned by the method call where the callback is used as an argument.
trade.setDepositTx(depositTx);
}
} catch (Throwable t) {
final Contract contract = trade.getContract();
if (contract != null)
contract.printDiff(processModel.getTradingPeer().getContractAsJson());
failed(t);
}
}
use of bisq.core.btc.wallet.TxBroadcastException in project bisq-core by bisq-network.
the class CreateMakerFeeTx method run.
@Override
protected void run() {
Offer offer = model.getOffer();
try {
runInterceptHook();
String id = offer.getId();
BtcWalletService walletService = model.getWalletService();
NodeAddress selectedArbitratorNodeAddress = ArbitratorSelectionRule.select(model.getUser().getAcceptedArbitratorAddresses(), model.getOffer());
log.debug("selectedArbitratorAddress " + selectedArbitratorNodeAddress);
Arbitrator selectedArbitrator = model.getUser().getAcceptedArbitratorByAddress(selectedArbitratorNodeAddress);
checkNotNull(selectedArbitrator, "selectedArbitrator must not be null at CreateOfferFeeTx");
Address fundingAddress = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.OFFER_FUNDING).getAddress();
Address reservedForTradeAddress = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.RESERVED_FOR_TRADE).getAddress();
Address changeAddress = walletService.getFreshAddressEntry().getAddress();
final TradeWalletService tradeWalletService = model.getTradeWalletService();
if (offer.isCurrencyForMakerFeeBtc()) {
tradeFeeTx = tradeWalletService.createBtcTradingFeeTx(fundingAddress, reservedForTradeAddress, changeAddress, model.getReservedFundsForOffer(), model.isUseSavingsWallet(), offer.getMakerFee(), offer.getTxFee(), selectedArbitrator.getBtcAddress(), new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
// we delay one render frame to be sure we don't get called before the method call has
// returned (tradeFeeTx would be null in that case)
UserThread.execute(() -> {
if (!completed) {
offer.setOfferFeePaymentTxId(tradeFeeTx.getHashAsString());
model.setTransaction(tradeFeeTx);
walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING);
model.getOffer().setState(Offer.State.OFFER_FEE_PAID);
complete();
} else {
log.warn("We got the onSuccess callback called after the timeout has been triggered a complete().");
}
});
}
@Override
public void onFailure(TxBroadcastException exception) {
if (!completed) {
failed(exception);
} else {
log.warn("We got the onFailure callback called after the timeout has been triggered a complete().");
}
}
});
} else {
final BsqWalletService bsqWalletService = model.getBsqWalletService();
Transaction preparedBurnFeeTx = model.getBsqWalletService().getPreparedBurnFeeTx(offer.getMakerFee());
Transaction txWithBsqFee = tradeWalletService.completeBsqTradingFeeTx(preparedBurnFeeTx, fundingAddress, reservedForTradeAddress, changeAddress, model.getReservedFundsForOffer(), model.isUseSavingsWallet(), offer.getTxFee());
Transaction signedTx = model.getBsqWalletService().signTx(txWithBsqFee);
WalletService.checkAllScriptSignaturesForTx(signedTx);
bsqWalletService.commitTx(signedTx);
// We need to create another instance, otherwise the tx would trigger an invalid state exception
// if it gets committed 2 times
tradeWalletService.commitTx(tradeWalletService.getClonedTransaction(signedTx));
bsqWalletService.broadcastTx(signedTx, new TxBroadcaster.Callback() {
@Override
public void onSuccess(@Nullable Transaction transaction) {
if (transaction != null) {
offer.setOfferFeePaymentTxId(transaction.getHashAsString());
model.setTransaction(transaction);
log.debug("onSuccess, offerId={}, OFFER_FUNDING", id);
walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.OFFER_FUNDING);
log.debug("Successfully sent tx with id " + transaction.getHashAsString());
model.getOffer().setState(Offer.State.OFFER_FEE_PAID);
complete();
}
}
@Override
public void onFailure(TxBroadcastException exception) {
log.error(exception.toString());
exception.printStackTrace();
offer.setErrorMessage("An error occurred.\n" + "Error message:\n" + exception.getMessage());
failed(exception);
}
});
}
} catch (Throwable t) {
offer.setErrorMessage("An error occurred.\n" + "Error message:\n" + t.getMessage());
failed(t);
}
}
use of bisq.core.btc.wallet.TxBroadcastException in project bisq-core by bisq-network.
the class BuyerAsTakerSignAndPublishDepositTx method run.
@Override
protected void run() {
try {
runInterceptHook();
log.debug("\n\n------------------------------------------------------------\n" + "Contract as json\n" + trade.getContractAsJson() + "\n------------------------------------------------------------\n");
byte[] contractHash = Hash.getSha256Hash(trade.getContractAsJson());
trade.setContractHash(contractHash);
List<RawTransactionInput> buyerInputs = checkNotNull(processModel.getRawTransactionInputs(), "buyerInputs must not be null");
BtcWalletService walletService = processModel.getBtcWalletService();
String id = processModel.getOffer().getId();
Optional<AddressEntry> addressEntryOptional = walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG);
checkArgument(addressEntryOptional.isPresent(), "addressEntryOptional must be present");
AddressEntry buyerMultiSigAddressEntry = addressEntryOptional.get();
Coin buyerInput = Coin.valueOf(buyerInputs.stream().mapToLong(input -> input.value).sum());
buyerMultiSigAddressEntry.setCoinLockedInMultiSig(buyerInput.subtract(trade.getTxFee().multiply(2)));
walletService.saveAddressEntryList();
TradingPeer tradingPeer = processModel.getTradingPeer();
byte[] buyerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(buyerMultiSigPubKey, buyerMultiSigAddressEntry.getPubKey()), "buyerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
Transaction depositTx = processModel.getTradeWalletService().takerSignsAndPublishesDepositTx(false, contractHash, processModel.getPreparedDepositTx(), buyerInputs, tradingPeer.getRawTransactionInputs(), buyerMultiSigPubKey, tradingPeer.getMultiSigPubKey(), trade.getArbitratorBtcPubKey(), new TxBroadcaster.Callback() {
@Override
public void onSuccess(Transaction transaction) {
if (!completed) {
// We set the depositTx before we change the state as the state change triggers code
// which expected the tx to be available. That case will usually never happen as the
// callback is called after the method call has returned but in some test scenarios
// with regtest we run into such issues, thus fixing it to make it more stict seems
// reasonable.
trade.setDepositTx(transaction);
log.trace("takerSignsAndPublishesDepositTx succeeded " + transaction);
trade.setState(Trade.State.TAKER_PUBLISHED_DEPOSIT_TX);
walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.RESERVED_FOR_TRADE);
complete();
} else {
log.warn("We got the onSuccess callback called after the timeout has been triggered a complete().");
}
}
@Override
public void onFailure(TxBroadcastException exception) {
if (!completed) {
failed(exception);
} else {
log.warn("We got the onFailure callback called after the timeout has been triggered a complete().");
}
}
});
if (trade.getDepositTx() == null) {
// We set the deposit tx in case we get the onFailure called. We cannot set it in the onFailure
// callback as the tx is returned by the method call where the callback is used as an argument.
trade.setDepositTx(depositTx);
}
} catch (Throwable t) {
failed(t);
}
}
Aggregations