add grpc error handling for confirming payment sent and received

This commit is contained in:
woodser 2022-07-29 17:58:52 -04:00
parent 6918ecf620
commit f61fd09127
7 changed files with 58 additions and 54 deletions

View file

@ -549,18 +549,19 @@ public class CoreApi {
Consumer<Trade> resultHandler, Consumer<Trade> resultHandler,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
Offer offer = coreOffersService.getOffer(offerId); Offer offer = coreOffersService.getOffer(offerId);
coreTradesService.takeOffer(offer, coreTradesService.takeOffer(offer, paymentAccountId, resultHandler, errorMessageHandler);
paymentAccountId,
resultHandler,
errorMessageHandler);
} }
public void confirmPaymentStarted(String tradeId) { public void confirmPaymentStarted(String tradeId,
coreTradesService.confirmPaymentStarted(tradeId); ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
coreTradesService.confirmPaymentStarted(tradeId, resultHandler, errorMessageHandler);
} }
public void confirmPaymentReceived(String tradeId) { public void confirmPaymentReceived(String tradeId,
coreTradesService.confirmPaymentReceived(tradeId); ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
coreTradesService.confirmPaymentReceived(tradeId, resultHandler, errorMessageHandler);
} }
public void keepFunds(String tradeId) { public void keepFunds(String tradeId) {

View file

@ -35,7 +35,7 @@ import bisq.core.user.User;
import bisq.core.util.validation.BtcAddressValidator; import bisq.core.util.validation.BtcAddressValidator;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler;
import org.bitcoinj.core.Coin; import org.bitcoinj.core.Coin;
import javax.inject.Inject; import javax.inject.Inject;
@ -131,35 +131,27 @@ class CoreTradesService {
} }
} }
void confirmPaymentStarted(String tradeId) { void confirmPaymentStarted(String tradeId,
ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
var trade = getTrade(tradeId); var trade = getTrade(tradeId);
if (isFollowingBuyerProtocol(trade)) { if (isFollowingBuyerProtocol(trade)) {
var tradeProtocol = tradeManager.getTradeProtocol(trade); var tradeProtocol = tradeManager.getTradeProtocol(trade);
((BuyerProtocol) tradeProtocol).onPaymentStarted( ((BuyerProtocol) tradeProtocol).onPaymentStarted(resultHandler, errorMessageHandler);
() -> {
},
errorMessage -> {
throw new IllegalStateException(errorMessage);
}
);
} else { } else {
throw new IllegalStateException("you are the seller and not sending payment"); throw new IllegalStateException("you are the seller and not sending payment");
} }
} }
void confirmPaymentReceived(String tradeId) { void confirmPaymentReceived(String tradeId,
ResultHandler resultHandler,
ErrorMessageHandler errorMessageHandler) {
var trade = getTrade(tradeId); var trade = getTrade(tradeId);
if (isFollowingBuyerProtocol(trade)) { if (isFollowingBuyerProtocol(trade)) {
throw new IllegalStateException("you are the buyer, and not receiving payment"); throw new IllegalStateException("you are the buyer, and not receiving payment");
} else { } else {
var tradeProtocol = tradeManager.getTradeProtocol(trade); var tradeProtocol = tradeManager.getTradeProtocol(trade);
((SellerProtocol) tradeProtocol).onPaymentReceived( ((SellerProtocol) tradeProtocol).onPaymentReceived(resultHandler, errorMessageHandler);
() -> {
},
errorMessage -> {
throw new IllegalStateException(errorMessage);
}
);
} }
} }

View file

