mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 11:39:29 +00:00
improve error handling in protocol pipelines
support getTrades() from grpc api consistently use timeouts in protocol pipelines remove trade and repost offer on protocol errors delete multisig wallet when trade removed protocol advances on ack messages instead of network onArrived() re-order protocol class methods to correct order
This commit is contained in:
parent
d2ddfad5bc
commit
7c9c35b1b8
21 changed files with 1096 additions and 930 deletions
|
@ -39,6 +39,7 @@ import bisq.proto.grpc.GetPaymentAccountFormRequest;
|
||||||
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
||||||
import bisq.proto.grpc.GetPaymentMethodsRequest;
|
import bisq.proto.grpc.GetPaymentMethodsRequest;
|
||||||
import bisq.proto.grpc.GetTradeRequest;
|
import bisq.proto.grpc.GetTradeRequest;
|
||||||
|
import bisq.proto.grpc.GetTradesRequest;
|
||||||
import bisq.proto.grpc.GetTransactionRequest;
|
import bisq.proto.grpc.GetTransactionRequest;
|
||||||
import bisq.proto.grpc.GetTxFeeRateRequest;
|
import bisq.proto.grpc.GetTxFeeRateRequest;
|
||||||
import bisq.proto.grpc.GetVersionRequest;
|
import bisq.proto.grpc.GetVersionRequest;
|
||||||
|
@ -349,6 +350,11 @@ public final class GrpcClient {
|
||||||
return grpcStubs.tradesService.getTrade(request).getTrade();
|
return grpcStubs.tradesService.getTrade(request).getTrade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<TradeInfo> getTrades() {
|
||||||
|
var request = GetTradesRequest.newBuilder().build();
|
||||||
|
return grpcStubs.tradesService.getTrades(request).getTradesList();
|
||||||
|
}
|
||||||
|
|
||||||
public void confirmPaymentStarted(String tradeId) {
|
public void confirmPaymentStarted(String tradeId) {
|
||||||
var request = ConfirmPaymentStartedRequest.newBuilder()
|
var request = ConfirmPaymentStartedRequest.newBuilder()
|
||||||
.setTradeId(tradeId)
|
.setTradeId(tradeId)
|
||||||
|
|
|
@ -244,11 +244,15 @@ public class CoreApi {
|
||||||
String paymentAccountId,
|
String paymentAccountId,
|
||||||
Consumer<Trade> resultHandler,
|
Consumer<Trade> resultHandler,
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
Offer offer = coreOffersService.getOffer(offerId);
|
try {
|
||||||
coreTradesService.takeOffer(offer,
|
Offer offer = coreOffersService.getOffer(offerId);
|
||||||
paymentAccountId,
|
coreTradesService.takeOffer(offer,
|
||||||
resultHandler,
|
paymentAccountId,
|
||||||
errorMessageHandler);
|
resultHandler,
|
||||||
|
errorMessageHandler);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void confirmPaymentStarted(String tradeId) {
|
public void confirmPaymentStarted(String tradeId) {
|
||||||
|
@ -271,6 +275,10 @@ public class CoreApi {
|
||||||
return coreTradesService.getTrade(tradeId);
|
return coreTradesService.getTrade(tradeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Trade> getTrades() {
|
||||||
|
return coreTradesService.getTrades();
|
||||||
|
}
|
||||||
|
|
||||||
public String getTradeRole(String tradeId) {
|
public String getTradeRole(String tradeId) {
|
||||||
return coreTradesService.getTradeRole(tradeId);
|
return coreTradesService.getTradeRole(tradeId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
@ -142,20 +143,31 @@ class CoreOffersService {
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Offer> getMyOffers(String direction, String currencyCode) {
|
List<Offer> getMyOffers(String direction, String currencyCode) {
|
||||||
|
|
||||||
|
// get my offers posted to books
|
||||||
List<Offer> offers = offerBookService.getOffers().stream()
|
List<Offer> offers = offerBookService.getOffers().stream()
|
||||||
.filter(o -> o.isMyOffer(keyRing))
|
.filter(o -> o.isMyOffer(keyRing))
|
||||||
.filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))
|
.filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))
|
||||||
.sorted(priceComparator(direction))
|
.sorted(priceComparator(direction))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// remove unreserved offers
|
||||||
Set<Offer> unreservedOffers = getUnreservedOffers(offers);
|
Set<Offer> unreservedOffers = getUnreservedOffers(offers);
|
||||||
offers.removeAll(unreservedOffers);
|
offers.removeAll(unreservedOffers);
|
||||||
|
|
||||||
// remove my unreserved offers from offer manager
|
// remove my unreserved offers from offer manager
|
||||||
List<OpenOffer> unreservedOpenOffers = new ArrayList<OpenOffer>();
|
List<OpenOffer> unreservedOpenOffers = new ArrayList<OpenOffer>();
|
||||||
for (Offer unreservedOffer : unreservedOffers) {
|
for (Offer unreservedOffer : unreservedOffers) {
|
||||||
unreservedOpenOffers.add(openOfferManager.getOpenOfferById(unreservedOffer.getId()).get());
|
unreservedOpenOffers.add(openOfferManager.getOpenOfferById(unreservedOffer.getId()).get());
|
||||||
}
|
}
|
||||||
openOfferManager.removeOpenOffers(unreservedOpenOffers, null);
|
openOfferManager.removeOpenOffers(unreservedOpenOffers, null);
|
||||||
|
|
||||||
|
// set offer state
|
||||||
|
for (Offer offer : offers) {
|
||||||
|
Optional<OpenOffer> openOffer = openOfferManager.getOpenOfferById(offer.getId());
|
||||||
|
if (openOffer.isPresent()) offer.setState(openOffer.get().getState() == OpenOffer.State.AVAILABLE ? Offer.State.AVAILABLE : Offer.State.NOT_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
return offers;
|
return offers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
@ -89,31 +90,35 @@ class CoreTradesService {
|
||||||
String paymentAccountId,
|
String paymentAccountId,
|
||||||
Consumer<Trade> resultHandler,
|
Consumer<Trade> resultHandler,
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
coreWalletsService.verifyWalletsAreAvailable();
|
try {
|
||||||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
coreWalletsService.verifyWalletsAreAvailable();
|
||||||
|
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||||
|
|
||||||
var paymentAccount = user.getPaymentAccount(paymentAccountId);
|
var paymentAccount = user.getPaymentAccount(paymentAccountId);
|
||||||
if (paymentAccount == null)
|
if (paymentAccount == null)
|
||||||
throw new IllegalArgumentException(format("payment account with id '%s' not found", paymentAccountId));
|
throw new IllegalArgumentException(format("payment account with id '%s' not found", paymentAccountId));
|
||||||
|
|
||||||
var useSavingsWallet = true;
|
var useSavingsWallet = true;
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
|
takeOfferModel.initModel(offer, paymentAccount, useSavingsWallet);
|
||||||
log.info("Initiating take {} offer, {}",
|
log.info("Initiating take {} offer, {}",
|
||||||
offer.isBuyOffer() ? "buy" : "sell",
|
offer.isBuyOffer() ? "buy" : "sell",
|
||||||
takeOfferModel);
|
takeOfferModel);
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
tradeManager.onTakeOffer(offer.getAmount(),
|
tradeManager.onTakeOffer(offer.getAmount(),
|
||||||
takeOfferModel.getTxFeeFromFeeService(),
|
takeOfferModel.getTxFeeFromFeeService(),
|
||||||
takeOfferModel.getTakerFee(),
|
takeOfferModel.getTakerFee(),
|
||||||
takeOfferModel.getFundsNeededForTrade(),
|
takeOfferModel.getFundsNeededForTrade(),
|
||||||
offer,
|
offer,
|
||||||
paymentAccountId,
|
paymentAccountId,
|
||||||
useSavingsWallet,
|
useSavingsWallet,
|
||||||
coreContext.isApiUser(),
|
coreContext.isApiUser(),
|
||||||
resultHandler::accept,
|
resultHandler::accept,
|
||||||
errorMessageHandler
|
errorMessageHandler
|
||||||
);
|
);
|
||||||
|
} catch (Exception e) {
|
||||||
|
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void confirmPaymentStarted(String tradeId) {
|
void confirmPaymentStarted(String tradeId) {
|
||||||
|
@ -224,6 +229,14 @@ class CoreTradesService {
|
||||||
return tradable.filter((t) -> t instanceof Trade).map(value -> (Trade) value);
|
return tradable.filter((t) -> t instanceof Trade).map(value -> (Trade) value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Trade> getTrades() {
|
||||||
|
coreWalletsService.verifyWalletsAreAvailable();
|
||||||
|
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||||
|
List<Trade> trades = new ArrayList<Trade>(tradeManager.getTrades());
|
||||||
|
trades.addAll(closedTradableManager.getClosedTrades());
|
||||||
|
return trades;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isFollowingBuyerProtocol(Trade trade) {
|
private boolean isFollowingBuyerProtocol(Trade trade) {
|
||||||
return tradeManager.getTradeProtocol(trade) instanceof BuyerProtocol;
|
return tradeManager.getTradeProtocol(trade) instanceof BuyerProtocol;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,12 +90,14 @@ public class MoneroWalletRpcManager {
|
||||||
* Stop an instance of monero-wallet-rpc.
|
* Stop an instance of monero-wallet-rpc.
|
||||||
*
|
*
|
||||||
* @param walletRpc the client connected to the monero-wallet-rpc instance to stop
|
* @param walletRpc the client connected to the monero-wallet-rpc instance to stop
|
||||||
|
* @param save specifies if the wallet should be saved before closing
|
||||||
*/
|
*/
|
||||||
public void stopInstance(MoneroWalletRpc walletRpc) {
|
public void stopInstance(MoneroWalletRpc walletRpc, boolean save) {
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Map.Entry<Integer, MoneroWalletRpc> entry : registeredPorts.entrySet()) {
|
for (Map.Entry<Integer, MoneroWalletRpc> entry : registeredPorts.entrySet()) {
|
||||||
if (walletRpc == entry.getValue()) {
|
if (walletRpc == entry.getValue()) {
|
||||||
walletRpc.stop();
|
walletRpc.close(save);
|
||||||
|
walletRpc.stopProcess();
|
||||||
found = true;
|
found = true;
|
||||||
try { unregisterPort(entry.getKey()); }
|
try { unregisterPort(entry.getKey()); }
|
||||||
catch (Exception e) { throw new MoneroError(e); }
|
catch (Exception e) { throw new MoneroError(e); }
|
||||||
|
|
|
@ -88,8 +88,6 @@ import static bisq.common.util.Preconditions.checkDir;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static com.google.common.base.Preconditions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import monero.common.MoneroUtils;
|
import monero.common.MoneroUtils;
|
||||||
import monero.daemon.MoneroDaemon;
|
import monero.daemon.MoneroDaemon;
|
||||||
import monero.daemon.MoneroDaemonRpc;
|
import monero.daemon.MoneroDaemonRpc;
|
||||||
|
@ -132,7 +130,8 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
private static final String MONERO_DAEMON_USERNAME = "superuser";
|
private static final String MONERO_DAEMON_USERNAME = "superuser";
|
||||||
private static final String MONERO_DAEMON_PASSWORD = "abctesting123";
|
private static final String MONERO_DAEMON_PASSWORD = "abctesting123";
|
||||||
private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
|
private static final MoneroWalletRpcManager MONERO_WALLET_RPC_MANAGER = new MoneroWalletRpcManager();
|
||||||
private static final String MONERO_WALLET_RPC_PATH = System.getProperty("user.dir") + File.separator + ".localnet" + File.separator + "monero-wallet-rpc"; // use wallet rpc in .localnet
|
private static final String MONERO_WALLET_RPC_DIR = System.getProperty("user.dir") + File.separator + ".localnet"; // .localnet contains monero-wallet-rpc and wallet files
|
||||||
|
private static final String MONERO_WALLET_RPC_PATH = MONERO_WALLET_RPC_DIR + File.separator + "monero-wallet-rpc";
|
||||||
private static final String MONERO_WALLET_RPC_USERNAME = "rpc_user";
|
private static final String MONERO_WALLET_RPC_USERNAME = "rpc_user";
|
||||||
private static final String MONERO_WALLET_RPC_PASSWORD = "abc123";
|
private static final String MONERO_WALLET_RPC_PASSWORD = "abc123";
|
||||||
private static final long MONERO_WALLET_SYNC_RATE = 5000l;
|
private static final long MONERO_WALLET_SYNC_RATE = 5000l;
|
||||||
|
@ -292,6 +291,11 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
// Meant to be overridden by subclasses
|
// Meant to be overridden by subclasses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean walletExists(String walletName) {
|
||||||
|
String path = directory.toString() + File.separator + walletName;
|
||||||
|
return new File(path + ".keys").exists();
|
||||||
|
}
|
||||||
|
|
||||||
public MoneroWalletRpc createWallet(MoneroWalletConfig config, Integer port) {
|
public MoneroWalletRpc createWallet(MoneroWalletConfig config, Integer port) {
|
||||||
|
|
||||||
// start monero-wallet-rpc instance
|
// start monero-wallet-rpc instance
|
||||||
|
@ -304,7 +308,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return walletRpc;
|
return walletRpc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
WalletConfig.MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc);
|
WalletConfig.MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc, false);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,7 +325,7 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return walletRpc;
|
return walletRpc;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
WalletConfig.MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc);
|
WalletConfig.MONERO_WALLET_RPC_MANAGER.stopInstance(walletRpc, false);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -347,8 +351,17 @@ public class WalletConfig extends AbstractIdleService {
|
||||||
return WalletConfig.MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
|
return WalletConfig.MONERO_WALLET_RPC_MANAGER.startInstance(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void closeWallet(MoneroWallet walletRpc) {
|
public void closeWallet(MoneroWallet walletRpc, boolean save) {
|
||||||
WalletConfig.MONERO_WALLET_RPC_MANAGER.stopInstance((MoneroWalletRpc) walletRpc);
|
WalletConfig.MONERO_WALLET_RPC_MANAGER.stopInstance((MoneroWalletRpc) walletRpc, save);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteWallet(String walletName) {
|
||||||
|
if (!walletExists(walletName)) throw new Error("Wallet does not exist at path: " + walletName);
|
||||||
|
String path = directory.toString() + File.separator + walletName;
|
||||||
|
if (!new File(path).delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
||||||
|
if (!new File(path + ".keys").delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
||||||
|
if (!new File(path + ".address.txt").delete()) throw new RuntimeException("Failed to delete wallet file: " + path);
|
||||||
|
//WalletsSetup.deleteRollingBackup(walletName); // TODO (woodser): necessary to delete rolling backup?
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class XmrWalletService {
|
||||||
// TODO (woodser): wallet has single password which is passed here?
|
// TODO (woodser): wallet has single password which is passed here?
|
||||||
// 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 synchronized MoneroWallet createMultisigWallet(String tradeId) {
|
||||||
if (multisigWallets.containsKey(tradeId)) return multisigWallets.get(tradeId);
|
if (multisigWallets.containsKey(tradeId)) return multisigWallets.get(tradeId);
|
||||||
String path = "xmr_multisig_trade_" + tradeId;
|
String path = "xmr_multisig_trade_" + tradeId;
|
||||||
MoneroWallet multisigWallet = null;
|
MoneroWallet multisigWallet = null;
|
||||||
|
@ -99,7 +99,7 @@ public class XmrWalletService {
|
||||||
return multisigWallet;
|
return multisigWallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MoneroWallet getMultisigWallet(String tradeId) {
|
public synchronized MoneroWallet getMultisigWallet(String tradeId) {
|
||||||
if (multisigWallets.containsKey(tradeId)) return multisigWallets.get(tradeId);
|
if (multisigWallets.containsKey(tradeId)) return multisigWallets.get(tradeId);
|
||||||
String path = "xmr_multisig_trade_" + tradeId;
|
String path = "xmr_multisig_trade_" + tradeId;
|
||||||
MoneroWallet multisigWallet = null;
|
MoneroWallet multisigWallet = null;
|
||||||
|
@ -111,6 +111,19 @@ public class XmrWalletService {
|
||||||
multisigWallet.startSyncing(5000l); // TODO (woodser): use sync period from config. apps stall if too many multisig wallets and too short sync period
|
multisigWallet.startSyncing(5000l); // TODO (woodser): use sync period from config. apps stall if too many multisig wallets and too short sync period
|
||||||
return multisigWallet;
|
return multisigWallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean deleteMultisigWallet(String tradeId) {
|
||||||
|
String walletName = "xmr_multisig_trade_" + tradeId;
|
||||||
|
if (!walletsSetup.getWalletConfig().walletExists(walletName)) return false;
|
||||||
|
try {
|
||||||
|
walletsSetup.getWalletConfig().closeWallet(getMultisigWallet(tradeId), false);
|
||||||
|
} catch (Exception err) {
|
||||||
|
// multisig wallet may not be open
|
||||||
|
}
|
||||||
|
walletsSetup.getWalletConfig().deleteWallet(walletName);
|
||||||
|
multisigWallets.remove(tradeId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
public XmrAddressEntry recoverAddressEntry(String offerId, String address, XmrAddressEntry.Context context) {
|
||||||
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
|
var available = findAddressEntry(address, XmrAddressEntry.Context.AVAILABLE);
|
||||||
|
@ -291,7 +304,7 @@ public class XmrWalletService {
|
||||||
threads.add(new Thread(new Runnable() {
|
threads.add(new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try { walletsSetup.getWalletConfig().closeWallet(openWallet); }
|
try { walletsSetup.getWalletConfig().closeWallet(openWallet, true); }
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
log.warn("Error closing monero-wallet-rpc subprocess. Was Haveno stopped manually with ctrl+c?");
|
log.warn("Error closing monero-wallet-rpc subprocess. Was Haveno stopped manually with ctrl+c?");
|
||||||
}
|
}
|
||||||
|
|
|
@ -391,11 +391,6 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
offer.getAmount(),
|
offer.getAmount(),
|
||||||
buyerSecurityDeposit,
|
buyerSecurityDeposit,
|
||||||
createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDeposit));
|
createOfferService.getSellerSecurityDepositAsDouble(buyerSecurityDeposit));
|
||||||
|
|
||||||
if (placeOfferProtocols.containsKey(offer.getId())) {
|
|
||||||
log.warn("We already have a place offer protocol for offer " + offer.getId() + ", ignoring");
|
|
||||||
throw new RuntimeException("We already have a place offer protocol for offer " + offer.getId() + ", ignoring");
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaceOfferModel model = new PlaceOfferModel(offer,
|
PlaceOfferModel model = new PlaceOfferModel(offer,
|
||||||
reservedFundsForOffer,
|
reservedFundsForOffer,
|
||||||
|
@ -596,6 +591,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void unreserveOpenOffer(OpenOffer openOffer) {
|
||||||
|
openOffer.setState(OpenOffer.State.AVAILABLE);
|
||||||
|
requestPersistence();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Getters
|
// Getters
|
||||||
|
|
|
@ -64,7 +64,7 @@ import bisq.network.p2p.DecryptedMessageWithPubKey;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
import bisq.network.p2p.network.TorNetworkNode;
|
import bisq.network.p2p.network.TorNetworkNode;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import bisq.common.ClockWatcher;
|
import bisq.common.ClockWatcher;
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
|
@ -445,8 +445,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
((ArbitratorProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> {
|
((ArbitratorProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> {
|
||||||
if (takeOfferRequestErrorMessageHandler != null)
|
log.warn("Arbitrator error during trade initialization: " + errorMessage);
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage); // TODO (woodser): separate handler? // TODO (woodser): ensure failed trade removed
|
removeTrade(trade);
|
||||||
});
|
});
|
||||||
|
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
|
@ -473,7 +473,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator?
|
openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator? probably. or, arbitrator does not have open offer?
|
||||||
|
|
||||||
Trade trade;
|
Trade trade;
|
||||||
if (offer.isBuyOffer())
|
if (offer.isBuyOffer())
|
||||||
|
@ -511,10 +511,11 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
trade.getSelf().setReserveTxKeyImages(offer.getOfferPayload().getReserveTxKeyImages());
|
trade.getSelf().setReserveTxKeyImages(offer.getOfferPayload().getReserveTxKeyImages());
|
||||||
tradableList.add(trade);
|
tradableList.add(trade);
|
||||||
|
|
||||||
((MakerProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> {
|
((MakerProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> {
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
log.warn("Maker error during trade initialization: " + errorMessage);
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
openOfferManager.unreserveOpenOffer(openOffer); // offer remains available // TODO: only unreserve if funds not deposited to multisig
|
||||||
}
|
removeTrade(trade);
|
||||||
|
if (takeOfferRequestErrorMessageHandler != null) takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
|
@ -532,13 +533,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) {
|
||||||
|
log.warn("No trade with id " + request.getTradeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
getTradeProtocol(trade).handleInitMultisigRequest(request, peer, errorMessage -> {
|
getTradeProtocol(trade).handleInitMultisigRequest(request, peer);
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSignContractRequest(SignContractRequest request, NodeAddress peer) {
|
private void handleSignContractRequest(SignContractRequest request, NodeAddress peer) {
|
||||||
|
@ -552,13 +552,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) {
|
||||||
|
log.warn("No trade with id " + request.getTradeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
getTradeProtocol(trade).handleSignContractRequest(request, peer, errorMessage -> {
|
getTradeProtocol(trade).handleSignContractRequest(request, peer);
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSignContractResponse(SignContractResponse request, NodeAddress peer) {
|
private void handleSignContractResponse(SignContractResponse request, NodeAddress peer) {
|
||||||
|
@ -572,13 +571,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) {
|
||||||
|
log.warn("No trade with id " + request.getTradeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
((TraderProtocol) getTradeProtocol(trade)).handleSignContractResponse(request, peer, errorMessage -> {
|
((TraderProtocol) getTradeProtocol(trade)).handleSignContractResponse(request, peer);
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDepositRequest(DepositRequest request, NodeAddress peer) {
|
private void handleDepositRequest(DepositRequest request, NodeAddress peer) {
|
||||||
|
@ -592,13 +590,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) {
|
||||||
|
log.warn("No trade with id " + request.getTradeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
((ArbitratorProtocol) getTradeProtocol(trade)).handleDepositRequest(request, peer, errorMessage -> {
|
((ArbitratorProtocol) getTradeProtocol(trade)).handleDepositRequest(request, peer);
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleDepositResponse(DepositResponse response, NodeAddress peer) {
|
private void handleDepositResponse(DepositResponse response, NodeAddress peer) {
|
||||||
|
@ -612,13 +609,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Trade> tradeOptional = getTradeById(response.getTradeId());
|
Optional<Trade> tradeOptional = getTradeById(response.getTradeId());
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + response.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) {
|
||||||
|
log.warn("No trade with id " + response.getTradeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
((TraderProtocol) getTradeProtocol(trade)).handleDepositResponse(response, peer, errorMessage -> {
|
((TraderProtocol) getTradeProtocol(trade)).handleDepositResponse(response, peer);
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer) {
|
private void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer) {
|
||||||
|
@ -632,13 +628,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
Optional<Trade> tradeOptional = getTradeById(request.getTradeId());
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) {
|
||||||
|
log.warn("No trade with id " + request.getTradeId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
((TraderProtocol) getTradeProtocol(trade)).handlePaymentAccountPayloadRequest(request, peer, errorMessage -> {
|
((TraderProtocol) getTradeProtocol(trade)).handlePaymentAccountPayloadRequest(request, peer);
|
||||||
if (takeOfferRequestErrorMessageHandler != null) {
|
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleUpdateMultisigRequest(UpdateMultisigRequest request, NodeAddress peer) {
|
private void handleUpdateMultisigRequest(UpdateMultisigRequest request, NodeAddress peer) {
|
||||||
|
@ -655,8 +650,9 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
if (!tradeOptional.isPresent()) throw new RuntimeException("No trade with id " + request.getTradeId()); // TODO (woodser): error handling
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
getTradeProtocol(trade).handleUpdateMultisigRequest(request, peer, errorMessage -> {
|
getTradeProtocol(trade).handleUpdateMultisigRequest(request, peer, errorMessage -> {
|
||||||
if (takeOfferRequestErrorMessageHandler != null)
|
log.warn("Error handling UpdateMultisigRequest: " + errorMessage);
|
||||||
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
if (takeOfferRequestErrorMessageHandler != null)
|
||||||
|
takeOfferRequestErrorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,7 +731,9 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
// take offer and persist trade on success
|
// take offer and persist trade on success
|
||||||
((TakerProtocol) tradeProtocol).onTakeOffer(result -> {
|
((TakerProtocol) tradeProtocol).onTakeOffer(result -> {
|
||||||
tradeResultHandler.handleResult(trade);
|
tradeResultHandler.handleResult(trade);
|
||||||
|
requestPersistence();
|
||||||
}, errorMessage -> {
|
}, errorMessage -> {
|
||||||
|
log.warn("Taker error during trade initialization: " + errorMessage);
|
||||||
removeTrade(trade);
|
removeTrade(trade);
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
});
|
});
|
||||||
|
@ -985,8 +983,25 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst();
|
return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Trade> getTrades() {
|
||||||
|
return ImmutableList.copyOf(getObservableList().stream()
|
||||||
|
.filter(e -> e instanceof Trade)
|
||||||
|
.map(e -> e)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
private void removeTrade(Trade trade) {
|
private void removeTrade(Trade trade) {
|
||||||
if (tradableList.remove(trade)) {
|
if (tradableList.remove(trade)) {
|
||||||
|
|
||||||
|
// unreserve taker trade key images
|
||||||
|
if (trade instanceof TakerTrade && trade.getSelf().getReserveTxKeyImages() != null) {
|
||||||
|
for (String keyImage : trade.getSelf().getReserveTxKeyImages()) {
|
||||||
|
xmrWalletService.getWallet().thawOutput(keyImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p2PService.removeDecryptedDirectMessageListener(getTradeProtocol(trade));
|
||||||
|
xmrWalletService.deleteMultisigWallet(trade.getId());
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,22 +31,34 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
||||||
// Incoming messages
|
// Incoming messages
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void handleInitTradeRequest(InitTradeRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) { // TODO (woodser): update impl to use errorMessageHandler
|
public void handleInitTradeRequest(InitTradeRequest message,
|
||||||
|
NodeAddress peer,
|
||||||
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
processModel.setTradeMessage(message); // TODO (woodser): confirm these are null without being set
|
processModel.setTradeMessage(message); // TODO (woodser): confirm these are null without being set
|
||||||
//processModel.setTempTradingPeerNodeAddress(peer);
|
//processModel.setTempTradingPeerNodeAddress(peer);
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
.with(message)
|
.with(message)
|
||||||
.from(peer))
|
.from(peer))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
ProcessInitTradeRequest.class,
|
ProcessInitTradeRequest.class,
|
||||||
ArbitratorProcessesReserveTx.class,
|
ArbitratorProcessesReserveTx.class,
|
||||||
ArbitratorSendsInitTradeAndMultisigRequests.class))
|
ArbitratorSendsInitTradeAndMultisigRequests.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(peer, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(peer, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||||
System.out.println("ArbitratorProtocol.handleInitMultisigRequest()");
|
System.out.println("ArbitratorProtocol.handleInitMultisigRequest()");
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
processModel.setTradeMessage(request);
|
processModel.setTradeMessage(request);
|
||||||
|
@ -54,20 +66,21 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
||||||
.with(request)
|
.with(request)
|
||||||
.from(sender))
|
.from(sender))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
ProcessInitMultisigRequest.class)
|
ProcessInitMultisigRequest.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
handleTaskRunnerSuccess(sender, request);
|
handleTaskRunnerSuccess(sender, request);
|
||||||
},
|
},
|
||||||
errorMessage -> {
|
errorMessage -> {
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
})))
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||||
System.out.println("ArbitratorProtocol.handleSignContractRequest()");
|
System.out.println("ArbitratorProtocol.handleSignContractRequest()");
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
processModel.setTradeMessage(message); // TODO (woodser): synchronize access since concurrent requests processed
|
processModel.setTradeMessage(message); // TODO (woodser): synchronize access since concurrent requests processed
|
||||||
|
@ -77,18 +90,19 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
// TODO (woodser): validate request
|
// TODO (woodser): validate request
|
||||||
ProcessSignContractRequest.class)
|
ProcessSignContractRequest.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
handleTaskRunnerSuccess(sender, message);
|
handleTaskRunnerSuccess(sender, message);
|
||||||
},
|
},
|
||||||
errorMessage -> {
|
errorMessage -> {
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
})))
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleDepositRequest(DepositRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
public void handleDepositRequest(DepositRequest request, NodeAddress sender) {
|
||||||
System.out.println("ArbitratorProtocol.handleDepositRequest()");
|
System.out.println("ArbitratorProtocol.handleDepositRequest()");
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
processModel.setTradeMessage(request);
|
processModel.setTradeMessage(request);
|
||||||
|
@ -96,15 +110,16 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
||||||
.with(request)
|
.with(request)
|
||||||
.from(sender))
|
.from(sender))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
ProcessDepositRequest.class)
|
ProcessDepositRequest.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
handleTaskRunnerSuccess(sender, request);
|
handleTaskRunnerSuccess(sender, request);
|
||||||
},
|
},
|
||||||
errorMessage -> {
|
errorMessage -> {
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
})))
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -64,11 +64,179 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
|
||||||
super(trade);
|
super(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// MakerProtocol
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO (woodser): these methods are duplicated with SellerAsMakerProtocol due to single inheritance
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInitTradeRequest(InitTradeRequest message,
|
||||||
|
NodeAddress peer,
|
||||||
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
|
expect(phase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(peer))
|
||||||
|
.setup(tasks(
|
||||||
|
ProcessInitTradeRequest.class,
|
||||||
|
//ApplyFilter.class, // TODO (woodser): these checks apply when maker signs availability request, but not here
|
||||||
|
//VerifyPeersAccountAgeWitness.class, // TODO (woodser): these checks apply after in multisig, means if rejected need to reimburse other's fee
|
||||||
|
MakerSendsInitTradeRequestIfUnreserved.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(peer, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(peer, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleInitMultisigRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request); // TODO (woodser): synchronize access since concurrent requests processed
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(request)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
ProcessInitMultisigRequest.class,
|
||||||
|
SendSignContractRequestAfterMultisig.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleSignContractRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractRequest.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleSignContractResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message); // TODO (woodser): synchronize access since concurrent requests processed
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleDepositResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||||
|
processModel.setTradeMessage(response);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(response)
|
||||||
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessDepositResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, response);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, response, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handlePaymentAccountPayloadRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(request)
|
||||||
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessPaymentAccountPayloadRequest.class,
|
||||||
|
MakerRemovesOpenOffer.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
stopTimeout();
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Handle take offer request
|
// User interaction
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// We keep the handler here in as well to make it more transparent which events we expect
|
||||||
|
@Override
|
||||||
|
public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
super.onPaymentStarted(resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Incoming message Payout tx
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// We keep the handler here in as well to make it more transparent which messages we expect
|
||||||
|
@Override
|
||||||
|
protected void handle(PayoutTxPublishedMessage message, NodeAddress peer) {
|
||||||
|
super.handle(message, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends TradeTask> getVerifyPeersFeePaymentClass() {
|
||||||
|
return MakerVerifyTakerFeePayment.class;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO (woodser): remove or ignore any unsupported requests
|
// TODO (woodser): remove or ignore any unsupported requests
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -96,175 +264,4 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
|
||||||
protected void handle(DepositTxAndDelayedPayoutTxMessage message, NodeAddress peer) {
|
protected void handle(DepositTxAndDelayedPayoutTxMessage message, NodeAddress peer) {
|
||||||
super.handle(message, peer);
|
super.handle(message, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// User interaction
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// We keep the handler here in as well to make it more transparent which events we expect
|
|
||||||
@Override
|
|
||||||
public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
super.onPaymentStarted(resultHandler, errorMessageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Incoming message Payout tx
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// We keep the handler here in as well to make it more transparent which messages we expect
|
|
||||||
@Override
|
|
||||||
protected void handle(PayoutTxPublishedMessage message, NodeAddress peer) {
|
|
||||||
super.handle(message, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<? extends TradeTask> getVerifyPeersFeePaymentClass() {
|
|
||||||
return MakerVerifyTakerFeePayment.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// MakerProtocol
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// TODO (woodser): these methods are duplicated with SellerAsMakerProtocol due to single inheritance
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleInitTradeRequest(InitTradeRequest message,
|
|
||||||
NodeAddress peer,
|
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
|
||||||
expect(phase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(peer))
|
|
||||||
.setup(tasks(
|
|
||||||
ProcessInitTradeRequest.class,
|
|
||||||
//ApplyFilter.class, // TODO (woodser): these checks apply when maker signs availability request, but not here
|
|
||||||
//VerifyPeersAccountAgeWitness.class, // TODO (woodser): these checks apply after in multisig, means if rejected need to reimburse other's fee
|
|
||||||
MakerSendsInitTradeRequestIfUnreserved.class,
|
|
||||||
MakerRemovesOpenOffer.class).
|
|
||||||
using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(peer, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(peer, message, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(30))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleInitMultisigRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request); // TODO (woodser): synchronize access since concurrent requests processed
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(request)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
ProcessInitMultisigRequest.class,
|
|
||||||
SendSignContractRequestAfterMultisig.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleSignContractRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleSignContractResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message); // TODO (woodser): synchronize access since concurrent requests processed
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleDepositResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
|
||||||
processModel.setTradeMessage(response);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(response)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessDepositResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, response);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, response, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handlePaymentAccountPayloadRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(request)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessPaymentAccountPayloadRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
stopTimeout();
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol {
|
public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol {
|
||||||
|
|
||||||
private TradeResultHandler tradeResultHandler;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -85,7 +83,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Take offer
|
// User interaction: Take offer
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// TODO (woodser): this implementation is duplicated with SellerAsTakerProtocol
|
// TODO (woodser): this implementation is duplicated with SellerAsTakerProtocol
|
||||||
|
@ -93,21 +91,183 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
||||||
public void onTakeOffer(TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) {
|
public void onTakeOffer(TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println("onTakeOffer()");
|
System.out.println("onTakeOffer()");
|
||||||
this.tradeResultHandler = tradeResultHandler;
|
this.tradeResultHandler = tradeResultHandler;
|
||||||
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
.with(TakerEvent.TAKE_OFFER)
|
.with(TakerEvent.TAKE_OFFER)
|
||||||
.from(trade.getTradingPeerNodeAddress()))
|
.from(trade.getTradingPeerNodeAddress()))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
TakerReservesTradeFunds.class,
|
TakerReservesTradeFunds.class,
|
||||||
TakerSendsInitTradeRequestToArbitrator.class) // TODO (woodser): app hangs if this pipeline fails. use .using() like below
|
TakerSendsInitTradeRequestToArbitrator.class) // TODO (woodser): app hangs if this pipeline fails. use .using() like below
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> { },
|
() -> { },
|
||||||
errorMessageHandler))
|
errorMessageHandler))
|
||||||
.withTimeout(30))
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TakerProtocol
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO (woodser): these methods are duplicated with SellerAsTakerProtocol due to single inheritance
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsTakerProtocol.handleInitMultisigRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(request)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
ProcessInitMultisigRequest.class,
|
||||||
|
SendSignContractRequestAfterMultisig.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handleSignContractRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractRequest.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handleSignContractResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handleDepositResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||||
|
processModel.setTradeMessage(response);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(response)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessDepositResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, response);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, response, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handlePaymentAccountPayloadRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(request)
|
||||||
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessPaymentAccountPayloadRequest.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
stopTimeout();
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
tradeResultHandler.handleResult(trade); // trade is initialized
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// User interaction
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// We keep the handler here in as well to make it more transparent which events we expect
|
||||||
|
@Override
|
||||||
|
public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
super.onPaymentStarted(resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Incoming message Payout tx
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// We keep the handler here in as well to make it more transparent which messages we expect
|
||||||
|
@Override
|
||||||
|
protected void handle(PayoutTxPublishedMessage message, NodeAddress peer) {
|
||||||
|
super.handle(message, peer);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Message dispatcher
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
||||||
|
super.onTradeMessage(message, peer);
|
||||||
|
|
||||||
|
if (message instanceof InputsForDepositTxResponse) {
|
||||||
|
handle((InputsForDepositTxResponse) message, peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<? extends TradeTask> getVerifyPeersFeePaymentClass() {
|
||||||
|
return TakerVerifyMakerFeePayment.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (woodser): remove unused
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Incoming messages Take offer process
|
// Incoming messages Take offer process
|
||||||
|
@ -149,167 +309,4 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
||||||
protected void handle(DepositTxAndDelayedPayoutTxMessage message, NodeAddress peer) {
|
protected void handle(DepositTxAndDelayedPayoutTxMessage message, NodeAddress peer) {
|
||||||
super.handle(message, peer);
|
super.handle(message, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// User interaction
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// We keep the handler here in as well to make it more transparent which events we expect
|
|
||||||
@Override
|
|
||||||
public void onPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
super.onPaymentStarted(resultHandler, errorMessageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Incoming message Payout tx
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// We keep the handler here in as well to make it more transparent which messages we expect
|
|
||||||
@Override
|
|
||||||
protected void handle(PayoutTxPublishedMessage message, NodeAddress peer) {
|
|
||||||
super.handle(message, peer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Message dispatcher
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
|
||||||
super.onTradeMessage(message, peer);
|
|
||||||
|
|
||||||
if (message instanceof InputsForDepositTxResponse) {
|
|
||||||
handle((InputsForDepositTxResponse) message, peer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Class<? extends TradeTask> getVerifyPeersFeePaymentClass() {
|
|
||||||
return TakerVerifyMakerFeePayment.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TakerProtocol
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// TODO (woodser): these methods are duplicated with SellerAsTakerProtocol due to single inheritance
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsTakerProtocol.handleInitMultisigRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(request)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
ProcessInitMultisigRequest.class,
|
|
||||||
SendSignContractRequestAfterMultisig.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
System.out.println("handle multisig pipeline completed successfully!");
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
System.out.println("error in handle multisig pipeline!!!: " + errorMessage);
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(30))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handleSignContractRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(30))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handleSignContractResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handleDepositResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
|
||||||
processModel.setTradeMessage(response);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(response)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessDepositResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, response);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, response, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handlePaymentAccountPayloadRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(request)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessPaymentAccountPayloadRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
stopTimeout();
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
tradeResultHandler.handleResult(trade); // trade is initialized
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,37 +64,153 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
||||||
super(trade);
|
super(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Incoming messages Take offer process
|
// MakerProtocol
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO (woodser): these methods are duplicated with BuyerAsMakerProtocol due to single inheritance
|
||||||
|
|
||||||
protected void handle(DepositTxMessage message, NodeAddress peer) {
|
@Override
|
||||||
expect(phase(Trade.Phase.TAKER_FEE_PUBLISHED)
|
public void handleInitTradeRequest(InitTradeRequest message,
|
||||||
|
NodeAddress peer,
|
||||||
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
|
expect(phase(Trade.Phase.INIT)
|
||||||
.with(message)
|
.with(message)
|
||||||
.from(peer))
|
.from(peer))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
MakerRemovesOpenOffer.class,
|
ProcessInitTradeRequest.class,
|
||||||
SellerAsMakerProcessDepositTxMessage.class,
|
//ApplyFilter.class, // TODO (woodser): these checks apply when maker signs availability request, but not here
|
||||||
SellerAsMakerFinalizesDepositTx.class,
|
//VerifyPeersAccountAgeWitness.class, // TODO (woodser): these checks apply after in multisig, means if rejected need to reimburse other's fee
|
||||||
SellerCreatesDelayedPayoutTx.class,
|
MakerSendsInitTradeRequestIfUnreserved.class)
|
||||||
SellerSignsDelayedPayoutTx.class,
|
.using(new TradeTaskRunner(trade,
|
||||||
SellerSendDelayedPayoutTxSignatureRequest.class)
|
() -> {
|
||||||
.withTimeout(60))
|
handleTaskRunnerSuccess(peer, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(peer, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Incoming message when buyer has clicked payment started button
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// We keep the handler here in as well to make it more transparent which messages we expect
|
|
||||||
@Override
|
@Override
|
||||||
protected void handle(CounterCurrencyTransferStartedMessage message, NodeAddress peer) {
|
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||||
super.handle(message, peer);
|
System.out.println("BuyerAsMakerProtocol.handleInitMultisigRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request); // TODO (woodser): synchronize access since concurrent requests processed
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(request)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
ProcessInitMultisigRequest.class,
|
||||||
|
SendSignContractRequestAfterMultisig.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleSignContractRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractRequest.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleSignContractResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message); // TODO (woodser): synchronize access since concurrent requests processed
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handleDepositResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||||
|
processModel.setTradeMessage(response);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(response)
|
||||||
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessDepositResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, response);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, response, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsMakerProtocol.handlePaymentAccountPayloadRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(request)
|
||||||
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessPaymentAccountPayloadRequest.class,
|
||||||
|
MakerRemovesOpenOffer.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
stopTimeout();
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// User interaction
|
// User interaction
|
||||||
|
@ -106,7 +222,6 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
||||||
super.onPaymentReceived(resultHandler, errorMessageHandler);
|
super.onPaymentReceived(resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Massage dispatcher
|
// Massage dispatcher
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -128,145 +243,35 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
||||||
return MakerVerifyTakerFeePayment.class;
|
return MakerVerifyTakerFeePayment.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
// TODO (woodser): remove unused
|
||||||
// MakerProtocol
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// TODO (woodser): these methods are duplicated with BuyerAsMakerProtocol due to single inheritance
|
|
||||||
|
|
||||||
@Override
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
public void handleInitTradeRequest(InitTradeRequest message,
|
// Incoming messages Take offer process
|
||||||
NodeAddress peer,
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
|
||||||
expect(phase(Trade.Phase.INIT)
|
protected void handle(DepositTxMessage message, NodeAddress peer) {
|
||||||
.with(message)
|
expect(phase(Trade.Phase.TAKER_FEE_PUBLISHED)
|
||||||
.from(peer))
|
.with(message)
|
||||||
.setup(tasks(
|
.from(peer))
|
||||||
ProcessInitTradeRequest.class,
|
.setup(tasks(
|
||||||
//ApplyFilter.class, // TODO (woodser): these checks apply when maker signs availability request, but not here
|
MakerRemovesOpenOffer.class,
|
||||||
//VerifyPeersAccountAgeWitness.class, // TODO (woodser): these checks apply after in multisig, means if rejected need to reimburse other's fee
|
SellerAsMakerProcessDepositTxMessage.class,
|
||||||
MakerSendsInitTradeRequestIfUnreserved.class,
|
SellerAsMakerFinalizesDepositTx.class,
|
||||||
MakerRemovesOpenOffer.class).
|
SellerCreatesDelayedPayoutTx.class,
|
||||||
using(new TradeTaskRunner(trade,
|
SellerSignsDelayedPayoutTx.class,
|
||||||
() -> {
|
SellerSendDelayedPayoutTxSignatureRequest.class)
|
||||||
handleTaskRunnerSuccess(peer, message);
|
.withTimeout(60))
|
||||||
},
|
.executeTasks();
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(peer, message, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(30))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleInitMultisigRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request); // TODO (woodser): synchronize access since concurrent requests processed
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(request)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
ProcessInitMultisigRequest.class,
|
|
||||||
SendSignContractRequestAfterMultisig.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleSignContractRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Incoming message when buyer has clicked payment started button
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// We keep the handler here in as well to make it more transparent which messages we expect
|
||||||
@Override
|
@Override
|
||||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
protected void handle(CounterCurrencyTransferStartedMessage message, NodeAddress peer) {
|
||||||
System.out.println("BuyerAsMakerProtocol.handleSignContractResponse()");
|
super.handle(message, peer);
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message); // TODO (woodser): synchronize access since concurrent requests processed
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handleDepositResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
|
||||||
processModel.setTradeMessage(response);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(response)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessDepositResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, response);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, response, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("BuyerAsMakerProtocol.handlePaymentAccountPayloadRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(request)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessPaymentAccountPayloadRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
stopTimeout();
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtocol {
|
public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtocol {
|
||||||
|
|
||||||
private TradeResultHandler tradeResultHandler;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -86,41 +84,141 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println("onTakeOffer()");
|
System.out.println("onTakeOffer()");
|
||||||
this.tradeResultHandler = tradeResultHandler;
|
this.tradeResultHandler = tradeResultHandler;
|
||||||
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
.with(TakerEvent.TAKE_OFFER)
|
.with(TakerEvent.TAKE_OFFER)
|
||||||
.from(trade.getTradingPeerNodeAddress()))
|
.from(trade.getTradingPeerNodeAddress()))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
TakerReservesTradeFunds.class,
|
TakerReservesTradeFunds.class,
|
||||||
TakerSendsInitTradeRequestToArbitrator.class)
|
TakerSendsInitTradeRequestToArbitrator.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> { },
|
() -> { },
|
||||||
errorMessageHandler))
|
errorMessageHandler))
|
||||||
.withTimeout(30))
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TakerProtocol
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO (woodser): these methods are duplicated with BuyerAsTakerProtocol due to single inheritance
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("BuyerAsTakerProtocol.handleInitMultisigRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(request)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
ProcessInitMultisigRequest.class,
|
||||||
|
SendSignContractRequestAfterMultisig.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||||
// Incoming messages Take offer process
|
System.out.println("SellerAsTakerProtocol.handleSignContractRequest()");
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message);
|
||||||
private void handle(InputsForDepositTxResponse message, NodeAddress peer) {
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
expect(phase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
.with(message)
|
||||||
.from(peer))
|
.from(sender))
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
TakerProcessesInputsForDepositTxResponse.class,
|
// TODO (woodser): validate request
|
||||||
ApplyFilter.class,
|
ProcessSignContractRequest.class)
|
||||||
VerifyPeersAccountAgeWitness.class,
|
.using(new TradeTaskRunner(trade,
|
||||||
//TakerVerifyAndSignContract.class,
|
() -> {
|
||||||
TakerPublishFeeTx.class,
|
handleTaskRunnerSuccess(sender, message);
|
||||||
SellerAsTakerSignsDepositTx.class,
|
},
|
||||||
SellerCreatesDelayedPayoutTx.class,
|
errorMessage -> {
|
||||||
SellerSignsDelayedPayoutTx.class,
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
SellerSendDelayedPayoutTxSignatureRequest.class)
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
.withTimeout(60))
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handleSignContractResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), message);
|
||||||
|
processModel.setTradeMessage(message);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT)
|
||||||
|
.with(message)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessSignContractResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, message);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, message, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handleDepositResponse()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||||
|
processModel.setTradeMessage(response);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
||||||
|
.with(response)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessDepositResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, response);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, response, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
|
.executeTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
||||||
|
System.out.println("SellerAsTakerProtocol.handlePaymentAccountPayloadRequest()");
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED) // TODO: only deposit_published should be expected
|
||||||
|
.with(request)
|
||||||
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
|
.setup(tasks(
|
||||||
|
// TODO (woodser): validate request
|
||||||
|
ProcessPaymentAccountPayloadRequest.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
stopTimeout();
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
tradeResultHandler.handleResult(trade); // trade is initialized
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +233,6 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
||||||
super.handle(message, peer);
|
super.handle(message, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// User interaction
|
// User interaction
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -146,7 +243,6 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
||||||
super.onPaymentReceived(resultHandler, errorMessageHandler);
|
super.onPaymentReceived(resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Massage dispatcher
|
// Massage dispatcher
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -167,126 +263,28 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
||||||
protected Class<? extends TradeTask> getVerifyPeersFeePaymentClass() {
|
protected Class<? extends TradeTask> getVerifyPeersFeePaymentClass() {
|
||||||
return TakerVerifyMakerFeePayment.class;
|
return TakerVerifyMakerFeePayment.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (woodser): remove unused calls and classes
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// TakerProtocol
|
// Incoming messages Take offer process
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// TODO (woodser): these methods are duplicated with BuyerAsTakerProtocol due to single inheritance
|
private void handle(InputsForDepositTxResponse message, NodeAddress peer) {
|
||||||
|
expect(phase(Trade.Phase.INIT)
|
||||||
@Override
|
.with(message)
|
||||||
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
.from(peer))
|
||||||
System.out.println("BuyerAsTakerProtocol.handleInitMultisigRequest()");
|
.setup(tasks(
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
TakerProcessesInputsForDepositTxResponse.class,
|
||||||
processModel.setTradeMessage(request);
|
ApplyFilter.class,
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
VerifyPeersAccountAgeWitness.class,
|
||||||
.with(request)
|
//TakerVerifyAndSignContract.class,
|
||||||
.from(sender))
|
TakerPublishFeeTx.class,
|
||||||
.setup(tasks(
|
SellerAsTakerSignsDepositTx.class,
|
||||||
ProcessInitMultisigRequest.class,
|
SellerCreatesDelayedPayoutTx.class,
|
||||||
SendSignContractRequestAfterMultisig.class)
|
SellerSignsDelayedPayoutTx.class,
|
||||||
.using(new TradeTaskRunner(trade,
|
SellerSendDelayedPayoutTxSignatureRequest.class)
|
||||||
() -> {
|
.withTimeout(60))
|
||||||
System.out.println("handle multisig pipeline completed successfully!");
|
.executeTasks();
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
System.out.println("error in handle multisig pipeline!!!: " + errorMessage);
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(30))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handleSignContractRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(30))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handleSignContractResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
|
||||||
processModel.setTradeMessage(message);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT)
|
|
||||||
.with(message)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessSignContractResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, message);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, message, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handleDepositResponse()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
|
||||||
processModel.setTradeMessage(response);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED)
|
|
||||||
.with(response)
|
|
||||||
.from(sender))
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessDepositResponse.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
handleTaskRunnerSuccess(sender, response);
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, response, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
System.out.println("SellerAsTakerProtocol.handlePaymentAccountPayloadRequest()");
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request);
|
|
||||||
expect(anyPhase(Trade.Phase.INIT, Trade.Phase.DEPOSIT_PUBLISHED) // TODO: only deposit_published should be expected
|
|
||||||
.with(request)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessPaymentAccountPayloadRequest.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
stopTimeout();
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
tradeResultHandler.handleResult(trade); // trade is initialized
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
})))
|
|
||||||
.executeTasks();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package bisq.core.trade.protocol;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
|
import bisq.core.trade.handlers.TradeResultHandler;
|
||||||
import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage;
|
import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage;
|
||||||
import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage;
|
import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
|
@ -60,6 +61,8 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
||||||
protected final ProcessModel processModel;
|
protected final ProcessModel processModel;
|
||||||
protected final Trade trade;
|
protected final Trade trade;
|
||||||
private Timer timeoutTimer;
|
private Timer timeoutTimer;
|
||||||
|
protected TradeResultHandler tradeResultHandler;
|
||||||
|
protected ErrorMessageHandler errorMessageHandler;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -205,8 +208,8 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
protected abstract void onTradeMessage(TradeMessage message, NodeAddress peer);
|
protected abstract void onTradeMessage(TradeMessage message, NodeAddress peer);
|
||||||
public abstract void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress peer, ErrorMessageHandler errorMessageHandler);
|
public abstract void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress peer);
|
||||||
public abstract void handleSignContractRequest(SignContractRequest request, NodeAddress peer, ErrorMessageHandler errorMessageHandler);
|
public abstract void handleSignContractRequest(SignContractRequest request, NodeAddress peer);
|
||||||
|
|
||||||
// TODO (woodser): update to use fluent for consistency
|
// TODO (woodser): update to use fluent for consistency
|
||||||
public void handleUpdateMultisigRequest(UpdateMultisigRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
public void handleUpdateMultisigRequest(UpdateMultisigRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
@ -348,12 +351,10 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
||||||
|
|
||||||
protected void startTimeout(long timeoutSec) {
|
protected void startTimeout(long timeoutSec) {
|
||||||
stopTimeout();
|
stopTimeout();
|
||||||
|
|
||||||
timeoutTimer = UserThread.runAfter(() -> {
|
timeoutTimer = UserThread.runAfter(() -> {
|
||||||
log.error("Timeout reached. TradeID={}, state={}, timeoutSec={}",
|
log.error("Timeout reached. TradeID={}, state={}, timeoutSec={}", trade.getId(), trade.stateProperty().get(), timeoutSec);
|
||||||
trade.getId(), trade.stateProperty().get(), timeoutSec);
|
|
||||||
trade.setErrorMessage("Timeout reached. Protocol did not complete in " + timeoutSec + " sec.");
|
trade.setErrorMessage("Timeout reached. Protocol did not complete in " + timeoutSec + " sec.");
|
||||||
|
if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage("Timeout reached. Protocol did not complete in " + timeoutSec + " sec. TradeID=" + trade.getId() + ", state=" + trade.stateProperty().get());
|
||||||
processModel.getTradeManager().requestPersistence();
|
processModel.getTradeManager().requestPersistence();
|
||||||
cleanup();
|
cleanup();
|
||||||
}, timeoutSec);
|
}, timeoutSec);
|
||||||
|
|
|
@ -23,10 +23,8 @@ import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
|
||||||
|
|
||||||
public interface TraderProtocol {
|
public interface TraderProtocol {
|
||||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress peer, ErrorMessageHandler errorMessageHandler);
|
public void handleSignContractResponse(SignContractResponse message, NodeAddress peer);
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress peer, ErrorMessageHandler errorMessageHandler);
|
public void handleDepositResponse(DepositResponse response, NodeAddress peer);
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer, ErrorMessageHandler errorMessageHandler);
|
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,9 @@ import bisq.core.trade.MakerTrade;
|
||||||
import bisq.core.trade.TakerTrade;
|
import bisq.core.trade.TakerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
|
import bisq.core.trade.protocol.TradeListener;
|
||||||
import bisq.core.trade.protocol.TradingPeer;
|
import bisq.core.trade.protocol.TradingPeer;
|
||||||
|
import bisq.network.p2p.AckMessage;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
|
||||||
|
|
||||||
private boolean ack1 = false;
|
private boolean ack1 = false;
|
||||||
private boolean ack2 = false;
|
private boolean ack2 = false;
|
||||||
|
private boolean failed = false;
|
||||||
private static Object lock = new Object();
|
private static Object lock = new Object();
|
||||||
MoneroWallet multisigWallet;
|
MoneroWallet multisigWallet;
|
||||||
|
|
||||||
|
@ -149,37 +151,31 @@ public class ProcessInitMultisigRequest extends TradeTask {
|
||||||
if (peer2Address == null) throw new RuntimeException("Peer2 address is null");
|
if (peer2Address == null) throw new RuntimeException("Peer2 address is null");
|
||||||
if (peer2PubKeyRing == null) throw new RuntimeException("Peer2 pub key ring null");
|
if (peer2PubKeyRing == null) throw new RuntimeException("Peer2 pub key ring null");
|
||||||
|
|
||||||
// send to peer 1
|
// complete on successful ack messages
|
||||||
sendInitMultisigRequest(peer1Address, peer1PubKeyRing, new SendDirectMessageListener() {
|
TradeListener ackListener = new TradeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onArrived() {
|
public void onAckMessage(AckMessage ackMessage, NodeAddress sender) {
|
||||||
log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), peer1Address, request.getTradeId(), request.getUid());
|
if (!ackMessage.getSourceMsgClassName().equals(InitMultisigRequest.class.getSimpleName())) return;
|
||||||
ack1 = true;
|
if (ackMessage.isSuccess()) {
|
||||||
if (ack1 && ack2) completeAux();
|
if (sender.equals(peer1Address)) ack1 = true;
|
||||||
}
|
if (sender.equals(peer2Address)) ack2 = true;
|
||||||
@Override
|
if (ack1 && ack2) {
|
||||||
public void onFault(String errorMessage) {
|
trade.removeListener(this);
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), request.getUid(), peer1Address, errorMessage);
|
completeAux();
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
}
|
||||||
failed();
|
} else {
|
||||||
}
|
if (!failed) {
|
||||||
});
|
failed = true;
|
||||||
|
failed(ackMessage.getErrorMessage()); // TODO: (woodser): only fail once? build into task?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trade.addListener(ackListener);
|
||||||
|
|
||||||
// send to peer 2
|
// send to peers
|
||||||
sendInitMultisigRequest(peer2Address, peer2PubKeyRing, new SendDirectMessageListener() {
|
sendInitMultisigRequest(peer1Address, peer1PubKeyRing);
|
||||||
@Override
|
sendInitMultisigRequest(peer2Address, peer2PubKeyRing);
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), peer2Address, request.getTradeId(), request.getUid());
|
|
||||||
ack2 = true;
|
|
||||||
if (ack1 && ack2) completeAux();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), request.getUid(), peer2Address, errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
completeAux();
|
completeAux();
|
||||||
}
|
}
|
||||||
|
@ -204,21 +200,32 @@ public class ProcessInitMultisigRequest extends TradeTask {
|
||||||
return peers;
|
return peers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendInitMultisigRequest(NodeAddress recipient, PubKeyRing pubKeyRing, SendDirectMessageListener listener) {
|
private void sendInitMultisigRequest(NodeAddress recipient, PubKeyRing pubKeyRing) {
|
||||||
|
|
||||||
// create multisig message with current multisig hex
|
// create request with current multisig hex
|
||||||
InitMultisigRequest request = new InitMultisigRequest(
|
InitMultisigRequest request = new InitMultisigRequest(
|
||||||
processModel.getOffer().getId(),
|
processModel.getOffer().getId(),
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
processModel.getPubKeyRing(),
|
processModel.getPubKeyRing(),
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
processModel.getPreparedMultisigHex(),
|
processModel.getPreparedMultisigHex(),
|
||||||
processModel.getMadeMultisigHex());
|
processModel.getMadeMultisigHex());
|
||||||
|
|
||||||
log.info("Send {} with offerId {} and uid {} to peer {}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid(), recipient);
|
log.info("Send {} with offerId {} and uid {} to peer {}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid(), recipient);
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(recipient, pubKeyRing, request, listener);
|
processModel.getP2PService().sendEncryptedDirectMessage(recipient, pubKeyRing, request, new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived: peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), recipient, request.getTradeId(), request.getUid());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), request.getUid(), recipient, errorMessage);
|
||||||
|
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void completeAux() {
|
private void completeAux() {
|
||||||
|
|
|
@ -29,7 +29,9 @@ import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeUtils;
|
import bisq.core.trade.TradeUtils;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
|
import bisq.core.trade.protocol.TradeListener;
|
||||||
import bisq.core.trade.protocol.TradingPeer;
|
import bisq.core.trade.protocol.TradingPeer;
|
||||||
|
import bisq.network.p2p.AckMessage;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -41,6 +43,7 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
|
|
||||||
private boolean ack1 = false;
|
private boolean ack1 = false;
|
||||||
private boolean ack2 = false;
|
private boolean ack2 = false;
|
||||||
|
private boolean failed = false;
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public ProcessSignContractRequest(TaskRunner taskHandler, Trade trade) {
|
public ProcessSignContractRequest(TaskRunner taskHandler, Trade trade) {
|
||||||
|
@ -61,73 +64,84 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash());
|
trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash());
|
||||||
trader.setPayoutAddressString(request.getPayoutAddress());
|
trader.setPayoutAddressString(request.getPayoutAddress());
|
||||||
|
|
||||||
// return contract signature when ready
|
// sign contract only when both deposit txs hashes known
|
||||||
// TODO (woodser): synchronize contract creation; both requests received at the same time
|
// TODO (woodser): synchronize contract creation; both requests received at the same time
|
||||||
// TODO (woodser): remove makerDepositTxId and takerDepositTxId from Trade
|
// TODO (woodser): remove makerDepositTxId and takerDepositTxId from Trade
|
||||||
if (processModel.getMaker().getDepositTxHash() != null && processModel.getTaker().getDepositTxHash() != null) { // TODO (woodser): synchronize on process model before setting hash so response only sent once
|
if (processModel.getMaker().getDepositTxHash() == null || processModel.getTaker().getDepositTxHash() == null) {
|
||||||
|
complete();
|
||||||
// create and sign contract
|
return;
|
||||||
Contract contract = TradeUtils.createContract(trade);
|
|
||||||
String contractAsJson = Utilities.objectToJson(contract);
|
|
||||||
String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson);
|
|
||||||
|
|
||||||
// save contract and signature
|
|
||||||
trade.setContract(contract);
|
|
||||||
trade.setContractAsJson(contractAsJson);
|
|
||||||
trade.getSelf().setContractSignature(signature);
|
|
||||||
|
|
||||||
// create response with contract signature
|
|
||||||
SignContractResponse response = new SignContractResponse(
|
|
||||||
trade.getOffer().getId(),
|
|
||||||
processModel.getMyNodeAddress(),
|
|
||||||
processModel.getPubKeyRing(),
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
Version.getP2PMessageVersion(),
|
|
||||||
new Date().getTime(),
|
|
||||||
signature);
|
|
||||||
|
|
||||||
// get response recipients. only arbitrator sends response to both peers
|
|
||||||
NodeAddress recipient1 = trade instanceof ArbitratorTrade ? trade.getMakerNodeAddress() : trade.getTradingPeerNodeAddress();
|
|
||||||
PubKeyRing recipient1PubKey = trade instanceof ArbitratorTrade ? trade.getMakerPubKeyRing() : trade.getTradingPeerPubKeyRing();
|
|
||||||
NodeAddress recipient2 = trade instanceof ArbitratorTrade ? trade.getTakerNodeAddress() : null;
|
|
||||||
PubKeyRing recipient2PubKey = trade instanceof ArbitratorTrade ? trade.getTakerPubKeyRing() : null;
|
|
||||||
|
|
||||||
// send response to recipient 1
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(recipient1, recipient1PubKey, response, new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", response.getClass().getSimpleName(), recipient1, trade.getId());
|
|
||||||
ack1 = true;
|
|
||||||
if (ack1 && (recipient2 == null || ack2)) complete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", response.getClass().getSimpleName(), recipient1, trade.getId(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + response + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// send response to recipient 2 if applicable
|
|
||||||
if (recipient2 != null) {
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(recipient2, recipient2PubKey, response, new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", response.getClass().getSimpleName(), recipient2, trade.getId());
|
|
||||||
ack2 = true;
|
|
||||||
if (ack1 && ack2) complete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", response.getClass().getSimpleName(), recipient2, trade.getId(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + response + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create and sign contract
|
||||||
|
Contract contract = TradeUtils.createContract(trade);
|
||||||
|
String contractAsJson = Utilities.objectToJson(contract);
|
||||||
|
String signature = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), contractAsJson);
|
||||||
|
|
||||||
|
// save contract and signature
|
||||||
|
trade.setContract(contract);
|
||||||
|
trade.setContractAsJson(contractAsJson);
|
||||||
|
trade.getSelf().setContractSignature(signature);
|
||||||
|
|
||||||
|
// get response recipients. only arbitrator sends response to both peers
|
||||||
|
NodeAddress recipient1 = trade instanceof ArbitratorTrade ? trade.getMakerNodeAddress() : trade.getTradingPeerNodeAddress();
|
||||||
|
PubKeyRing recipient1PubKey = trade instanceof ArbitratorTrade ? trade.getMakerPubKeyRing() : trade.getTradingPeerPubKeyRing();
|
||||||
|
NodeAddress recipient2 = trade instanceof ArbitratorTrade ? trade.getTakerNodeAddress() : null;
|
||||||
|
PubKeyRing recipient2PubKey = trade instanceof ArbitratorTrade ? trade.getTakerPubKeyRing() : null;
|
||||||
|
|
||||||
|
// complete on successful ack messages
|
||||||
|
TradeListener ackListener = new TradeListener() {
|
||||||
|
@Override
|
||||||
|
public void onAckMessage(AckMessage ackMessage, NodeAddress sender) {
|
||||||
|
if (!ackMessage.getSourceMsgClassName().equals(SignContractResponse.class.getSimpleName())) return;
|
||||||
|
if (ackMessage.isSuccess()) {
|
||||||
|
if (sender.equals(trade.getTradingPeerNodeAddress())) ack1 = true;
|
||||||
|
if (sender.equals(trade.getArbitratorNodeAddress())) ack2 = true;
|
||||||
|
if (trade instanceof ArbitratorTrade ? ack1 && ack2 : ack1) { // only arbitrator sends response to both peers
|
||||||
|
trade.removeListener(this);
|
||||||
|
complete();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!failed) {
|
||||||
|
failed = true;
|
||||||
|
failed(ackMessage.getErrorMessage()); // TODO: (woodser): only fail once? build into task?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trade.addListener(ackListener);
|
||||||
|
|
||||||
|
// send contract signature response(s)
|
||||||
|
if (recipient1 != null) sendSignContractResponse(recipient1, recipient1PubKey, signature);
|
||||||
|
if (recipient2 != null) sendSignContractResponse(recipient2, recipient2PubKey, signature);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendSignContractResponse(NodeAddress nodeAddress, PubKeyRing pubKeyRing, String contractSignature) {
|
||||||
|
|
||||||
|
// create response with contract signature
|
||||||
|
SignContractResponse response = new SignContractResponse(
|
||||||
|
trade.getOffer().getId(),
|
||||||
|
processModel.getMyNodeAddress(),
|
||||||
|
processModel.getPubKeyRing(),
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
Version.getP2PMessageVersion(),
|
||||||
|
new Date().getTime(),
|
||||||
|
contractSignature);
|
||||||
|
|
||||||
|
// send request
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(nodeAddress, pubKeyRing, response, new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived: trading peer={}; offerId={}; uid={}", response.getClass().getSimpleName(), nodeAddress, trade.getId());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("Sending {} failed: uid={}; peer={}; error={}", response.getClass().getSimpleName(), nodeAddress, trade.getId(), errorMessage);
|
||||||
|
appendToErrorMessage("Sending message failed: message=" + response + "\nerrorMessage=" + errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package bisq.core.trade.protocol.tasks;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
import bisq.common.crypto.PubKeyRing;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.btc.model.XmrAddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
|
@ -26,7 +27,10 @@ import bisq.core.trade.SellerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeUtils;
|
import bisq.core.trade.TradeUtils;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
|
import bisq.core.trade.protocol.TradeListener;
|
||||||
import bisq.core.util.ParsingUtils;
|
import bisq.core.util.ParsingUtils;
|
||||||
|
import bisq.network.p2p.AckMessage;
|
||||||
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
@ -42,6 +46,7 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
||||||
|
|
||||||
private boolean ack1 = false;
|
private boolean ack1 = false;
|
||||||
private boolean ack2 = false;
|
private boolean ack2 = false;
|
||||||
|
private boolean failed = false;
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public SendSignContractRequestAfterMultisig(TaskRunner taskHandler, Trade trade) {
|
public SendSignContractRequestAfterMultisig(TaskRunner taskHandler, Trade trade) {
|
||||||
|
@ -54,7 +59,7 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// skip if multisig wallet not complete
|
// skip if multisig wallet not complete
|
||||||
if (!processModel.isMultisigSetupComplete()) return;
|
if (!processModel.isMultisigSetupComplete()) return; // TODO: woodser: this does not ack original request?
|
||||||
|
|
||||||
// skip if deposit tx already created
|
// skip if deposit tx already created
|
||||||
if (processModel.getDepositTxXmr() != null) return;
|
if (processModel.getDepositTxXmr() != null) return;
|
||||||
|
@ -74,7 +79,7 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
||||||
MoneroTxWallet depositTx = TradeUtils.createDepositTx(trade.getXmrWalletService(), tradeFee, multisigAddress, depositAmount);
|
MoneroTxWallet depositTx = TradeUtils.createDepositTx(trade.getXmrWalletService(), tradeFee, multisigAddress, depositAmount);
|
||||||
|
|
||||||
// freeze deposit outputs
|
// freeze deposit outputs
|
||||||
// TODO (woodser): save frozen key images and unfreeze if trade fails before sent to multisig
|
// TODO (woodser): save frozen key images and unfreeze if trade fails before deposited to multisig
|
||||||
for (MoneroOutput input : depositTx.getInputs()) {
|
for (MoneroOutput input : depositTx.getInputs()) {
|
||||||
wallet.freezeOutput(input.getKeyImage().getHex());
|
wallet.freezeOutput(input.getKeyImage().getHex());
|
||||||
}
|
}
|
||||||
|
@ -83,52 +88,68 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
||||||
processModel.setDepositTxXmr(depositTx);
|
processModel.setDepositTxXmr(depositTx);
|
||||||
trade.getSelf().setDepositTxHash(depositTx.getHash());
|
trade.getSelf().setDepositTxHash(depositTx.getHash());
|
||||||
|
|
||||||
// create request for peer and arbitrator to sign contract
|
// complete on successful ack messages
|
||||||
SignContractRequest request = new SignContractRequest(
|
TradeListener ackListener = new TradeListener() {
|
||||||
trade.getOffer().getId(),
|
@Override
|
||||||
processModel.getMyNodeAddress(),
|
public void onAckMessage(AckMessage ackMessage, NodeAddress sender) {
|
||||||
processModel.getPubKeyRing(),
|
if (!ackMessage.getSourceMsgClassName().equals(SignContractRequest.class.getSimpleName())) return;
|
||||||
UUID.randomUUID().toString(),
|
if (ackMessage.isSuccess()) {
|
||||||
Version.getP2PMessageVersion(),
|
if (sender.equals(trade.getTradingPeerNodeAddress())) ack1 = true;
|
||||||
new Date().getTime(),
|
if (sender.equals(trade.getArbitratorNodeAddress())) ack2 = true;
|
||||||
trade.getProcessModel().getAccountId(),
|
if (ack1 && ack2) {
|
||||||
trade.getProcessModel().getPaymentAccountPayload(trade).getHash(),
|
trade.removeListener(this);
|
||||||
trade.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString(),
|
completeAux();
|
||||||
depositTx.getHash());
|
}
|
||||||
|
} else {
|
||||||
|
if (!failed) {
|
||||||
|
failed = true;
|
||||||
|
failed(ackMessage.getErrorMessage()); // TODO: (woodser): only fail once? build into task?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
trade.addListener(ackListener);
|
||||||
|
|
||||||
// send request to trading peer
|
// send sign contract requests to peer and arbitrator
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradingPeerNodeAddress(), trade.getTradingPeerPubKeyRing(), request, new SendDirectMessageListener() {
|
sendSignContractRequest(trade.getTradingPeerNodeAddress(), trade.getTradingPeerPubKeyRing(), offer, depositTx);
|
||||||
@Override
|
sendSignContractRequest(trade.getArbitratorNodeAddress(), trade.getArbitratorPubKeyRing(), offer, depositTx);
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getTradingPeerNodeAddress(), trade.getId());
|
|
||||||
ack1 = true;
|
|
||||||
if (ack1 && ack2) complete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getTradingPeerNodeAddress(), trade.getId(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// send request to arbitrator
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getArbitratorNodeAddress(), trade.getArbitratorPubKeyRing(), request, new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getArbitratorNodeAddress(), trade.getId());
|
|
||||||
ack2 = true;
|
|
||||||
if (ack1 && ack2) complete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getArbitratorNodeAddress(), trade.getId(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendSignContractRequest(NodeAddress nodeAddress, PubKeyRing pubKeyRing, Offer offer, MoneroTxWallet depositTx) {
|
||||||
|
|
||||||
|
// create request to sign contract
|
||||||
|
SignContractRequest request = new SignContractRequest(
|
||||||
|
trade.getOffer().getId(),
|
||||||
|
processModel.getMyNodeAddress(),
|
||||||
|
processModel.getPubKeyRing(),
|
||||||
|
UUID.randomUUID().toString(), // TODO: ensure not reusing request id across protocol
|
||||||
|
Version.getP2PMessageVersion(),
|
||||||
|
new Date().getTime(),
|
||||||
|
trade.getProcessModel().getAccountId(),
|
||||||
|
trade.getProcessModel().getPaymentAccountPayload(trade).getHash(),
|
||||||
|
trade.getXmrWalletService().getAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString(),
|
||||||
|
depositTx.getHash());
|
||||||
|
|
||||||
|
// send request
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(nodeAddress, pubKeyRing, request, new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), nodeAddress, trade.getId());
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), nodeAddress, trade.getId(), errorMessage);
|
||||||
|
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void completeAux() {
|
||||||
|
processModel.getXmrWalletService().getWallet().save();
|
||||||
|
complete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import bisq.proto.grpc.ConfirmPaymentStartedReply;
|
||||||
import bisq.proto.grpc.ConfirmPaymentStartedRequest;
|
import bisq.proto.grpc.ConfirmPaymentStartedRequest;
|
||||||
import bisq.proto.grpc.GetTradeReply;
|
import bisq.proto.grpc.GetTradeReply;
|
||||||
import bisq.proto.grpc.GetTradeRequest;
|
import bisq.proto.grpc.GetTradeRequest;
|
||||||
|
import bisq.proto.grpc.GetTradesReply;
|
||||||
|
import bisq.proto.grpc.GetTradesRequest;
|
||||||
import bisq.proto.grpc.KeepFundsReply;
|
import bisq.proto.grpc.KeepFundsReply;
|
||||||
import bisq.proto.grpc.KeepFundsRequest;
|
import bisq.proto.grpc.KeepFundsRequest;
|
||||||
import bisq.proto.grpc.TakeOfferReply;
|
import bisq.proto.grpc.TakeOfferReply;
|
||||||
|
@ -40,8 +42,9 @@ import io.grpc.stub.StreamObserver;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import static bisq.core.api.model.TradeInfo.toTradeInfo;
|
import static bisq.core.api.model.TradeInfo.toTradeInfo;
|
||||||
|
@ -87,6 +90,25 @@ class GrpcTradesService extends TradesImplBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getTrades(GetTradesRequest req,
|
||||||
|
StreamObserver<GetTradesReply> responseObserver) {
|
||||||
|
try {
|
||||||
|
List<TradeInfo> trades = coreApi.getTrades()
|
||||||
|
.stream().map(TradeInfo::toTradeInfo)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
var reply = GetTradesReply.newBuilder()
|
||||||
|
.addAllTrades(trades.stream()
|
||||||
|
.map(TradeInfo::toProtoMessage)
|
||||||
|
.collect(Collectors.toList()))
|
||||||
|
.build();
|
||||||
|
responseObserver.onNext(reply);
|
||||||
|
responseObserver.onCompleted();
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
exceptionHandler.handleException(log, cause, responseObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void takeOffer(TakeOfferRequest req,
|
public void takeOffer(TakeOfferRequest req,
|
||||||
StreamObserver<TakeOfferReply> responseObserver) {
|
StreamObserver<TakeOfferReply> responseObserver) {
|
||||||
|
@ -173,8 +195,9 @@ class GrpcTradesService extends TradesImplBase {
|
||||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||||
new HashMap<>() {{
|
new HashMap<>() {{
|
||||||
put(getGetTradeMethod().getFullMethodName(), new GrpcCallRateMeter(3, SECONDS));
|
put(getGetTradeMethod().getFullMethodName(), new GrpcCallRateMeter(10, SECONDS));
|
||||||
put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getGetTradesMethod().getFullMethodName(), new GrpcCallRateMeter(10, SECONDS));
|
||||||
|
put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(10, SECONDS));
|
||||||
put(getConfirmPaymentStartedMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getConfirmPaymentStartedMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||||
put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||||
put(getKeepFundsMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
|
put(getKeepFundsMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
|
||||||
|
|
|
@ -300,6 +300,8 @@ message StopReply {
|
||||||
service Trades {
|
service Trades {
|
||||||
rpc GetTrade (GetTradeRequest) returns (GetTradeReply) {
|
rpc GetTrade (GetTradeRequest) returns (GetTradeReply) {
|
||||||
}
|
}
|
||||||
|
rpc GetTrades (GetTradesRequest) returns (GetTradesReply) {
|
||||||
|
}
|
||||||
rpc TakeOffer (TakeOfferRequest) returns (TakeOfferReply) {
|
rpc TakeOffer (TakeOfferRequest) returns (TakeOfferReply) {
|
||||||
}
|
}
|
||||||
rpc ConfirmPaymentStarted (ConfirmPaymentStartedRequest) returns (ConfirmPaymentStartedReply) {
|
rpc ConfirmPaymentStarted (ConfirmPaymentStartedRequest) returns (ConfirmPaymentStartedReply) {
|
||||||
|
@ -344,6 +346,13 @@ message GetTradeReply {
|
||||||
TradeInfo trade = 1;
|
TradeInfo trade = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GetTradesRequest {
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTradesReply {
|
||||||
|
repeated TradeInfo trades = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message KeepFundsRequest {
|
message KeepFundsRequest {
|
||||||
string trade_id = 1;
|
string trade_id = 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue