add multisig wallet state and wait for multisig to complete

refactor trade protocol
This commit is contained in:
woodser 2022-07-30 12:36:52 -04:00
parent f61fd09127
commit 50126874a0
17 changed files with 305 additions and 748 deletions

View File

@ -107,7 +107,10 @@ public abstract class Trade implements Tradable, Model {
// #################### Phase INIT // #################### Phase INIT
// When trade protocol starts no funds are on stake // When trade protocol starts no funds are on stake
PREPARATION(Phase.INIT), PREPARATION(Phase.INIT),
CONTRACT_SIGNATURE_REQUESTED(Phase.INIT), // TODO (woodser): add more states for initializing multisig, etc to support trade initialization notifications MULTISIG_PREPARED(Phase.INIT),
MULTISIG_MADE(Phase.INIT),
MULTISIG_COMPLETED(Phase.INIT),
CONTRACT_SIGNATURE_REQUESTED(Phase.INIT),
CONTRACT_SIGNED(Phase.INIT), CONTRACT_SIGNED(Phase.INIT),
// At first part maker/taker have different roles // At first part maker/taker have different roles
@ -894,7 +897,6 @@ public abstract class Trade implements Tradable, Model {
// create block listener // create block listener
depositTxListener = new MoneroWalletListener() { depositTxListener = new MoneroWalletListener() {
Long unlockHeight = null; Long unlockHeight = null;
@Override @Override
@ -903,6 +905,9 @@ public abstract class Trade implements Tradable, Model {
// ignore if no longer listening // ignore if no longer listening
if (depositTxListener == null) return; if (depositTxListener == null) return;
// use latest height
height = havenoWallet.getHeight();
// ignore if before unlock height // ignore if before unlock height
if (unlockHeight != null && height < unlockHeight) return; if (unlockHeight != null && height < unlockHeight) return;

View File

@ -3,16 +3,15 @@ package bisq.core.trade.protocol;
import bisq.core.trade.ArbitratorTrade; import bisq.core.trade.ArbitratorTrade;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;
import bisq.core.trade.messages.DepositRequest; import bisq.core.trade.messages.DepositRequest;
import bisq.core.trade.messages.InitMultisigRequest; import bisq.core.trade.messages.DepositResponse;
import bisq.core.trade.messages.InitTradeRequest; import bisq.core.trade.messages.InitTradeRequest;
import bisq.core.trade.messages.SignContractRequest; import bisq.core.trade.messages.PaymentAccountPayloadRequest;
import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeAndMultisigRequests; import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeAndMultisigRequests;
import bisq.core.trade.protocol.tasks.ArbitratorProcessesDepositRequest; import bisq.core.trade.protocol.tasks.ArbitratorProcessesDepositRequest;
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
import bisq.core.trade.protocol.tasks.ArbitratorProcessesReserveTx; import bisq.core.trade.protocol.tasks.ArbitratorProcessesReserveTx;
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest; import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
import bisq.core.util.Validator; import bisq.core.util.Validator;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
@ -59,60 +58,12 @@ public class ArbitratorProtocol extends DisputeProtocol {
} }
@Override @Override
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) { public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
System.out.println("ArbitratorProtocol.handleInitMultisigRequest()"); log.warn("Arbitrator ignoring SignContractResponse");
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(anyPhase(Trade.Phase.INIT)
.with(request)
.from(sender))
.setup(tasks(
ProcessInitMultisigRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
}
@Override
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
System.out.println("ArbitratorProtocol.handleSignContractRequest()");
synchronized (trade) {
latchTrade();
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
ProcessSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
public void handleDepositRequest(DepositRequest request, NodeAddress sender) { public void handleDepositRequest(DepositRequest request, NodeAddress sender) {
System.out.println("ArbitratorProtocol.handleDepositRequest()"); System.out.println("ArbitratorProtocol.handleDepositRequest() " + trade.getId());
synchronized (trade) { synchronized (trade) {
latchTrade(); latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request); Validator.checkTradeId(processModel.getOfferId(), request);
@ -139,6 +90,16 @@ public class ArbitratorProtocol extends DisputeProtocol {
} }
} }
@Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
log.warn("Arbitrator ignoring DepositResponse");
}
@Override
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
log.warn("Arbitrator ignoring PaymentAccountPayloadRequest");
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Message dispatcher // Message dispatcher
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -28,30 +28,22 @@ import bisq.core.trade.messages.PaymentAccountPayloadRequest;
import bisq.core.trade.messages.PaymentReceivedMessage; import bisq.core.trade.messages.PaymentReceivedMessage;
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.tasks.ProcessDepositResponse;
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest; import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
import bisq.core.trade.protocol.tasks.ProcessPaymentAccountPayloadRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
import bisq.core.trade.protocol.tasks.SendSignContractRequestAfterMultisig;
import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.protocol.tasks.TradeTask;
import bisq.core.trade.protocol.tasks.buyer.BuyerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.buyer.BuyerFinalizesDelayedPayoutTx;
import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest; import bisq.core.trade.protocol.tasks.buyer.BuyerProcessDelayedPayoutTxSignatureRequest;
import bisq.core.trade.protocol.tasks.buyer.BuyerSendsDelayedPayoutTxSignatureResponse; import bisq.core.trade.protocol.tasks.buyer.BuyerSendsDelayedPayoutTxSignatureResponse;
import bisq.core.trade.protocol.tasks.buyer.BuyerSignsDelayedPayoutTx; import bisq.core.trade.protocol.tasks.buyer.BuyerSignsDelayedPayoutTx;
import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesPreparedDelayedPayoutTx; import bisq.core.trade.protocol.tasks.buyer.BuyerVerifiesPreparedDelayedPayoutTx;
import bisq.core.trade.protocol.tasks.maker.MakerRemovesOpenOffer; import bisq.core.trade.protocol.tasks.maker.MaybeRemoveOpenOffer;
import bisq.core.trade.protocol.tasks.maker.MakerSendsInitTradeRequestIfUnreserved; import bisq.core.trade.protocol.tasks.maker.MakerSendsInitTradeRequestIfUnreserved;
import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment; import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment;
import bisq.core.util.Validator;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.fxmisc.easybind.EasyBind;
@Slf4j @Slf4j
public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol { public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol {
@ -68,8 +60,6 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
// MakerProtocol // MakerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// TODO (woodser): these methods are duplicated with SellerAsMakerProtocol due to single inheritance
@Override @Override
public void handleInitTradeRequest(InitTradeRequest message, public void handleInitTradeRequest(InitTradeRequest message,
NodeAddress peer, NodeAddress peer,
@ -102,162 +92,27 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
@Override @Override
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) { public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleInitMultisigRequest()"); super.handleInitMultisigRequest(request, sender);
synchronized (trade) {
latchTrade();
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,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) { public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId()); super.handleSignContractRequest(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNATURE_REQUESTED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNATURE_REQUESTED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract request after contract signature requested
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNATURE_REQUESTED) new Thread(() -> handleSignContractRequest(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) { public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId()); super.handleSignContractResponse(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract response after contract signed
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNED) new Thread(() -> handleSignContractResponse(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) { public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleDepositResponse()"); super.handleDepositResponse(response, sender);
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), response);
processModel.setTradeMessage(response);
expect(state(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST)
.with(response)
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
.setup(tasks(
// TODO (woodser): validate request
ProcessDepositResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, response);
},
errorMessage -> {
handleTaskRunnerFault(sender, response, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) { public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountPayloadRequest() " + trade.getId()); super.handlePaymentAccountPayloadRequest(request, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), request);
if (trade.getState() == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(state(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG)
.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();
this.errorMessageHandler = null;
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) new Thread(() -> handlePaymentAccountPayloadRequest(request, sender)).start(); // process notification without trade lock
});
}
}
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -297,7 +152,7 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
.with(message) .with(message)
.from(peer)) .from(peer))
.setup(tasks( .setup(tasks(
MakerRemovesOpenOffer.class, MaybeRemoveOpenOffer.class,
BuyerProcessDelayedPayoutTxSignatureRequest.class, BuyerProcessDelayedPayoutTxSignatureRequest.class,
BuyerVerifiesPreparedDelayedPayoutTx.class, BuyerVerifiesPreparedDelayedPayoutTx.class,
BuyerSignsDelayedPayoutTx.class, BuyerSignsDelayedPayoutTx.class,

View File

@ -33,12 +33,6 @@ import bisq.core.trade.messages.SignContractRequest;
import bisq.core.trade.messages.SignContractResponse; import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
import bisq.core.trade.protocol.tasks.ProcessPaymentAccountPayloadRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
import bisq.core.trade.protocol.tasks.SendSignContractRequestAfterMultisig;
import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.protocol.tasks.TradeTask;
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
import bisq.core.trade.protocol.tasks.buyer.BuyerFinalizesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.buyer.BuyerFinalizesDelayedPayoutTx;
@ -54,13 +48,11 @@ import bisq.core.trade.protocol.tasks.taker.TakerPublishFeeTx;
import bisq.core.trade.protocol.tasks.taker.TakerReservesTradeFunds; import bisq.core.trade.protocol.tasks.taker.TakerReservesTradeFunds;
import bisq.core.trade.protocol.tasks.taker.TakerSendsInitTradeRequestToArbitrator; import bisq.core.trade.protocol.tasks.taker.TakerSendsInitTradeRequestToArbitrator;
import bisq.core.trade.protocol.tasks.taker.TakerVerifyMakerFeePayment; import bisq.core.trade.protocol.tasks.taker.TakerVerifyMakerFeePayment;
import bisq.core.util.Validator;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.fxmisc.easybind.EasyBind;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -86,8 +78,6 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
// Take offer // Take offer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// TODO (woodser): these methods are duplicated with SellerAsTakerProtocol due to single inheritance
@Override @Override
public void onTakeOffer(TradeResultHandler tradeResultHandler, public void onTakeOffer(TradeResultHandler tradeResultHandler,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
@ -119,161 +109,27 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
@Override @Override
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) { public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleInitMultisigRequest()"); super.handleInitMultisigRequest(request, sender);
synchronized (trade) {
latchTrade();
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,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) { public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId()); super.handleSignContractRequest(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNATURE_REQUESTED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNATURE_REQUESTED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract request after contract signature requested
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNATURE_REQUESTED) new Thread(() -> handleSignContractRequest(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) { public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse()"); super.handleSignContractResponse(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNED) new Thread(() -> handleSignContractResponse(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) { public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleDepositResponse()"); super.handleDepositResponse(response, sender);
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), response);
processModel.setTradeMessage(response);
expect(state(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST)
.with(response)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessDepositResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, response);
},
errorMessage -> {
handleTaskRunnerFault(sender, response, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) { public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountPayloadRequest()"); super.handlePaymentAccountPayloadRequest(request, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), request);
if (trade.getState() == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(state(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG)
.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();
this.errorMessageHandler = null;
tradeResultHandler.handleResult(trade); // trade is initialized
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) new Thread(() -> handlePaymentAccountPayloadRequest(request, sender)).start(); // process notification without trade lock
});
}
}
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -142,8 +142,8 @@ public abstract class BuyerProtocol extends DisputeProtocol {
.using(new TradeTaskRunner(trade, .using(new TradeTaskRunner(trade,
() -> { () -> {
this.errorMessageHandler = null; this.errorMessageHandler = null;
resultHandler.handleResult();
handleTaskRunnerSuccess(event); handleTaskRunnerSuccess(event);
resultHandler.handleResult();
}, },
(errorMessage) -> { (errorMessage) -> {
handleTaskRunnerFault(event, errorMessage); handleTaskRunnerFault(event, errorMessage);

View File

@ -183,10 +183,6 @@ public class ProcessModel implements Model, PersistablePayload {
@Setter @Setter
private String multisigAddress; private String multisigAddress;
@Nullable @Nullable
@Getter
@Setter
private boolean multisigSetupComplete; // TODO (woodser): redundant with multisigAddress existing, remove
// We want to indicate the user the state of the message delivery of the // We want to indicate the user the state of the message delivery of the
// PaymentSentMessage. As well we do an automatic re-send in case it was not ACKed yet. // PaymentSentMessage. As well we do an automatic re-send in case it was not ACKed yet.
@ -247,7 +243,6 @@ public class ProcessModel implements Model, PersistablePayload {
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex)); Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));
Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex)); Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex));
Optional.ofNullable(multisigAddress).ifPresent(e -> builder.setMultisigAddress(multisigAddress)); Optional.ofNullable(multisigAddress).ifPresent(e -> builder.setMultisigAddress(multisigAddress));
Optional.ofNullable(multisigSetupComplete).ifPresent(e -> builder.setMultisigSetupComplete(multisigSetupComplete));
return builder.build(); return builder.build();
} }
@ -279,7 +274,6 @@ public class ProcessModel implements Model, PersistablePayload {
processModel.setPreparedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getPreparedMultisigHex())); processModel.setPreparedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getPreparedMultisigHex()));
processModel.setMadeMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getMadeMultisigHex())); processModel.setMadeMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getMadeMultisigHex()));
processModel.setMultisigAddress(ProtoUtil.stringOrNullFromProto(proto.getMultisigAddress())); processModel.setMultisigAddress(ProtoUtil.stringOrNullFromProto(proto.getMultisigAddress()));
processModel.setMultisigSetupComplete(proto.getMultisigSetupComplete());
String paymentStartedMessageStateString = ProtoUtil.stringOrNullFromProto(proto.getPaymentStartedMessageState()); String paymentStartedMessageStateString = ProtoUtil.stringOrNullFromProto(proto.getPaymentStartedMessageState());
MessageState paymentStartedMessageState = ProtoUtil.enumFromProto(MessageState.class, paymentStartedMessageStateString); MessageState paymentStartedMessageState = ProtoUtil.enumFromProto(MessageState.class, paymentStartedMessageStateString);

View File

@ -21,23 +21,17 @@ package bisq.core.trade.protocol;
import bisq.core.trade.SellerAsMakerTrade; import bisq.core.trade.SellerAsMakerTrade;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;
import bisq.core.trade.messages.PaymentSentMessage; import bisq.core.trade.messages.PaymentSentMessage;
import bisq.core.trade.messages.SignContractRequest;
import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.messages.DepositResponse; import bisq.core.trade.messages.DepositResponse;
import bisq.core.trade.messages.DepositTxMessage; import bisq.core.trade.messages.DepositTxMessage;
import bisq.core.trade.messages.InitMultisigRequest; import bisq.core.trade.messages.InitMultisigRequest;
import bisq.core.trade.messages.InitTradeRequest; import bisq.core.trade.messages.InitTradeRequest;
import bisq.core.trade.messages.PaymentAccountPayloadRequest; import bisq.core.trade.messages.PaymentAccountPayloadRequest;
import bisq.core.trade.messages.SignContractRequest;
import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest; import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
import bisq.core.trade.protocol.tasks.ProcessPaymentAccountPayloadRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
import bisq.core.trade.protocol.tasks.SendSignContractRequestAfterMultisig;
import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.protocol.tasks.TradeTask;
import bisq.core.trade.protocol.tasks.maker.MakerRemovesOpenOffer; import bisq.core.trade.protocol.tasks.maker.MaybeRemoveOpenOffer;
import bisq.core.trade.protocol.tasks.maker.MakerSendsInitTradeRequestIfUnreserved; import bisq.core.trade.protocol.tasks.maker.MakerSendsInitTradeRequestIfUnreserved;
import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment; import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment;
import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx;
@ -45,13 +39,11 @@ import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureR
import bisq.core.trade.protocol.tasks.seller.SellerSignsDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerSignsDelayedPayoutTx;
import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerFinalizesDepositTx; import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerFinalizesDepositTx;
import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerProcessDepositTxMessage; import bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerProcessDepositTxMessage;
import bisq.core.util.Validator;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.fxmisc.easybind.EasyBind;
@Slf4j @Slf4j
public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtocol { public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtocol {
@ -68,8 +60,6 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
// MakerProtocol // MakerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// TODO (woodser): these methods are duplicated with BuyerAsMakerProtocol due to single inheritance
@Override @Override
public void handleInitTradeRequest(InitTradeRequest message, public void handleInitTradeRequest(InitTradeRequest message,
NodeAddress peer, NodeAddress peer,
@ -102,161 +92,27 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
@Override @Override
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) { public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleInitMultisigRequest()"); super.handleInitMultisigRequest(request, sender);
synchronized (trade) {
latchTrade();
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,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) { public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId()); super.handleSignContractRequest(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNATURE_REQUESTED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNATURE_REQUESTED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract request after contract signature requested
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNATURE_REQUESTED) new Thread(() -> handleSignContractRequest(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) { public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse()"); super.handleSignContractResponse(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNED) new Thread(() -> handleSignContractResponse(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) { public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleDepositResponse()"); super.handleDepositResponse(response, sender);
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), response);
processModel.setTradeMessage(response);
expect(state(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST)
.with(response)
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
.setup(tasks(
// TODO (woodser): validate request
ProcessDepositResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, response);
},
errorMessage -> {
handleTaskRunnerFault(sender, response, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) { public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountPayloadRequest()"); super.handlePaymentAccountPayloadRequest(request, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), request);
if (trade.getState() == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(state(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG)
.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();
this.errorMessageHandler = null;
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) new Thread(() -> handlePaymentAccountPayloadRequest(request, sender)).start(); // process notification without trade lock
});
}
}
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
@ -301,7 +157,7 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
.with(message) .with(message)
.from(peer)) .from(peer))
.setup(tasks( .setup(tasks(
MakerRemovesOpenOffer.class, MaybeRemoveOpenOffer.class,
SellerAsMakerProcessDepositTxMessage.class, SellerAsMakerProcessDepositTxMessage.class,
SellerAsMakerFinalizesDepositTx.class, SellerAsMakerFinalizesDepositTx.class,
SellerCreatesDelayedPayoutTx.class, SellerCreatesDelayedPayoutTx.class,

View File

@ -23,20 +23,14 @@ import bisq.core.trade.SellerAsTakerTrade;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;
import bisq.core.trade.handlers.TradeResultHandler; import bisq.core.trade.handlers.TradeResultHandler;
import bisq.core.trade.messages.PaymentSentMessage; import bisq.core.trade.messages.PaymentSentMessage;
import bisq.core.trade.messages.SignContractRequest;
import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.messages.DepositResponse; import bisq.core.trade.messages.DepositResponse;
import bisq.core.trade.messages.InitMultisigRequest; import bisq.core.trade.messages.InitMultisigRequest;
import bisq.core.trade.messages.InputsForDepositTxResponse; import bisq.core.trade.messages.InputsForDepositTxResponse;
import bisq.core.trade.messages.PaymentAccountPayloadRequest; import bisq.core.trade.messages.PaymentAccountPayloadRequest;
import bisq.core.trade.messages.SignContractRequest;
import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
import bisq.core.trade.protocol.tasks.ProcessPaymentAccountPayloadRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
import bisq.core.trade.protocol.tasks.SendSignContractRequestAfterMultisig;
import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.protocol.tasks.TradeTask;
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx;
@ -48,14 +42,12 @@ import bisq.core.trade.protocol.tasks.taker.TakerPublishFeeTx;
import bisq.core.trade.protocol.tasks.taker.TakerReservesTradeFunds; import bisq.core.trade.protocol.tasks.taker.TakerReservesTradeFunds;
import bisq.core.trade.protocol.tasks.taker.TakerSendsInitTradeRequestToArbitrator; import bisq.core.trade.protocol.tasks.taker.TakerSendsInitTradeRequestToArbitrator;
import bisq.core.trade.protocol.tasks.taker.TakerVerifyMakerFeePayment; import bisq.core.trade.protocol.tasks.taker.TakerVerifyMakerFeePayment;
import bisq.core.util.Validator;
import bisq.network.p2p.NodeAddress; import bisq.network.p2p.NodeAddress;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ResultHandler; import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.fxmisc.easybind.EasyBind;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@ -79,8 +71,6 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
// Take offer // Take offer
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// TODO (woodser): these methods are duplicated with BuyerAsTakerProtocol due to single inheritance
@Override @Override
public void onTakeOffer(TradeResultHandler tradeResultHandler, public void onTakeOffer(TradeResultHandler tradeResultHandler,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
@ -112,163 +102,28 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
@Override @Override
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) { public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleInitMultisigRequest()"); super.handleInitMultisigRequest(request, sender);
synchronized (trade) {
latchTrade();
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,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) { public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId()); super.handleSignContractRequest(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNATURE_REQUESTED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNATURE_REQUESTED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract request after contract signature requested
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNATURE_REQUESTED) new Thread(() -> handleSignContractRequest(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) { public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId()); super.handleSignContractResponse(message, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNED) new Thread(() -> handleSignContractResponse(message, sender)).start(); // process notification without trade lock
});
}
}
} }
@Override @Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) { public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleDepositResponse()"); super.handleDepositResponse(response, sender);
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), response);
processModel.setTradeMessage(response);
expect(state(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST)
.with(response)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessDepositResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, response);
},
errorMessage -> {
handleTaskRunnerFault(sender, response, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
} }
@Override @Override
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) { public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountPayloadRequest() " + trade.getId()); super.handlePaymentAccountPayloadRequest(request, sender);
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), request);
if (trade.getState() == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(state(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG)
.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();
this.errorMessageHandler = null;
tradeResultHandler.handleResult(trade); // trade is initialized
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) new Thread(() -> handlePaymentAccountPayloadRequest(request, sender)).start(); // process notification without trade lock
});
} }
}
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message when buyer has clicked payment started button // Incoming message when buyer has clicked payment started button

