add confirmed trade state

This commit is contained in:
woodser 2022-09-01 15:11:37 -04:00
parent 3b788d1fea
commit 355a6146b6
27 changed files with 90 additions and 105 deletions

View file

@ -74,7 +74,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
return trade; return trade;
} }
protected final void waitForDepositConfirmation(Logger log, protected final void waitForDepositUnlocked(Logger log,
TestInfo testInfo, TestInfo testInfo,
GrpcClient grpcClient, GrpcClient grpcClient,
String tradeId) { String tradeId) {

View file

@ -77,7 +77,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD); alicesUsdOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), USD);
assertEquals(0, alicesUsdOffers.size()); assertEquals(0, alicesUsdOffers.size());
genBtcBlocksThenWait(1, 2_500); genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId); trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade); verifyTakerDepositConfirmed(trade);
@ -93,7 +93,7 @@ public class TakeBuyBTCOfferTest extends AbstractTradeTest {
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) { public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
try { try {
var trade = aliceClient.getTrade(tradeId); var trade = aliceClient.getTrade(tradeId);
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, aliceClient, trade.getTradeId());
aliceClient.confirmPaymentStarted(trade.getTradeId()); aliceClient.confirmPaymentStarted(trade.getTradeId());
sleep(6_000); sleep(6_000);
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId); waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);

View file

@ -139,7 +139,7 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL); alicesOffers = aliceClient.getMyOffersSortedByDate(BUY.name(), BRL);
assertEquals(0, alicesOffers.size()); assertEquals(0, alicesOffers.size());
genBtcBlocksThenWait(1, 2_500); genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId); trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade); verifyTakerDepositConfirmed(trade);
@ -176,7 +176,7 @@ public class TakeBuyBTCOfferWithNationalBankAcctTest extends AbstractTradeTest {
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) { public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
try { try {
var trade = aliceClient.getTrade(tradeId); var trade = aliceClient.getTrade(tradeId);
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, aliceClient, trade.getTradeId());
aliceClient.confirmPaymentStarted(trade.getTradeId()); aliceClient.confirmPaymentStarted(trade.getTradeId());
sleep(6_000); sleep(6_000);
waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId); waitForBuyerSeesPaymentInitiatedMessage(log, testInfo, aliceClient, tradeId);

View file

@ -83,7 +83,7 @@ public class TakeBuyXMROfferTest extends AbstractTradeTest {
alicesXmrOffers = aliceClient.getMyOffersSortedByDate(XMR); alicesXmrOffers = aliceClient.getMyOffersSortedByDate(XMR);
assertEquals(0, alicesXmrOffers.size()); assertEquals(0, alicesXmrOffers.size());
genBtcBlocksThenWait(1, 2_500); genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId); trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade); verifyTakerDepositConfirmed(trade);

View file

@ -84,7 +84,7 @@ public class TakeSellBTCOfferTest extends AbstractTradeTest {
var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), USD); var takeableUsdOffers = bobClient.getOffersSortedByDate(SELL.name(), USD);
assertEquals(0, takeableUsdOffers.size()); assertEquals(0, takeableUsdOffers.size());
genBtcBlocksThenWait(1, 2_500); genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId); trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade); verifyTakerDepositConfirmed(trade);
logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId)); logTrade(log, testInfo, "Alice's Maker/Buyer View", aliceClient.getTrade(tradeId));

View file

