schedule offer using available and pending funds
Some checks failed
Codacy Coverage Reporter / Publish coverage (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
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

This commit is contained in:
woodser 2024-10-02 08:16:33 -04:00
parent 5c0f49d58f
commit d61eabc3d2

View file

@ -934,6 +934,23 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
return; return;
} }
// cancel offer if scheduled txs unavailable
if (openOffer.getScheduledTxHashes() != null) {
boolean scheduledTxsAvailable = true;
for (MoneroTxWallet tx : xmrWalletService.getTxs(openOffer.getScheduledTxHashes())) {
if (!tx.isLocked() && !isOutputsAvailable(tx)) {
scheduledTxsAvailable = false;
break;
}
}
if (!scheduledTxsAvailable) {
log.warn("Canceling offer {} because scheduled txs are no longer available", openOffer.getId());
doCancelOffer(openOffer);
resultHandler.handleResult(null);
return;
}
}
// get amount needed to reserve offer // get amount needed to reserve offer
BigInteger amountNeeded = openOffer.getOffer().getAmountNeeded(); BigInteger amountNeeded = openOffer.getOffer().getAmountNeeded();
@ -1138,25 +1155,31 @@ 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 locked txs // get earliest available or pending txs with sufficient incoming amount
List<MoneroTxWallet> lockedTxs = xmrWalletService.getTxs(new MoneroTxQuery().setIsLocked(true));
// get earliest unscheduled txs with sufficient incoming amount
List<String> scheduledTxHashes = new ArrayList<String>();
BigInteger scheduledAmount = BigInteger.ZERO; BigInteger scheduledAmount = BigInteger.ZERO;
for (MoneroTxWallet lockedTx : lockedTxs) { Set<MoneroTxWallet> scheduledTxs = new HashSet<MoneroTxWallet>();
if (isTxScheduledByOtherOffer(openOffers, openOffer, lockedTx.getHash())) continue; for (MoneroTxWallet tx : xmrWalletService.getTxs()) {
if (lockedTx.getIncomingTransfers() == null || lockedTx.getIncomingTransfers().isEmpty()) continue;
scheduledTxHashes.add(lockedTx.getHash()); // skip if outputs unavailable
for (MoneroIncomingTransfer transfer : lockedTx.getIncomingTransfers()) { if (tx.getIncomingTransfers() == null || tx.getIncomingTransfers().isEmpty()) continue;
if (transfer.getAccountIndex() == 0) scheduledAmount = scheduledAmount.add(transfer.getAmount()); if (!isOutputsAvailable(tx)) continue;
if (isTxScheduledByOtherOffer(openOffers, openOffer, tx.getHash())) continue;
// add scheduled tx
for (MoneroIncomingTransfer transfer : tx.getIncomingTransfers()) {
if (transfer.getAccountIndex() == 0) {
scheduledAmount = scheduledAmount.add(transfer.getAmount());
scheduledTxs.add(tx);
}
} }
// break if sufficient funds
if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break; if (scheduledAmount.compareTo(offerReserveAmount) >= 0) break;
} }
if (scheduledAmount.compareTo(offerReserveAmount) < 0) throw new RuntimeException("Not enough funds to schedule offer"); if (scheduledAmount.compareTo(offerReserveAmount) < 0) throw new RuntimeException("Not enough funds to schedule offer");
// schedule txs // schedule txs
openOffer.setScheduledTxHashes(scheduledTxHashes); openOffer.setScheduledTxHashes(scheduledTxs.stream().map(tx -> tx.getHash()).collect(Collectors.toList()));
openOffer.setScheduledAmount(scheduledAmount.toString()); openOffer.setScheduledAmount(scheduledAmount.toString());
openOffer.setState(OpenOffer.State.PENDING); openOffer.setState(OpenOffer.State.PENDING);
} }
@ -1192,6 +1215,14 @@ 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) {