View File

@ -138,8 +138,8 @@ public abstract class SellerProtocol extends DisputeProtocol {
SellerSendsPaymentReceivedMessage.class) SellerSendsPaymentReceivedMessage.class)
.using(new TradeTaskRunner(trade, () -> { .using(new TradeTaskRunner(trade, () -> {
this.errorMessageHandler = null; this.errorMessageHandler = null;
resultHandler.handleResult();
handleTaskRunnerSuccess(event); handleTaskRunnerSuccess(event);
resultHandler.handleResult();
}, (errorMessage) -> { }, (errorMessage) -> {
handleTaskRunnerFault(event, errorMessage); handleTaskRunnerFault(event, errorMessage);
}))) })))

View File

@ -23,12 +23,22 @@ import bisq.core.trade.TradeManager;
import bisq.core.trade.TradeUtils; import bisq.core.trade.TradeUtils;
import bisq.core.trade.handlers.TradeResultHandler; import bisq.core.trade.handlers.TradeResultHandler;
import bisq.core.trade.messages.PaymentSentMessage; import bisq.core.trade.messages.PaymentSentMessage;
import bisq.core.trade.messages.DepositResponse;
import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage; import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage;
import bisq.core.trade.messages.InitMultisigRequest; import bisq.core.trade.messages.InitMultisigRequest;
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
import bisq.core.trade.messages.SignContractRequest; import bisq.core.trade.messages.SignContractRequest;
import bisq.core.trade.messages.SignContractResponse;
import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.messages.UpdateMultisigRequest; import bisq.core.trade.messages.UpdateMultisigRequest;
import bisq.core.trade.protocol.tasks.MaybeSendSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
import bisq.core.trade.protocol.tasks.ProcessPaymentAccountPayloadRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
import bisq.core.trade.protocol.tasks.ProcessUpdateMultisigRequest; import bisq.core.trade.protocol.tasks.ProcessUpdateMultisigRequest;
import bisq.core.trade.protocol.tasks.maker.MaybeRemoveOpenOffer;
import bisq.core.util.Validator; import bisq.core.util.Validator;
import bisq.network.p2p.AckMessage; import bisq.network.p2p.AckMessage;
@ -54,7 +64,7 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.fxmisc.easybind.EasyBind;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@Slf4j @Slf4j
@ -213,8 +223,162 @@ 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);
public abstract void handleSignContractRequest(SignContractRequest request, NodeAddress peer); public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleInitMultisigRequest()");
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(anyPhase(Trade.Phase.INIT)
.with(request)
.from(sender))
.setup(tasks(
ProcessInitMultisigRequest.class,
MaybeSendSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, request);
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
}
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId());
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.MULTISIG_COMPLETED || trade.getState() == Trade.State.CONTRACT_SIGNATURE_REQUESTED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(anyState(Trade.State.MULTISIG_COMPLETED, Trade.State.CONTRACT_SIGNATURE_REQUESTED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractRequest.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract request after multisig created
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MULTISIG_COMPLETED) new Thread(() -> handleSignContractRequest(message, sender)).start(); // process notification without trade lock
});
}
}
}
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleSignContractResponse() " + trade.getId());
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), message);
if (trade.getState() == Trade.State.CONTRACT_SIGNED) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
processModel.setTradeMessage(message);
expect(state(Trade.State.CONTRACT_SIGNED)
.with(message)
.from(sender))
.setup(tasks(
// TODO (woodser): validate request
ProcessSignContractResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, message);
},
errorMessage -> {
handleTaskRunnerFault(sender, message, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT)) // extend timeout
.executeTasks(true);
awaitTradeLatch();
} else {
// process sign contract response after contract signed
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.CONTRACT_SIGNED) new Thread(() -> handleSignContractResponse(message, sender)).start(); // process notification without trade lock
});
}
}
}
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleDepositResponse()");
synchronized (trade) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), response);
processModel.setTradeMessage(response);
expect(state(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST)
.with(response)
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
.setup(tasks(
// TODO (woodser): validate request
ProcessDepositResponse.class)
.using(new TradeTaskRunner(trade,
() -> {
startTimeout(TRADE_TIMEOUT);
handleTaskRunnerSuccess(sender, response);
},
errorMessage -> {
handleTaskRunnerFault(sender, response, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
}
}
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountPayloadRequest()");
synchronized (trade) {
Validator.checkTradeId(processModel.getOfferId(), request);
if (trade.getState() == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);
expect(state(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG)
.with(request)
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
.setup(tasks(
// TODO (woodser): validate request
ProcessPaymentAccountPayloadRequest.class,
MaybeRemoveOpenOffer.class)
.using(new TradeTaskRunner(trade,
() -> {
stopTimeout();
this.errorMessageHandler = null;
handleTaskRunnerSuccess(sender, request);
if (tradeResultHandler != null) tradeResultHandler.handleResult(trade); // trade is initialized
},
errorMessage -> {
handleTaskRunnerFault(sender, request, errorMessage);
}))
.withTimeout(TRADE_TIMEOUT))
.executeTasks(true);
awaitTradeLatch();
} else {
EasyBind.subscribe(trade.stateProperty(), state -> {
if (state == Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG) new Thread(() -> handlePaymentAccountPayloadRequest(request, sender)).start(); // process notification without trade lock
});
}
}
}
// 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) {
@ -237,7 +401,6 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
awaitTradeLatch(); awaitTradeLatch();
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// FluentProtocol // FluentProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -20,6 +20,7 @@ package bisq.core.trade.protocol.tasks;
import bisq.common.app.Version; import bisq.common.app.Version;
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.trade.ArbitratorTrade;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;
import bisq.core.trade.Trade.State; import bisq.core.trade.Trade.State;
import bisq.core.trade.messages.SignContractRequest; import bisq.core.trade.messages.SignContractRequest;
@ -32,13 +33,13 @@ import monero.wallet.model.MoneroTxWallet;
// TODO (woodser): separate classes for deposit tx creation and contract request, or combine into ProcessInitMultisigRequest // TODO (woodser): separate classes for deposit tx creation and contract request, or combine into ProcessInitMultisigRequest
@Slf4j @Slf4j
public class SendSignContractRequestAfterMultisig extends TradeTask { public class MaybeSendSignContractRequest extends TradeTask {
private boolean ack1 = false; // TODO (woodser) these represent onArrived(), not the ack private boolean ack1 = false; // TODO (woodser) these represent onArrived(), not the ack
private boolean ack2 = false; private boolean ack2 = false;
@SuppressWarnings({"unused"}) @SuppressWarnings({"unused"})
public SendSignContractRequestAfterMultisig(TaskRunner taskHandler, Trade trade) { public MaybeSendSignContractRequest(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade); super(taskHandler, trade);
} }
@ -47,10 +48,16 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
try { try {
runInterceptHook(); runInterceptHook();
// skip if multisig wallet not complete // skip if arbitrator
if (!processModel.isMultisigSetupComplete()) { if (trade instanceof ArbitratorTrade) {
complete(); complete();
return; // TODO: woodser: this does not ack original request? return;
}
// skip if multisig wallet not complete
if (processModel.getMultisigAddress() == null) {
complete();
return;
} }
// skip if deposit tx already created // skip if deposit tx already created
@ -71,7 +78,7 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
// TODO (woodser): save frozen key images and unfreeze if trade fails before deposited to multisig // TODO (woodser): save frozen key images and unfreeze if trade fails before deposited to multisig
// save process state // save process state
processModel.setDepositTxXmr(depositTx); processModel.setDepositTxXmr(depositTx); // TODO: trade.getSelf().setDepositTx()
trade.getSelf().setDepositTxHash(depositTx.getHash()); trade.getSelf().setDepositTxHash(depositTx.getHash());
trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getAddressEntry(processModel.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString()); // TODO (woodser): allow custom payout address? trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getAddressEntry(processModel.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString()); // TODO (woodser): allow custom payout address?

View File

@ -23,9 +23,7 @@ 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;
@ -92,8 +90,9 @@ public class ProcessInitMultisigRequest extends TradeTask {
log.info("Preparing multisig wallet for trade {}", trade.getId()); log.info("Preparing multisig wallet for trade {}", trade.getId());
multisigWallet = xmrWalletService.createMultisigWallet(trade.getId()); multisigWallet = xmrWalletService.createMultisigWallet(trade.getId());
processModel.setPreparedMultisigHex(multisigWallet.prepareMultisig()); processModel.setPreparedMultisigHex(multisigWallet.prepareMultisig());
trade.setStateIfValidTransitionTo(Trade.State.MULTISIG_PREPARED);
updateParticipants = true; updateParticipants = true;
} else if (!processModel.isMultisigSetupComplete()) { } else if (processModel.getMultisigAddress() == null) {
multisigWallet = xmrWalletService.getMultisigWallet(trade.getId()); multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
} }
@ -103,15 +102,16 @@ public class ProcessInitMultisigRequest extends TradeTask {
log.info("Making multisig wallet for trade {}", trade.getId()); log.info("Making multisig wallet for trade {}", trade.getId());
MoneroMultisigInitResult result = multisigWallet.makeMultisig(Arrays.asList(peers[0].getPreparedMultisigHex(), peers[1].getPreparedMultisigHex()), 2, xmrWalletService.getWalletPassword()); // TODO (woodser): xmrWalletService.makeMultisig(tradeId, multisigHexes, threshold)? MoneroMultisigInitResult result = multisigWallet.makeMultisig(Arrays.asList(peers[0].getPreparedMultisigHex(), peers[1].getPreparedMultisigHex()), 2, xmrWalletService.getWalletPassword()); // TODO (woodser): xmrWalletService.makeMultisig(tradeId, multisigHexes, threshold)?
processModel.setMadeMultisigHex(result.getMultisigHex()); processModel.setMadeMultisigHex(result.getMultisigHex());
trade.setStateIfValidTransitionTo(Trade.State.MULTISIG_MADE);
updateParticipants = true; updateParticipants = true;
} }
// exchange multisig keys if applicable // exchange multisig keys if applicable
if (!processModel.isMultisigSetupComplete() && peers[0].getMadeMultisigHex() != null && peers[1].getMadeMultisigHex() != null) { if (processModel.getMultisigAddress() == null && peers[0].getMadeMultisigHex() != null && peers[1].getMadeMultisigHex() != null) {
log.info("Exchanging multisig wallet keys for trade {}", trade.getId()); log.info("Exchanging multisig wallet keys for trade {}", trade.getId());
multisigWallet.exchangeMultisigKeys(Arrays.asList(peers[0].getMadeMultisigHex(), peers[1].getMadeMultisigHex()), xmrWalletService.getWalletPassword()); multisigWallet.exchangeMultisigKeys(Arrays.asList(peers[0].getMadeMultisigHex(), peers[1].getMadeMultisigHex()), xmrWalletService.getWalletPassword());
processModel.setMultisigSetupComplete(true); // TODO: (woodser): remove this field?
processModel.setMultisigAddress(multisigWallet.getPrimaryAddress()); processModel.setMultisigAddress(multisigWallet.getPrimaryAddress());
trade.setStateIfValidTransitionTo(Trade.State.MULTISIG_COMPLETED);
processModel.getProvider().getXmrWalletService().closeMultisigWallet(trade.getId()); // save and close multisig wallet once it's created processModel.getProvider().getXmrWalletService().closeMultisigWallet(trade.getId()); // save and close multisig wallet once it's created
} }

View File

@ -17,6 +17,7 @@
package bisq.core.trade.protocol.tasks.maker; package bisq.core.trade.protocol.tasks.maker;
import bisq.core.trade.MakerTrade;
import bisq.core.trade.Trade; import bisq.core.trade.Trade;
import bisq.core.trade.protocol.tasks.TradeTask; import bisq.core.trade.protocol.tasks.TradeTask;
@ -27,8 +28,8 @@ import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
public class MakerRemovesOpenOffer extends TradeTask { public class MaybeRemoveOpenOffer extends TradeTask {
public MakerRemovesOpenOffer(TaskRunner<Trade> taskHandler, Trade trade) { public MaybeRemoveOpenOffer(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade); super(taskHandler, trade);
} }
@ -37,7 +38,9 @@ public class MakerRemovesOpenOffer extends TradeTask {
try { try {
runInterceptHook(); runInterceptHook();
if (trade instanceof MakerTrade) {
processModel.getOpenOfferManager().closeOpenOffer(checkNotNull(trade.getOffer())); processModel.getOpenOfferManager().closeOpenOffer(checkNotNull(trade.getOffer()));
}
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {

View File

@ -128,8 +128,7 @@ class GrpcTradesService extends TradesImplBase {
responseObserver.onCompleted(); responseObserver.onCompleted();
}, },
errorMessage -> { errorMessage -> {
if (!errorMessageHandler.isErrorHandled()) if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
errorMessageHandler.handleErrorMessage(errorMessage);
}); });
} catch (Throwable cause) { } catch (Throwable cause) {
cause.printStackTrace(); cause.printStackTrace();

View File

@ -43,7 +43,7 @@ import bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerSendsInputsForD
import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerCreatesDepositTxInputs; import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerCreatesDepositTxInputs;
import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSendsDepositTxMessage; import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSendsDepositTxMessage;
import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignsDepositTx; import bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignsDepositTx;
import bisq.core.trade.protocol.tasks.maker.MakerRemovesOpenOffer; import bisq.core.trade.protocol.tasks.maker.MaybeRemoveOpenOffer;
import bisq.core.trade.protocol.tasks.maker.MakerSetsLockTime; import bisq.core.trade.protocol.tasks.maker.MakerSetsLockTime;
import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment; import bisq.core.trade.protocol.tasks.maker.MakerVerifyTakerFeePayment;
import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx; import bisq.core.trade.protocol.tasks.seller.SellerCreatesDelayedPayoutTx;
@ -157,7 +157,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
BuyerAsMakerSendsInputsForDepositTxResponse.class, BuyerAsMakerSendsInputsForDepositTxResponse.class,
BuyerProcessDelayedPayoutTxSignatureRequest.class, BuyerProcessDelayedPayoutTxSignatureRequest.class,
MakerRemovesOpenOffer.class, MaybeRemoveOpenOffer.class,
BuyerVerifiesPreparedDelayedPayoutTx.class, BuyerVerifiesPreparedDelayedPayoutTx.class,
BuyerSignsDelayedPayoutTx.class, BuyerSignsDelayedPayoutTx.class,
BuyerSendsDelayedPayoutTxSignatureResponse.class, BuyerSendsDelayedPayoutTxSignatureResponse.class,
@ -215,7 +215,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
SellerAsMakerSendsInputsForDepositTxResponse.class, SellerAsMakerSendsInputsForDepositTxResponse.class,
//SellerAsMakerProcessDepositTxMessage.class, //SellerAsMakerProcessDepositTxMessage.class,
MakerRemovesOpenOffer.class, MaybeRemoveOpenOffer.class,
SellerAsMakerFinalizesDepositTx.class, SellerAsMakerFinalizesDepositTx.class,
SellerCreatesDelayedPayoutTx.class, SellerCreatesDelayedPayoutTx.class,
SellerSendDelayedPayoutTxSignatureRequest.class, SellerSendDelayedPayoutTxSignatureRequest.class,

View File

@ -403,6 +403,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
// #################### Phase PREPARATION // #################### Phase PREPARATION
case PREPARATION: case PREPARATION:
case CONTRACT_SIGNATURE_REQUESTED: case CONTRACT_SIGNATURE_REQUESTED:
case CONTRACT_SIGNED:
sellerState.set(UNDEFINED); sellerState.set(UNDEFINED);
buyerState.set(BuyerState.UNDEFINED); buyerState.set(BuyerState.UNDEFINED);
break; break;

View File

@ -1624,43 +1624,46 @@ message Trade {
enum State { enum State {
PB_ERROR_STATE = 0; PB_ERROR_STATE = 0;
PREPARATION = 1; PREPARATION = 1;
CONTRACT_SIGNATURE_REQUESTED = 2; MULTISIG_PREPARED = 2;
CONTRACT_SIGNED = 3; MULTISIG_MADE = 3;
TAKER_PUBLISHED_TAKER_FEE_TX = 4; MULTISIG_COMPLETED = 4;
MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST = 5; CONTRACT_SIGNATURE_REQUESTED = 5;
MAKER_SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST = 6; CONTRACT_SIGNED = 6;
MAKER_STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 7; TAKER_PUBLISHED_TAKER_FEE_TX = 7;
MAKER_SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 8; MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST = 8;
TAKER_RECEIVED_PUBLISH_DEPOSIT_TX_REQUEST = 9; MAKER_SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST = 9;
ARBITRATOR_PUBLISHED_DEPOSIT_TX = 10; MAKER_STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 10;
TAKER_SAW_DEPOSIT_TX_IN_NETWORK = 11; MAKER_SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 11;
TAKER_SENT_DEPOSIT_TX_PUBLISHED_MSG = 12; TAKER_RECEIVED_PUBLISH_DEPOSIT_TX_REQUEST = 12;
TAKER_SAW_ARRIVED_DEPOSIT_TX_PUBLISHED_MSG = 13; ARBITRATOR_PUBLISHED_DEPOSIT_TX = 13;
TAKER_STORED_IN_MAILBOX_DEPOSIT_TX_PUBLISHED_MSG = 14; TAKER_SAW_DEPOSIT_TX_IN_NETWORK = 14;
TAKER_SEND_FAILED_DEPOSIT_TX_PUBLISHED_MSG = 15; TAKER_SENT_DEPOSIT_TX_PUBLISHED_MSG = 15;
MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG = 16; TAKER_SAW_ARRIVED_DEPOSIT_TX_PUBLISHED_MSG = 16;
MAKER_SAW_DEPOSIT_TX_IN_NETWORK = 17; TAKER_STORED_IN_MAILBOX_DEPOSIT_TX_PUBLISHED_MSG = 17;
DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN = 18; TAKER_SEND_FAILED_DEPOSIT_TX_PUBLISHED_MSG = 18;
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 19; MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG = 19;
BUYER_SENT_PAYMENT_SENT_MSG = 20; MAKER_SAW_DEPOSIT_TX_IN_NETWORK = 20;
BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG = 21; DEPOSIT_UNLOCKED_IN_BLOCK_CHAIN = 21;
BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG = 22; BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 22;
BUYER_SEND_FAILED_PAYMENT_SENT_MSG = 23; BUYER_SENT_PAYMENT_SENT_MSG = 23;
SELLER_RECEIVED_PAYMENT_SENT_MSG = 24; BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG = 24;
SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT = 25; BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG = 25;
SELLER_SENT_PAYMENT_RECEIVED_MSG = 26; BUYER_SEND_FAILED_PAYMENT_SENT_MSG = 26;
SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG = 27; SELLER_RECEIVED_PAYMENT_SENT_MSG = 27;
SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG = 28; SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT = 28;
SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG = 29; SELLER_SENT_PAYMENT_RECEIVED_MSG = 29;
SELLER_PUBLISHED_PAYOUT_TX = 30; SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG = 30;
SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG = 31; SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG = 31;
SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG = 32; SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG = 32;
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 33; SELLER_PUBLISHED_PAYOUT_TX = 33;
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 34; SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG = 34;
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 35; SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG = 35;
BUYER_SAW_PAYOUT_TX_IN_NETWORK = 36; SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 36;
BUYER_PUBLISHED_PAYOUT_TX = 37; SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 37;
WITHDRAW_COMPLETED = 38; BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 38;
BUYER_SAW_PAYOUT_TX_IN_NETWORK = 39;
BUYER_PUBLISHED_PAYOUT_TX = 40;
WITHDRAW_COMPLETED = 41;
} }
enum Phase { enum Phase {
@ -1789,7 +1792,6 @@ message ProcessModel {
string prepared_multisig_hex = 1007; string prepared_multisig_hex = 1007;
string made_multisig_hex = 1008; string made_multisig_hex = 1008;
string multisig_address = 1009; string multisig_address = 1009;
bool multisig_setup_complete = 1010; // TODO: remove this field
} }
message TradingPeer { message TradingPeer {