@ -185,23 +185,21 @@ public class XmrWalletService {
// TODO (woodser): test retaking failed trade. create new multisig wallet or replace? cannot reuse // TODO (woodser): test retaking failed trade. create new multisig wallet or replace? cannot reuse
public MoneroWallet createMultisigWallet(String tradeId) { public MoneroWallet createMultisigWallet(String tradeId) {
log.info("{}.createMultisigWallet({})", getClass().getSimpleName(), tradeId); log.info("{}.createMultisigWallet({})", getClass().getSimpleName(), tradeId);
Trade trade = tradeManager.getOpenTrade(tradeId).get(); if (multisigWallets.containsKey(tradeId)) return multisigWallets.get(tradeId);
if (multisigWallets.containsKey(trade.getId())) return multisigWallets.get(trade.getId()); String path = MONERO_MULTISIG_WALLET_PREFIX + tradeId;
String path = MONERO_MULTISIG_WALLET_PREFIX + trade.getId();
MoneroWallet multisigWallet = createWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null, false); // auto-assign port MoneroWallet multisigWallet = createWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null, false); // auto-assign port
multisigWallets.put(trade.getId(), multisigWallet); multisigWallets.put(tradeId, multisigWallet);
return multisigWallet; return multisigWallet;
} }
// TODO (woodser): provide progress notifications during open? // TODO (woodser): provide progress notifications during open?
public MoneroWallet getMultisigWallet(String tradeId) { public MoneroWallet getMultisigWallet(String tradeId) {
log.info("{}.getMultisigWallet({})", getClass().getSimpleName(), tradeId); log.info("{}.getMultisigWallet({})", getClass().getSimpleName(), tradeId);
Trade trade = tradeManager.getTrade(tradeId); if (multisigWallets.containsKey(tradeId)) return multisigWallets.get(tradeId);
if (multisigWallets.containsKey(trade.getId())) return multisigWallets.get(trade.getId()); String path = MONERO_MULTISIG_WALLET_PREFIX + tradeId;
String path = MONERO_MULTISIG_WALLET_PREFIX + trade.getId(); if (!walletExists(path)) throw new RuntimeException("Multisig wallet does not exist for trade " + tradeId);
if (!walletExists(path)) throw new RuntimeException("Multisig wallet does not exist for trade " + trade.getId());
MoneroWallet multisigWallet = openWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null); MoneroWallet multisigWallet = openWallet(new MoneroWalletConfig().setPath(path).setPassword(getWalletPassword()), null);
multisigWallets.put(trade.getId(), multisigWallet); multisigWallets.put(tradeId, multisigWallet);
return multisigWallet; return multisigWallet;
} }

View file

@ -923,7 +923,7 @@ public abstract class Trade implements Tradable, Model {
} }
// check if deposit txs unlocked // check if deposit txs unlocked
if (unlockHeight != null && height == unlockHeight) { if (unlockHeight != null && height >= unlockHeight) {
log.info("Multisig deposits unlocked for trade {}", getId()); log.info("Multisig deposits unlocked for trade {}", getId());
setUnlockedState(); setUnlockedState();
xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified

View file

@ -127,6 +127,8 @@ public abstract class BuyerProtocol extends DisputeProtocol {
public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
System.out.println("BuyerProtocol.onPaymentStarted()"); System.out.println("BuyerProtocol.onPaymentStarted()");
synchronized (trade) { synchronized (trade) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
BuyerEvent event = BuyerEvent.PAYMENT_SENT; BuyerEvent event = BuyerEvent.PAYMENT_SENT;
expect(phase(Trade.Phase.DEPOSIT_UNLOCKED) expect(phase(Trade.Phase.DEPOSIT_UNLOCKED)
.with(event) .with(event)
@ -139,15 +141,16 @@ public abstract class BuyerProtocol extends DisputeProtocol {
BuyerSendsPaymentSentMessage.class) // don't latch trade because this blocks and runs in background BuyerSendsPaymentSentMessage.class) // don't latch trade because this blocks and runs in background
.using(new TradeTaskRunner(trade, .using(new TradeTaskRunner(trade,
() -> { () -> {
this.errorMessageHandler = null;
resultHandler.handleResult(); resultHandler.handleResult();
handleTaskRunnerSuccess(event); handleTaskRunnerSuccess(event);
}, },
(errorMessage) -> { (errorMessage) -> {
errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(event, errorMessage); handleTaskRunnerFault(event, errorMessage);
}))) })))
.run(() -> trade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_PAYMENT_SENT)) .run(() -> trade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_PAYMENT_SENT))
.executeTasks(); .executeTasks();
awaitTradeLatch();
} }
} }
@ -169,14 +172,11 @@ public abstract class BuyerProtocol extends DisputeProtocol {
BuyerProcessesPaymentReceivedMessage.class) BuyerProcessesPaymentReceivedMessage.class)
.using(new TradeTaskRunner(trade, .using(new TradeTaskRunner(trade,
() -> { () -> {
stopTimeout();
handleTaskRunnerSuccess(peer, message); handleTaskRunnerSuccess(peer, message);
}, },
errorMessage -> { errorMessage -> {
stopTimeout();
handleTaskRunnerFault(peer, message, errorMessage); handleTaskRunnerFault(peer, message, errorMessage);
})) })))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true); .executeTasks(true);
awaitTradeLatch(); awaitTradeLatch();
} }

