fix scheduling offers by computing spendable amount from txs
Some checks failed
CI / build (macos-13) (push) Has been cancelled
CI / build (ubuntu-22.04) (push) Has been cancelled
CI / build (windows-latest) (push) Has been cancelled
Codacy Coverage Reporter / Publish coverage (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled

This commit is contained in:
woodser 2024-12-17 10:36:14 -05:00
parent 544d69827a
commit 7e29dc188d
2 changed files with 38 additions and 29 deletions

View file

@ -127,7 +127,6 @@ public class CreateOfferService {
isPrivateOffer, isPrivateOffer,
buyerAsTakerWithoutDeposit); buyerAsTakerWithoutDeposit);
// verify buyer as taker security deposit // verify buyer as taker security deposit
boolean isBuyerMaker = offerUtil.isBuyOffer(direction); boolean isBuyerMaker = offerUtil.isBuyOffer(direction);
if (!isBuyerMaker && !isPrivateOffer && buyerAsTakerWithoutDeposit) { if (!isBuyerMaker && !isPrivateOffer && buyerAsTakerWithoutDeposit) {

View file

@ -948,7 +948,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
if (openOffer.getScheduledTxHashes() != null) { if (openOffer.getScheduledTxHashes() != null) {
boolean scheduledTxsAvailable = true; boolean scheduledTxsAvailable = true;
for (MoneroTxWallet tx : xmrWalletService.getTxs(openOffer.getScheduledTxHashes())) { for (MoneroTxWallet tx : xmrWalletService.getTxs(openOffer.getScheduledTxHashes())) {
if (!tx.isLocked() && !isOutputsAvailable(tx)) { if (!tx.isLocked() && !hasSpendableAmount(tx)) {
scheduledTxsAvailable = false; scheduledTxsAvailable = false;
break; break;
} }
@ -1165,31 +1165,21 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
throw new RuntimeException("Not enough money in Haveno wallet"); throw new RuntimeException("Not enough money in Haveno wallet");
} }
// get earliest available or pending txs with sufficient incoming amount // get earliest available or pending txs with sufficient spendable amount
BigInteger scheduledAmount = BigInteger.ZERO; BigInteger scheduledAmount = BigInteger.ZERO;
Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>(); Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>();
for (MoneroTxWallet tx : xmrWalletService.getTxs()) { for (MoneroTxWallet tx : xmrWalletService.getTxs()) {
// skip if no funds available // get spendable amount
BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx); // amount sent to self always shows 0, so compute from destinations manually BigInteger spendableAmount = getSpendableAmount(tx);
if (sentToSelfAmount.equals(BigInteger.ZERO) && (tx.getIncomingTransfers() == null || tx.getIncomingTransfers().isEmpty())) continue;
if (!isOutputsAvailable(tx)) continue; // skip if no spendable amount or already scheduled
if (spendableAmount.equals(BigInteger.ZERO)) continue;
if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue; if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue;
// schedule transaction if funds sent to self, because they are not included in incoming transfers // TODO: fix in libraries? // schedule tx
if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) { scheduledAmount = scheduledAmount.add(spendableAmount);
scheduledAmount = scheduledAmount.add(sentToSelfAmount);
scheduledTxs.add(tx); scheduledTxs.add(tx);
} else if (tx.getIncomingTransfers() != null) {
// schedule transaction if incoming tranfers to account 0
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
if (transfer.getAccountIndex() == 0) {
scheduledAmount = scheduledAmount.add(transfer.getAmount());
scheduledTxs.add(tx);
}
}
}
// break if sufficient funds // break if sufficient funds
if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break; if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break;
@ -1202,6 +1192,34 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
openOffer.setState(OpenOffer.State.PENDING); openOffer.setState(OpenOffer.State.PENDING);
} }
private BigInteger getSpendableAmount(MoneroTxWallet tx) {
// compute spendable amount from outputs if confirmed
if (tx.isConfirmed()) {
BigInteger spendableAmount = BigInteger.ZERO;
if (tx.getOutputsWallet() != null) {
for (MoneroOutputWallet output : tx.getOutputsWallet()) {
if (!output.isSpent() && !output.isFrozen() && output.getAccountIndex() == 0) {
spendableAmount = spendableAmount.add(output.getAmount());
}
}
}
return spendableAmount;
}
// funds sent to self always show 0 incoming amount, so compute from destinations manually
// TODO: this excludes change output, so change is missing from spendable amount until confirmed
BigInteger sentToSelfAmount = xmrWalletService.getAmountSentToSelf(tx);
if (sentToSelfAmount.compareTo(BigInteger.ZERO) > 0) return sentToSelfAmount;
// if not confirmed and not sent to self, return incoming amount
return tx.getIncomingAmount() == null ? BigInteger.ZERO : tx.getIncomingAmount();
}
private boolean hasSpendableAmount(MoneroTxWallet tx) {
return getSpendableAmount(tx).compareTo(BigInteger.ZERO) > 0;
}
private BigInteger getScheduledAmount(List<OpenOffer> openOffers) { private BigInteger getScheduledAmount(List<OpenOffer> openOffers) {
BigInteger scheduledAmount = BigInteger.ZERO; BigInteger scheduledAmount = BigInteger.ZERO;
for (OpenOffer openOffer : openOffers) { for (OpenOffer openOffer : openOffers) {
@ -1233,14 +1251,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
return false; return false;
} }
private boolean isOutputsAvailable(MoneroTxWallet tx) {
if (tx.getOutputsWallet() == null) return false;
for (MoneroOutputWallet output : tx.getOutputsWallet()) {
if (output.isSpent() || output.isFrozen()) return false;
}
return true;
}
private void signAndPostOffer(OpenOffer openOffer, private void signAndPostOffer(OpenOffer openOffer,
boolean useSavingsWallet, // TODO: remove this? boolean useSavingsWallet, // TODO: remove this?
TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {