use backup arbitrator if signing arbitrator not available
This commit is contained in:
parent
7d21bdf9f3
commit
4dafd57026
11
Makefile
11
Makefile
@ -53,6 +53,17 @@ arbitrator-desktop:
|
|||||||
--appName=haveno-XMR_STAGENET_arbitrator \
|
--appName=haveno-XMR_STAGENET_arbitrator \
|
||||||
--apiPassword=apitest \
|
--apiPassword=apitest \
|
||||||
--apiPort=9998
|
--apiPort=9998
|
||||||
|
|
||||||
|
arbitrator-desktop2:
|
||||||
|
# Arbitrator and mediator need to be registerd in the UI after launching it.
|
||||||
|
./haveno-desktop \
|
||||||
|
--baseCurrencyNetwork=XMR_STAGENET \
|
||||||
|
--useLocalhostForP2P=true \
|
||||||
|
--useDevPrivilegeKeys=true \
|
||||||
|
--nodePort=7777 \
|
||||||
|
--appName=haveno-XMR_STAGENET_arbitrator2 \
|
||||||
|
--apiPassword=apitest \
|
||||||
|
--apiPort=10001
|
||||||
|
|
||||||
alice-desktop:
|
alice-desktop:
|
||||||
./haveno-desktop \
|
./haveno-desktop \
|
||||||
|
@ -216,8 +216,8 @@ public class OfferFilter {
|
|||||||
public boolean hasValidSignature(Offer offer) {
|
public boolean hasValidSignature(Offer offer) {
|
||||||
|
|
||||||
// get arbitrator
|
// get arbitrator
|
||||||
Mediator arbitrator = user.getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorNodeAddress()); // TODO (woodser): does this return null if arbitrator goes offline?
|
Mediator arbitrator = user.getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
||||||
if (arbitrator == null) return false; // TODO (woodser): if arbitrator is null, get arbirator's pub key ring from store, otherwise cannot validate and offer is not seen by takers when arbitrator goes offline
|
if (arbitrator == null) return false; // invalid arbitrator
|
||||||
|
|
||||||
// validate arbitrator signature
|
// validate arbitrator signature
|
||||||
return TradeUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator);
|
return TradeUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator);
|
||||||
|
@ -169,7 +169,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
|||||||
|
|
||||||
// address and signature of signing arbitrator
|
// address and signature of signing arbitrator
|
||||||
@Setter
|
@Setter
|
||||||
private NodeAddress arbitratorNodeAddress;
|
private NodeAddress arbitratorSigner;
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
private String arbitratorSignature;
|
private String arbitratorSignature;
|
||||||
@ -255,7 +255,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
|||||||
this.hashOfChallenge = hashOfChallenge;
|
this.hashOfChallenge = hashOfChallenge;
|
||||||
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
|
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
this.arbitratorNodeAddress = arbitratorSigner;
|
this.arbitratorSigner = arbitratorSigner;
|
||||||
this.arbitratorSignature = arbitratorSignature;
|
this.arbitratorSignature = arbitratorSignature;
|
||||||
this.reserveTxKeyImages = reserveTxKeyImages;
|
this.reserveTxKeyImages = reserveTxKeyImages;
|
||||||
}
|
}
|
||||||
@ -295,7 +295,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
|||||||
.setUpperClosePrice(upperClosePrice)
|
.setUpperClosePrice(upperClosePrice)
|
||||||
.setIsPrivateOffer(isPrivateOffer)
|
.setIsPrivateOffer(isPrivateOffer)
|
||||||
.setProtocolVersion(protocolVersion)
|
.setProtocolVersion(protocolVersion)
|
||||||
.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage());
|
.setArbitratorSigner(arbitratorSigner.toProtoMessage());
|
||||||
|
|
||||||
builder.setOfferFeePaymentTxId(checkNotNull(offerFeePaymentTxId,
|
builder.setOfferFeePaymentTxId(checkNotNull(offerFeePaymentTxId,
|
||||||
"OfferPayload is in invalid state: offerFeePaymentTxID is not set when adding to P2P network."));
|
"OfferPayload is in invalid state: offerFeePaymentTxID is not set when adding to P2P network."));
|
||||||
@ -357,7 +357,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
|||||||
hashOfChallenge,
|
hashOfChallenge,
|
||||||
extraDataMapMap,
|
extraDataMapMap,
|
||||||
proto.getProtocolVersion(),
|
proto.getProtocolVersion(),
|
||||||
NodeAddress.fromProto(proto.getArbitratorNodeAddress()),
|
NodeAddress.fromProto(proto.getArbitratorSigner()),
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getArbitratorSignature()),
|
ProtoUtil.stringOrNullFromProto(proto.getArbitratorSignature()),
|
||||||
proto.getReserveTxKeyImagesList() == null ? null : new ArrayList<String>(proto.getReserveTxKeyImagesList()));
|
proto.getReserveTxKeyImagesList() == null ? null : new ArrayList<String>(proto.getReserveTxKeyImagesList()));
|
||||||
}
|
}
|
||||||
@ -424,7 +424,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
|||||||
",\n hashOfChallenge='" + hashOfChallenge + '\'' +
|
",\n hashOfChallenge='" + hashOfChallenge + '\'' +
|
||||||
",\n extraDataMap=" + extraDataMap +
|
",\n extraDataMap=" + extraDataMap +
|
||||||
",\n protocolVersion=" + protocolVersion +
|
",\n protocolVersion=" + protocolVersion +
|
||||||
",\n arbitratorSigner=" + arbitratorNodeAddress +
|
",\n arbitratorSigner=" + arbitratorSigner +
|
||||||
",\n arbitratorSignature=" + arbitratorSignature +
|
",\n arbitratorSignature=" + arbitratorSignature +
|
||||||
"\n}";
|
"\n}";
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,7 @@ import bisq.network.p2p.NodeAddress;
|
|||||||
import bisq.common.Timer;
|
import bisq.common.Timer;
|
||||||
import bisq.common.UserThread;
|
import bisq.common.UserThread;
|
||||||
import bisq.common.proto.ProtoUtil;
|
import bisq.common.proto.ProtoUtil;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
@ -58,10 +56,17 @@ public final class OpenOffer implements Tradable {
|
|||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
private NodeAddress arbitratorNodeAddress;
|
private NodeAddress backupArbitrator;
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
private List<String> frozenKeyImages = new ArrayList<>();
|
private String reserveTxHash;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private String reserveTxHex;
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
private String reserveTxKey;
|
||||||
|
|
||||||
|
|
||||||
// Added in v1.5.3.
|
// Added in v1.5.3.
|
||||||
// If market price reaches that trigger price the offer gets deactivated
|
// If market price reaches that trigger price the offer gets deactivated
|
||||||
@ -81,11 +86,17 @@ public final class OpenOffer implements Tradable {
|
|||||||
state = State.AVAILABLE;
|
state = State.AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OpenOffer(Offer offer, long triggerPrice, List<String> frozenKeyImages) {
|
public OpenOffer(Offer offer,
|
||||||
|
long triggerPrice,
|
||||||
|
String reserveTxHash,
|
||||||
|
String reserveTxHex,
|
||||||
|
String reserveTxKey) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.triggerPrice = triggerPrice;
|
this.triggerPrice = triggerPrice;
|
||||||
state = State.AVAILABLE;
|
state = State.AVAILABLE;
|
||||||
this.frozenKeyImages = frozenKeyImages;
|
this.reserveTxHash = reserveTxHash;
|
||||||
|
this.reserveTxHex = reserveTxHex;
|
||||||
|
this.reserveTxKey = reserveTxKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -94,12 +105,18 @@ public final class OpenOffer implements Tradable {
|
|||||||
|
|
||||||
private OpenOffer(Offer offer,
|
private OpenOffer(Offer offer,
|
||||||
State state,
|
State state,
|
||||||
@Nullable NodeAddress arbitratorNodeAddress,
|
@Nullable NodeAddress backupArbitrator,
|
||||||
long triggerPrice) {
|
long triggerPrice,
|
||||||
|
String reserveTxHash,
|
||||||
|
String reserveTxHex,
|
||||||
|
String reserveTxKey) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.arbitratorNodeAddress = arbitratorNodeAddress;
|
this.backupArbitrator = backupArbitrator;
|
||||||
this.triggerPrice = triggerPrice;
|
this.triggerPrice = triggerPrice;
|
||||||
|
this.reserveTxHash = reserveTxHash;
|
||||||
|
this.reserveTxHex = reserveTxHex;
|
||||||
|
this.reserveTxKey = reserveTxKey;
|
||||||
|
|
||||||
if (this.state == State.RESERVED)
|
if (this.state == State.RESERVED)
|
||||||
setState(State.AVAILABLE);
|
setState(State.AVAILABLE);
|
||||||
@ -111,9 +128,11 @@ public final class OpenOffer implements Tradable {
|
|||||||
.setOffer(offer.toProtoMessage())
|
.setOffer(offer.toProtoMessage())
|
||||||
.setTriggerPrice(triggerPrice)
|
.setTriggerPrice(triggerPrice)
|
||||||
.setState(protobuf.OpenOffer.State.valueOf(state.name()))
|
.setState(protobuf.OpenOffer.State.valueOf(state.name()))
|
||||||
.addAllFrozenKeyImages(frozenKeyImages);
|
.setReserveTxHash(reserveTxHash)
|
||||||
|
.setReserveTxHex(reserveTxHex)
|
||||||
|
.setReserveTxKey(reserveTxKey);
|
||||||
|
|
||||||
Optional.ofNullable(arbitratorNodeAddress).ifPresent(nodeAddress -> builder.setArbitratorNodeAddress(nodeAddress.toProtoMessage()));
|
Optional.ofNullable(backupArbitrator).ifPresent(nodeAddress -> builder.setBackupArbitrator(nodeAddress.toProtoMessage()));
|
||||||
|
|
||||||
return protobuf.Tradable.newBuilder().setOpenOffer(builder).build();
|
return protobuf.Tradable.newBuilder().setOpenOffer(builder).build();
|
||||||
}
|
}
|
||||||
@ -121,9 +140,11 @@ public final class OpenOffer implements Tradable {
|
|||||||
public static Tradable fromProto(protobuf.OpenOffer proto) {
|
public static Tradable fromProto(protobuf.OpenOffer proto) {
|
||||||
OpenOffer openOffer = new OpenOffer(Offer.fromProto(proto.getOffer()),
|
OpenOffer openOffer = new OpenOffer(Offer.fromProto(proto.getOffer()),
|
||||||
ProtoUtil.enumFromProto(OpenOffer.State.class, proto.getState().name()),
|
ProtoUtil.enumFromProto(OpenOffer.State.class, proto.getState().name()),
|
||||||
proto.hasArbitratorNodeAddress() ? NodeAddress.fromProto(proto.getArbitratorNodeAddress()) : null,
|
proto.hasBackupArbitrator() ? NodeAddress.fromProto(proto.getBackupArbitrator()) : null,
|
||||||
proto.getTriggerPrice());
|
proto.getTriggerPrice(),
|
||||||
openOffer.setFrozenKeyImages(proto.getFrozenKeyImagesList());
|
proto.getReserveTxHash(),
|
||||||
|
proto.getReserveTxHex(),
|
||||||
|
proto.getReserveTxKey());
|
||||||
return openOffer;
|
return openOffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +208,7 @@ public final class OpenOffer implements Tradable {
|
|||||||
return "OpenOffer{" +
|
return "OpenOffer{" +
|
||||||
",\n offer=" + offer +
|
",\n offer=" + offer +
|
||||||
",\n state=" + state +
|
",\n state=" + state +
|
||||||
",\n arbitratorNodeAddress=" + arbitratorNodeAddress +
|
",\n arbitratorNodeAddress=" + backupArbitrator +
|
||||||
",\n triggerPrice=" + triggerPrice +
|
",\n triggerPrice=" + triggerPrice +
|
||||||
"\n}";
|
"\n}";
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,6 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import monero.daemon.model.MoneroOutput;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@ -416,10 +415,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
model,
|
model,
|
||||||
transaction -> {
|
transaction -> {
|
||||||
|
|
||||||
// save frozen key images with open offer
|
// save reserve tx with open offer
|
||||||
List<String> frozenKeyImages = new ArrayList<String>();
|
OpenOffer openOffer = new OpenOffer(offer, triggerPrice, model.getReserveTx().getHash(), model.getReserveTx().getFullHex(), model.getReserveTx().getKey());
|
||||||
for (MoneroOutput output : model.getReserveTx().getInputs()) frozenKeyImages.add(output.getKeyImage().getHex());
|
|
||||||
OpenOffer openOffer = new OpenOffer(offer, triggerPrice, frozenKeyImages);
|
|
||||||
openOffers.add(openOffer);
|
openOffers.add(openOffer);
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
resultHandler.handleResult(transaction);
|
resultHandler.handleResult(transaction);
|
||||||
@ -577,7 +574,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
closedTradableManager.add(openOffer);
|
closedTradableManager.add(openOffer);
|
||||||
log.info("onRemoved offerId={}", offer.getId());
|
log.info("onRemoved offerId={}", offer.getId());
|
||||||
btcWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
btcWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||||
for (String frozenKeyImage : openOffer.getFrozenKeyImages()) xmrWalletService.getWallet().thawOutput(frozenKeyImage);
|
for (String frozenKeyImage : offer.getOfferPayload().getReserveTxKeyImages()) xmrWalletService.getWallet().thawOutput(frozenKeyImage);
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
}
|
}
|
||||||
@ -642,7 +639,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify arbitrator is signer of offer payload
|
// verify arbitrator is signer of offer payload
|
||||||
if (!request.getOfferPayload().getArbitratorNodeAddress().equals(thisAddress)) {
|
if (!thisAddress.equals(request.getOfferPayload().getArbitratorSigner())) {
|
||||||
errorMessage = "Cannot sign offer because offer payload is for a different arbitrator";
|
errorMessage = "Cannot sign offer because offer payload is for a different arbitrator";
|
||||||
log.info(errorMessage);
|
log.info(errorMessage);
|
||||||
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||||
@ -784,7 +781,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
Optional<OpenOffer> openOfferOptional = getOpenOfferById(request.offerId);
|
Optional<OpenOffer> openOfferOptional = getOpenOfferById(request.offerId);
|
||||||
AvailabilityResult availabilityResult;
|
AvailabilityResult availabilityResult;
|
||||||
String makerSignature = null;
|
String makerSignature = null;
|
||||||
NodeAddress arbitratorNodeAddress = null;
|
NodeAddress backupArbitratorNodeAddress = null;
|
||||||
if (openOfferOptional.isPresent()) {
|
if (openOfferOptional.isPresent()) {
|
||||||
OpenOffer openOffer = openOfferOptional.get();
|
OpenOffer openOffer = openOfferOptional.get();
|
||||||
if (!apiUserDeniedByOffer(request)) {
|
if (!apiUserDeniedByOffer(request)) {
|
||||||
@ -793,10 +790,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
Offer offer = openOffer.getOffer();
|
Offer offer = openOffer.getOffer();
|
||||||
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
|
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
|
||||||
|
|
||||||
// use signing arbitrator if available, otherwise use least used arbitrator
|
// set backup arbitrator if signer is not available
|
||||||
boolean isSignerOnline = true;
|
Mediator backupMediator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, mediatorManager, offer.getOfferPayload().getArbitratorSigner());
|
||||||
arbitratorNodeAddress = isSignerOnline ? offer.getOfferPayload().getArbitratorNodeAddress() : DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, mediatorManager).getNodeAddress();
|
backupArbitratorNodeAddress = backupMediator == null ? null : backupMediator.getNodeAddress();
|
||||||
openOffer.setArbitratorNodeAddress(arbitratorNodeAddress);
|
openOffer.setBackupArbitrator(backupArbitratorNodeAddress);
|
||||||
|
|
||||||
// maker signs taker's request // TODO (woodser): should maker signature include selected arbitrator?
|
// maker signs taker's request // TODO (woodser): should maker signature include selected arbitrator?
|
||||||
String tradeRequestAsJson = Utilities.objectToJson(request.getTradeRequest());
|
String tradeRequestAsJson = Utilities.objectToJson(request.getTradeRequest());
|
||||||
@ -848,7 +845,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
||||||
availabilityResult,
|
availabilityResult,
|
||||||
makerSignature,
|
makerSignature,
|
||||||
arbitratorNodeAddress);
|
backupArbitratorNodeAddress);
|
||||||
log.info("Send {} with offerId {} and uid {} to peer {}",
|
log.info("Send {} with offerId {} and uid {} to peer {}",
|
||||||
offerAvailabilityResponse.getClass().getSimpleName(), offerAvailabilityResponse.getOfferId(),
|
offerAvailabilityResponse.getClass().getSimpleName(), offerAvailabilityResponse.getOfferId(),
|
||||||
offerAvailabilityResponse.getUid(), peer);
|
offerAvailabilityResponse.getUid(), peer);
|
||||||
@ -933,6 +930,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Update persisted offer if a new capability is required after a software update
|
// Update persisted offer if a new capability is required after a software update
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO (woodser): arbitrator signature will be invalid if offer updated (exclude updateable fields from signature? re-sign?)
|
||||||
|
|
||||||
private void maybeUpdatePersistedOffers() {
|
private void maybeUpdatePersistedOffers() {
|
||||||
// We need to clone to avoid ConcurrentModificationException
|
// We need to clone to avoid ConcurrentModificationException
|
||||||
@ -1019,7 +1018,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
originalOfferPayload.getHashOfChallenge(),
|
originalOfferPayload.getHashOfChallenge(),
|
||||||
updatedExtraDataMap,
|
updatedExtraDataMap,
|
||||||
protocolVersion,
|
protocolVersion,
|
||||||
originalOfferPayload.getArbitratorNodeAddress(),
|
originalOfferPayload.getArbitratorSigner(),
|
||||||
originalOfferPayload.getArbitratorSignature(),
|
originalOfferPayload.getArbitratorSignature(),
|
||||||
originalOfferPayload.getReserveTxKeyImages());
|
originalOfferPayload.getReserveTxKeyImages());
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import bisq.core.support.dispute.agent.DisputeAgent;
|
|||||||
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
||||||
import bisq.core.trade.statistics.TradeStatistics3;
|
import bisq.core.trade.statistics.TradeStatistics3;
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.util.Tuple2;
|
import bisq.common.util.Tuple2;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
@ -43,15 +43,25 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class DisputeAgentSelection {
|
public class DisputeAgentSelection {
|
||||||
public static final int LOOK_BACK_RANGE = 100;
|
public static final int LOOK_BACK_RANGE = 100;
|
||||||
|
|
||||||
public static <T extends DisputeAgent> T getLeastUsedArbitrator(TradeStatisticsManager tradeStatisticsManager,
|
public static <T extends DisputeAgent> T getLeastUsedArbitrator(TradeStatisticsManager tradeStatisticsManager,
|
||||||
DisputeAgentManager<T> disputeAgentManager) {
|
DisputeAgentManager<T> disputeAgentManager) {
|
||||||
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||||
disputeAgentManager);
|
disputeAgentManager,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends DisputeAgent> T getLeastUsedArbitrator(TradeStatisticsManager tradeStatisticsManager,
|
||||||
|
DisputeAgentManager<T> disputeAgentManager,
|
||||||
|
NodeAddress excludedArbitrator) {
|
||||||
|
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||||
|
disputeAgentManager,
|
||||||
|
excludedArbitrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T extends DisputeAgent> T getLeastUsedDisputeAgent(TradeStatisticsManager tradeStatisticsManager,
|
private static <T extends DisputeAgent> T getLeastUsedDisputeAgent(TradeStatisticsManager tradeStatisticsManager,
|
||||||
DisputeAgentManager<T> disputeAgentManager) {
|
DisputeAgentManager<T> disputeAgentManager,
|
||||||
|
NodeAddress excludedDisputeAgent) {
|
||||||
// We take last 100 entries from trade statistics
|
// We take last 100 entries from trade statistics
|
||||||
List<TradeStatistics3> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
|
List<TradeStatistics3> list = new ArrayList<>(tradeStatisticsManager.getObservableTradeStatisticsSet());
|
||||||
list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong));
|
list.sort(Comparator.comparing(TradeStatistics3::getDateAsLong));
|
||||||
@ -71,6 +81,9 @@ public class DisputeAgentSelection {
|
|||||||
.map(disputeAgent -> disputeAgent.getNodeAddress().getFullAddress())
|
.map(disputeAgent -> disputeAgent.getNodeAddress().getFullAddress())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
if (excludedDisputeAgent != null) disputeAgents.remove(excludedDisputeAgent.getFullAddress());
|
||||||
|
if (disputeAgents.isEmpty()) return null;
|
||||||
|
|
||||||
String result = getLeastUsedDisputeAgent(lastAddressesUsedInTrades, disputeAgents);
|
String result = getLeastUsedDisputeAgent(lastAddressesUsedInTrades, disputeAgents);
|
||||||
|
|
||||||
Optional<T> optionalDisputeAgent = disputeAgentManager.getObservableMap().values().stream()
|
Optional<T> optionalDisputeAgent = disputeAgentManager.getObservableMap().values().stream()
|
||||||
|
@ -67,7 +67,7 @@ public class OfferAvailabilityModel implements Model {
|
|||||||
private String makerSignature;
|
private String makerSignature;
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
private NodeAddress arbitratorNodeAddress;
|
private NodeAddress backupArbitrator;
|
||||||
|
|
||||||
// Added in v1.5.5
|
// Added in v1.5.5
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -53,19 +53,17 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check maker signature for trade request
|
// verify maker signature for trade request
|
||||||
if (!TradeUtils.isMakerSignatureValid(model.getTradeRequest(), offerAvailabilityResponse.getMakerSignature(), offer.getPubKeyRing())) {
|
if (!TradeUtils.isMakerSignatureValid(model.getTradeRequest(), offerAvailabilityResponse.getMakerSignature(), offer.getPubKeyRing())) {
|
||||||
offer.setState(Offer.State.NOT_AVAILABLE);
|
offer.setState(Offer.State.NOT_AVAILABLE);
|
||||||
failed("Take offer attempt failed because maker signature is invalid");
|
failed("Take offer attempt failed because maker signature is invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
offer.setState(Offer.State.AVAILABLE);
|
|
||||||
|
|
||||||
|
offer.setState(Offer.State.AVAILABLE);
|
||||||
model.setMakerSignature(offerAvailabilityResponse.getMakerSignature());
|
model.setMakerSignature(offerAvailabilityResponse.getMakerSignature());
|
||||||
model.setArbitratorNodeAddress(offerAvailabilityResponse.getArbitratorNodeAddress());
|
model.setBackupArbitrator(offerAvailabilityResponse.getBackupArbitrator());
|
||||||
checkNotNull(model.getMakerSignature());
|
checkNotNull(model.getMakerSignature());
|
||||||
checkNotNull(model.getArbitratorNodeAddress());
|
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
@ -37,6 +37,7 @@ import bisq.common.taskrunner.TaskRunner;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
// TODO (woodser): rename to TakerSendOfferAvailabilityRequest and group with other taker tasks
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
||||||
public SendOfferAvailabilityRequest(TaskRunner<OfferAvailabilityModel> taskHandler, OfferAvailabilityModel model) {
|
public SendOfferAvailabilityRequest(TaskRunner<OfferAvailabilityModel> taskHandler, OfferAvailabilityModel model) {
|
||||||
@ -78,7 +79,7 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
|
|||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
offer.getMakerNodeAddress(),
|
offer.getMakerNodeAddress(),
|
||||||
P2PService.getMyNodeAddress(),
|
P2PService.getMyNodeAddress(),
|
||||||
null, // maker provides node address of arbitrator on response
|
null, // maker provides node address of backup arbitrator on response
|
||||||
null, // reserve tx not sent from taker to maker
|
null, // reserve tx not sent from taker to maker
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
@ -46,19 +46,19 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String makerSignature;
|
private final String makerSignature;
|
||||||
private final NodeAddress arbitratorNodeAddress;
|
private final NodeAddress backupArbitrator;
|
||||||
|
|
||||||
public OfferAvailabilityResponse(String offerId,
|
public OfferAvailabilityResponse(String offerId,
|
||||||
AvailabilityResult availabilityResult,
|
AvailabilityResult availabilityResult,
|
||||||
String makerSignature,
|
String makerSignature,
|
||||||
NodeAddress arbitratorNodeAddress) {
|
NodeAddress backupArbitrator) {
|
||||||
this(offerId,
|
this(offerId,
|
||||||
availabilityResult,
|
availabilityResult,
|
||||||
Capabilities.app,
|
Capabilities.app,
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
makerSignature,
|
makerSignature,
|
||||||
arbitratorNodeAddress);
|
backupArbitrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -77,19 +77,19 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
|||||||
this.availabilityResult = availabilityResult;
|
this.availabilityResult = availabilityResult;
|
||||||
this.supportedCapabilities = supportedCapabilities;
|
this.supportedCapabilities = supportedCapabilities;
|
||||||
this.makerSignature = makerSignature;
|
this.makerSignature = makerSignature;
|
||||||
this.arbitratorNodeAddress = arbitratorNodeAddress;
|
this.backupArbitrator = arbitratorNodeAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
||||||
final protobuf.OfferAvailabilityResponse.Builder builder = protobuf.OfferAvailabilityResponse.newBuilder()
|
final protobuf.OfferAvailabilityResponse.Builder builder = protobuf.OfferAvailabilityResponse.newBuilder()
|
||||||
.setOfferId(offerId)
|
.setOfferId(offerId)
|
||||||
.setAvailabilityResult(protobuf.AvailabilityResult.valueOf(availabilityResult.name()))
|
.setAvailabilityResult(protobuf.AvailabilityResult.valueOf(availabilityResult.name()));
|
||||||
.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage());
|
|
||||||
|
|
||||||
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
|
Optional.ofNullable(supportedCapabilities).ifPresent(e -> builder.addAllSupportedCapabilities(Capabilities.toIntList(supportedCapabilities)));
|
||||||
Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid));
|
Optional.ofNullable(uid).ifPresent(e -> builder.setUid(uid));
|
||||||
Optional.ofNullable(makerSignature).ifPresent(e -> builder.setMakerSignature(makerSignature));
|
Optional.ofNullable(makerSignature).ifPresent(e -> builder.setMakerSignature(makerSignature));
|
||||||
|
Optional.ofNullable(backupArbitrator).ifPresent(nodeAddress -> builder.setBackupArbitrator(nodeAddress.toProtoMessage()));
|
||||||
|
|
||||||
return getNetworkEnvelopeBuilder()
|
return getNetworkEnvelopeBuilder()
|
||||||
.setOfferAvailabilityResponse(builder)
|
.setOfferAvailabilityResponse(builder)
|
||||||
@ -103,6 +103,6 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
|||||||
messageVersion,
|
messageVersion,
|
||||||
proto.getUid().isEmpty() ? null : proto.getUid(),
|
proto.getUid().isEmpty() ? null : proto.getUid(),
|
||||||
proto.getMakerSignature().isEmpty() ? null : proto.getMakerSignature(),
|
proto.getMakerSignature().isEmpty() ? null : proto.getMakerSignature(),
|
||||||
NodeAddress.fromProto(proto.getArbitratorNodeAddress()));
|
proto.hasBackupArbitrator() ? NodeAddress.fromProto(proto.getBackupArbitrator()) : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ public class PlaceOfferProtocol {
|
|||||||
// Called from UI
|
// Called from UI
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// TODO (woodser): this returns before offer is placed
|
||||||
public void placeOffer() {
|
public void placeOffer() {
|
||||||
log.debug("placeOffer() " + model.getOffer().getId());
|
log.debug("placeOffer() " + model.getOffer().getId());
|
||||||
TaskRunner<PlaceOfferModel> taskRunner = new TaskRunner<>(model,
|
TaskRunner<PlaceOfferModel> taskRunner = new TaskRunner<>(model,
|
||||||
@ -82,7 +83,7 @@ public class PlaceOfferProtocol {
|
|||||||
log.debug("handleSignOfferResponse() " + model.getOffer().getId());
|
log.debug("handleSignOfferResponse() " + model.getOffer().getId());
|
||||||
model.setSignOfferResponse(response);
|
model.setSignOfferResponse(response);
|
||||||
|
|
||||||
if (!model.getOffer().getOfferPayload().getArbitratorNodeAddress().equals(sender)) {
|
if (!model.getOffer().getOfferPayload().getArbitratorSigner().equals(sender)) {
|
||||||
log.warn("Ignoring sign offer response from different sender");
|
log.warn("Ignoring sign offer response from different sender");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class MakerProcessesSignOfferResponse extends Task<PlaceOfferModel> {
|
|||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// validate arbitrator signature
|
// validate arbitrator signature
|
||||||
Mediator arbitrator = checkNotNull(model.getUser().getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorNodeAddress()), "user.getAcceptedMediatorByAddress(arbitratorNodeAddress) must not be null");
|
Mediator arbitrator = checkNotNull(model.getUser().getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedMediatorByAddress(arbitratorSigner) must not be null");
|
||||||
if (!TradeUtils.isArbitratorSignatureValid(model.getSignOfferResponse().getSignedOfferPayload(), arbitrator)) {
|
if (!TradeUtils.isArbitratorSignatureValid(model.getSignOfferResponse().getSignedOfferPayload(), arbitrator)) {
|
||||||
throw new RuntimeException("Offer payload has invalid arbitrator signature");
|
throw new RuntimeException("Offer payload has invalid arbitrator signature");
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ public class MakerReservesTradeFunds extends Task<PlaceOfferModel> {
|
|||||||
// TODO (woodser): persist
|
// TODO (woodser): persist
|
||||||
model.setReserveTx(reserveTx);
|
model.setReserveTx(reserveTx);
|
||||||
offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages);
|
offer.getOfferPayload().setReserveTxKeyImages(reservedKeyImages);
|
||||||
offer.setOfferFeePaymentTxId(reserveTx.getHash()); // TODO (woodser): rename this to reserve tx id
|
offer.setOfferFeePaymentTxId(reserveTx.getHash()); // TODO (woodser): don't use this field
|
||||||
offer.setState(Offer.State.OFFER_FEE_RESERVED);
|
offer.setState(Offer.State.OFFER_FEE_RESERVED);
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
@ -68,7 +68,7 @@ public class MakerSendsSignOfferRequest extends Task<PlaceOfferModel> {
|
|||||||
returnAddress);
|
returnAddress);
|
||||||
|
|
||||||
// get signing arbitrator
|
// get signing arbitrator
|
||||||
Mediator arbitrator = checkNotNull(model.getUser().getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorNodeAddress()), "user.getAcceptedMediatorByAddress(mediatorNodeAddress) must not be null");
|
Mediator arbitrator = checkNotNull(model.getUser().getAcceptedMediatorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedMediatorByAddress(mediatorNodeAddress) must not be null");
|
||||||
|
|
||||||
// send request
|
// send request
|
||||||
model.getP2PService().sendEncryptedDirectMessage(arbitrator.getNodeAddress(), arbitrator.getPubKeyRing(), request, new SendDirectMessageListener() {
|
model.getP2PService().sendEncryptedDirectMessage(arbitrator.getNodeAddress(), arbitrator.getPubKeyRing(), request, new SendDirectMessageListener() {
|
||||||
|
@ -433,11 +433,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||||||
request.getTakerNodeAddress(),
|
request.getTakerNodeAddress(),
|
||||||
request.getArbitratorNodeAddress());
|
request.getArbitratorNodeAddress());
|
||||||
|
|
||||||
// set reserve tx hash
|
// set reserve tx hash if available
|
||||||
Optional<SignedOffer> signedOfferOptional = openOfferManager.getSignedOfferById(request.getTradeId());
|
Optional<SignedOffer> signedOfferOptional = openOfferManager.getSignedOfferById(request.getTradeId());
|
||||||
if (!signedOfferOptional.isPresent()) return;
|
if (signedOfferOptional.isPresent()) {
|
||||||
SignedOffer signedOffer = signedOfferOptional.get();
|
SignedOffer signedOffer = signedOfferOptional.get();
|
||||||
trade.getMaker().setReserveTxHash(signedOffer.getReserveTxHash());
|
trade.getMaker().setReserveTxHash(signedOffer.getReserveTxHash());
|
||||||
|
}
|
||||||
|
|
||||||
initTradeAndProtocol(trade, getTradeProtocol(trade));
|
initTradeAndProtocol(trade, getTradeProtocol(trade));
|
||||||
tradableList.add(trade);
|
tradableList.add(trade);
|
||||||
}
|
}
|
||||||
@ -464,20 +466,15 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||||||
}
|
}
|
||||||
|
|
||||||
Offer offer = openOffer.getOffer();
|
Offer offer = openOffer.getOffer();
|
||||||
openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator?
|
|
||||||
|
|
||||||
// verify request is from signing arbitrator when they're online, else from selected arbitrator
|
// verify request is from signer or backup arbitrator
|
||||||
if (!sender.equals(offer.getOfferPayload().getArbitratorNodeAddress())) {
|
if (!sender.equals(offer.getOfferPayload().getArbitratorSigner()) && !sender.equals(openOffer.getBackupArbitrator())) { // TODO (woodser): get backup arbitrator from maker-signed InitTradeRequest and remove from OpenOffer
|
||||||
boolean isSignerOnline = true; // TODO (woodser): determine if signer is online and test
|
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from signer or backup arbitrator", sender, request.getTradeId());
|
||||||
if (isSignerOnline) {
|
return;
|
||||||
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from signing arbitrator when online", sender, request.getTradeId());
|
|
||||||
return;
|
|
||||||
} else if (!sender.equals(openOffer.getArbitratorNodeAddress())) {
|
|
||||||
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from selected arbitrator when signing arbitrator is offline", sender, request.getTradeId());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openOfferManager.reserveOpenOffer(openOffer); // TODO (woodser): reserve offer if arbitrator?
|
||||||
|
|
||||||
Trade trade;
|
Trade trade;
|
||||||
if (offer.isBuyOffer())
|
if (offer.isBuyOffer())
|
||||||
trade = new BuyerAsMakerTrade(offer,
|
trade = new BuyerAsMakerTrade(offer,
|
||||||
@ -504,12 +501,14 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||||||
|
|
||||||
//System.out.println("TradeManager trade.setTradingPeerNodeAddress(): " + sender);
|
//System.out.println("TradeManager trade.setTradingPeerNodeAddress(): " + sender);
|
||||||
//trade.setTradingPeerNodeAddress(sender);
|
//trade.setTradingPeerNodeAddress(sender);
|
||||||
// TODO (woodser): what if maker's address changes while offer open, or taker's address changes after multisig deposit available? need to verify and update
|
// TODO (woodser): what if maker's address changes while offer open, or taker's address changes after multisig deposit available? need to verify and update. see OpenOfferManager.maybeUpdatePersistedOffers()
|
||||||
trade.setArbitratorPubKeyRing(user.getAcceptedMediatorByAddress(sender).getPubKeyRing());
|
trade.setArbitratorPubKeyRing(user.getAcceptedMediatorByAddress(sender).getPubKeyRing());
|
||||||
trade.setMakerPubKeyRing(trade.getOffer().getPubKeyRing());
|
trade.setMakerPubKeyRing(trade.getOffer().getPubKeyRing());
|
||||||
initTradeAndProtocol(trade, getTradeProtocol(trade));
|
initTradeAndProtocol(trade, getTradeProtocol(trade));
|
||||||
trade.getProcessModel().setReserveTxHash(offer.getOfferFeePaymentTxId()); // TODO (woodser): initialize in initTradeAndProtocol ?
|
trade.getSelf().setReserveTxHash(openOffer.getReserveTxHash()); // TODO (woodser): initialize in initTradeAndProtocol?
|
||||||
trade.getProcessModel().setFrozenKeyImages(openOffer.getFrozenKeyImages());
|
trade.getSelf().setReserveTxHex(openOffer.getReserveTxHex());
|
||||||
|
trade.getSelf().setReserveTxKey(openOffer.getReserveTxKey());
|
||||||
|
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 -> {
|
||||||
@ -702,7 +701,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
model.getPeerNodeAddress(),
|
model.getPeerNodeAddress(),
|
||||||
P2PService.getMyNodeAddress(),
|
P2PService.getMyNodeAddress(),
|
||||||
offer.getOfferPayload().getArbitratorNodeAddress());
|
offer.getOfferPayload().getArbitratorSigner());
|
||||||
} else {
|
} else {
|
||||||
trade = new BuyerAsTakerTrade(offer,
|
trade = new BuyerAsTakerTrade(offer,
|
||||||
amount,
|
amount,
|
||||||
@ -713,12 +712,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
model.getPeerNodeAddress(),
|
model.getPeerNodeAddress(),
|
||||||
P2PService.getMyNodeAddress(),
|
P2PService.getMyNodeAddress(),
|
||||||
offer.getOfferPayload().getArbitratorNodeAddress());
|
offer.getOfferPayload().getArbitratorSigner());
|
||||||
}
|
}
|
||||||
|
|
||||||
trade.getProcessModel().setTradeMessage(model.getTradeRequest());
|
trade.getProcessModel().setTradeMessage(model.getTradeRequest());
|
||||||
trade.getProcessModel().setMakerSignature(model.getMakerSignature());
|
trade.getProcessModel().setMakerSignature(model.getMakerSignature());
|
||||||
trade.getProcessModel().setArbitratorNodeAddress(model.getArbitratorNodeAddress());
|
trade.getProcessModel().setBackupArbitrator(model.getBackupArbitrator()); // backup arbitrator only used if signer offline
|
||||||
trade.getProcessModel().setUseSavingsWallet(useSavingsWallet);
|
trade.getProcessModel().setUseSavingsWallet(useSavingsWallet);
|
||||||
trade.getProcessModel().setFundsNeededForTradeAsLong(fundsNeededForTrade.value);
|
trade.getProcessModel().setFundsNeededForTradeAsLong(fundsNeededForTrade.value);
|
||||||
trade.setTakerPubKeyRing(model.getPubKeyRing());
|
trade.setTakerPubKeyRing(model.getPubKeyRing());
|
||||||
|
@ -7,11 +7,10 @@ import bisq.core.trade.messages.InitMultisigRequest;
|
|||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeRequestToMakerIfFromTaker;
|
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeAndMultisigRequests;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessDepositRequest;
|
import bisq.core.trade.protocol.tasks.ProcessDepositRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
|
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.ArbitratorSendsInitMultisigRequestsIfFundsReserved;
|
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
|
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
|
||||||
import bisq.core.util.Validator;
|
import bisq.core.util.Validator;
|
||||||
@ -42,8 +41,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
|||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
ProcessInitTradeRequest.class,
|
ProcessInitTradeRequest.class,
|
||||||
ArbitratorProcessesReserveTx.class,
|
ArbitratorProcessesReserveTx.class,
|
||||||
ArbitratorSendsInitTradeRequestToMakerIfFromTaker.class,
|
ArbitratorSendsInitTradeAndMultisigRequests.class))
|
||||||
ArbitratorSendsInitMultisigRequestsIfFundsReserved.class))
|
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +42,7 @@ import bisq.core.trade.protocol.tasks.buyer.BuyerSendsDelayedPayoutTxSignatureRe
|
|||||||
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.MakerRemovesOpenOffer;
|
||||||
|
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.core.util.Validator;
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
|
|||||||
ProcessInitTradeRequest.class,
|
ProcessInitTradeRequest.class,
|
||||||
//ApplyFilter.class, // TODO (woodser): these checks apply when maker signs availability request, but not here
|
//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
|
//VerifyPeersAccountAgeWitness.class, // TODO (woodser): these checks apply after in multisig, means if rejected need to reimburse other's fee
|
||||||
//MakerSendsInitTradeRequestIfUnreserved.class, // TODO (woodser): implement this
|
MakerSendsInitTradeRequestIfUnreserved.class,
|
||||||
MakerRemovesOpenOffer.class).
|
MakerRemovesOpenOffer.class).
|
||||||
using(new TradeTaskRunner(trade,
|
using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -76,9 +76,9 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
|||||||
|
|
||||||
public BuyerAsTakerProtocol(BuyerAsTakerTrade trade) {
|
public BuyerAsTakerProtocol(BuyerAsTakerTrade trade) {
|
||||||
super(trade);
|
super(trade);
|
||||||
|
|
||||||
Offer offer = checkNotNull(trade.getOffer());
|
Offer offer = checkNotNull(trade.getOffer());
|
||||||
trade.getTradingPeer().setPubKeyRing(offer.getPubKeyRing());
|
trade.getTradingPeer().setPubKeyRing(offer.getPubKeyRing());
|
||||||
|
trade.setMakerPubKeyRing(offer.getPubKeyRing());
|
||||||
|
|
||||||
// TODO (woodser): setup deposit and payout listeners on construction for startup like before rebase?
|
// TODO (woodser): setup deposit and payout listeners on construction for startup like before rebase?
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
|||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
TakerReservesTradeFunds.class,
|
TakerReservesTradeFunds.class,
|
||||||
TakerSendsInitTradeRequestToArbitrator.class)
|
TakerSendsInitTradeRequestToArbitrator.class) // TODO (woodser): app hangs if this pipeline fails. use .using() like below
|
||||||
.withTimeout(30))
|
.withTimeout(30))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import bisq.core.btc.wallet.XmrWalletService;
|
|||||||
import bisq.core.filter.FilterManager;
|
import bisq.core.filter.FilterManager;
|
||||||
import bisq.core.network.MessageState;
|
import bisq.core.network.MessageState;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferPayload.Direction;
|
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||||
@ -33,9 +32,7 @@ import bisq.core.proto.CoreProtoResolver;
|
|||||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
|
||||||
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
|
||||||
import bisq.core.trade.ArbitratorTrade;
|
|
||||||
import bisq.core.trade.MakerTrade;
|
import bisq.core.trade.MakerTrade;
|
||||||
import bisq.core.trade.TakerTrade;
|
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeManager;
|
import bisq.core.trade.TradeManager;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
@ -60,7 +57,6 @@ import org.bitcoinj.core.Transaction;
|
|||||||
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -145,7 +141,7 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||||||
// After successful verified we copy that over to the trade.tradingPeerAddress
|
// After successful verified we copy that over to the trade.tradingPeerAddress
|
||||||
@Nullable
|
@Nullable
|
||||||
@Setter
|
@Setter
|
||||||
private NodeAddress tempTradingPeerNodeAddress; // TODO (woodser): remove entirely
|
private NodeAddress tempTradingPeerNodeAddress; // TODO (woodser): remove entirely?
|
||||||
|
|
||||||
// Added in v.1.1.6
|
// Added in v.1.1.6
|
||||||
@Nullable
|
@Nullable
|
||||||
@ -166,17 +162,11 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||||||
private String makerSignature;
|
private String makerSignature;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private NodeAddress arbitratorNodeAddress;
|
private NodeAddress backupArbitrator;
|
||||||
@Nullable
|
@Nullable
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
transient private MoneroTxWallet reserveTx;
|
transient private MoneroTxWallet reserveTx;
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
private String reserveTxHash;
|
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
private List<String> frozenKeyImages = new ArrayList<>();
|
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
transient private MoneroTxWallet depositTxXmr;
|
transient private MoneroTxWallet depositTxXmr;
|
||||||
@ -247,20 +237,18 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||||||
.setFundsNeededForTradeAsLong(fundsNeededForTradeAsLong)
|
.setFundsNeededForTradeAsLong(fundsNeededForTradeAsLong)
|
||||||
.setPaymentStartedMessageState(paymentStartedMessageStateProperty.get().name())
|
.setPaymentStartedMessageState(paymentStartedMessageStateProperty.get().name())
|
||||||
.setBuyerPayoutAmountFromMediation(buyerPayoutAmountFromMediation)
|
.setBuyerPayoutAmountFromMediation(buyerPayoutAmountFromMediation)
|
||||||
.setSellerPayoutAmountFromMediation(sellerPayoutAmountFromMediation)
|
.setSellerPayoutAmountFromMediation(sellerPayoutAmountFromMediation);
|
||||||
.addAllFrozenKeyImages(frozenKeyImages);
|
|
||||||
Optional.ofNullable(maker).ifPresent(e -> builder.setMaker((protobuf.TradingPeer) maker.toProtoMessage()));
|
Optional.ofNullable(maker).ifPresent(e -> builder.setMaker((protobuf.TradingPeer) maker.toProtoMessage()));
|
||||||
Optional.ofNullable(taker).ifPresent(e -> builder.setTaker((protobuf.TradingPeer) taker.toProtoMessage()));
|
Optional.ofNullable(taker).ifPresent(e -> builder.setTaker((protobuf.TradingPeer) taker.toProtoMessage()));
|
||||||
Optional.ofNullable(arbitrator).ifPresent(e -> builder.setArbitrator((protobuf.TradingPeer) arbitrator.toProtoMessage()));
|
Optional.ofNullable(arbitrator).ifPresent(e -> builder.setArbitrator((protobuf.TradingPeer) arbitrator.toProtoMessage()));
|
||||||
Optional.ofNullable(takeOfferFeeTxId).ifPresent(builder::setTakeOfferFeeTxId);
|
Optional.ofNullable(takeOfferFeeTxId).ifPresent(builder::setTakeOfferFeeTxId);
|
||||||
Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash));
|
|
||||||
Optional.ofNullable(payoutTxSignature).ifPresent(e -> builder.setPayoutTxSignature(ByteString.copyFrom(payoutTxSignature)));
|
Optional.ofNullable(payoutTxSignature).ifPresent(e -> builder.setPayoutTxSignature(ByteString.copyFrom(payoutTxSignature)));
|
||||||
Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(ProtoUtil.collectionToProto(rawTransactionInputs, protobuf.RawTransactionInput.class)));
|
Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(ProtoUtil.collectionToProto(rawTransactionInputs, protobuf.RawTransactionInput.class)));
|
||||||
Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress);
|
Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress);
|
||||||
Optional.ofNullable(myMultiSigPubKey).ifPresent(e -> builder.setMyMultiSigPubKey(ByteString.copyFrom(myMultiSigPubKey)));
|
Optional.ofNullable(myMultiSigPubKey).ifPresent(e -> builder.setMyMultiSigPubKey(ByteString.copyFrom(myMultiSigPubKey)));
|
||||||
Optional.ofNullable(tempTradingPeerNodeAddress).ifPresent(e -> builder.setTempTradingPeerNodeAddress(tempTradingPeerNodeAddress.toProtoMessage()));
|
Optional.ofNullable(tempTradingPeerNodeAddress).ifPresent(e -> builder.setTempTradingPeerNodeAddress(tempTradingPeerNodeAddress.toProtoMessage()));
|
||||||
Optional.ofNullable(makerSignature).ifPresent(e -> builder.setMakerSignature(makerSignature));
|
Optional.ofNullable(makerSignature).ifPresent(e -> builder.setMakerSignature(makerSignature));
|
||||||
Optional.ofNullable(arbitratorNodeAddress).ifPresent(e -> builder.setArbitratorNodeAddress(arbitratorNodeAddress.toProtoMessage()));
|
Optional.ofNullable(backupArbitrator).ifPresent(e -> builder.setBackupArbitrator(backupArbitrator.toProtoMessage()));
|
||||||
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(multisigSetupComplete).ifPresent(e -> builder.setMultisigSetupComplete(multisigSetupComplete));
|
Optional.ofNullable(multisigSetupComplete).ifPresent(e -> builder.setMultisigSetupComplete(multisigSetupComplete));
|
||||||
@ -282,8 +270,6 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||||||
processModel.setSellerPayoutAmountFromMediation(proto.getSellerPayoutAmountFromMediation());
|
processModel.setSellerPayoutAmountFromMediation(proto.getSellerPayoutAmountFromMediation());
|
||||||
|
|
||||||
// nullable
|
// nullable
|
||||||
processModel.setReserveTxHash(proto.getReserveTxHash());
|
|
||||||
processModel.setFrozenKeyImages(proto.getFrozenKeyImagesList());
|
|
||||||
processModel.setTakeOfferFeeTxId(ProtoUtil.stringOrNullFromProto(proto.getTakeOfferFeeTxId()));
|
processModel.setTakeOfferFeeTxId(ProtoUtil.stringOrNullFromProto(proto.getTakeOfferFeeTxId()));
|
||||||
processModel.setPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getPayoutTxSignature()));
|
processModel.setPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getPayoutTxSignature()));
|
||||||
List<RawTransactionInput> rawTransactionInputs = proto.getRawTransactionInputsList().isEmpty() ?
|
List<RawTransactionInput> rawTransactionInputs = proto.getRawTransactionInputsList().isEmpty() ?
|
||||||
@ -295,7 +281,7 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||||||
processModel.setTempTradingPeerNodeAddress(proto.hasTempTradingPeerNodeAddress() ? NodeAddress.fromProto(proto.getTempTradingPeerNodeAddress()) : null);
|
processModel.setTempTradingPeerNodeAddress(proto.hasTempTradingPeerNodeAddress() ? NodeAddress.fromProto(proto.getTempTradingPeerNodeAddress()) : null);
|
||||||
processModel.setMediatedPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getMediatedPayoutTxSignature()));
|
processModel.setMediatedPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getMediatedPayoutTxSignature()));
|
||||||
processModel.setMakerSignature(proto.getMakerSignature());
|
processModel.setMakerSignature(proto.getMakerSignature());
|
||||||
processModel.setArbitratorNodeAddress(proto.hasArbitratorNodeAddress() ? NodeAddress.fromProto(proto.getArbitratorNodeAddress()) : null);
|
processModel.setBackupArbitrator(proto.hasBackupArbitrator() ? NodeAddress.fromProto(proto.getBackupArbitrator()) : null);
|
||||||
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.setMultisigSetupComplete(proto.getMultisigSetupComplete());
|
processModel.setMultisigSetupComplete(proto.getMultisigSetupComplete());
|
||||||
|
@ -38,6 +38,7 @@ import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
|
|||||||
import bisq.core.trade.protocol.tasks.SendSignContractRequestAfterMultisig;
|
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.MakerRemovesOpenOffer;
|
||||||
|
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;
|
||||||
import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureRequest;
|
import bisq.core.trade.protocol.tasks.seller.SellerSendDelayedPayoutTxSignatureRequest;
|
||||||
@ -144,7 +145,7 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
|||||||
ProcessInitTradeRequest.class,
|
ProcessInitTradeRequest.class,
|
||||||
//ApplyFilter.class, // TODO (woodser): these checks apply when maker signs availability request, but not here
|
//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
|
//VerifyPeersAccountAgeWitness.class, // TODO (woodser): these checks apply after in multisig, means if rejected need to reimburse other's fee
|
||||||
//MakerSendsInitTradeRequestIfUnreserved.class, // TODO (woodser): implement this
|
MakerSendsInitTradeRequestIfUnreserved.class,
|
||||||
MakerRemovesOpenOffer.class).
|
MakerRemovesOpenOffer.class).
|
||||||
using(new TradeTaskRunner(trade,
|
using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
|
@ -73,6 +73,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
|||||||
super(trade);
|
super(trade);
|
||||||
Offer offer = checkNotNull(trade.getOffer());
|
Offer offer = checkNotNull(trade.getOffer());
|
||||||
trade.getTradingPeer().setPubKeyRing(offer.getPubKeyRing());
|
trade.getTradingPeer().setPubKeyRing(offer.getPubKeyRing());
|
||||||
|
trade.setMakerPubKeyRing(offer.getPubKeyRing());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import bisq.common.proto.persistable.PersistablePayload;
|
|||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -102,6 +102,8 @@ public final class TradingPeer implements PersistablePayload {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private String reserveTxKey;
|
private String reserveTxKey;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
private List<String> reserveTxKeyImages = new ArrayList<>();
|
||||||
|
@Nullable
|
||||||
private String preparedMultisigHex;
|
private String preparedMultisigHex;
|
||||||
@Nullable
|
@Nullable
|
||||||
private String madeMultisigHex;
|
private String madeMultisigHex;
|
||||||
@ -120,7 +122,8 @@ public final class TradingPeer implements PersistablePayload {
|
|||||||
@Override
|
@Override
|
||||||
public Message toProtoMessage() {
|
public Message toProtoMessage() {
|
||||||
final protobuf.TradingPeer.Builder builder = protobuf.TradingPeer.newBuilder()
|
final protobuf.TradingPeer.Builder builder = protobuf.TradingPeer.newBuilder()
|
||||||
.setChangeOutputValue(changeOutputValue);
|
.setChangeOutputValue(changeOutputValue)
|
||||||
|
.addAllReserveTxKeyImages(reserveTxKeyImages);
|
||||||
Optional.ofNullable(accountId).ifPresent(builder::setAccountId);
|
Optional.ofNullable(accountId).ifPresent(builder::setAccountId);
|
||||||
Optional.ofNullable(paymentAccountId).ifPresent(builder::setPaymentAccountId);
|
Optional.ofNullable(paymentAccountId).ifPresent(builder::setPaymentAccountId);
|
||||||
Optional.ofNullable(paymentMethodId).ifPresent(builder::setPaymentMethodId);
|
Optional.ofNullable(paymentMethodId).ifPresent(builder::setPaymentMethodId);
|
||||||
@ -183,6 +186,7 @@ public final class TradingPeer implements PersistablePayload {
|
|||||||
tradingPeer.setReserveTxHash(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()));
|
tradingPeer.setReserveTxHash(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()));
|
||||||
tradingPeer.setReserveTxHex(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()));
|
tradingPeer.setReserveTxHex(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()));
|
||||||
tradingPeer.setReserveTxKey(ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()));
|
tradingPeer.setReserveTxKey(ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()));
|
||||||
|
tradingPeer.setReserveTxKeyImages(proto.getReserveTxKeyImagesList());
|
||||||
tradingPeer.setPreparedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getPreparedMultisigHex()));
|
tradingPeer.setPreparedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getPreparedMultisigHex()));
|
||||||
tradingPeer.setMadeMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getMadeMultisigHex()));
|
tradingPeer.setMadeMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getMadeMultisigHex()));
|
||||||
tradingPeer.setSignedPayoutTxHex(ProtoUtil.stringOrNullFromProto(proto.getSignedPayoutTxHex()));
|
tradingPeer.setSignedPayoutTxHex(ProtoUtil.stringOrNullFromProto(proto.getSignedPayoutTxHex()));
|
||||||
|
@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bisq.
|
|
||||||
*
|
|
||||||
* Bisq is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.trade.protocol.tasks;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
|
||||||
import bisq.common.crypto.Sig;
|
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
|
||||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
|
||||||
import bisq.core.trade.Trade;
|
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.UUID;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import monero.wallet.MoneroWallet;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arbitrator sends InitMultisigRequest to maker and taker if both reserve txs received.
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class ArbitratorSendsInitMultisigRequestsIfFundsReserved extends TradeTask {
|
|
||||||
|
|
||||||
private boolean takerAck;
|
|
||||||
private boolean makerAck;
|
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
|
||||||
public ArbitratorSendsInitMultisigRequestsIfFundsReserved(TaskRunner taskHandler, Trade trade) {
|
|
||||||
super(taskHandler, trade);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void run() {
|
|
||||||
try {
|
|
||||||
runInterceptHook();
|
|
||||||
|
|
||||||
// skip if arbitrator does not have maker reserve tx
|
|
||||||
if (processModel.getMaker().getReserveTxHash() == null) {
|
|
||||||
log.info("Arbitrator does not have maker reserve tx for offerId {}, waiting to receive before initializing multisig wallet", processModel.getOffer().getId());
|
|
||||||
complete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create wallet for multisig
|
|
||||||
MoneroWallet multisigWallet = processModel.getXmrWalletService().createMultisigWallet(trade.getId());
|
|
||||||
|
|
||||||
// prepare multisig
|
|
||||||
String preparedHex = multisigWallet.prepareMultisig();
|
|
||||||
processModel.setPreparedMultisigHex(preparedHex);
|
|
||||||
|
|
||||||
// create message to initialize multisig
|
|
||||||
InitMultisigRequest request = new InitMultisigRequest(
|
|
||||||
processModel.getOffer().getId(),
|
|
||||||
processModel.getMyNodeAddress(),
|
|
||||||
processModel.getPubKeyRing(),
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
Version.getP2PMessageVersion(),
|
|
||||||
new Date().getTime(),
|
|
||||||
preparedHex,
|
|
||||||
null);
|
|
||||||
|
|
||||||
// send request to maker
|
|
||||||
log.info("Send {} with offerId {} and uid {} to maker {}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid(), trade.getMakerNodeAddress());
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(
|
|
||||||
trade.getMakerNodeAddress(),
|
|
||||||
trade.getMakerPubKeyRing(),
|
|
||||||
request,
|
|
||||||
new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived at arbitrator: offerId={}; uid={}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid());
|
|
||||||
makerAck = true;
|
|
||||||
checkComplete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), request.getUid(), trade.getMakerNodeAddress(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// send request to taker
|
|
||||||
log.info("Send {} with offerId {} and uid {} to taker {}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid(), trade.getTakerNodeAddress());
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(
|
|
||||||
trade.getTakerNodeAddress(),
|
|
||||||
trade.getTakerPubKeyRing(),
|
|
||||||
request,
|
|
||||||
new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived at peer: offerId={}; uid={}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid());
|
|
||||||
takerAck = true;
|
|
||||||
checkComplete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), request.getUid(), trade.getTakerNodeAddress(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
failed(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkComplete() {
|
|
||||||
if (makerAck && takerAck) complete();
|
|
||||||
}
|
|
||||||
}
|
|
@ -22,24 +22,33 @@ import bisq.common.app.Version;
|
|||||||
import bisq.common.crypto.Sig;
|
import bisq.common.crypto.Sig;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.protocol.TradeListener;
|
import bisq.core.trade.protocol.TradeListener;
|
||||||
import bisq.network.p2p.AckMessage;
|
import bisq.network.p2p.AckMessage;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
import bisq.network.p2p.P2PService;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import monero.wallet.MoneroWallet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Arbitrator sends InitTradeRequest to maker after receiving InitTradeRequest
|
* Arbitrator sends InitTradeRequest to maker after receiving InitTradeRequest
|
||||||
* from taker and verifying taker reserve tx.
|
* from taker and verifying taker reserve tx.
|
||||||
|
*
|
||||||
|
* Arbitrator sends InitMultisigRequests after the maker acks.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ArbitratorSendsInitTradeRequestToMakerIfFromTaker extends TradeTask {
|
public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
|
|
||||||
|
private boolean takerAck;
|
||||||
|
private boolean makerAck;
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public ArbitratorSendsInitTradeRequestToMakerIfFromTaker(TaskRunner taskHandler, Trade trade) {
|
public ArbitratorSendsInitTradeAndMultisigRequests(TaskRunner taskHandler, Trade trade) {
|
||||||
super(taskHandler, trade);
|
super(taskHandler, trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,12 +57,15 @@ public class ArbitratorSendsInitTradeRequestToMakerIfFromTaker extends TradeTask
|
|||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// collect fields for request
|
// skip if request not from taker
|
||||||
String offerId = processModel.getOffer().getId();
|
|
||||||
InitTradeRequest request = (InitTradeRequest) processModel.getTradeMessage();
|
InitTradeRequest request = (InitTradeRequest) processModel.getTradeMessage();
|
||||||
|
if (!request.getSenderNodeAddress().equals(trade.getTakerNodeAddress())) {
|
||||||
|
complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// arbitrator signs offer id as nonce to avoid challenge protocol
|
// arbitrator signs offer id as nonce to avoid challenge protocol
|
||||||
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), offerId.getBytes(Charsets.UTF_8));
|
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), processModel.getOfferId().getBytes(Charsets.UTF_8));
|
||||||
|
|
||||||
// save pub keys
|
// save pub keys
|
||||||
processModel.getArbitrator().setPubKeyRing(processModel.getPubKeyRing()); // TODO (woodser): why duplicating field in process model
|
processModel.getArbitrator().setPubKeyRing(processModel.getPubKeyRing()); // TODO (woodser): why duplicating field in process model
|
||||||
@ -63,7 +75,7 @@ public class ArbitratorSendsInitTradeRequestToMakerIfFromTaker extends TradeTask
|
|||||||
|
|
||||||
// create request to initialize trade with maker
|
// create request to initialize trade with maker
|
||||||
InitTradeRequest makerRequest = new InitTradeRequest(
|
InitTradeRequest makerRequest = new InitTradeRequest(
|
||||||
offerId,
|
processModel.getOfferId(),
|
||||||
request.getSenderNodeAddress(),
|
request.getSenderNodeAddress(),
|
||||||
request.getPubKeyRing(),
|
request.getPubKeyRing(),
|
||||||
trade.getTradeAmount().value,
|
trade.getTradeAmount().value,
|
||||||
@ -91,7 +103,7 @@ public class ArbitratorSendsInitTradeRequestToMakerIfFromTaker extends TradeTask
|
|||||||
public void onAckMessage(AckMessage ackMessage, NodeAddress sender) {
|
public void onAckMessage(AckMessage ackMessage, NodeAddress sender) {
|
||||||
if (sender.equals(trade.getMakerNodeAddress()) && ackMessage.getSourceMsgClassName().equals(InitTradeRequest.class.getSimpleName())) {
|
if (sender.equals(trade.getMakerNodeAddress()) && ackMessage.getSourceMsgClassName().equals(InitTradeRequest.class.getSimpleName())) {
|
||||||
trade.removeListener(this);
|
trade.removeListener(this);
|
||||||
if (ackMessage.isSuccess()) complete();
|
if (ackMessage.isSuccess()) sendInitMultisigRequests();
|
||||||
else failed("Received unsuccessful ack for InitTradeRequest from maker"); // TODO (woodser): maker should not do this, penalize them by broadcasting reserve tx?
|
else failed("Received unsuccessful ack for InitTradeRequest from maker"); // TODO (woodser): maker should not do this, penalize them by broadcasting reserve tx?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,4 +134,80 @@ public class ArbitratorSendsInitTradeRequestToMakerIfFromTaker extends TradeTask
|
|||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendInitMultisigRequests() {
|
||||||
|
|
||||||
|
// ensure arbitrator has maker's reserve tx
|
||||||
|
if (processModel.getMaker().getReserveTxHash() == null) {
|
||||||
|
log.warn("Arbitrator {} does not have maker's reserve tx after initializing trade", P2PService.getMyNodeAddress());
|
||||||
|
failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create wallet for multisig
|
||||||
|
MoneroWallet multisigWallet = processModel.getXmrWalletService().createMultisigWallet(trade.getId());
|
||||||
|
|
||||||
|
// prepare multisig
|
||||||
|
String preparedHex = multisigWallet.prepareMultisig();
|
||||||
|
processModel.setPreparedMultisigHex(preparedHex);
|
||||||
|
|
||||||
|
// create message to initialize multisig
|
||||||
|
InitMultisigRequest initMultisigRequest = new InitMultisigRequest(
|
||||||
|
processModel.getOffer().getId(),
|
||||||
|
processModel.getMyNodeAddress(),
|
||||||
|
processModel.getPubKeyRing(),
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
Version.getP2PMessageVersion(),
|
||||||
|
new Date().getTime(),
|
||||||
|
preparedHex,
|
||||||
|
null);
|
||||||
|
|
||||||
|
// send request to maker
|
||||||
|
log.info("Send {} with offerId {} and uid {} to maker {}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid(), trade.getMakerNodeAddress());
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(
|
||||||
|
trade.getMakerNodeAddress(),
|
||||||
|
trade.getMakerPubKeyRing(),
|
||||||
|
initMultisigRequest,
|
||||||
|
new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived at arbitrator: offerId={}; uid={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid());
|
||||||
|
makerAck = true;
|
||||||
|
checkComplete();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("Sending {} failed: uid={}; peer={}; error={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getUid(), trade.getMakerNodeAddress(), errorMessage);
|
||||||
|
appendToErrorMessage("Sending message failed: message=" + initMultisigRequest + "\nerrorMessage=" + errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// send request to taker
|
||||||
|
log.info("Send {} with offerId {} and uid {} to taker {}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid(), trade.getTakerNodeAddress());
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(
|
||||||
|
trade.getTakerNodeAddress(),
|
||||||
|
trade.getTakerPubKeyRing(),
|
||||||
|
initMultisigRequest,
|
||||||
|
new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived at peer: offerId={}; uid={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getTradeId(), initMultisigRequest.getUid());
|
||||||
|
takerAck = true;
|
||||||
|
checkComplete();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("Sending {} failed: uid={}; peer={}; error={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getUid(), trade.getTakerNodeAddress(), errorMessage);
|
||||||
|
appendToErrorMessage("Sending message failed: message=" + initMultisigRequest + "\nerrorMessage=" + errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkComplete() {
|
||||||
|
if (makerAck && takerAck) complete();
|
||||||
|
}
|
||||||
}
|
}
|
@ -23,7 +23,6 @@ import bisq.core.btc.model.XmrAddressEntry;
|
|||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.trade.MakerTrade;
|
import bisq.core.trade.MakerTrade;
|
||||||
import bisq.core.trade.SellerTrade;
|
import bisq.core.trade.SellerTrade;
|
||||||
import bisq.core.trade.TakerTrade;
|
|
||||||
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;
|
||||||
@ -62,8 +61,8 @@ public class SendSignContractRequestAfterMultisig extends TradeTask {
|
|||||||
|
|
||||||
// thaw reserved outputs
|
// thaw reserved outputs
|
||||||
MoneroWallet wallet = trade.getXmrWalletService().getWallet();
|
MoneroWallet wallet = trade.getXmrWalletService().getWallet();
|
||||||
for (String frozenKeyImage : processModel.getFrozenKeyImages()) {
|
for (String reserveTxKeyImage : trade.getSelf().getReserveTxKeyImages()) {
|
||||||
wallet.thawOutput(frozenKeyImage);
|
wallet.thawOutput(reserveTxKeyImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// create deposit tx
|
// create deposit tx
|
||||||
|
@ -101,8 +101,7 @@ public class UpdateMultisigWithTradingPeer extends TradeTask {
|
|||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
updatedMultisigHex);
|
updatedMultisigHex);
|
||||||
|
|
||||||
System.out.println("SENDING MESSAGE!!!!!!!");
|
System.out.println("Sending message: " + message);
|
||||||
System.out.println(message);
|
|
||||||
|
|
||||||
// TODO (woodser): trade.getTradingPeerNodeAddress() and/or trade.getTradingPeerPubKeyRing() are null on restart of application, so cannot send payment to complete trade
|
// TODO (woodser): trade.getTradingPeerNodeAddress() and/or trade.getTradingPeerPubKeyRing() are null on restart of application, so cannot send payment to complete trade
|
||||||
log.info("Send {} with offerId {} and uid {} to peer {}", message.getClass().getSimpleName(), message.getTradeId(), message.getUid(), trade.getTradingPeerNodeAddress());
|
log.info("Send {} with offerId {} and uid {} to peer {}", message.getClass().getSimpleName(), message.getTradeId(), message.getUid(), trade.getTradingPeerNodeAddress());
|
||||||
|
@ -18,14 +18,11 @@
|
|||||||
package bisq.core.trade.protocol.tasks.maker;
|
package bisq.core.trade.protocol.tasks.maker;
|
||||||
|
|
||||||
import bisq.core.btc.model.XmrAddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||||
import bisq.core.user.User;
|
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
@ -34,8 +31,6 @@ import bisq.common.taskrunner.TaskRunner;
|
|||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -55,88 +50,63 @@ public class MakerSendsInitTradeRequestIfUnreserved extends TradeTask {
|
|||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
System.out.println("MAKER SENDING INIT TRADE REQ TO ARBITRATOR");
|
// skip if arbitrator is signer and therefore already has reserve tx
|
||||||
|
Offer offer = processModel.getOffer();
|
||||||
|
if (offer.getOfferPayload().getArbitratorSigner().equals(trade.getArbitratorNodeAddress())) {
|
||||||
|
complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// verify trade
|
// verify trade
|
||||||
InitTradeRequest request = (InitTradeRequest) processModel.getTradeMessage();
|
InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // arbitrator's InitTradeRequest to maker
|
||||||
checkNotNull(request);
|
checkNotNull(makerRequest);
|
||||||
checkTradeId(processModel.getOfferId(), request);
|
checkTradeId(processModel.getOfferId(), makerRequest);
|
||||||
|
|
||||||
// collect fields to send taker prepared multisig response // TODO (woodser): this should happen on response from arbitrator
|
|
||||||
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
|
||||||
String offerId = processModel.getOffer().getId();
|
|
||||||
String payoutAddress = walletService.getWallet().createSubaddress(0).getAddress(); // TODO (woodser): register TRADE_PAYOUT?
|
|
||||||
walletService.getWallet().save();
|
|
||||||
checkNotNull(trade.getTradeAmount(), "TradeAmount must not be null");
|
|
||||||
// checkNotNull(trade.getTakerFeeTxId(), "TakeOfferFeeTxId must not be null"); // TODO (woodser): no taker fee tx yet if creating multisig first
|
|
||||||
final User user = processModel.getUser();
|
|
||||||
checkNotNull(user, "User must not be null");
|
|
||||||
final List<NodeAddress> acceptedMediatorAddresses = user.getAcceptedMediatorAddresses();
|
|
||||||
checkNotNull(acceptedMediatorAddresses, "acceptedMediatorAddresses must not be null");
|
|
||||||
|
|
||||||
// maker signs offer id as nonce to avoid challenge protocol
|
// maker signs offer id as nonce to avoid challenge protocol // TODO (woodser): is this necessary?
|
||||||
final PaymentAccountPayload paymentAccountPayload = checkNotNull(processModel.getPaymentAccountPayload(trade), "processModel.getPaymentAccountPayload(trade) must not be null");
|
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), offer.getId().getBytes(Charsets.UTF_8));
|
||||||
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), offerId.getBytes(Charsets.UTF_8));
|
|
||||||
|
|
||||||
System.out.println("MAKER SENDING ARBITRTATOR SENDER NODE ADDRESS");
|
|
||||||
System.out.println(processModel.getMyNodeAddress());
|
|
||||||
|
|
||||||
if (true) throw new RuntimeException("Not yet implemented");
|
// create request to arbitrator
|
||||||
|
InitTradeRequest arbitratorRequest = new InitTradeRequest(
|
||||||
// create message to initialize trade
|
offer.getId(),
|
||||||
InitTradeRequest message = new InitTradeRequest(
|
|
||||||
offerId,
|
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
processModel.getPubKeyRing(),
|
processModel.getPubKeyRing(),
|
||||||
trade.getTradeAmount().value,
|
offer.getAmount().value,
|
||||||
trade.getTradePrice().getValue(),
|
offer.getPrice().getValue(),
|
||||||
trade.getTakerFee().getValue(),
|
offer.getMakerFee().value,
|
||||||
processModel.getAccountId(),
|
trade.getProcessModel().getAccountId(),
|
||||||
paymentAccountPayload.getId(),
|
offer.getMakerPaymentAccountId(),
|
||||||
paymentAccountPayload.getPaymentMethodId(),
|
offer.getOfferPayload().getPaymentMethodId(),
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
sig,
|
sig,
|
||||||
new Date().getTime(),
|
makerRequest.getCurrentDate(),
|
||||||
trade.getMakerNodeAddress(),
|
trade.getMakerNodeAddress(),
|
||||||
trade.getTakerNodeAddress(),
|
trade.getTakerNodeAddress(),
|
||||||
trade.getArbitratorNodeAddress(),
|
trade.getArbitratorNodeAddress(),
|
||||||
processModel.getReserveTx().getHash(), // TODO (woodser): need to first create and save reserve tx
|
trade.getSelf().getReserveTxHash(),
|
||||||
processModel.getReserveTx().getFullHex(),
|
trade.getSelf().getReserveTxHex(),
|
||||||
processModel.getReserveTx().getKey(),
|
trade.getSelf().getReserveTxKey(),
|
||||||
processModel.getXmrWalletService().getAddressEntry(offerId, XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString(),
|
model.getXmrWalletService().getOrCreateAddressEntry(offer.getId(), XmrAddressEntry.Context.TRADE_PAYOUT).getAddressString(),
|
||||||
null);
|
null);
|
||||||
|
|
||||||
log.info("Send {} with offerId {} and uid {} to peer {}",
|
|
||||||
message.getClass().getSimpleName(), message.getTradeId(),
|
|
||||||
message.getUid(), trade.getArbitratorNodeAddress());
|
|
||||||
|
|
||||||
|
|
||||||
System.out.println("MAKER TRADE INFO");
|
|
||||||
System.out.println("Trading peer node address: " + trade.getTradingPeerNodeAddress());
|
|
||||||
System.out.println("Maker node address: " + trade.getMakerNodeAddress());
|
|
||||||
System.out.println("Taker node adddress: " + trade.getTakerNodeAddress());
|
|
||||||
System.out.println("Arbitrator node address: " + trade.getArbitratorNodeAddress());
|
|
||||||
|
|
||||||
// send request to arbitrator
|
// send request to arbitrator
|
||||||
|
log.info("Sending {} with offerId {} and uid {} to arbitrator {} with pub key ring {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitratorNodeAddress(), trade.getArbitratorPubKeyRing());
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(
|
processModel.getP2PService().sendEncryptedDirectMessage(
|
||||||
trade.getArbitratorNodeAddress(),
|
trade.getArbitratorNodeAddress(),
|
||||||
trade.getArbitratorPubKeyRing(),
|
trade.getArbitratorPubKeyRing(),
|
||||||
message,
|
arbitratorRequest,
|
||||||
new SendDirectMessageListener() {
|
new SendDirectMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onArrived() {
|
public void onArrived() {
|
||||||
log.info("{} arrived at arbitrator: offerId={}; uid={}", message.getClass().getSimpleName(), message.getTradeId(), message.getUid());
|
log.info("{} arrived at arbitrator: offerId={}", InitTradeRequest.class.getSimpleName(), trade.getId());
|
||||||
complete();
|
complete();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onFault(String errorMessage) {
|
public void onFault(String errorMessage) {
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", message.getClass().getSimpleName(), message.getUid(), trade.getTradingPeerNodeAddress(), errorMessage);
|
log.warn("Failed to send {} to arbitrator, error={}.", InitTradeRequest.class.getSimpleName(), errorMessage);
|
||||||
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
failed();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
|
@ -49,19 +49,18 @@ public class TakerReservesTradeFunds extends TradeTask {
|
|||||||
|
|
||||||
// freeze trade funds
|
// freeze trade funds
|
||||||
// TODO (woodser): synchronize to handle potential race condition where concurrent trades freeze each other's outputs
|
// TODO (woodser): synchronize to handle potential race condition where concurrent trades freeze each other's outputs
|
||||||
List<String> frozenKeyImages = new ArrayList<String>();
|
List<String> reserveTxKeyImages = new ArrayList<String>();
|
||||||
MoneroWallet wallet = model.getXmrWalletService().getWallet();
|
MoneroWallet wallet = model.getXmrWalletService().getWallet();
|
||||||
for (MoneroOutput input : reserveTx.getInputs()) {
|
for (MoneroOutput input : reserveTx.getInputs()) {
|
||||||
frozenKeyImages.add(input.getKeyImage().getHex());
|
reserveTxKeyImages.add(input.getKeyImage().getHex());
|
||||||
wallet.freezeOutput(input.getKeyImage().getHex());
|
wallet.freezeOutput(input.getKeyImage().getHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
// save process state
|
// save process state
|
||||||
// TODO (woodser): persist
|
// TODO (woodser): persist
|
||||||
processModel.setReserveTx(reserveTx);
|
processModel.setReserveTx(reserveTx);
|
||||||
processModel.setReserveTxHash(reserveTx.getHash());
|
processModel.getTaker().setReserveTxKeyImages(reserveTxKeyImages);
|
||||||
processModel.setFrozenKeyImages(frozenKeyImages);
|
trade.setTakerFeeTxId(reserveTx.getHash()); // TODO (woodser): this should be multisig deposit tx id? how is it used?
|
||||||
trade.setTakerFeeTxId(reserveTx.getHash());
|
|
||||||
//trade.setState(Trade.State.TAKER_PUBLISHED_TAKER_FEE_TX); // TODO (woodser): fee tx is not broadcast separate, update states
|
//trade.setState(Trade.State.TAKER_PUBLISHED_TAKER_FEE_TX); // TODO (woodser): fee tx is not broadcast separate, update states
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
@ -21,7 +21,7 @@ import bisq.core.support.dispute.mediation.mediator.Mediator;
|
|||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||||
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
@ -42,62 +42,85 @@ public class TakerSendsInitTradeRequestToArbitrator extends TradeTask {
|
|||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// get primary arbitrator
|
// send request to offer signer
|
||||||
Mediator arbitrator = processModel.getUser().getAcceptedMediatorByAddress(trade.getArbitratorNodeAddress());
|
sendInitTradeRequest(trade.getOffer().getOfferPayload().getArbitratorSigner(), new SendDirectMessageListener() {
|
||||||
if (arbitrator == null) throw new RuntimeException("Cannot get arbitrator instance from node address"); // TODO (woodser): null if arbitrator goes offline or never seen?
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
// save pub keys
|
log.info("{} arrived at arbitrator: offerId={}", InitTradeRequest.class.getSimpleName(), trade.getId());
|
||||||
processModel.getArbitrator().setPubKeyRing(arbitrator.getPubKeyRing());
|
complete();
|
||||||
trade.setArbitratorPubKeyRing(processModel.getArbitrator().getPubKeyRing());
|
}
|
||||||
trade.setMakerPubKeyRing(trade.getTradingPeer().getPubKeyRing());
|
|
||||||
|
// send request to backup arbitrator if signer unavailable
|
||||||
// send trade request to arbitrator
|
@Override
|
||||||
InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage();
|
public void onFault(String errorMessage) {
|
||||||
InitTradeRequest arbitratorRequest = new InitTradeRequest(
|
log.info("Sending {} to signing arbitrator {} failed, using backup arbitrator {}. error={}", InitTradeRequest.class.getSimpleName(), trade.getOffer().getOfferPayload().getArbitratorSigner(), processModel.getBackupArbitrator(), errorMessage);
|
||||||
makerRequest.getTradeId(),
|
if (processModel.getBackupArbitrator() == null) {
|
||||||
makerRequest.getSenderNodeAddress(),
|
log.warn("Cannot take offer because signing arbitrator is offline and backup arbitrator is null");
|
||||||
makerRequest.getPubKeyRing(),
|
failed();
|
||||||
makerRequest.getTradeAmount(),
|
return;
|
||||||
makerRequest.getTradePrice(),
|
}
|
||||||
makerRequest.getTradeFee(),
|
sendInitTradeRequest(processModel.getBackupArbitrator(), new SendDirectMessageListener() {
|
||||||
makerRequest.getAccountId(),
|
|
||||||
makerRequest.getPaymentAccountId(),
|
|
||||||
makerRequest.getPaymentMethodId(),
|
|
||||||
makerRequest.getUid(),
|
|
||||||
Version.getP2PMessageVersion(),
|
|
||||||
makerRequest.getAccountAgeWitnessSignatureOfOfferId(),
|
|
||||||
makerRequest.getCurrentDate(),
|
|
||||||
makerRequest.getMakerNodeAddress(),
|
|
||||||
makerRequest.getTakerNodeAddress(),
|
|
||||||
trade.getArbitratorNodeAddress(),
|
|
||||||
processModel.getReserveTx().getHash(),
|
|
||||||
processModel.getReserveTx().getFullHex(),
|
|
||||||
processModel.getReserveTx().getKey(),
|
|
||||||
makerRequest.getPayoutAddress(),
|
|
||||||
processModel.getMakerSignature());
|
|
||||||
|
|
||||||
// send request to arbitrator
|
|
||||||
System.out.println("SENDING INIT TRADE REQUEST TO ARBITRATOR!");
|
|
||||||
log.info("Send {} with offerId {} and uid {} to arbitrator {} with pub key ring", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitratorNodeAddress(), trade.getArbitratorPubKeyRing());
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(
|
|
||||||
trade.getArbitratorNodeAddress(),
|
|
||||||
trade.getArbitratorPubKeyRing(),
|
|
||||||
arbitratorRequest,
|
|
||||||
new SendDirectMessageListener() {
|
|
||||||
@Override
|
@Override
|
||||||
public void onArrived() {
|
public void onArrived() {
|
||||||
log.info("{} arrived at arbitrator: offerId={}; uid={}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid());
|
log.info("{} arrived at backup arbitrator: offerId={}", InitTradeRequest.class.getSimpleName(), trade.getId());
|
||||||
|
complete();
|
||||||
}
|
}
|
||||||
@Override // TODO (woodser): handle case where primary arbitrator is unavailable so use backup arbitrator, distinguish offline from bad ack
|
@Override
|
||||||
public void onFault(String errorMessage) {
|
public void onFault(String errorMessage) { // TODO (woodser): distinguish nack from offline
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getUid(), trade.getArbitratorNodeAddress(), errorMessage);
|
log.warn("Cannot take offer because arbitrators are unavailable: error={}.", InitTradeRequest.class.getSimpleName(), errorMessage);
|
||||||
appendToErrorMessage("Sending message failed: message=" + arbitratorRequest + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
failed();
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
);
|
}
|
||||||
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendInitTradeRequest(NodeAddress arbitratorNodeAddress, SendDirectMessageListener listener) {
|
||||||
|
|
||||||
|
// get registered arbitrator
|
||||||
|
Mediator arbitrator = processModel.getUser().getAcceptedMediatorByAddress(arbitratorNodeAddress);
|
||||||
|
if (arbitrator == null) throw new RuntimeException("Node address " + arbitratorNodeAddress + " is not a registered arbitrator");
|
||||||
|
|
||||||
|
// set pub keys
|
||||||
|
processModel.getArbitrator().setPubKeyRing(arbitrator.getPubKeyRing());
|
||||||
|
trade.setArbitratorNodeAddress(arbitratorNodeAddress);
|
||||||
|
trade.setArbitratorPubKeyRing(processModel.getArbitrator().getPubKeyRing());
|
||||||
|
|
||||||
|
// create request to arbitrator
|
||||||
|
InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // taker's InitTradeRequest to maker
|
||||||
|
InitTradeRequest arbitratorRequest = new InitTradeRequest(
|
||||||
|
makerRequest.getTradeId(),
|
||||||
|
makerRequest.getSenderNodeAddress(),
|
||||||
|
makerRequest.getPubKeyRing(),
|
||||||
|
makerRequest.getTradeAmount(),
|
||||||
|
makerRequest.getTradePrice(),
|
||||||
|
makerRequest.getTradeFee(),
|
||||||
|
makerRequest.getAccountId(),
|
||||||
|
makerRequest.getPaymentAccountId(),
|
||||||
|
makerRequest.getPaymentMethodId(),
|
||||||
|
makerRequest.getUid(),
|
||||||
|
Version.getP2PMessageVersion(),
|
||||||
|
makerRequest.getAccountAgeWitnessSignatureOfOfferId(),
|
||||||
|
makerRequest.getCurrentDate(),
|
||||||
|
makerRequest.getMakerNodeAddress(),
|
||||||
|
makerRequest.getTakerNodeAddress(),
|
||||||
|
trade.getArbitratorNodeAddress(),
|
||||||
|
processModel.getReserveTx().getHash(),
|
||||||
|
processModel.getReserveTx().getFullHex(),
|
||||||
|
processModel.getReserveTx().getKey(),
|
||||||
|
makerRequest.getPayoutAddress(),
|
||||||
|
processModel.getMakerSignature());
|
||||||
|
|
||||||
|
// send request to arbitrator
|
||||||
|
log.info("Sending {} with offerId {} and uid {} to arbitrator {} with pub key ring {}", arbitratorRequest.getClass().getSimpleName(), arbitratorRequest.getTradeId(), arbitratorRequest.getUid(), trade.getArbitratorNodeAddress(), trade.getArbitratorPubKeyRing());
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(
|
||||||
|
arbitratorNodeAddress,
|
||||||
|
arbitrator.getPubKeyRing(),
|
||||||
|
arbitratorRequest,
|
||||||
|
listener
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ public class TradableListTest {
|
|||||||
|
|
||||||
// test adding an OpenOffer and convert toProto
|
// test adding an OpenOffer and convert toProto
|
||||||
Offer offer = new Offer(offerPayload);
|
Offer offer = new Offer(offerPayload);
|
||||||
OpenOffer openOffer = new OpenOffer(offer);
|
OpenOffer openOffer = new OpenOffer(offer, 0, "", "", "");
|
||||||
openOfferTradableList.add(openOffer);
|
openOfferTradableList.add(openOffer);
|
||||||
message = (protobuf.PersistableEnvelope) openOfferTradableList.toProtoMessage();
|
message = (protobuf.PersistableEnvelope) openOfferTradableList.toProtoMessage();
|
||||||
assertEquals(message.getMessageCase(), TRADABLE_LIST);
|
assertEquals(message.getMessageCase(), TRADABLE_LIST);
|
||||||
|
@ -219,7 +219,7 @@ class EditOfferDataModel extends MutableOfferDataModel {
|
|||||||
offerPayload.getHashOfChallenge(),
|
offerPayload.getHashOfChallenge(),
|
||||||
offerPayload.getExtraDataMap(),
|
offerPayload.getExtraDataMap(),
|
||||||
offerPayload.getProtocolVersion(),
|
offerPayload.getProtocolVersion(),
|
||||||
offerPayload.getArbitratorNodeAddress(),
|
offerPayload.getArbitratorSigner(),
|
||||||
offerPayload.getArbitratorSignature(),
|
offerPayload.getArbitratorSignature(),
|
||||||
offerPayload.getReserveTxKeyImages());
|
offerPayload.getReserveTxKeyImages());
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ message OfferAvailabilityResponse {
|
|||||||
repeated int32 supported_capabilities = 3;
|
repeated int32 supported_capabilities = 3;
|
||||||
string uid = 4;
|
string uid = 4;
|
||||||
string maker_signature = 5;
|
string maker_signature = 5;
|
||||||
NodeAddress arbitrator_node_address = 6;
|
NodeAddress backup_arbitrator = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RefreshOfferMessage {
|
message RefreshOfferMessage {
|
||||||
@ -853,7 +853,7 @@ message OfferPayload {
|
|||||||
map<string, string> extra_data = 34;
|
map<string, string> extra_data = 34;
|
||||||
int32 protocol_version = 35;
|
int32 protocol_version = 35;
|
||||||
|
|
||||||
NodeAddress arbitrator_node_address = 1001;
|
NodeAddress arbitrator_signer = 1001;
|
||||||
string arbitrator_signature = 1002;
|
string arbitrator_signature = 1002;
|
||||||
repeated string reserve_tx_key_images = 1003;
|
repeated string reserve_tx_key_images = 1003;
|
||||||
}
|
}
|
||||||
@ -1472,9 +1472,11 @@ message OpenOffer {
|
|||||||
|
|
||||||
Offer offer = 1;
|
Offer offer = 1;
|
||||||
State state = 2;
|
State state = 2;
|
||||||
NodeAddress arbitrator_node_address = 3;
|
NodeAddress backup_arbitrator = 3;
|
||||||
int64 trigger_price = 4;
|
int64 trigger_price = 4;
|
||||||
repeated string frozen_key_images = 5;
|
string reserve_tx_hash = 5;
|
||||||
|
string reserve_tx_hex = 6;
|
||||||
|
string reserve_tx_key = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message Tradable {
|
message Tradable {
|
||||||
@ -1644,18 +1646,16 @@ message ProcessModel {
|
|||||||
int64 seller_payout_amount_from_mediation = 20;
|
int64 seller_payout_amount_from_mediation = 20;
|
||||||
|
|
||||||
string maker_signature = 1001;
|
string maker_signature = 1001;
|
||||||
NodeAddress arbitrator_node_address = 1002;
|
NodeAddress backup_arbitrator = 1002;
|
||||||
TradingPeer maker = 1003;
|
TradingPeer maker = 1003;
|
||||||
TradingPeer taker = 1004;
|
TradingPeer taker = 1004;
|
||||||
TradingPeer arbitrator = 1005;
|
TradingPeer arbitrator = 1005;
|
||||||
NodeAddress temp_trading_peer_node_address = 1006;
|
NodeAddress temp_trading_peer_node_address = 1006;
|
||||||
string reserve_tx_hash = 1007;
|
string prepared_multisig_hex = 1007;
|
||||||
repeated string frozen_key_images = 1008;
|
string made_multisig_hex = 1008;
|
||||||
string prepared_multisig_hex = 1009;
|
bool multisig_setup_complete = 1009;
|
||||||
string made_multisig_hex = 1010;
|
bool maker_ready_to_fund_multisig = 1010;
|
||||||
bool multisig_setup_complete = 1011;
|
bool multisig_deposit_initiated = 1011;
|
||||||
bool maker_ready_to_fund_multisig = 1012;
|
|
||||||
bool multisig_deposit_initiated = 1013;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TradingPeer {
|
message TradingPeer {
|
||||||
@ -1681,12 +1681,13 @@ message TradingPeer {
|
|||||||
string reserve_tx_hash = 1001;
|
string reserve_tx_hash = 1001;
|
||||||
string reserve_tx_hex = 1002;
|
string reserve_tx_hex = 1002;
|
||||||
string reserve_tx_key = 1003;
|
string reserve_tx_key = 1003;
|
||||||
string prepared_multisig_hex = 1004;
|
repeated string reserve_tx_key_images = 1004;
|
||||||
string made_multisig_hex = 1005;
|
string prepared_multisig_hex = 1005;
|
||||||
string signed_payout_tx_hex = 1006;
|
string made_multisig_hex = 1006;
|
||||||
string deposit_tx_hash = 1007;
|
string signed_payout_tx_hex = 1007;
|
||||||
string deposit_tx_hex = 1008;
|
string deposit_tx_hash = 1008;
|
||||||
string deposit_tx_key = 1009;
|
string deposit_tx_hex = 1009;
|
||||||
|
string deposit_tx_key = 1010;
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Loading…
Reference in New Issue
Block a user