View file

@ -125,6 +125,8 @@ public abstract class SellerProtocol extends DisputeProtocol {
public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
log.info("SellerProtocol.onPaymentReceived()"); log.info("SellerProtocol.onPaymentReceived()");
synchronized (trade) { synchronized (trade) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
SellerEvent event = SellerEvent.PAYMENT_RECEIVED; SellerEvent event = SellerEvent.PAYMENT_RECEIVED;
expect(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED) expect(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
.with(event) .with(event)
@ -135,14 +137,15 @@ public abstract class SellerProtocol extends DisputeProtocol {
SellerPreparesPaymentReceivedMessage.class, SellerPreparesPaymentReceivedMessage.class,
SellerSendsPaymentReceivedMessage.class) SellerSendsPaymentReceivedMessage.class)
.using(new TradeTaskRunner(trade, () -> { .using(new TradeTaskRunner(trade, () -> {
this.errorMessageHandler = null;
resultHandler.handleResult(); resultHandler.handleResult();
handleTaskRunnerSuccess(event); handleTaskRunnerSuccess(event);
}, (errorMessage) -> { }, (errorMessage) -> {
errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(event, errorMessage); handleTaskRunnerFault(event, errorMessage);
}))) })))
.run(() -> trade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT)) .run(() -> trade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT))
.executeTasks(); .executeTasks();
awaitTradeLatch();
} }
} }

View file

@ -115,11 +115,7 @@ class GrpcTradesService extends TradesImplBase {
@Override @Override
public void takeOffer(TakeOfferRequest req, public void takeOffer(TakeOfferRequest req,
StreamObserver<TakeOfferReply> responseObserver) { StreamObserver<TakeOfferReply> responseObserver) {
GrpcErrorMessageHandler errorMessageHandler = GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getTakeOfferMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
new GrpcErrorMessageHandler(getTakeOfferMethod().getFullMethodName(),
responseObserver,
exceptionHandler,
log);
try { try {
coreApi.takeOffer(req.getOfferId(), coreApi.takeOffer(req.getOfferId(),
req.getPaymentAccountId(), req.getPaymentAccountId(),
@ -144,12 +140,19 @@ class GrpcTradesService extends TradesImplBase {
@Override @Override
public void confirmPaymentStarted(ConfirmPaymentStartedRequest req, public void confirmPaymentStarted(ConfirmPaymentStartedRequest req,
StreamObserver<ConfirmPaymentStartedReply> responseObserver) { StreamObserver<ConfirmPaymentStartedReply> responseObserver) {
GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getConfirmPaymentStartedMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
try { try {
coreApi.confirmPaymentStarted(req.getTradeId()); coreApi.confirmPaymentStarted(req.getTradeId(),
var reply = ConfirmPaymentStartedReply.newBuilder().build(); () -> {
responseObserver.onNext(reply); var reply = ConfirmPaymentStartedReply.newBuilder().build();
responseObserver.onCompleted(); responseObserver.onNext(reply);
responseObserver.onCompleted();
},
errorMessage -> {
if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
});
} catch (Throwable cause) { } catch (Throwable cause) {
cause.printStackTrace();
exceptionHandler.handleException(log, cause, responseObserver); exceptionHandler.handleException(log, cause, responseObserver);
} }
} }
@ -157,12 +160,19 @@ class GrpcTradesService extends TradesImplBase {
@Override @Override
public void confirmPaymentReceived(ConfirmPaymentReceivedRequest req, public void confirmPaymentReceived(ConfirmPaymentReceivedRequest req,
StreamObserver<ConfirmPaymentReceivedReply> responseObserver) { StreamObserver<ConfirmPaymentReceivedReply> responseObserver) {
GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getConfirmPaymentReceivedMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
try { try {
coreApi.confirmPaymentReceived(req.getTradeId()); coreApi.confirmPaymentReceived(req.getTradeId(),
var reply = ConfirmPaymentReceivedReply.newBuilder().build(); () -> {
responseObserver.onNext(reply); var reply = ConfirmPaymentReceivedReply.newBuilder().build();
responseObserver.onCompleted(); responseObserver.onNext(reply);
responseObserver.onCompleted();
},
errorMessage -> {
if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
});
} catch (Throwable cause) { } catch (Throwable cause) {
cause.printStackTrace();
exceptionHandler.handleException(log, cause, responseObserver); exceptionHandler.handleException(log, cause, responseObserver);
} }
} }