@ -93,7 +93,7 @@ public class TakeSellXMROfferTest extends AbstractTradeTest {
assertEquals(0, alicesXmrOffers.size()); assertEquals(0, alicesXmrOffers.size());
genBtcBlocksThenWait(1, 2_500); genBtcBlocksThenWait(1, 2_500);
waitForDepositConfirmation(log, testInfo, bobClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, bobClient, trade.getTradeId());
trade = bobClient.getTrade(tradeId); trade = bobClient.getTrade(tradeId);
verifyTakerDepositConfirmed(trade); verifyTakerDepositConfirmed(trade);
@ -109,7 +109,7 @@ public class TakeSellXMROfferTest extends AbstractTradeTest {
public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) { public void testAlicesConfirmPaymentStarted(final TestInfo testInfo) {
try { try {
var trade = aliceClient.getTrade(tradeId); var trade = aliceClient.getTrade(tradeId);
waitForDepositConfirmation(log, testInfo, aliceClient, trade.getTradeId()); waitForDepositUnlocked(log, testInfo, aliceClient, trade.getTradeId());
log.debug("Alice sends XMR payment to Bob for trade {}", trade.getTradeId()); log.debug("Alice sends XMR payment to Bob for trade {}", trade.getTradeId());
aliceClient.confirmPaymentStarted(trade.getTradeId()); aliceClient.confirmPaymentStarted(trade.getTradeId());
sleep(3500); sleep(3500);

View file

@ -121,10 +121,11 @@ public abstract class Trade implements Tradable, Model {
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED), SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
// deposit published // deposit published
SAW_DEPOSIT_TXS_IN_NETWORK(Phase.DEPOSITS_PUBLISHED), // TODO: seeing in network usually happens after arbitrator publishes DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN(Phase.DEPOSITS_PUBLISHED), // TODO: seeing in network usually happens after arbitrator publishes
ARBITRATOR_PUBLISHED_DEPOSIT_TXS(Phase.DEPOSITS_PUBLISHED), ARBITRATOR_PUBLISHED_DEPOSIT_TXS(Phase.DEPOSITS_PUBLISHED),
// deposit confirmed (TODO) // deposit confirmed (TODO)
DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN(Phase.DEPOSITS_CONFIRMED),
// deposit unlocked // deposit unlocked
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN(Phase.DEPOSITS_UNLOCKED), DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN(Phase.DEPOSITS_UNLOCKED),
@ -191,6 +192,7 @@ public abstract class Trade implements Tradable, Model {
INIT, INIT,
DEPOSIT_REQUESTED, // TODO (woodser): remove unused phases DEPOSIT_REQUESTED, // TODO (woodser): remove unused phases
DEPOSITS_PUBLISHED, DEPOSITS_PUBLISHED,
DEPOSITS_CONFIRMED,
DEPOSITS_UNLOCKED, DEPOSITS_UNLOCKED,
PAYMENT_SENT, PAYMENT_SENT,
PAYMENT_RECEIVED, PAYMENT_RECEIVED,
@ -849,7 +851,6 @@ public abstract class Trade implements Tradable, Model {
} }
// get daemon and primary wallet // get daemon and primary wallet
MoneroDaemon daemon = processModel.getXmrWalletService().getDaemon();
MoneroWallet havenoWallet = processModel.getXmrWalletService().getWallet(); MoneroWallet havenoWallet = processModel.getXmrWalletService().getWallet();
// fetch deposit txs from daemon // fetch deposit txs from daemon
@ -857,19 +858,19 @@ public abstract class Trade implements Tradable, Model {
// handle deposit txs seen // handle deposit txs seen
if (txs.size() == 2) { if (txs.size() == 2) {
setStatePublished();
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash()); boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
makerDepositTx = makerFirst ? txs.get(0) : txs.get(1); makerDepositTx = makerFirst ? txs.get(0) : txs.get(1);
takerDepositTx = makerFirst ? txs.get(1) : txs.get(0); takerDepositTx = makerFirst ? txs.get(1) : txs.get(0);
// check if deposit txs unlocked // check if deposit txs unlocked
if (txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) { if (txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) {
setStateConfirmed();
long unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK; long unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK;
if (havenoWallet.getHeight() >= unlockHeight) { if (havenoWallet.getHeight() >= unlockHeight) {
setUnlockedState(); setStateUnlocked();
return; return;
} }
} else {
setStateIfValidTransitionTo(Trade.State.SAW_DEPOSIT_TXS_IN_NETWORK);
} }
} }
@ -880,39 +881,40 @@ public abstract class Trade implements Tradable, Model {
@Override @Override
public void onNewBlock(long height) { public void onNewBlock(long height) {
// ignore if no longer listening // skip if no longer listening
if (depositTxListener == null) return; if (depositTxListener == null) return;
// use latest height // use latest height
height = havenoWallet.getHeight(); height = havenoWallet.getHeight();
// ignore if before unlock height // skip if before unlock height
if (unlockHeight != null && height < unlockHeight) return; if (unlockHeight != null && height < unlockHeight) return;
// fetch txs from daemon // fetch txs from daemon
List<MoneroTx> txs = xmrWalletService.getTxs(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash())); List<MoneroTx> txs = xmrWalletService.getTxs(Arrays.asList(processModel.getMaker().getDepositTxHash(), processModel.getTaker().getDepositTxHash()));
// ignore if deposit txs not seen // skip if deposit txs not seen
if (txs.size() != 2) return; if (txs.size() != 2) return;
setStatePublished();
// update deposit txs // update deposit txs
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash()); boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
makerDepositTx = makerFirst ? txs.get(0) : txs.get(1); makerDepositTx = makerFirst ? txs.get(0) : txs.get(1);
takerDepositTx = makerFirst ? txs.get(1) : txs.get(0); takerDepositTx = makerFirst ? txs.get(1) : txs.get(0);
// compute unlock height // check if deposit txs confirmed and compute unlock height
if (unlockHeight == null && txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) { if (txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed() && unlockHeight == null) {
log.info("Multisig deposits confirmed for trade {}", getId());
setStateConfirmed();
unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK; unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK;
} }
// 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();
xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified
depositTxListener = null; // prevent re-applying trade state in subsequent requests depositTxListener = null; // prevent re-applying trade state in subsequent requests
} else if (txs.size() == 2) { setStateUnlocked();
setStateIfValidTransitionTo(Trade.State.SAW_DEPOSIT_TXS_IN_NETWORK);
} }
} }
}; };
@ -1299,6 +1301,10 @@ public abstract class Trade implements Tradable, Model {
disputeState != DisputeState.REFUND_REQUEST_CLOSED; disputeState != DisputeState.REFUND_REQUEST_CLOSED;
} }
public boolean isDepositConfirmed() {
return getState().getPhase().ordinal() >= Phase.DEPOSITS_CONFIRMED.ordinal();
}
public boolean isDepositUnlocked() { public boolean isDepositUnlocked() {
return getState().getPhase().ordinal() >= Phase.DEPOSITS_UNLOCKED.ordinal(); return getState().getPhase().ordinal() >= Phase.DEPOSITS_UNLOCKED.ordinal();
} }
@ -1434,40 +1440,16 @@ public abstract class Trade implements Tradable, Model {
return tradeVolumeProperty; return tradeVolumeProperty;
} }
// private void setupConfidenceListener() { private void setStatePublished() {
// if (getDepositTx() != null) { if (!isDepositPublished()) setState(State.DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN);
// TransactionConfidence transactionConfidence = getDepositTx().getConfidence();
// if (transactionConfidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
// setConfirmedState();
// } else {
// ListenableFuture<TransactionConfidence> future = transactionConfidence.getDepthFuture(1);
// Futures.addCallback(future, new FutureCallback<>() {
// @Override
// public void onSuccess(TransactionConfidence result) {
// setConfirmedState();
// }
//
// @Override
// public void onFailure(@NotNull Throwable t) {
// t.printStackTrace();
// log.error(t.getMessage());
// throw new RuntimeException(t);
// }
// }, MoreExecutors.directExecutor());
// }
// } else {
// log.error("depositTx == null. That must not happen.");
// }
// }
private void setUnlockedState() {
// we only apply the state if we are not already further in the process
if (!isDepositUnlocked()) {
// As setState is called here from the trade itself we cannot trigger a requestPersistence call.
// But as we get setupConfidenceListener called at startup anyway there is no issue if it would not be
// persisted in case the shutdown routine did not persist the trade.
setStateIfValidTransitionTo(State.DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN); // TODO (woodser): for xmr this means deposit txs have unlocked after 10 confirmations
} }
private void setStateConfirmed() {
if (!isDepositConfirmed()) setState(State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN);
}
private void setStateUnlocked() {
if (!isDepositUnlocked()) setState(State.DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN);
} }
@Override @Override

