use of bisq.core.btc.InsufficientFundsException in project bisq-core by bisq-network.
the class BtcWalletService method getFeeEstimationTransactionForMultipleAddresses.
public Transaction getFeeEstimationTransactionForMultipleAddresses(Set<String> fromAddresses, Coin amount) throws AddressFormatException, AddressEntryException, InsufficientFundsException {
Set<AddressEntry> addressEntries = fromAddresses.stream().map(address -> {
Optional<AddressEntry> addressEntryOptional = findAddressEntry(address, AddressEntry.Context.AVAILABLE);
if (!addressEntryOptional.isPresent())
addressEntryOptional = findAddressEntry(address, AddressEntry.Context.OFFER_FUNDING);
if (!addressEntryOptional.isPresent())
addressEntryOptional = findAddressEntry(address, AddressEntry.Context.TRADE_PAYOUT);
if (!addressEntryOptional.isPresent())
addressEntryOptional = findAddressEntry(address, AddressEntry.Context.ARBITRATOR);
return addressEntryOptional;
}).filter(Optional::isPresent).map(Optional::get).collect(Collectors.toSet());
if (addressEntries.isEmpty())
throw new AddressEntryException("No Addresses for withdraw found in our wallet");
try {
Coin fee;
int counter = 0;
int txSize = 0;
Transaction tx;
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
do {
counter++;
fee = txFeeForWithdrawalPerByte.multiply(txSize);
// We use a dummy address for the output
SendRequest sendRequest = getSendRequestForMultipleAddresses(fromAddresses, getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddressString(), amount, fee, null, aesKey);
wallet.completeTx(sendRequest);
tx = sendRequest.tx;
txSize = tx.bitcoinSerialize().length;
printTx("FeeEstimationTransactionForMultipleAddresses", tx);
} while (feeEstimationNotSatisfied(counter, tx));
if (counter == 10)
log.error("Could not calculate the fee. Tx=" + tx);
return tx;
} 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"));
}
}
use of bisq.core.btc.InsufficientFundsException in project bisq-desktop by bisq-network.
the class BuyerStep4View method reviewWithdrawal.
@SuppressWarnings("PointlessBooleanExpression")
private void reviewWithdrawal() {
Coin amount = trade.getPayoutAmount();
BtcWalletService walletService = model.dataModel.btcWalletService;
AddressEntry fromAddressesEntry = walletService.getOrCreateAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT);
String fromAddresses = fromAddressesEntry.getAddressString();
String toAddresses = withdrawAddressTextField.getText();
if (new BtcAddressValidator().validate(toAddresses).isValid) {
Coin balance = walletService.getBalanceForAddress(fromAddressesEntry.getAddress());
try {
Transaction feeEstimationTransaction = walletService.getFeeEstimationTransaction(fromAddresses, toAddresses, amount, AddressEntry.Context.TRADE_PAYOUT);
Coin fee = feeEstimationTransaction.getFee();
// noinspection UnusedAssignment
Coin receiverAmount = amount.subtract(fee);
if (balance.isZero()) {
new Popup<>().warning(Res.get("portfolio.pending.step5_buyer.alreadyWithdrawn")).show();
model.dataModel.tradeManager.addTradeToClosedTrades(trade);
} else {
if (toAddresses.isEmpty()) {
validateWithdrawAddress();
} else if (Restrictions.isAboveDust(amount, fee)) {
BSFormatter formatter = model.btcFormatter;
int txSize = feeEstimationTransaction.bitcoinSerialize().length;
double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
double kb = txSize / 1000d;
String recAmount = formatter.formatCoinWithCode(receiverAmount);
new Popup<>().headLine(Res.get("portfolio.pending.step5_buyer.confirmWithdrawal")).confirmation(Res.get("shared.sendFundsDetailsWithFee", formatter.formatCoinWithCode(amount), fromAddresses, toAddresses, formatter.formatCoinWithCode(fee), feePerByte, kb, recAmount)).actionButtonText(Res.get("shared.yes")).onAction(() -> doWithdrawal(amount, fee)).closeButtonText(Res.get("shared.cancel")).onClose(() -> {
useSavingsWalletButton.setDisable(false);
withdrawToExternalWalletButton.setDisable(false);
}).show();
} else {
new Popup<>().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
}
}
} catch (AddressFormatException e) {
validateWithdrawAddress();
} catch (AddressEntryException e) {
log.error(e.getMessage());
} catch (InsufficientFundsException e) {
log.error(e.getMessage());
e.printStackTrace();
new Popup<>().warning(e.getMessage()).show();
}
} else {
new Popup<>().warning(Res.get("validation.btc.invalidAddress")).show();
}
}
use of bisq.core.btc.InsufficientFundsException in project bisq-desktop by bisq-network.
the class WithdrawalView method onWithdraw.
// /////////////////////////////////////////////////////////////////////////////////////////
// UI handlers
// /////////////////////////////////////////////////////////////////////////////////////////
@FXML
public void onWithdraw() {
if (GUIUtil.isReadyForTxBroadcast(p2PService, walletsSetup)) {
try {
// We do not know sendersAmount if senderPaysFee is true. We repeat fee calculation after first attempt if senderPaysFee is true.
Transaction feeEstimationTransaction = walletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, amountAsCoin);
if (feeExcluded && feeEstimationTransaction != null) {
sendersAmount = amountAsCoin.add(feeEstimationTransaction.getFee());
feeEstimationTransaction = walletService.getFeeEstimationTransactionForMultipleAddresses(fromAddresses, sendersAmount);
}
checkNotNull(feeEstimationTransaction, "feeEstimationTransaction must not be null");
Coin fee = feeEstimationTransaction.getFee();
sendersAmount = feeExcluded ? amountAsCoin.add(fee) : amountAsCoin;
Coin receiverAmount = feeExcluded ? amountAsCoin : amountAsCoin.subtract(fee);
if (areInputsValid()) {
int txSize = feeEstimationTransaction.bitcoinSerialize().length;
log.info("Fee for tx with size {}: {} " + Res.getBaseCurrencyCode() + "", txSize, fee.toPlainString());
if (receiverAmount.isPositive()) {
double feePerByte = CoinUtil.getFeePerByte(fee, txSize);
double kb = txSize / 1000d;
new Popup<>().headLine(Res.get("funds.withdrawal.confirmWithdrawalRequest")).confirmation(Res.get("shared.sendFundsDetailsWithFee", formatter.formatCoinWithCode(sendersAmount), withdrawFromTextField.getText(), withdrawToTextField.getText(), formatter.formatCoinWithCode(fee), feePerByte, kb, formatter.formatCoinWithCode(receiverAmount))).actionButtonText(Res.get("shared.yes")).onAction(() -> doWithdraw(sendersAmount, fee, new FutureCallback<Transaction>() {
@Override
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
if (transaction != null) {
log.debug("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
} else {
log.error("onWithdraw transaction is null");
}
List<Trade> trades = new ArrayList<>(tradeManager.getTradableList());
trades.stream().filter(Trade::isPayoutPublished).forEach(trade -> {
walletService.getAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT).ifPresent(addressEntry -> {
if (walletService.getBalanceForAddress(addressEntry.getAddress()).isZero())
tradeManager.addTradeToClosedTrades(trade);
});
});
}
@Override
public void onFailure(@NotNull Throwable t) {
log.error("onWithdraw onFailure");
}
})).closeButtonText(Res.get("shared.cancel")).show();
} else {
new Popup<>().warning(Res.get("portfolio.pending.step5_buyer.amountTooLow")).show();
}
}
} catch (InsufficientFundsException e) {
new Popup<>().warning(Res.get("funds.withdrawal.warn.amountExceeds") + "\n\nError message:\n" + e.getMessage()).show();
} catch (Throwable e) {
e.printStackTrace();
log.error(e.toString());
new Popup<>().warning(e.toString()).show();
}
} else {
GUIUtil.showNotReadyForTxBroadcastPopups(p2PService, walletsSetup);
}
}
use of bisq.core.btc.InsufficientFundsException in project bisq-core by bisq-network.
the class BtcWalletService method getFeeEstimationTransaction.
// /////////////////////////////////////////////////////////////////////////////////////////
// Withdrawal Fee calculation
// /////////////////////////////////////////////////////////////////////////////////////////
public Transaction getFeeEstimationTransaction(String fromAddress, String toAddress, Coin amount, AddressEntry.Context context) throws AddressFormatException, AddressEntryException, InsufficientFundsException {
Optional<AddressEntry> addressEntry = findAddressEntry(fromAddress, context);
if (!addressEntry.isPresent())
throw new AddressEntryException("WithdrawFromAddress is not found in our wallet.");
checkNotNull(addressEntry.get().getAddress(), "addressEntry.get().getAddress() must nto be null");
try {
Coin fee;
int counter = 0;
int txSize = 0;
Transaction tx;
Coin txFeeForWithdrawalPerByte = getTxFeeForWithdrawalPerByte();
do {
counter++;
fee = txFeeForWithdrawalPerByte.multiply(txSize);
SendRequest sendRequest = getSendRequest(fromAddress, toAddress, amount, fee, aesKey, context);
wallet.completeTx(sendRequest);
tx = sendRequest.tx;
txSize = tx.bitcoinSerialize().length;
printTx("FeeEstimationTransaction", tx);
} while (feeEstimationNotSatisfied(counter, tx));
if (counter == 10)
log.error("Could not calculate the fee. Tx=" + tx);
return tx;
} 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"));
}
}
use of bisq.core.btc.InsufficientFundsException 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.");
}
}
}
Aggregations