fix bugs resetting trade payout address entries

This commit is contained in:
woodser 2023-07-27 08:02:03 -04:00
parent aa36518f69
commit 900d3a91e1
6 changed files with 71 additions and 73 deletions

View File

@ -667,7 +667,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
getOpenOfferById(offer.getId()).ifPresent(openOffer -> {
removeOpenOffer(openOffer);
openOffer.setState(OpenOffer.State.CLOSED);
xmrWalletService.resetOfferFundingForOpenOffer(offer.getId());
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
offerBookService.removeOffer(openOffer.getOffer().getOfferPayload(),
() -> log.info("Successfully removed offer {}", offer.getId()),
log::error);
@ -764,6 +764,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
}
public boolean hasAvailableOutput(BigInteger amount) {
return findSplitOutputFundingTx(getOpenOffers(), null, amount, null) != null;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Place offer helpers
///////////////////////////////////////////////////////////////////////////////////////////
@ -811,7 +815,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// find tx with exact input amount
MoneroTxWallet splitOutputTx = findSplitOutputFundingTx(openOffers, openOffer);
if (openOffer.getScheduledTxHashes() == null && splitOutputTx != null) {
if (splitOutputTx != null && openOffer.getScheduledTxHashes() == null) {
openOffer.setScheduledTxHashes(Arrays.asList(splitOutputTx.getHash()));
openOffer.setSplitOutputTxHash(splitOutputTx.getHash());
openOffer.setScheduledAmount(offerReserveAmount.toString());
@ -829,7 +833,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
// on error, create new tx to split output if offer subaddress does not have exact output
int offerSubaddress = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).getSubaddressIndex();
if (!splitOutputTx.getOutgoingTransfer().getSubaddressIndices().equals(Arrays.asList(offerSubaddress))) {
log.warn("Splitting new output because spending existing output(s) failed for offer {}", openOffer.getId());
log.warn("Splitting new output because spending existing output(s) failed for offer {}. Split output tx subaddresses={}. Offer funding subadress={}", openOffer.getId(), splitOutputTx.getOutgoingTransfer().getSubaddressIndices(), offerSubaddress);
splitOrSchedule(openOffers, openOffer, offerReserveAmount);
resultHandler.handleResult(null);
} else {
@ -846,7 +850,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
signAndPostOffer(openOffer, true, resultHandler, errorMessageHandler);
return;
} else if (openOffer.getScheduledTxHashes() == null) {
scheduleOfferWithEarliestTxs(openOffers, openOffer);
scheduleWithEarliestTxs(openOffers, openOffer);
}
}
@ -860,29 +864,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}).start();
}
private void splitOrSchedule(List<OpenOffer> openOffers, OpenOffer openOffer, BigInteger offerReserveAmount) {
// handle sufficient available balance to split output
boolean sufficientAvailableBalance = xmrWalletService.getWallet().getUnlockedBalance(0).compareTo(offerReserveAmount) >= 0;
if (sufficientAvailableBalance) {
// create and relay tx to split output
MoneroTxWallet splitOutputTx = createAndRelaySplitOutputTx(openOffer);
// schedule txs
openOffer.setScheduledTxHashes(Arrays.asList(splitOutputTx.getHash()));
openOffer.setSplitOutputTxHash(splitOutputTx.getHash());
openOffer.setScheduledAmount(offerReserveAmount.toString());
openOffer.setState(OpenOffer.State.SCHEDULED);
} else if (openOffer.getScheduledTxHashes() == null) {
scheduleOfferWithEarliestTxs(openOffers, openOffer);
}
}
public boolean hasAvailableOutput(BigInteger amount) {
return findSplitOutputFundingTx(getOpenOffers(), null, amount, null) != null;
}
private MoneroTxWallet findSplitOutputFundingTx(List<OpenOffer> openOffers, OpenOffer openOffer) {
XmrAddressEntry addressEntry = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING);
return findSplitOutputFundingTx(openOffers, openOffer, openOffer.getOffer().getReserveAmount(), addressEntry.getSubaddressIndex());
@ -968,7 +949,38 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
return earliestUnscheduledTx;
}
private void scheduleOfferWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openOffer) {
private void splitOrSchedule(List<OpenOffer> openOffers, OpenOffer openOffer, BigInteger offerReserveAmount) {
// handle sufficient available balance to split output
boolean sufficientAvailableBalance = xmrWalletService.getWallet().getUnlockedBalance(0).compareTo(offerReserveAmount) >= 0;
if (sufficientAvailableBalance) {
splitAndSchedule(openOffer);
} else if (openOffer.getScheduledTxHashes() == null) {
scheduleWithEarliestTxs(openOffers, openOffer);
}
}
private MoneroTxWallet splitAndSchedule(OpenOffer openOffer) {
BigInteger reserveAmount = openOffer.getOffer().getReserveAmount();
xmrWalletService.swapAddressEntryToAvailable(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); // change funding subaddress in case funded with unsuitable output(s)
XmrAddressEntry entry = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING);
log.info("Creating split output tx to fund offer {} at subaddress {}", openOffer.getId(), entry.getSubaddressIndex());
MoneroTxWallet splitOutputTx = xmrWalletService.getWallet().createTx(new MoneroTxConfig()
.setAccountIndex(0)
.setAddress(entry.getAddressString())
.setAmount(reserveAmount)
.setRelay(true));
log.info("Done creating split output tx to fund offer {}", openOffer.getId());
// schedule txs
openOffer.setScheduledTxHashes(Arrays.asList(splitOutputTx.getHash()));
openOffer.setSplitOutputTxHash(splitOutputTx.getHash());
openOffer.setScheduledAmount(openOffer.getOffer().getReserveAmount().toString());
openOffer.setState(OpenOffer.State.SCHEDULED);
return splitOutputTx;
}
private void scheduleWithEarliestTxs(List<OpenOffer> openOffers, OpenOffer openOffer) {
// check for sufficient balance - scheduled offers amount
BigInteger offerReserveAmount = openOffer.getOffer().getReserveAmount();
@ -999,20 +1011,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
openOffer.setState(OpenOffer.State.SCHEDULED);
}
private MoneroTxWallet createAndRelaySplitOutputTx(OpenOffer openOffer) {
BigInteger reserveAmount = openOffer.getOffer().getReserveAmount();
xmrWalletService.swapAddressEntryToAvailable(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); // change funding subaddress in case funded with unsuitable output(s)
String fundingSubaddress = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING).getAddressString();
log.info("Creating split output tx to fund offer {}", openOffer.getId());
MoneroTxWallet splitOutputTx = xmrWalletService.getWallet().createTx(new MoneroTxConfig()
.setAccountIndex(0)
.setAddress(fundingSubaddress)
.setAmount(reserveAmount)
.setRelay(true));
log.info("Done creating split output tx to fund offer {}", openOffer.getId());
return splitOutputTx;
}
private BigInteger getScheduledAmount(List<OpenOffer> openOffers) {
BigInteger scheduledAmount = BigInteger.valueOf(0);
for (OpenOffer openOffer : openOffers) {

View File

@ -691,26 +691,26 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
private void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress peer) {
log.info("Received InitMultisigRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid());
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid());
try {
Validator.nonEmptyStringOf(request.getTradeId());
} catch (Throwable t) {
log.warn("Invalid InitMultisigRequest " + request.toString());
return;
}
try {
Validator.nonEmptyStringOf(request.getTradeId());
} catch (Throwable t) {
log.warn("Invalid InitMultisigRequest " + request.toString());
return;
}
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId() + " at node " + P2PService.getMyNodeAddress());
return;
}
Trade trade = tradeOptional.get();
getTradeProtocol(trade).handleInitMultisigRequest(request, peer);
Optional<Trade> tradeOptional = getOpenTrade(request.getTradeId());
if (!tradeOptional.isPresent()) {
log.warn("No trade with id " + request.getTradeId() + " at node " + P2PService.getMyNodeAddress());
return;
}
Trade trade = tradeOptional.get();
getTradeProtocol(trade).handleInitMultisigRequest(request, peer);
}
private void handleSignContractRequest(SignContractRequest request, NodeAddress peer) {
log.info("Received SignContractRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid());
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid());
try {
Validator.nonEmptyStringOf(request.getTradeId());
@ -729,7 +729,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
private void handleSignContractResponse(SignContractResponse request, NodeAddress peer) {
log.info("Received SignContractResponse from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid());
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid());
try {
Validator.nonEmptyStringOf(request.getTradeId());
@ -748,7 +748,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
private void handleDepositRequest(DepositRequest request, NodeAddress peer) {
log.info("Received DepositRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid());
log.info("Received {} for trade {} from {} with uid {}", request.getClass().getSimpleName(), request.getTradeId(), peer, request.getUid());
try {
Validator.nonEmptyStringOf(request.getTradeId());
@ -767,7 +767,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
private void handleDepositResponse(DepositResponse response, NodeAddress peer) {
log.info("Received DepositResponse from {} with tradeId {} and uid {}", peer, response.getTradeId(), response.getUid());
log.info("Received {} for trade {} from {} with uid {}", response.getClass().getSimpleName(), response.getTradeId(), peer, response.getUid());
try {
Validator.nonEmptyStringOf(response.getTradeId());

View File

@ -38,7 +38,7 @@ public class RemoveOffer extends TradeTask {
if (trade instanceof MakerTrade) {
processModel.getOpenOfferManager().closeOpenOffer(checkNotNull(trade.getOffer()));
} else {
trade.getXmrWalletService().resetOfferFundingForOpenOffer(trade.getId());
trade.getXmrWalletService().resetAddressEntriesForOpenOffer(trade.getId());
}
complete();

View File

@ -687,7 +687,7 @@ public class XmrWalletService {
wallet.startSyncing(connectionsService.getRefreshPeriodMs());
if (getMoneroNetworkType() != MoneroNetworkType.MAINNET) log.info("Monero wallet balance={}, unlocked balance={}", wallet.getBalance(0), wallet.getUnlockedBalance(0));
// TODO: using this to signify both daemon and wallet synced, use separate sync handlers
// TODO: using this to signify both daemon and wallet synced, use separate sync handlers?
connectionsService.doneDownload();
// notify setup that main wallet is initialized
@ -978,12 +978,11 @@ public class XmrWalletService {
public synchronized void resetAddressEntriesForOpenOffer(String offerId) {
log.info("resetAddressEntriesForOpenOffer offerId={}", offerId);
swapAddressEntryToAvailable(offerId, XmrAddressEntry.Context.OFFER_FUNDING);
swapAddressEntryToAvailable(offerId, XmrAddressEntry.Context.TRADE_PAYOUT);
}
public synchronized void resetOfferFundingForOpenOffer(String offerId) {
log.info("resetOfferFundingForOpenOffer offerId={}", offerId);
swapAddressEntryToAvailable(offerId, XmrAddressEntry.Context.OFFER_FUNDING);
// swap trade payout to available if applicable
if (tradeManager == null) return;
Trade trade = tradeManager.getTrade(offerId);
if (trade == null || trade.isPayoutUnlocked()) swapAddressEntryToAvailable(offerId, XmrAddressEntry.Context.TRADE_PAYOUT);
}
public synchronized void resetAddressEntriesForTrade(String offerId) {
@ -1136,9 +1135,10 @@ public class XmrWalletService {
}
public Stream<XmrAddressEntry> getAddressEntriesForAvailableBalanceStream() {
Stream<XmrAddressEntry> availableAndPayout = Stream.concat(getAddressEntries(XmrAddressEntry.Context.TRADE_PAYOUT).stream(), getFundedAvailableAddressEntries().stream());
Stream<XmrAddressEntry> available = Stream.concat(availableAndPayout, getAddressEntries(XmrAddressEntry.Context.ARBITRATOR).stream());
Stream<XmrAddressEntry> available = getFundedAvailableAddressEntries().stream();
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.ARBITRATOR).stream());
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.OFFER_FUNDING).stream().filter(entry -> !tradeManager.getOpenOfferManager().getOpenOfferById(entry.getOfferId()).isPresent()));
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.TRADE_PAYOUT).stream().filter(entry -> tradeManager.getTrade(entry.getOfferId()) == null || tradeManager.getTrade(entry.getOfferId()).isPayoutUnlocked()));
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).compareTo(BigInteger.valueOf(0)) > 0);
}

View File

@ -1017,8 +1017,8 @@ funds.tab.transactions=Transactions
funds.deposit.unused=Unused
funds.deposit.usedInTx=Used in {0} transaction(s)
funds.deposit.baseAddress=Base address
funds.deposit.offerFunding=Reserved for offer funding
funds.deposit.tradePayout=Reserved for trade payout
funds.deposit.offerFunding=Reserved for offer funding ({0})
funds.deposit.tradePayout=Reserved for trade payout ({0})
funds.deposit.fundHavenoWallet=Fund Haveno wallet
funds.deposit.noAddresses=No deposit addresses have been generated yet
funds.deposit.fundWallet=Fund your wallet

View File

@ -104,10 +104,10 @@ class DepositListItem {
usage = numTxsWithOutputs == 0 ? Res.get("funds.deposit.unused") : Res.get("funds.deposit.usedInTx", numTxsWithOutputs);
break;
case OFFER_FUNDING:
usage = Res.get("funds.deposit.offerFunding");
usage = Res.get("funds.deposit.offerFunding", addressEntry.getShortOfferId());
break;
case TRADE_PAYOUT:
usage = Res.get("funds.deposit.tradePayout");
usage = Res.get("funds.deposit.tradePayout", addressEntry.getShortOfferId());
break;
default:
usage = addressEntry.getContext().toString();