View file

@ -55,7 +55,7 @@ public abstract class BuyerProtocol extends DisputeProtocol {
// TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades // TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades
given(phase(Trade.Phase.DEPOSITS_PUBLISHED) given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
.with(BuyerEvent.STARTUP)) .with(BuyerEvent.STARTUP))
.setup(tasks(SetupDepositTxsListener.class)) .setup(tasks(SetupDepositTxsListener.class))
.executeTasks(); .executeTasks();

View file

@ -50,7 +50,7 @@ public abstract class SellerProtocol extends DisputeProtocol {
// TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades // TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades
given(phase(Trade.Phase.DEPOSITS_PUBLISHED) given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
.with(BuyerEvent.STARTUP)) .with(BuyerEvent.STARTUP))
.setup(tasks(SetupDepositTxsListener.class)) .setup(tasks(SetupDepositTxsListener.class))
.executeTasks(); .executeTasks();

View file

@ -2203,7 +2203,7 @@ notification.trade.headline=Notification for trade with ID {0}
notification.ticket.headline=Support ticket for trade with ID {0} notification.ticket.headline=Support ticket for trade with ID {0}
notification.trade.completed=The trade is now completed and you can withdraw your funds. notification.trade.completed=The trade is now completed and you can withdraw your funds.
notification.trade.accepted=Your offer has been accepted by a XMR {0}. notification.trade.accepted=Your offer has been accepted by a XMR {0}.
notification.trade.confirmed=Your trade has been confirmed.\nYou can start the payment now. notification.trade.unlocked=Your trade has been confirmed.\nYou can start the payment now.
notification.trade.paymentStarted=The XMR buyer has started the payment. notification.trade.paymentStarted=The XMR buyer has started the payment.
notification.trade.selectTrade=Select trade notification.trade.selectTrade=Select trade
notification.trade.peerOpenedDispute=Your trading peer has opened a {0}. notification.trade.peerOpenedDispute=Your trading peer has opened a {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=Oznámení o obchodu s ID {0}
notification.ticket.headline=Úkol na podporu pro obchod s ID {0} notification.ticket.headline=Úkol na podporu pro obchod s ID {0}
notification.trade.completed=Obchod je nyní dokončen a můžete si vybrat své prostředky. notification.trade.completed=Obchod je nyní dokončen a můžete si vybrat své prostředky.
notification.trade.accepted=Vaše nabídka byla přijata BTC {0}. notification.trade.accepted=Vaše nabídka byla přijata BTC {0}.
notification.trade.confirmed=Váš obchod má alespoň jedno potvrzení blockchainu.\nPlatbu můžete začít hned teď. notification.trade.unlocked=Váš obchod má alespoň jedno potvrzení blockchainu.\nPlatbu můžete začít hned teď.
notification.trade.paymentStarted=Kupující BTC zahájil platbu. notification.trade.paymentStarted=Kupující BTC zahájil platbu.
notification.trade.selectTrade=Vyberte obchod notification.trade.selectTrade=Vyberte obchod
notification.trade.peerOpenedDispute=Váš obchodní partner otevřel {0}. notification.trade.peerOpenedDispute=Váš obchodní partner otevřel {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=Benachrichtigung zum Handel mit der ID {0}
notification.ticket.headline=Support-Ticket für den Handel mit der ID {0} notification.ticket.headline=Support-Ticket für den Handel mit der ID {0}
notification.trade.completed=Ihr Handel ist jetzt abgeschlossen und Sie können Ihre Gelder abheben. notification.trade.completed=Ihr Handel ist jetzt abgeschlossen und Sie können Ihre Gelder abheben.
notification.trade.accepted=Ihr Angebot wurde von einem BTC-{0} angenommen. notification.trade.accepted=Ihr Angebot wurde von einem BTC-{0} angenommen.
notification.trade.confirmed=Ihr Handel hat wenigstens eine Blockchain-Bestätigung.\nSie können die Zahlung nun beginnen. notification.trade.unlocked=Ihr Handel hat wenigstens eine Blockchain-Bestätigung.\nSie können die Zahlung nun beginnen.
notification.trade.paymentStarted=Der BTC-Käufer hat die Zahlung begonnen. notification.trade.paymentStarted=Der BTC-Käufer hat die Zahlung begonnen.
notification.trade.selectTrade=Handel wählen notification.trade.selectTrade=Handel wählen
notification.trade.peerOpenedDispute=Ihr Handelspartner hat ein/einen {0} geöffnet. notification.trade.peerOpenedDispute=Ihr Handelspartner hat ein/einen {0} geöffnet.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=Notificación de intercambio con ID {0}
notification.ticket.headline=Ticket de soporte de intercambio con ID {0} notification.ticket.headline=Ticket de soporte de intercambio con ID {0}
notification.trade.completed=El intercambio se ha completado y puede retirar sus fondos. notification.trade.completed=El intercambio se ha completado y puede retirar sus fondos.
notification.trade.accepted=Su oferta ha sido aceptada por un {0} BTC notification.trade.accepted=Su oferta ha sido aceptada por un {0} BTC
notification.trade.confirmed=Su intercambio tiene al menos una confirmación en la cadena de bloques.\nPuede comenzar el pago ahora. notification.trade.unlocked=Su intercambio tiene al menos una confirmación en la cadena de bloques.\nPuede comenzar el pago ahora.
notification.trade.paymentStarted=El comprador de BTC ha comenzado el pago. notification.trade.paymentStarted=El comprador de BTC ha comenzado el pago.
notification.trade.selectTrade=Seleccionar intercambio notification.trade.selectTrade=Seleccionar intercambio
notification.trade.peerOpenedDispute=Su pareja de intercambio ha abierto un {0}. notification.trade.peerOpenedDispute=Su pareja de intercambio ha abierto un {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=اعلان برای معامله با شناسه {0}
notification.ticket.headline=تیکت پشتیبانی برای معامله با شناسه {0} notification.ticket.headline=تیکت پشتیبانی برای معامله با شناسه {0}
notification.trade.completed=معامله اکنون کامل شده است و می توانید وجوه خود را برداشت کنید. notification.trade.completed=معامله اکنون کامل شده است و می توانید وجوه خود را برداشت کنید.
notification.trade.accepted=پیشنهاد شما توسط BTC {0} پذیرفته شده است. notification.trade.accepted=پیشنهاد شما توسط BTC {0} پذیرفته شده است.
notification.trade.confirmed=معامله شما دارای حداقل یک تایید بلاک چین است.\n شما اکنون می توانید پرداخت را شروع کنید. notification.trade.unlocked=معامله شما دارای حداقل یک تایید بلاک چین است.\n شما اکنون می توانید پرداخت را شروع کنید.
notification.trade.paymentStarted=خریدار BTC پرداخت را آغاز کرده است. notification.trade.paymentStarted=خریدار BTC پرداخت را آغاز کرده است.
notification.trade.selectTrade=انتخاب معامله notification.trade.selectTrade=انتخاب معامله
notification.trade.peerOpenedDispute=همتای معامله شما یک {0} را باز کرده است. notification.trade.peerOpenedDispute=همتای معامله شما یک {0} را باز کرده است.

View file

@ -1687,7 +1687,7 @@ notification.trade.headline=Notification pour la transaction avec l''ID {0}
notification.ticket.headline=Ticket de support pour l''échange avec l''ID {0} notification.ticket.headline=Ticket de support pour l''échange avec l''ID {0}
notification.trade.completed=La transaction est maintenant terminée et vous pouvez retirer vos fonds. notification.trade.completed=La transaction est maintenant terminée et vous pouvez retirer vos fonds.
notification.trade.accepted=Votre ordre a été accepté par un BTC {0}. notification.trade.accepted=Votre ordre a été accepté par un BTC {0}.
notification.trade.confirmed=Votre échange avait au moins une confirmation sur la blockchain.\nVous pouvez effectuer le paiement maintenant. notification.trade.unlocked=Votre échange avait au moins une confirmation sur la blockchain.\nVous pouvez effectuer le paiement maintenant.
notification.trade.paymentStarted=L'acheteur de BTC a initié le paiement. notification.trade.paymentStarted=L'acheteur de BTC a initié le paiement.
notification.trade.selectTrade=Choisir un trade notification.trade.selectTrade=Choisir un trade
notification.trade.peerOpenedDispute=Votre pair de trading a ouvert un {0}. notification.trade.peerOpenedDispute=Votre pair de trading a ouvert un {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=Notifica per scambi con ID {0}
notification.ticket.headline=Biglietto di supporto per scambi con ID {0} notification.ticket.headline=Biglietto di supporto per scambi con ID {0}
notification.trade.completed=Il commercio è ora completato e puoi ritirare i tuoi fondi. notification.trade.completed=Il commercio è ora completato e puoi ritirare i tuoi fondi.
notification.trade.accepted=La tua offerta è stata accettata da un BTC {0}. notification.trade.accepted=La tua offerta è stata accettata da un BTC {0}.
notification.trade.confirmed=Il tuo trade ha almeno una conferma blockchain.\nPuoi iniziare il pagamento ora. notification.trade.unlocked=Il tuo trade ha almeno una conferma blockchain.\nPuoi iniziare il pagamento ora.
notification.trade.paymentStarted=L'acquirente BTC ha avviato il pagamento. notification.trade.paymentStarted=L'acquirente BTC ha avviato il pagamento.
notification.trade.selectTrade=Seleziona scambio notification.trade.selectTrade=Seleziona scambio
notification.trade.peerOpenedDispute=Il tuo peer di trading ha aperto un {0}. notification.trade.peerOpenedDispute=Il tuo peer di trading ha aperto un {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=ID {0}とのトレードの通知
notification.ticket.headline=ID {0}とのトレード用サポートチケット notification.ticket.headline=ID {0}とのトレード用サポートチケット
notification.trade.completed=これでトレードは完了し、資金を出金することができます。 notification.trade.completed=これでトレードは完了し、資金を出金することができます。
notification.trade.accepted=あなたのオファーはBTC {0}によって承認されました。 notification.trade.accepted=あなたのオファーはBTC {0}によって承認されました。
notification.trade.confirmed=あなたのトレードには少なくとも1つのブロックチェーン承認があります。\nあなたは今、支払いを始めることができます。 notification.trade.unlocked=あなたのトレードには少なくとも1つのブロックチェーン承認があります。\nあなたは今、支払いを始めることができます。
notification.trade.paymentStarted=BTCの買い手が支払いを開始しました。 notification.trade.paymentStarted=BTCの買い手が支払いを開始しました。
notification.trade.selectTrade=取引を選択 notification.trade.selectTrade=取引を選択
notification.trade.peerOpenedDispute=あなたの取引相手は{0}をオープンしました。 notification.trade.peerOpenedDispute=あなたの取引相手は{0}をオープンしました。

View file

@ -1694,7 +1694,7 @@ notification.trade.headline=Notificação para o oferta com ID {0}
notification.ticket.headline=Ticket de suporte para a oferta com ID {0} notification.ticket.headline=Ticket de suporte para a oferta com ID {0}
notification.trade.completed=A negociação foi concluída e você já pode retirar seus fundos. notification.trade.completed=A negociação foi concluída e você já pode retirar seus fundos.
notification.trade.accepted=Sua oferta foi aceita por um {0}. notification.trade.accepted=Sua oferta foi aceita por um {0}.
notification.trade.confirmed=Sua negociação tem pelo menos uma confirmação da blockchain.\nVocê já pode iniciar o pagamento. notification.trade.unlocked=Sua negociação tem pelo menos uma confirmação da blockchain.\nVocê já pode iniciar o pagamento.
notification.trade.paymentStarted=O comprador BTC iniciou o pagamento notification.trade.paymentStarted=O comprador BTC iniciou o pagamento
notification.trade.selectTrade=Selecionar negociação notification.trade.selectTrade=Selecionar negociação
notification.trade.peerOpenedDispute=Seu parceiro de negociação abriu um {0}. notification.trade.peerOpenedDispute=Seu parceiro de negociação abriu um {0}.

View file

@ -1684,7 +1684,7 @@ notification.trade.headline=Notificação para o oferta com ID {0}
notification.ticket.headline=Bilhete de apoio para o negócio com ID {0} notification.ticket.headline=Bilhete de apoio para o negócio com ID {0}
notification.trade.completed=O negócio completou e você já pode levantar seus fundos. notification.trade.completed=O negócio completou e você já pode levantar seus fundos.
notification.trade.accepted=Sua oferta foi aceite por um {0} de BTC. notification.trade.accepted=Sua oferta foi aceite por um {0} de BTC.
notification.trade.confirmed=Seu negócio tem pelo menos uma confirmação da blockchain.\nVocê pode começar o pagamento agora. notification.trade.unlocked=Seu negócio tem pelo menos uma confirmação da blockchain.\nVocê pode começar o pagamento agora.
notification.trade.paymentStarted=O comprador de BTC iniciou o pagamento notification.trade.paymentStarted=O comprador de BTC iniciou o pagamento
notification.trade.selectTrade=Selecionar negócio notification.trade.selectTrade=Selecionar negócio
notification.trade.peerOpenedDispute=Seu par de negociação abriu um {0}. notification.trade.peerOpenedDispute=Seu par de negociação abriu um {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=Уведомление о сделке с идент
notification.ticket.headline=Запрос в службу поддержки для сделки с идентификатором {0} notification.ticket.headline=Запрос в службу поддержки для сделки с идентификатором {0}
notification.trade.completed=Сделка завершена, и вы можете вывести свои средства. notification.trade.completed=Сделка завершена, и вы можете вывести свои средства.
notification.trade.accepted=Ваше предложение принял {0} ВТС. notification.trade.accepted=Ваше предложение принял {0} ВТС.
notification.trade.confirmed=Ваша сделка была подтверждена в блокчейне не менее одного раза.\nМожете начать оплату. notification.trade.unlocked=Ваша сделка была подтверждена в блокчейне не менее одного раза.\nМожете начать оплату.
notification.trade.paymentStarted=Покупатель ВТС начал оплату. notification.trade.paymentStarted=Покупатель ВТС начал оплату.
notification.trade.selectTrade=Выбрать сделку notification.trade.selectTrade=Выбрать сделку
notification.trade.peerOpenedDispute=Ваш контрагент открыл {0}. notification.trade.peerOpenedDispute=Ваш контрагент открыл {0}.

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=การแจ้งเตือนการซื
notification.ticket.headline=ศูนย์ช่วยเหลือสนับสนุนการซื้อขายด้วย ID {0} notification.ticket.headline=ศูนย์ช่วยเหลือสนับสนุนการซื้อขายด้วย ID {0}
notification.trade.completed=การค้าเสร็จสิ้นแล้วและคุณสามารถถอนเงินของคุณได้ notification.trade.completed=การค้าเสร็จสิ้นแล้วและคุณสามารถถอนเงินของคุณได้
notification.trade.accepted=ข้อเสนอของคุณได้รับการยอมรับจาก BTC {0} แล้ว notification.trade.accepted=ข้อเสนอของคุณได้รับการยอมรับจาก BTC {0} แล้ว
notification.trade.confirmed=การซื้อขายของคุณมีการยืนยัน blockchain อย่างน้อยหนึ่งรายการ\nคุณสามารถเริ่มการชำระเงินได้เลย notification.trade.unlocked=การซื้อขายของคุณมีการยืนยัน blockchain อย่างน้อยหนึ่งรายการ\nคุณสามารถเริ่มการชำระเงินได้เลย
notification.trade.paymentStarted=ผู้ซื้อ BTC ได้เริ่มการชำระเงินแล้ว notification.trade.paymentStarted=ผู้ซื้อ BTC ได้เริ่มการชำระเงินแล้ว
notification.trade.selectTrade=เลือกการซื้อขาย notification.trade.selectTrade=เลือกการซื้อขาย
notification.trade.peerOpenedDispute=เครือข่ายทางการค้าของคุณได้เริ่มต้นเปิดที่ {0} notification.trade.peerOpenedDispute=เครือข่ายทางการค้าของคุณได้เริ่มต้นเปิดที่ {0}

View file

@ -1688,7 +1688,7 @@ notification.trade.headline=Thông báo với giao dịch có ID {0}
notification.ticket.headline=Vé hỗ trợ cho giao dịch có ID {0} notification.ticket.headline=Vé hỗ trợ cho giao dịch có ID {0}
notification.trade.completed=giao dịch đã hoàn thành và bạn có thể rút tiền. notification.trade.completed=giao dịch đã hoàn thành và bạn có thể rút tiền.
notification.trade.accepted=Chào giá của bạn đã được chấp thuận bởi BTC {0}. notification.trade.accepted=Chào giá của bạn đã được chấp thuận bởi BTC {0}.
notification.trade.confirmed=giao dịch của bạn có ít nhất một xác nhận blockchain.\nBạn có thể bắt đầu thanh toán bây giờ. notification.trade.unlocked=giao dịch của bạn có ít nhất một xác nhận blockchain.\nBạn có thể bắt đầu thanh toán bây giờ.
notification.trade.paymentStarted=Người mua BTC đã bắt đầu thanh toán. notification.trade.paymentStarted=Người mua BTC đã bắt đầu thanh toán.
notification.trade.selectTrade=Lựa chọn giao dịch notification.trade.selectTrade=Lựa chọn giao dịch
notification.trade.peerOpenedDispute=Đối tác giao dịch của bạn đã mở một {0}. notification.trade.peerOpenedDispute=Đối tác giao dịch của bạn đã mở một {0}.

View file

@ -1690,7 +1690,7 @@ notification.trade.headline=交易 ID {0} 的通知
notification.ticket.headline=交易 ID {0} 的帮助话题 notification.ticket.headline=交易 ID {0} 的帮助话题
notification.trade.completed=交易现在完成,您可以提取资金。 notification.trade.completed=交易现在完成,您可以提取资金。
notification.trade.accepted=您 BTC {0} 的报价被接受。 notification.trade.accepted=您 BTC {0} 的报价被接受。
notification.trade.confirmed=您的交易至少有一个区块链确认。\n您现在可以开始付款。 notification.trade.unlocked=您的交易至少有一个区块链确认。\n您现在可以开始付款。
notification.trade.paymentStarted=BTC 买家已经开始付款。 notification.trade.paymentStarted=BTC 买家已经开始付款。
notification.trade.selectTrade=选择交易 notification.trade.selectTrade=选择交易
notification.trade.peerOpenedDispute=您的交易对象创建了一个 {0}。 notification.trade.peerOpenedDispute=您的交易对象创建了一个 {0}。

View file

@ -1686,7 +1686,7 @@ notification.trade.headline=交易 ID {0} 的通知
notification.ticket.headline=交易 ID {0} 的幫助話題 notification.ticket.headline=交易 ID {0} 的幫助話題
notification.trade.completed=交易現在完成,您可以提取資金。 notification.trade.completed=交易現在完成,您可以提取資金。
notification.trade.accepted=您 BTC {0} 的報價被接受。 notification.trade.accepted=您 BTC {0} 的報價被接受。
notification.trade.confirmed=您的交易至少有一個區塊鏈確認。\n您現在可以開始付款。 notification.trade.unlocked=您的交易至少有一個區塊鏈確認。\n您現在可以開始付款。
notification.trade.paymentStarted=BTC 買家已經開始付款。 notification.trade.paymentStarted=BTC 買家已經開始付款。
notification.trade.selectTrade=選擇交易 notification.trade.selectTrade=選擇交易
notification.trade.peerOpenedDispute=您的交易對象創建了一個 {0}。 notification.trade.peerOpenedDispute=您的交易對象創建了一個 {0}。

View file

@ -187,13 +187,14 @@ public class NotificationCenter {
message = Res.get("notification.trade.completed"); message = Res.get("notification.trade.completed");
} else { } else {
if (trade instanceof MakerTrade && if (trade instanceof MakerTrade &&
phase.ordinal() == Trade.Phase.DEPOSITS_PUBLISHED.ordinal()) { phase.ordinal() == Trade.Phase.DEPOSITS_PUBLISHED.ordinal() ||
phase.ordinal() == Trade.Phase.DEPOSITS_CONFIRMED.ordinal()) {
final String role = trade instanceof BuyerTrade ? Res.get("shared.seller") : Res.get("shared.buyer"); final String role = trade instanceof BuyerTrade ? Res.get("shared.seller") : Res.get("shared.buyer");
message = Res.get("notification.trade.accepted", role); message = Res.get("notification.trade.accepted", role);
} }
if (trade instanceof BuyerTrade && phase.ordinal() == Trade.Phase.DEPOSITS_UNLOCKED.ordinal()) if (trade instanceof BuyerTrade && phase.ordinal() == Trade.Phase.DEPOSITS_UNLOCKED.ordinal())
message = Res.get("notification.trade.confirmed"); message = Res.get("notification.trade.unlocked");
else if (trade instanceof SellerTrade && phase.ordinal() == Trade.Phase.PAYMENT_SENT.ordinal()) else if (trade instanceof SellerTrade && phase.ordinal() == Trade.Phase.PAYMENT_SENT.ordinal())
message = Res.get("notification.trade.paymentStarted"); message = Res.get("notification.trade.paymentStarted");
} }

View file

@ -420,12 +420,12 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
// deposit published // deposit published
case ARBITRATOR_PUBLISHED_DEPOSIT_TXS: case ARBITRATOR_PUBLISHED_DEPOSIT_TXS:
case SAW_DEPOSIT_TXS_IN_NETWORK: case DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN:
case DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN: // TODO: separate step to wait for first confirmation
buyerState.set(BuyerState.STEP1); buyerState.set(BuyerState.STEP1);
sellerState.set(SellerState.STEP1); sellerState.set(SellerState.STEP1);
break; break;
// buyer and seller step 2 // buyer and seller step 2
// deposit unlocked // deposit unlocked
case DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN: case DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN:

View file

@ -1635,28 +1635,29 @@ message Trade {
STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 10; STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 10;
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 11; SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 11;
ARBITRATOR_PUBLISHED_DEPOSIT_TXS = 12; ARBITRATOR_PUBLISHED_DEPOSIT_TXS = 12;
SAW_DEPOSIT_TXS_IN_NETWORK = 13; DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN = 13;
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN = 14; DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN = 14;
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 15; DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN = 15;
BUYER_SENT_PAYMENT_SENT_MSG = 16; BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 16;
BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG = 17; BUYER_SENT_PAYMENT_SENT_MSG = 17;
BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG = 18; BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG = 18;
BUYER_SEND_FAILED_PAYMENT_SENT_MSG = 19; BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG = 19;
SELLER_RECEIVED_PAYMENT_SENT_MSG = 20; BUYER_SEND_FAILED_PAYMENT_SENT_MSG = 20;
SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT = 21; SELLER_RECEIVED_PAYMENT_SENT_MSG = 21;
SELLER_SENT_PAYMENT_RECEIVED_MSG = 22; SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT = 22;
SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG = 23; SELLER_SENT_PAYMENT_RECEIVED_MSG = 23;
SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG = 24; SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG = 24;
SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG = 25; SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG = 25;
SELLER_PUBLISHED_PAYOUT_TX = 26; SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG = 26;
SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG = 27; SELLER_PUBLISHED_PAYOUT_TX = 27;
SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG = 28; SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG = 28;
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 29; SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG = 29;
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 30; SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 30;
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 31; SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 31;
BUYER_SAW_PAYOUT_TX_IN_NETWORK = 32; BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 32;
BUYER_PUBLISHED_PAYOUT_TX = 33; BUYER_SAW_PAYOUT_TX_IN_NETWORK = 33;
WITHDRAW_COMPLETED = 34; BUYER_PUBLISHED_PAYOUT_TX = 34;
WITHDRAW_COMPLETED = 35;
} }
enum Phase { enum Phase {
@ -1664,11 +1665,12 @@ message Trade {
INIT = 1; INIT = 1;
DEPOSIT_REQUESTED = 2; DEPOSIT_REQUESTED = 2;
DEPOSITS_PUBLISHED = 3; DEPOSITS_PUBLISHED = 3;
DEPOSITS_UNLOCKED = 4; DEPOSITS_CONFIRMED = 4;
PAYMENT_SENT = 5; DEPOSITS_UNLOCKED = 5;
PAYMENT_RECEIVED = 6; PAYMENT_SENT = 6;
PAYOUT_PUBLISHED = 7; PAYMENT_RECEIVED = 7;
WITHDRAWN = 8; PAYOUT_PUBLISHED = 8;
WITHDRAWN = 9;
} }
enum DisputeState { enum DisputeState {