mirror of
https://github.com/boldsuck/haveno.git
synced 2025-01-03 14:49:25 +00:00
select online, registered, and least used arbitrator (#400)
support registering and unregistering arbitrators over grpc maker always sends InitTradeRequest to arbitrator share original contract for comparision remove backup arbitator from model cleanup trade states
This commit is contained in:
parent
757c7cf19c
commit
3727d12ef6
51 changed files with 472 additions and 382 deletions
|
@ -14,7 +14,7 @@ import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.TestInfo;
|
import org.junit.jupiter.api.TestInfo;
|
||||||
|
|
||||||
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL;
|
||||||
import static bisq.core.trade.Trade.Phase.DEPOSIT_UNLOCKED;
|
import static bisq.core.trade.Trade.Phase.DEPOSITS_UNLOCKED;
|
||||||
import static bisq.core.trade.Trade.Phase.PAYMENT_SENT;
|
import static bisq.core.trade.Trade.Phase.PAYMENT_SENT;
|
||||||
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
|
||||||
import static bisq.core.trade.Trade.State.BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG;
|
import static bisq.core.trade.Trade.State.BUYER_SAW_ARRIVED_PAYMENT_SENT_MSG;
|
||||||
|
@ -80,7 +80,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||||
String tradeId) {
|
String tradeId) {
|
||||||
Predicate<TradeInfo> isTradeInDepositUnlockedStateAndPhase = (t) ->
|
Predicate<TradeInfo> isTradeInDepositUnlockedStateAndPhase = (t) ->
|
||||||
t.getState().equals(DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN.name())
|
t.getState().equals(DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN.name())
|
||||||
&& t.getPhase().equals(DEPOSIT_UNLOCKED.name());
|
&& t.getPhase().equals(DEPOSITS_UNLOCKED.name());
|
||||||
|
|
||||||
String userName = toUserName.apply(grpcClient);
|
String userName = toUserName.apply(grpcClient);
|
||||||
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
for (int i = 1; i <= maxTradeStateAndPhaseChecks.get(); i++) {
|
||||||
|
@ -95,7 +95,7 @@ public class AbstractTradeTest extends AbstractOfferTest {
|
||||||
genBtcBlocksThenWait(1, 4_000);
|
genBtcBlocksThenWait(1, 4_000);
|
||||||
} else {
|
} else {
|
||||||
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN)
|
EXPECTED_PROTOCOL_STATUS.setState(DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN)
|
||||||
.setPhase(DEPOSIT_UNLOCKED)
|
.setPhase(DEPOSITS_UNLOCKED)
|
||||||
.setDepositPublished(true)
|
.setDepositPublished(true)
|
||||||
.setDepositConfirmed(true);
|
.setDepositConfirmed(true);
|
||||||
verifyExpectedProtocolStatus(trade);
|
verifyExpectedProtocolStatus(trade);
|
||||||
|
|
|
@ -406,8 +406,12 @@ public class CoreApi {
|
||||||
// Dispute Agents
|
// Dispute Agents
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
public void registerDisputeAgent(String disputeAgentType, String registrationKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
coreDisputeAgentsService.registerDisputeAgent(disputeAgentType, registrationKey);
|
coreDisputeAgentsService.registerDisputeAgent(disputeAgentType, registrationKey, resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterDisputeAgent(String disputeAgentType, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
coreDisputeAgentsService.unregisterDisputeAgent(disputeAgentType, resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -31,7 +31,8 @@ import bisq.network.p2p.P2PService;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
import bisq.common.config.Config;
|
||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
|
import bisq.common.handlers.ResultHandler;
|
||||||
import org.bitcoinj.core.ECKey;
|
import org.bitcoinj.core.ECKey;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
@ -88,14 +89,10 @@ class CoreDisputeAgentsService {
|
||||||
this.languageCodes = asList("de", "en", "es", "fr");
|
this.languageCodes = asList("de", "en", "es", "fr");
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
void registerDisputeAgent(String disputeAgentType, String registrationKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
if (!p2PService.isBootstrapped())
|
if (!p2PService.isBootstrapped())
|
||||||
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
||||||
|
|
||||||
if (config.baseCurrencyNetwork.isMainnet()
|
|
||||||
|| !config.useLocalhostForP2P)
|
|
||||||
throw new IllegalStateException("dispute agents must be registered in a Bisq UI");
|
|
||||||
|
|
||||||
Optional<SupportType> supportType = getSupportType(disputeAgentType);
|
Optional<SupportType> supportType = getSupportType(disputeAgentType);
|
||||||
if (supportType.isPresent()) {
|
if (supportType.isPresent()) {
|
||||||
ECKey ecKey;
|
ECKey ecKey;
|
||||||
|
@ -104,16 +101,18 @@ class CoreDisputeAgentsService {
|
||||||
case ARBITRATION:
|
case ARBITRATION:
|
||||||
if (user.getRegisteredArbitrator() != null) {
|
if (user.getRegisteredArbitrator() != null) {
|
||||||
log.warn("ignoring request to re-register as arbitrator");
|
log.warn("ignoring request to re-register as arbitrator");
|
||||||
|
resultHandler.handleResult();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecKey = arbitratorManager.getRegistrationKey(registrationKey);
|
ecKey = arbitratorManager.getRegistrationKey(registrationKey);
|
||||||
if (ecKey == null) throw new IllegalStateException("invalid registration key");
|
if (ecKey == null) throw new IllegalStateException("invalid registration key");
|
||||||
signature = arbitratorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
signature = arbitratorManager.signStorageSignaturePubKey(Objects.requireNonNull(ecKey));
|
||||||
registerArbitrator(nodeAddress, languageCodes, ecKey, signature);
|
registerArbitrator(nodeAddress, languageCodes, ecKey, signature, resultHandler, errorMessageHandler);
|
||||||
return;
|
return;
|
||||||
case MEDIATION:
|
case MEDIATION:
|
||||||
if (user.getRegisteredMediator() != null) {
|
if (user.getRegisteredMediator() != null) {
|
||||||
log.warn("ignoring request to re-register as mediator");
|
log.warn("ignoring request to re-register as mediator");
|
||||||
|
resultHandler.handleResult();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecKey = mediatorManager.getRegistrationKey(registrationKey);
|
ecKey = mediatorManager.getRegistrationKey(registrationKey);
|
||||||
|
@ -124,6 +123,7 @@ class CoreDisputeAgentsService {
|
||||||
case REFUND:
|
case REFUND:
|
||||||
if (user.getRegisteredRefundAgent() != null) {
|
if (user.getRegisteredRefundAgent() != null) {
|
||||||
log.warn("ignoring request to re-register as refund agent");
|
log.warn("ignoring request to re-register as refund agent");
|
||||||
|
resultHandler.handleResult();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ecKey = refundAgentManager.getRegistrationKey(registrationKey);
|
ecKey = refundAgentManager.getRegistrationKey(registrationKey);
|
||||||
|
@ -139,10 +139,38 @@ class CoreDisputeAgentsService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void unregisterDisputeAgent(String disputeAgentType, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
if (!p2PService.isBootstrapped())
|
||||||
|
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
||||||
|
|
||||||
|
Optional<SupportType> supportType = getSupportType(disputeAgentType);
|
||||||
|
if (supportType.isPresent()) {
|
||||||
|
switch (supportType.get()) {
|
||||||
|
case ARBITRATION:
|
||||||
|
if (user.getRegisteredArbitrator() == null) {
|
||||||
|
errorMessageHandler.handleErrorMessage("User is not arbitrator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unregisterDisputeAgent(resultHandler, errorMessageHandler);
|
||||||
|
return;
|
||||||
|
case MEDIATION:
|
||||||
|
throw new IllegalStateException("unregister mediator not implemented");
|
||||||
|
case REFUND:
|
||||||
|
throw new IllegalStateException("unregister refund agent not implemented");
|
||||||
|
case TRADE:
|
||||||
|
throw new IllegalArgumentException("trade agent registration not supported");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException(format("unknown dispute agent type '%s'", disputeAgentType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void registerArbitrator(NodeAddress nodeAddress,
|
private void registerArbitrator(NodeAddress nodeAddress,
|
||||||
List<String> languageCodes,
|
List<String> languageCodes,
|
||||||
ECKey ecKey,
|
ECKey ecKey,
|
||||||
String signature) {
|
String signature,
|
||||||
|
ResultHandler resultHandler,
|
||||||
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
Arbitrator arbitrator = new Arbitrator(
|
Arbitrator arbitrator = new Arbitrator(
|
||||||
p2PService.getAddress(),
|
p2PService.getAddress(),
|
||||||
xmrWalletService.getWallet().getPrimaryAddress(), // TODO: how is this used?
|
xmrWalletService.getWallet().getPrimaryAddress(), // TODO: how is this used?
|
||||||
|
@ -155,10 +183,9 @@ class CoreDisputeAgentsService {
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
arbitratorManager.addDisputeAgent(arbitrator, () -> {
|
arbitratorManager.addDisputeAgent(arbitrator, () -> {
|
||||||
}, errorMessage -> {
|
if (!arbitratorManager.getDisputeAgentByNodeAddress(nodeAddress).isPresent()) errorMessageHandler.handleErrorMessage("could not register arbitrator");
|
||||||
});
|
else resultHandler.handleResult();
|
||||||
arbitratorManager.getDisputeAgentByNodeAddress(nodeAddress).orElseThrow(() ->
|
}, errorMessageHandler);
|
||||||
new IllegalStateException("could not register arbitrator"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerMediator(NodeAddress nodeAddress,
|
private void registerMediator(NodeAddress nodeAddress,
|
||||||
|
@ -219,4 +246,10 @@ class CoreDisputeAgentsService {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void unregisterDisputeAgent(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
arbitratorManager.removeDisputeAgent(resultHandler, errorMesage -> {
|
||||||
|
errorMessageHandler.handleErrorMessage("Error unregistering dispute agent: " + errorMesage);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,6 +74,8 @@ public class OfferInfo implements Payload {
|
||||||
private final String pubKeyRing;
|
private final String pubKeyRing;
|
||||||
private final String versionNumber;
|
private final String versionNumber;
|
||||||
private final int protocolVersion;
|
private final int protocolVersion;
|
||||||
|
@Nullable
|
||||||
|
private final String arbitratorSigner;
|
||||||
|
|
||||||
public OfferInfo(OfferInfoBuilder builder) {
|
public OfferInfo(OfferInfoBuilder builder) {
|
||||||
this.id = builder.getId();
|
this.id = builder.getId();
|
||||||
|
@ -104,6 +106,7 @@ public class OfferInfo implements Payload {
|
||||||
this.pubKeyRing = builder.getPubKeyRing();
|
this.pubKeyRing = builder.getPubKeyRing();
|
||||||
this.versionNumber = builder.getVersionNumber();
|
this.versionNumber = builder.getVersionNumber();
|
||||||
this.protocolVersion = builder.getProtocolVersion();
|
this.protocolVersion = builder.getProtocolVersion();
|
||||||
|
this.arbitratorSigner = builder.getArbitratorSigner();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OfferInfo toOfferInfo(Offer offer) {
|
public static OfferInfo toOfferInfo(Offer offer) {
|
||||||
|
@ -166,7 +169,8 @@ public class OfferInfo implements Payload {
|
||||||
.withOwnerNodeAddress(offer.getOfferPayload().getOwnerNodeAddress().getFullAddress())
|
.withOwnerNodeAddress(offer.getOfferPayload().getOwnerNodeAddress().getFullAddress())
|
||||||
.withPubKeyRing(offer.getOfferPayload().getPubKeyRing().toString())
|
.withPubKeyRing(offer.getOfferPayload().getPubKeyRing().toString())
|
||||||
.withVersionNumber(offer.getOfferPayload().getVersionNr())
|
.withVersionNumber(offer.getOfferPayload().getVersionNr())
|
||||||
.withProtocolVersion(offer.getOfferPayload().getProtocolVersion());
|
.withProtocolVersion(offer.getOfferPayload().getProtocolVersion())
|
||||||
|
.withArbitratorSigner(offer.getOfferPayload().getArbitratorSigner() == null ? null : offer.getOfferPayload().getArbitratorSigner().getFullAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -203,6 +207,7 @@ public class OfferInfo implements Payload {
|
||||||
.setPubKeyRing(pubKeyRing)
|
.setPubKeyRing(pubKeyRing)
|
||||||
.setVersionNr(versionNumber)
|
.setVersionNr(versionNumber)
|
||||||
.setProtocolVersion(protocolVersion);
|
.setProtocolVersion(protocolVersion);
|
||||||
|
Optional.ofNullable(arbitratorSigner).ifPresent(builder::setArbitratorSigner);
|
||||||
Optional.ofNullable(offerFeePaymentTxId).ifPresent(builder::setOfferFeePaymentTxId);
|
Optional.ofNullable(offerFeePaymentTxId).ifPresent(builder::setOfferFeePaymentTxId);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
@ -238,6 +243,7 @@ public class OfferInfo implements Payload {
|
||||||
.withPubKeyRing(proto.getPubKeyRing())
|
.withPubKeyRing(proto.getPubKeyRing())
|
||||||
.withVersionNumber(proto.getVersionNr())
|
.withVersionNumber(proto.getVersionNr())
|
||||||
.withProtocolVersion(proto.getProtocolVersion())
|
.withProtocolVersion(proto.getProtocolVersion())
|
||||||
|
.withArbitratorSigner(proto.getArbitratorSigner())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,11 @@ public class TradeInfo implements Payload {
|
||||||
? ""
|
? ""
|
||||||
: trade.getTradingPeerNodeAddress().getFullAddress();
|
: trade.getTradingPeerNodeAddress().getFullAddress();
|
||||||
|
|
||||||
|
private static final Function<Trade, String> toArbitratorNodeAddress = (trade) ->
|
||||||
|
trade.getArbitratorNodeAddress() == null
|
||||||
|
? ""
|
||||||
|
: trade.getArbitratorNodeAddress().getFullAddress();
|
||||||
|
|
||||||
private static final Function<Trade, String> toRoundedVolume = (trade) ->
|
private static final Function<Trade, String> toRoundedVolume = (trade) ->
|
||||||
trade.getVolume() == null
|
trade.getVolume() == null
|
||||||
? ""
|
? ""
|
||||||
|
@ -70,6 +75,7 @@ public class TradeInfo implements Payload {
|
||||||
private final long amountAsLong;
|
private final long amountAsLong;
|
||||||
private final String price;
|
private final String price;
|
||||||
private final String volume;
|
private final String volume;
|
||||||
|
private final String arbitratorNodeAddress;
|
||||||
private final String tradingPeerNodeAddress;
|
private final String tradingPeerNodeAddress;
|
||||||
private final String state;
|
private final String state;
|
||||||
private final String phase;
|
private final String phase;
|
||||||
|
@ -98,6 +104,7 @@ public class TradeInfo implements Payload {
|
||||||
this.amountAsLong = builder.getAmountAsLong();
|
this.amountAsLong = builder.getAmountAsLong();
|
||||||
this.price = builder.getPrice();
|
this.price = builder.getPrice();
|
||||||
this.volume = builder.getVolume();
|
this.volume = builder.getVolume();
|
||||||
|
this.arbitratorNodeAddress = builder.getArbitratorNodeAddress();
|
||||||
this.tradingPeerNodeAddress = builder.getTradingPeerNodeAddress();
|
this.tradingPeerNodeAddress = builder.getTradingPeerNodeAddress();
|
||||||
this.state = builder.getState();
|
this.state = builder.getState();
|
||||||
this.phase = builder.getPhase();
|
this.phase = builder.getPhase();
|
||||||
|
@ -149,6 +156,7 @@ public class TradeInfo implements Payload {
|
||||||
.withAmountAsLong(trade.getAmountAsLong())
|
.withAmountAsLong(trade.getAmountAsLong())
|
||||||
.withPrice(toPreciseTradePrice.apply(trade))
|
.withPrice(toPreciseTradePrice.apply(trade))
|
||||||
.withVolume(toRoundedVolume.apply(trade))
|
.withVolume(toRoundedVolume.apply(trade))
|
||||||
|
.withArbitratorNodeAddress(toArbitratorNodeAddress.apply(trade))
|
||||||
.withTradingPeerNodeAddress(toPeerNodeAddress.apply(trade))
|
.withTradingPeerNodeAddress(toPeerNodeAddress.apply(trade))
|
||||||
.withState(trade.getState().name())
|
.withState(trade.getState().name())
|
||||||
.withPhase(trade.getPhase().name())
|
.withPhase(trade.getPhase().name())
|
||||||
|
@ -186,6 +194,7 @@ public class TradeInfo implements Payload {
|
||||||
.setAmountAsLong(amountAsLong)
|
.setAmountAsLong(amountAsLong)
|
||||||
.setPrice(price)
|
.setPrice(price)
|
||||||
.setTradeVolume(volume)
|
.setTradeVolume(volume)
|
||||||
|
.setArbitratorNodeAddress(arbitratorNodeAddress)
|
||||||
.setTradingPeerNodeAddress(tradingPeerNodeAddress)
|
.setTradingPeerNodeAddress(tradingPeerNodeAddress)
|
||||||
.setState(state)
|
.setState(state)
|
||||||
.setPhase(phase)
|
.setPhase(phase)
|
||||||
|
@ -220,6 +229,7 @@ public class TradeInfo implements Payload {
|
||||||
.withPeriodState(proto.getPeriodState())
|
.withPeriodState(proto.getPeriodState())
|
||||||
.withState(proto.getState())
|
.withState(proto.getState())
|
||||||
.withPhase(proto.getPhase())
|
.withPhase(proto.getPhase())
|
||||||
|
.withArbitratorNodeAddress(proto.getArbitratorNodeAddress())
|
||||||
.withTradingPeerNodeAddress(proto.getTradingPeerNodeAddress())
|
.withTradingPeerNodeAddress(proto.getTradingPeerNodeAddress())
|
||||||
.withIsDepositPublished(proto.getIsDepositPublished())
|
.withIsDepositPublished(proto.getIsDepositPublished())
|
||||||
.withIsDepositUnlocked(proto.getIsDepositUnlocked())
|
.withIsDepositUnlocked(proto.getIsDepositUnlocked())
|
||||||
|
@ -247,6 +257,7 @@ public class TradeInfo implements Payload {
|
||||||
", payoutTxId='" + payoutTxId + '\'' + "\n" +
|
", payoutTxId='" + payoutTxId + '\'' + "\n" +
|
||||||
", amountAsLong='" + amountAsLong + '\'' + "\n" +
|
", amountAsLong='" + amountAsLong + '\'' + "\n" +
|
||||||
", price='" + price + '\'' + "\n" +
|
", price='" + price + '\'' + "\n" +
|
||||||
|
", arbitratorNodeAddress='" + arbitratorNodeAddress + '\'' + "\n" +
|
||||||
", tradingPeerNodeAddress='" + tradingPeerNodeAddress + '\'' + "\n" +
|
", tradingPeerNodeAddress='" + tradingPeerNodeAddress + '\'' + "\n" +
|
||||||
", state='" + state + '\'' + "\n" +
|
", state='" + state + '\'' + "\n" +
|
||||||
", phase='" + phase + '\'' + "\n" +
|
", phase='" + phase + '\'' + "\n" +
|
||||||
|
|
|
@ -61,6 +61,7 @@ public final class OfferInfoBuilder {
|
||||||
private String pubKeyRing;
|
private String pubKeyRing;
|
||||||
private String versionNumber;
|
private String versionNumber;
|
||||||
private int protocolVersion;
|
private int protocolVersion;
|
||||||
|
private String arbitratorSigner;
|
||||||
|
|
||||||
public OfferInfoBuilder withId(String id) {
|
public OfferInfoBuilder withId(String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -217,6 +218,11 @@ public final class OfferInfoBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OfferInfoBuilder withArbitratorSigner(String arbitratorSigner) {
|
||||||
|
this.arbitratorSigner = arbitratorSigner;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public OfferInfo build() {
|
public OfferInfo build() {
|
||||||
return new OfferInfo(this);
|
return new OfferInfo(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ public final class TradeInfoV1Builder {
|
||||||
private long amountAsLong;
|
private long amountAsLong;
|
||||||
private String price;
|
private String price;
|
||||||
private String volume;
|
private String volume;
|
||||||
|
private String arbitratorNodeAddress;
|
||||||
private String tradingPeerNodeAddress;
|
private String tradingPeerNodeAddress;
|
||||||
private String state;
|
private String state;
|
||||||
private String phase;
|
private String phase;
|
||||||
|
@ -151,6 +152,11 @@ public final class TradeInfoV1Builder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TradeInfoV1Builder withArbitratorNodeAddress(String arbitratorNodeAddress) {
|
||||||
|
this.arbitratorNodeAddress = arbitratorNodeAddress;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public TradeInfoV1Builder withTradingPeerNodeAddress(String tradingPeerNodeAddress) {
|
public TradeInfoV1Builder withTradingPeerNodeAddress(String tradingPeerNodeAddress) {
|
||||||
this.tradingPeerNodeAddress = tradingPeerNodeAddress;
|
this.tradingPeerNodeAddress = tradingPeerNodeAddress;
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -72,7 +72,7 @@ public class TradeEvents {
|
||||||
case DEPOSIT_REQUESTED:
|
case DEPOSIT_REQUESTED:
|
||||||
case DEPOSITS_PUBLISHED:
|
case DEPOSITS_PUBLISHED:
|
||||||
break;
|
break;
|
||||||
case DEPOSIT_UNLOCKED:
|
case DEPOSITS_UNLOCKED:
|
||||||
if (trade.getContract() != null && pubKeyRingProvider.get().equals(trade.getContract().getBuyerPubKeyRing()))
|
if (trade.getContract() != null && pubKeyRingProvider.get().equals(trade.getContract().getBuyerPubKeyRing()))
|
||||||
msg = Res.get("account.notifications.trade.message.msg.conf", shortId);
|
msg = Res.get("account.notifications.trade.message.msg.conf", shortId);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -154,14 +154,6 @@ public class CreateOfferService {
|
||||||
boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode);
|
boolean isCryptoCurrency = CurrencyUtil.isCryptoCurrency(currencyCode);
|
||||||
String baseCurrencyCode = isCryptoCurrency ? currencyCode : Res.getBaseCurrencyCode();
|
String baseCurrencyCode = isCryptoCurrency ? currencyCode : Res.getBaseCurrencyCode();
|
||||||
String counterCurrencyCode = isCryptoCurrency ? Res.getBaseCurrencyCode() : currencyCode;
|
String counterCurrencyCode = isCryptoCurrency ? Res.getBaseCurrencyCode() : currencyCode;
|
||||||
List<NodeAddress> acceptedArbitratorAddresses = user.getAcceptedArbitratorAddresses();
|
|
||||||
ArrayList<NodeAddress> arbitratorNodeAddresses = acceptedArbitratorAddresses != null ?
|
|
||||||
Lists.newArrayList(acceptedArbitratorAddresses) :
|
|
||||||
new ArrayList<>();
|
|
||||||
List<NodeAddress> acceptedMediatorAddresses = user.getAcceptedMediatorAddresses();
|
|
||||||
ArrayList<NodeAddress> mediatorNodeAddresses = acceptedMediatorAddresses != null ?
|
|
||||||
Lists.newArrayList(acceptedMediatorAddresses) :
|
|
||||||
new ArrayList<>();
|
|
||||||
String countryCode = PaymentAccountUtil.getCountryCode(paymentAccount);
|
String countryCode = PaymentAccountUtil.getCountryCode(paymentAccount);
|
||||||
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
|
List<String> acceptedCountryCodes = PaymentAccountUtil.getAcceptedCountryCodes(paymentAccount);
|
||||||
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
String bankId = PaymentAccountUtil.getBankId(paymentAccount);
|
||||||
|
@ -191,10 +183,6 @@ public class CreateOfferService {
|
||||||
currencyCode,
|
currencyCode,
|
||||||
makerFeeAsCoin);
|
makerFeeAsCoin);
|
||||||
|
|
||||||
// select signing arbitrator
|
|
||||||
Arbitrator arbitrator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, arbitratorManager);
|
|
||||||
if (arbitrator == null) throw new RuntimeException("No arbitrators available");
|
|
||||||
|
|
||||||
OfferPayload offerPayload = new OfferPayload(offerId,
|
OfferPayload offerPayload = new OfferPayload(offerId,
|
||||||
creationTime,
|
creationTime,
|
||||||
makerAddress,
|
makerAddress,
|
||||||
|
@ -230,7 +218,7 @@ public class CreateOfferService {
|
||||||
hashOfChallenge,
|
hashOfChallenge,
|
||||||
extraDataMap,
|
extraDataMap,
|
||||||
Version.TRADE_PROTOCOL_VERSION,
|
Version.TRADE_PROTOCOL_VERSION,
|
||||||
arbitrator.getNodeAddress(),
|
null,
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
Offer offer = new Offer(offerPayload);
|
Offer offer = new Offer(offerPayload);
|
||||||
|
|
|
@ -78,6 +78,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
||||||
|
|
||||||
// address and signature of signing arbitrator
|
// address and signature of signing arbitrator
|
||||||
@Setter
|
@Setter
|
||||||
|
@Nullable
|
||||||
protected NodeAddress arbitratorSigner;
|
protected NodeAddress arbitratorSigner;
|
||||||
@Setter
|
@Setter
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -192,7 +193,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
||||||
@Nullable String hashOfChallenge,
|
@Nullable String hashOfChallenge,
|
||||||
@Nullable Map<String, String> extraDataMap,
|
@Nullable Map<String, String> extraDataMap,
|
||||||
int protocolVersion,
|
int protocolVersion,
|
||||||
NodeAddress arbitratorSigner,
|
@Nullable NodeAddress arbitratorSigner,
|
||||||
@Nullable String arbitratorSignature,
|
@Nullable String arbitratorSignature,
|
||||||
@Nullable List<String> reserveTxKeyImages) {
|
@Nullable List<String> reserveTxKeyImages) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
@ -297,8 +298,8 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
||||||
.setLowerClosePrice(lowerClosePrice)
|
.setLowerClosePrice(lowerClosePrice)
|
||||||
.setUpperClosePrice(upperClosePrice)
|
.setUpperClosePrice(upperClosePrice)
|
||||||
.setIsPrivateOffer(isPrivateOffer)
|
.setIsPrivateOffer(isPrivateOffer)
|
||||||
.setProtocolVersion(protocolVersion)
|
.setProtocolVersion(protocolVersion);
|
||||||
.setArbitratorSigner(arbitratorSigner.toProtoMessage());
|
Optional.ofNullable(arbitratorSigner).ifPresent(e -> builder.setArbitratorSigner(arbitratorSigner.toProtoMessage()));
|
||||||
Optional.ofNullable(offerFeePaymentTxId).ifPresent(builder::setOfferFeePaymentTxId);
|
Optional.ofNullable(offerFeePaymentTxId).ifPresent(builder::setOfferFeePaymentTxId);
|
||||||
Optional.ofNullable(countryCode).ifPresent(builder::setCountryCode);
|
Optional.ofNullable(countryCode).ifPresent(builder::setCountryCode);
|
||||||
Optional.ofNullable(bankId).ifPresent(builder::setBankId);
|
Optional.ofNullable(bankId).ifPresent(builder::setBankId);
|
||||||
|
@ -356,7 +357,7 @@ public final class OfferPayload implements ProtectedStoragePayload, ExpirablePay
|
||||||
hashOfChallenge,
|
hashOfChallenge,
|
||||||
extraDataMapMap,
|
extraDataMapMap,
|
||||||
proto.getProtocolVersion(),
|
proto.getProtocolVersion(),
|
||||||
NodeAddress.fromProto(proto.getArbitratorSigner()),
|
proto.hasArbitratorSigner() ? NodeAddress.fromProto(proto.getArbitratorSigner()) : null,
|
||||||
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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,6 @@ package bisq.core.offer;
|
||||||
|
|
||||||
import bisq.core.trade.Tradable;
|
import bisq.core.trade.Tradable;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -55,10 +53,6 @@ public final class OpenOffer implements Tradable {
|
||||||
private final Offer offer;
|
private final Offer offer;
|
||||||
@Getter
|
@Getter
|
||||||
private State state;
|
private State state;
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@Nullable
|
|
||||||
private NodeAddress backupArbitrator;
|
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
private boolean autoSplit;
|
private boolean autoSplit;
|
||||||
|
@ -113,7 +107,6 @@ public final class OpenOffer implements Tradable {
|
||||||
|
|
||||||
private OpenOffer(Offer offer,
|
private OpenOffer(Offer offer,
|
||||||
State state,
|
State state,
|
||||||
@Nullable NodeAddress backupArbitrator,
|
|
||||||
long triggerPrice,
|
long triggerPrice,
|
||||||
boolean autoSplit,
|
boolean autoSplit,
|
||||||
@Nullable String scheduledAmount,
|
@Nullable String scheduledAmount,
|
||||||
|
@ -123,7 +116,6 @@ public final class OpenOffer implements Tradable {
|
||||||
@Nullable String reserveTxKey) {
|
@Nullable String reserveTxKey) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.backupArbitrator = backupArbitrator;
|
|
||||||
this.triggerPrice = triggerPrice;
|
this.triggerPrice = triggerPrice;
|
||||||
this.autoSplit = autoSplit;
|
this.autoSplit = autoSplit;
|
||||||
this.scheduledTxHashes = scheduledTxHashes;
|
this.scheduledTxHashes = scheduledTxHashes;
|
||||||
|
@ -144,7 +136,6 @@ public final class OpenOffer implements Tradable {
|
||||||
.setAutoSplit(autoSplit);
|
.setAutoSplit(autoSplit);
|
||||||
|
|
||||||
Optional.ofNullable(scheduledAmount).ifPresent(e -> builder.setScheduledAmount(scheduledAmount));
|
Optional.ofNullable(scheduledAmount).ifPresent(e -> builder.setScheduledAmount(scheduledAmount));
|
||||||
Optional.ofNullable(backupArbitrator).ifPresent(nodeAddress -> builder.setBackupArbitrator(nodeAddress.toProtoMessage()));
|
|
||||||
Optional.ofNullable(scheduledTxHashes).ifPresent(e -> builder.addAllScheduledTxHashes(scheduledTxHashes));
|
Optional.ofNullable(scheduledTxHashes).ifPresent(e -> builder.addAllScheduledTxHashes(scheduledTxHashes));
|
||||||
Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash));
|
Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash));
|
||||||
Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex));
|
Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex));
|
||||||
|
@ -156,7 +147,6 @@ 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.hasBackupArbitrator() ? NodeAddress.fromProto(proto.getBackupArbitrator()) : null,
|
|
||||||
proto.getTriggerPrice(),
|
proto.getTriggerPrice(),
|
||||||
proto.getAutoSplit(),
|
proto.getAutoSplit(),
|
||||||
proto.getScheduledAmount(),
|
proto.getScheduledAmount(),
|
||||||
|
@ -227,7 +217,6 @@ public final class OpenOffer implements Tradable {
|
||||||
return "OpenOffer{" +
|
return "OpenOffer{" +
|
||||||
",\n offer=" + offer +
|
",\n offer=" + offer +
|
||||||
",\n state=" + state +
|
",\n state=" + state +
|
||||||
",\n arbitratorNodeAddress=" + backupArbitrator +
|
|
||||||
",\n triggerPrice=" + triggerPrice +
|
",\n triggerPrice=" + triggerPrice +
|
||||||
"\n}";
|
"\n}";
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import bisq.core.btc.wallet.TradeWalletService;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
import bisq.core.exceptions.TradePriceOutOfToleranceException;
|
import bisq.core.exceptions.TradePriceOutOfToleranceException;
|
||||||
import bisq.core.filter.FilterManager;
|
import bisq.core.filter.FilterManager;
|
||||||
import bisq.core.offer.availability.DisputeAgentSelection;
|
|
||||||
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
import bisq.core.offer.messages.OfferAvailabilityRequest;
|
||||||
import bisq.core.offer.messages.OfferAvailabilityResponse;
|
import bisq.core.offer.messages.OfferAvailabilityResponse;
|
||||||
import bisq.core.offer.messages.SignOfferRequest;
|
import bisq.core.offer.messages.SignOfferRequest;
|
||||||
|
@ -703,6 +702,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
// handle result
|
// handle result
|
||||||
resultHandler.handleResult(null);
|
resultHandler.handleResult(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -760,7 +760,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
|
|
||||||
// set reserve tx on open offer
|
// set reserve tx on open offer
|
||||||
openOffer.setReserveTxHash(model.getReserveTx().getHash());
|
openOffer.setReserveTxHash(model.getReserveTx().getHash());
|
||||||
openOffer.setReserveTxHex(model.getReserveTx().getHash());
|
openOffer.setReserveTxHex(model.getReserveTx().getFullHex());
|
||||||
openOffer.setReserveTxKey(model.getReserveTx().getKey());
|
openOffer.setReserveTxKey(model.getReserveTx().getKey());
|
||||||
|
|
||||||
// set offer state
|
// set offer state
|
||||||
|
@ -948,7 +948,6 @@ 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 backupArbitratorNodeAddress = null;
|
|
||||||
if (openOfferOptional.isPresent()) {
|
if (openOfferOptional.isPresent()) {
|
||||||
OpenOffer openOffer = openOfferOptional.get();
|
OpenOffer openOffer = openOfferOptional.get();
|
||||||
if (!apiUserDeniedByOffer(request)) {
|
if (!apiUserDeniedByOffer(request)) {
|
||||||
|
@ -957,12 +956,7 @@ 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()))) {
|
||||||
|
|
||||||
// set backup arbitrator if signer is not available
|
// maker signs taker's request
|
||||||
Mediator backupMediator = DisputeAgentSelection.getLeastUsedArbitrator(tradeStatisticsManager, mediatorManager, offer.getOfferPayload().getArbitratorSigner());
|
|
||||||
backupArbitratorNodeAddress = backupMediator == null ? null : backupMediator.getNodeAddress();
|
|
||||||
openOffer.setBackupArbitrator(backupArbitratorNodeAddress);
|
|
||||||
|
|
||||||
// maker signs taker's request // TODO (woodser): should maker signature include selected arbitrator?
|
|
||||||
String tradeRequestAsJson = JsonUtil.objectToJson(request.getTradeRequest());
|
String tradeRequestAsJson = JsonUtil.objectToJson(request.getTradeRequest());
|
||||||
makerSignature = Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), tradeRequestAsJson);
|
makerSignature = Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), tradeRequestAsJson);
|
||||||
|
|
||||||
|
@ -1005,8 +999,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||||
|
|
||||||
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
OfferAvailabilityResponse offerAvailabilityResponse = new OfferAvailabilityResponse(request.offerId,
|
||||||
availabilityResult,
|
availabilityResult,
|
||||||
makerSignature,
|
makerSignature);
|
||||||
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);
|
||||||
|
|
|
@ -53,7 +53,7 @@ public class DisputeAgentSelection {
|
||||||
|
|
||||||
public static <T extends DisputeAgent> T getLeastUsedArbitrator(TradeStatisticsManager tradeStatisticsManager,
|
public static <T extends DisputeAgent> T getLeastUsedArbitrator(TradeStatisticsManager tradeStatisticsManager,
|
||||||
DisputeAgentManager<T> disputeAgentManager,
|
DisputeAgentManager<T> disputeAgentManager,
|
||||||
NodeAddress excludedArbitrator) {
|
Set<NodeAddress> excludedArbitrator) {
|
||||||
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||||
disputeAgentManager,
|
disputeAgentManager,
|
||||||
excludedArbitrator);
|
excludedArbitrator);
|
||||||
|
@ -61,7 +61,7 @@ public class DisputeAgentSelection {
|
||||||
|
|
||||||
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) {
|
Set<NodeAddress> excludedDisputeAgents) {
|
||||||
// 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));
|
||||||
|
@ -81,7 +81,7 @@ 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 (excludedDisputeAgents != null) disputeAgents.removeAll(excludedDisputeAgents.stream().map(NodeAddress::getFullAddress).collect(Collectors.toList()));
|
||||||
if (disputeAgents.isEmpty()) return null;
|
if (disputeAgents.isEmpty()) return null;
|
||||||
|
|
||||||
String result = getLeastUsedDisputeAgent(lastAddressesUsedInTrades, disputeAgents);
|
String result = getLeastUsedDisputeAgent(lastAddressesUsedInTrades, disputeAgents);
|
||||||
|
|
|
@ -65,9 +65,6 @@ public class OfferAvailabilityModel implements Model {
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
private String makerSignature;
|
private String makerSignature;
|
||||||
@Setter
|
|
||||||
@Getter
|
|
||||||
private NodeAddress backupArbitrator;
|
|
||||||
|
|
||||||
// Added in v1.5.5
|
// Added in v1.5.5
|
||||||
@Getter
|
@Getter
|
||||||
|
|
|
@ -62,7 +62,6 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
|
||||||
|
|
||||||
offer.setState(Offer.State.AVAILABLE);
|
offer.setState(Offer.State.AVAILABLE);
|
||||||
model.setMakerSignature(offerAvailabilityResponse.getMakerSignature());
|
model.setMakerSignature(offerAvailabilityResponse.getMakerSignature());
|
||||||
model.setBackupArbitrator(offerAvailabilityResponse.getBackupArbitrator());
|
|
||||||
checkNotNull(model.getMakerSignature());
|
checkNotNull(model.getMakerSignature());
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
|
|
|
@ -20,7 +20,6 @@ package bisq.core.offer.messages;
|
||||||
|
|
||||||
import bisq.core.offer.AvailabilityResult;
|
import bisq.core.offer.AvailabilityResult;
|
||||||
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
|
||||||
import bisq.network.p2p.SupportedCapabilitiesMessage;
|
import bisq.network.p2p.SupportedCapabilitiesMessage;
|
||||||
|
|
||||||
import bisq.common.app.Capabilities;
|
import bisq.common.app.Capabilities;
|
||||||
|
@ -46,19 +45,16 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String makerSignature;
|
private final String makerSignature;
|
||||||
private final NodeAddress backupArbitrator;
|
|
||||||
|
|
||||||
public OfferAvailabilityResponse(String offerId,
|
public OfferAvailabilityResponse(String offerId,
|
||||||
AvailabilityResult availabilityResult,
|
AvailabilityResult availabilityResult,
|
||||||
String makerSignature,
|
String makerSignature) {
|
||||||
NodeAddress backupArbitrator) {
|
|
||||||
this(offerId,
|
this(offerId,
|
||||||
availabilityResult,
|
availabilityResult,
|
||||||
Capabilities.app,
|
Capabilities.app,
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
makerSignature,
|
makerSignature);
|
||||||
backupArbitrator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,13 +67,11 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
||||||
@Nullable Capabilities supportedCapabilities,
|
@Nullable Capabilities supportedCapabilities,
|
||||||
String messageVersion,
|
String messageVersion,
|
||||||
@Nullable String uid,
|
@Nullable String uid,
|
||||||
String makerSignature,
|
String makerSignature) {
|
||||||
NodeAddress arbitratorNodeAddress) {
|
|
||||||
super(messageVersion, offerId, uid);
|
super(messageVersion, offerId, uid);
|
||||||
this.availabilityResult = availabilityResult;
|
this.availabilityResult = availabilityResult;
|
||||||
this.supportedCapabilities = supportedCapabilities;
|
this.supportedCapabilities = supportedCapabilities;
|
||||||
this.makerSignature = makerSignature;
|
this.makerSignature = makerSignature;
|
||||||
this.backupArbitrator = arbitratorNodeAddress;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,7 +83,6 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
||||||
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)
|
||||||
|
@ -102,7 +95,6 @@ public final class OfferAvailabilityResponse extends OfferMessage implements Sup
|
||||||
Capabilities.fromIntList(proto.getSupportedCapabilitiesList()),
|
Capabilities.fromIntList(proto.getSupportedCapabilitiesList()),
|
||||||
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());
|
||||||
proto.hasBackupArbitrator() ? NodeAddress.fromProto(proto.getBackupArbitrator()) : null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,15 @@
|
||||||
|
|
||||||
package bisq.core.offer.placeoffer.tasks;
|
package bisq.core.offer.placeoffer.tasks;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
|
import bisq.common.handlers.ResultHandler;
|
||||||
import bisq.common.taskrunner.Task;
|
import bisq.common.taskrunner.Task;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.btc.model.XmrAddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
|
import bisq.core.offer.availability.DisputeAgentSelection;
|
||||||
import bisq.core.offer.messages.SignOfferRequest;
|
import bisq.core.offer.messages.SignOfferRequest;
|
||||||
import bisq.core.offer.placeoffer.PlaceOfferModel;
|
import bisq.core.offer.placeoffer.PlaceOfferModel;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
|
@ -34,6 +36,8 @@ import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.P2PService;
|
import bisq.network.p2p.P2PService;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -50,9 +54,7 @@ public class MakerSendsSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
|
|
||||||
Offer offer = model.getOffer();
|
Offer offer = model.getOffer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
|
@ -73,44 +75,13 @@ public class MakerSendsSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
offer.getOfferPayload().getReserveTxKeyImages(),
|
offer.getOfferPayload().getReserveTxKeyImages(),
|
||||||
returnAddress);
|
returnAddress);
|
||||||
|
|
||||||
// get signing arbitrator
|
// send request to least used arbitrators until success
|
||||||
Arbitrator arbitrator = checkNotNull(model.getUser().getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner()), "user.getAcceptedArbitratorByAddress(arbitratorNodeAddress) must not be null");
|
sendSignOfferRequests(request, () -> {
|
||||||
|
|
||||||
// complete on successful ack message
|
|
||||||
DecryptedDirectMessageListener ackListener = new DecryptedDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onDirectMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, NodeAddress sender) {
|
|
||||||
if (!(decryptedMessageWithPubKey.getNetworkEnvelope() instanceof AckMessage)) return;
|
|
||||||
if (!sender.equals(arbitrator.getNodeAddress())) return;
|
|
||||||
AckMessage ackMessage = (AckMessage) decryptedMessageWithPubKey.getNetworkEnvelope();
|
|
||||||
if (!ackMessage.getSourceMsgClassName().equals(SignOfferRequest.class.getSimpleName())) return;
|
|
||||||
if (!ackMessage.getSourceUid().equals(request.getUid())) return;
|
|
||||||
if (ackMessage.isSuccess()) {
|
|
||||||
offer.setState(Offer.State.OFFER_FEE_RESERVED);
|
|
||||||
model.getP2PService().removeDecryptedDirectMessageListener(this);
|
|
||||||
complete();
|
complete();
|
||||||
} else {
|
}, (errorMessage) -> {
|
||||||
if (!failed) {
|
log.warn("Error signing offer: " + errorMessage);
|
||||||
failed = true;
|
appendToErrorMessage("Error signing offer: " + errorMessage);
|
||||||
failed(ackMessage.getErrorMessage()); // TODO: (woodser): only fail once? build into task?
|
failed(errorMessage);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
model.getP2PService().addDecryptedDirectMessageListener(ackListener);
|
|
||||||
|
|
||||||
// send request
|
|
||||||
model.getP2PService().sendEncryptedDirectMessage(arbitrator.getNodeAddress(), arbitrator.getPubKeyRing(), request, new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: arbitrator={}; offerId={}; uid={}", request.getClass().getSimpleName(), arbitrator.getNodeAddress(), offer.getId());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), arbitrator.getNodeAddress(), offer.getId(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
offer.setErrorMessage("An error occurred.\n" +
|
offer.setErrorMessage("An error occurred.\n" +
|
||||||
|
@ -119,4 +90,77 @@ public class MakerSendsSignOfferRequest extends Task<PlaceOfferModel> {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendSignOfferRequests(SignOfferRequest request, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
Arbitrator leastUsedArbitrator = DisputeAgentSelection.getLeastUsedArbitrator(model.getTradeStatisticsManager(), model.getArbitratorManager());
|
||||||
|
sendSignOfferRequests(request, leastUsedArbitrator.getNodeAddress(), new HashSet<NodeAddress>(), resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSignOfferRequests(SignOfferRequest request, NodeAddress arbitratorNodeAddress, Set<NodeAddress> excludedArbitrators, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
|
||||||
|
// complete on successful ack message
|
||||||
|
DecryptedDirectMessageListener ackListener = new DecryptedDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onDirectMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, NodeAddress sender) {
|
||||||
|
if (!(decryptedMessageWithPubKey.getNetworkEnvelope() instanceof AckMessage)) return;
|
||||||
|
if (!sender.equals(arbitratorNodeAddress)) return;
|
||||||
|
AckMessage ackMessage = (AckMessage) decryptedMessageWithPubKey.getNetworkEnvelope();
|
||||||
|
if (!ackMessage.getSourceMsgClassName().equals(SignOfferRequest.class.getSimpleName())) return;
|
||||||
|
if (!ackMessage.getSourceUid().equals(request.getUid())) return;
|
||||||
|
if (ackMessage.isSuccess()) {
|
||||||
|
model.getP2PService().removeDecryptedDirectMessageListener(this);
|
||||||
|
model.getOffer().getOfferPayload().setArbitratorSigner(arbitratorNodeAddress);
|
||||||
|
model.getOffer().setState(Offer.State.OFFER_FEE_RESERVED);
|
||||||
|
resultHandler.handleResult();
|
||||||
|
} else {
|
||||||
|
log.warn("Arbitrator nacked request: {}", errorMessage);
|
||||||
|
handleArbitratorFailure(request, arbitratorNodeAddress, excludedArbitrators, resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
model.getP2PService().addDecryptedDirectMessageListener(ackListener);
|
||||||
|
|
||||||
|
// send sign offer request
|
||||||
|
sendSignOfferRequest(request, arbitratorNodeAddress, new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived at arbitrator: offerId={}", request.getClass().getSimpleName(), model.getOffer().getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
// if unavailable, try alternative arbitrator
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.warn("Arbitrator unavailable: {}", errorMessage);
|
||||||
|
handleArbitratorFailure(request, arbitratorNodeAddress, excludedArbitrators, resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendSignOfferRequest(SignOfferRequest request, NodeAddress arbitratorNodeAddress, SendDirectMessageListener listener) {
|
||||||
|
|
||||||
|
// get registered arbitrator
|
||||||
|
Arbitrator arbitrator = model.getUser().getAcceptedArbitratorByAddress(arbitratorNodeAddress);
|
||||||
|
if (arbitrator == null) throw new RuntimeException("Node address " + arbitratorNodeAddress + " is not a registered arbitrator");
|
||||||
|
request.getOfferPayload().setArbitratorSigner(arbitratorNodeAddress);
|
||||||
|
|
||||||
|
// send request to arbitrator
|
||||||
|
log.info("Sending {} with offerId {} and uid {} to arbitrator {}", request.getClass().getSimpleName(), request.getOfferId(), request.getUid(), arbitratorNodeAddress);
|
||||||
|
model.getP2PService().sendEncryptedDirectMessage(
|
||||||
|
arbitratorNodeAddress,
|
||||||
|
arbitrator.getPubKeyRing(),
|
||||||
|
request,
|
||||||
|
listener
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleArbitratorFailure(SignOfferRequest request, NodeAddress arbitratorNodeAddress, Set<NodeAddress> excludedArbitrators, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
excludedArbitrators.add(arbitratorNodeAddress);
|
||||||
|
Arbitrator altArbitrator = DisputeAgentSelection.getLeastUsedArbitrator(model.getTradeStatisticsManager(), model.getArbitratorManager(), excludedArbitrators);
|
||||||
|
if (altArbitrator == null) {
|
||||||
|
errorMessageHandler.handleErrorMessage("Offer could not be signed by any arbitrator");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("Using alternative arbitrator {}", altArbitrator.getNodeAddress());
|
||||||
|
sendSignOfferRequests(request, altArbitrator.getNodeAddress(), excludedArbitrators, resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ import java.math.BigInteger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -94,13 +93,12 @@ public abstract class DisputeAgentManager<T extends DisputeAgent> {
|
||||||
public DisputeAgentManager(KeyRing keyRing,
|
public DisputeAgentManager(KeyRing keyRing,
|
||||||
DisputeAgentService<T> disputeAgentService,
|
DisputeAgentService<T> disputeAgentService,
|
||||||
User user,
|
User user,
|
||||||
FilterManager filterManager,
|
FilterManager filterManager) {
|
||||||
boolean useDevPrivilegeKeys) {
|
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
this.disputeAgentService = disputeAgentService;
|
this.disputeAgentService = disputeAgentService;
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.filterManager = filterManager;
|
this.filterManager = filterManager;
|
||||||
publicKeys = useDevPrivilegeKeys ? Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) : getPubKeyList();
|
publicKeys = getPubKeyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -245,6 +243,8 @@ public abstract class DisputeAgentManager<T extends DisputeAgent> {
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
},
|
},
|
||||||
errorMessageHandler);
|
errorMessageHandler);
|
||||||
|
} else {
|
||||||
|
errorMessageHandler.handleErrorMessage("User is not registered dispute agent");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import bisq.common.crypto.KeyRing;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.inject.Named;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -41,16 +40,18 @@ public class ArbitratorManager extends DisputeAgentManager<Arbitrator> {
|
||||||
public ArbitratorManager(KeyRing keyRing,
|
public ArbitratorManager(KeyRing keyRing,
|
||||||
ArbitratorService arbitratorService,
|
ArbitratorService arbitratorService,
|
||||||
User user,
|
User user,
|
||||||
FilterManager filterManager,
|
FilterManager filterManager) {
|
||||||
@Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) {
|
super(keyRing, arbitratorService, user, filterManager);
|
||||||
super(keyRing, arbitratorService, user, filterManager, useDevPrivilegeKeys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<String> getPubKeyList() {
|
protected List<String> getPubKeyList() {
|
||||||
switch (Config.baseCurrencyNetwork()) {
|
switch (Config.baseCurrencyNetwork()) {
|
||||||
case XMR_LOCAL:
|
case XMR_LOCAL:
|
||||||
throw new RuntimeException("No arbitrator pub key list for local XMR testnet. Set useDevPrivilegeKeys=true");
|
return List.of(
|
||||||
|
"027a381b5333a56e1cc3d90d3a7d07f26509adf7029ed06fc997c656621f8da1ee",
|
||||||
|
"024baabdba90e7cc0dc4626ef73ea9d722ea7085d1104491da8c76f28187513492",
|
||||||
|
"026eeec3c119dd6d537249d74e5752a642dd2c3cc5b6a9b44588eb58344f29b519");
|
||||||
case XMR_STAGENET:
|
case XMR_STAGENET:
|
||||||
return List.of(
|
return List.of(
|
||||||
"03bb559ce207a4deb51d4c705076c95b85ad8581d35936b2a422dcb504eaf7cdb0",
|
"03bb559ce207a4deb51d4c705076c95b85ad8581d35936b2a422dcb504eaf7cdb0",
|
||||||
|
|
|
@ -23,11 +23,9 @@ import bisq.core.user.User;
|
||||||
|
|
||||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
|
||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@ -40,9 +38,8 @@ public class MediatorManager extends DisputeAgentManager<Mediator> {
|
||||||
public MediatorManager(KeyRing keyRing,
|
public MediatorManager(KeyRing keyRing,
|
||||||
MediatorService mediatorService,
|
MediatorService mediatorService,
|
||||||
User user,
|
User user,
|
||||||
FilterManager filterManager,
|
FilterManager filterManager) {
|
||||||
@Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) {
|
super(keyRing, mediatorService, user, filterManager);
|
||||||
super(keyRing, mediatorService, user, filterManager, useDevPrivilegeKeys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -23,12 +23,10 @@ import bisq.core.user.User;
|
||||||
|
|
||||||
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
import bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||||
|
|
||||||
import bisq.common.config.Config;
|
|
||||||
import bisq.common.crypto.KeyRing;
|
import bisq.common.crypto.KeyRing;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -42,9 +40,8 @@ public class RefundAgentManager extends DisputeAgentManager<RefundAgent> {
|
||||||
public RefundAgentManager(KeyRing keyRing,
|
public RefundAgentManager(KeyRing keyRing,
|
||||||
RefundAgentService refundAgentService,
|
RefundAgentService refundAgentService,
|
||||||
User user,
|
User user,
|
||||||
FilterManager filterManager,
|
FilterManager filterManager) {
|
||||||
@Named(Config.USE_DEV_PRIVILEGE_KEYS) boolean useDevPrivilegeKeys) {
|
super(keyRing, refundAgentService, user, filterManager);
|
||||||
super(keyRing, refundAgentService, user, filterManager, useDevPrivilegeKeys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -127,7 +127,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
// deposit confirmed (TODO)
|
// deposit confirmed (TODO)
|
||||||
|
|
||||||
// deposit unlocked
|
// deposit unlocked
|
||||||
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN(Phase.DEPOSIT_UNLOCKED),
|
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN(Phase.DEPOSITS_UNLOCKED),
|
||||||
|
|
||||||
// payment sent
|
// payment sent
|
||||||
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT(Phase.PAYMENT_SENT),
|
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT(Phase.PAYMENT_SENT),
|
||||||
|
@ -191,7 +191,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
INIT,
|
INIT,
|
||||||
DEPOSIT_REQUESTED, // TODO (woodser): remove unused phases
|
DEPOSIT_REQUESTED, // TODO (woodser): remove unused phases
|
||||||
DEPOSITS_PUBLISHED,
|
DEPOSITS_PUBLISHED,
|
||||||
DEPOSIT_UNLOCKED, // TODO (woodser): rename to or add DEPOSIT_UNLOCKED
|
DEPOSITS_UNLOCKED,
|
||||||
PAYMENT_SENT,
|
PAYMENT_SENT,
|
||||||
PAYMENT_RECEIVED,
|
PAYMENT_RECEIVED,
|
||||||
PAYOUT_PUBLISHED,
|
PAYOUT_PUBLISHED,
|
||||||
|
@ -1291,7 +1291,7 @@ public abstract class Trade implements Tradable, Model {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDepositUnlocked() {
|
public boolean isDepositUnlocked() {
|
||||||
return getState().getPhase().ordinal() >= Phase.DEPOSIT_UNLOCKED.ordinal();
|
return getState().getPhase().ordinal() >= Phase.DEPOSITS_UNLOCKED.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPaymentSent() {
|
public boolean isPaymentSent() {
|
||||||
|
|
|
@ -500,9 +500,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
|
|
||||||
Offer offer = openOffer.getOffer();
|
Offer offer = openOffer.getOffer();
|
||||||
|
|
||||||
// verify request is from signer or backup arbitrator
|
// verify request is from arbitrator
|
||||||
if (!sender.equals(offer.getOfferPayload().getArbitratorSigner()) && !sender.equals(openOffer.getBackupArbitrator())) { // TODO (woodser): get backup arbitrator from maker-signed InitTradeRequest and remove from OpenOffer
|
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(sender);
|
||||||
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request must be from signer or backup arbitrator", sender, request.getTradeId());
|
if (arbitrator == null) {
|
||||||
|
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because request is not from accepted arbitrator", sender, request.getTradeId());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,7 +763,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
|
|
||||||
trade.getProcessModel().setTradeMessage(model.getTradeRequest());
|
trade.getProcessModel().setTradeMessage(model.getTradeRequest());
|
||||||
trade.getProcessModel().setMakerSignature(model.getMakerSignature());
|
trade.getProcessModel().setMakerSignature(model.getMakerSignature());
|
||||||
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());
|
||||||
|
|
|
@ -30,14 +30,13 @@ import java.util.Optional;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Value
|
@Value
|
||||||
public final class SignContractResponse extends TradeMessage implements DirectMessage {
|
public final class SignContractResponse extends TradeMessage implements DirectMessage {
|
||||||
private final NodeAddress senderNodeAddress;
|
private final NodeAddress senderNodeAddress;
|
||||||
private final PubKeyRing pubKeyRing;
|
private final PubKeyRing pubKeyRing;
|
||||||
private final long currentDate;
|
private final long currentDate;
|
||||||
|
private final String contractAsJson;
|
||||||
private final String contractSignature;
|
private final String contractSignature;
|
||||||
|
|
||||||
public SignContractResponse(String tradeId,
|
public SignContractResponse(String tradeId,
|
||||||
|
@ -46,11 +45,13 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
String uid,
|
String uid,
|
||||||
String messageVersion,
|
String messageVersion,
|
||||||
long currentDate,
|
long currentDate,
|
||||||
|
String contractAsJson,
|
||||||
String contractSignature) {
|
String contractSignature) {
|
||||||
super(messageVersion, tradeId, uid);
|
super(messageVersion, tradeId, uid);
|
||||||
this.senderNodeAddress = senderNodeAddress;
|
this.senderNodeAddress = senderNodeAddress;
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.currentDate = currentDate;
|
this.currentDate = currentDate;
|
||||||
|
this.contractAsJson = contractAsJson;
|
||||||
this.contractSignature = contractSignature;
|
this.contractSignature = contractSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +68,7 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
||||||
.setUid(uid);
|
.setUid(uid);
|
||||||
|
|
||||||
|
Optional.ofNullable(contractAsJson).ifPresent(e -> builder.setContractAsJson(contractAsJson));
|
||||||
Optional.ofNullable(contractSignature).ifPresent(e -> builder.setContractSignature(contractSignature));
|
Optional.ofNullable(contractSignature).ifPresent(e -> builder.setContractSignature(contractSignature));
|
||||||
|
|
||||||
builder.setCurrentDate(currentDate);
|
builder.setCurrentDate(currentDate);
|
||||||
|
@ -83,6 +85,7 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
proto.getUid(),
|
proto.getUid(),
|
||||||
messageVersion,
|
messageVersion,
|
||||||
proto.getCurrentDate(),
|
proto.getCurrentDate(),
|
||||||
|
ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()),
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getContractSignature()));
|
ProtoUtil.stringOrNullFromProto(proto.getContractSignature()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +95,7 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
"\n senderNodeAddress=" + senderNodeAddress +
|
"\n senderNodeAddress=" + senderNodeAddress +
|
||||||
",\n pubKeyRing=" + pubKeyRing +
|
",\n pubKeyRing=" + pubKeyRing +
|
||||||
",\n currentDate=" + currentDate +
|
",\n currentDate=" + currentDate +
|
||||||
|
",\n contractAsJson='" + contractAsJson +
|
||||||
",\n contractSignature='" + contractSignature +
|
",\n contractSignature='" + contractSignature +
|
||||||
"\n} " + super.toString();
|
"\n} " + super.toString();
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeAndMultisigRequests;
|
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeOrMultisigRequests;
|
||||||
import bisq.core.trade.protocol.tasks.ArbitratorProcessesDepositRequest;
|
import bisq.core.trade.protocol.tasks.ArbitratorProcessesDepositRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ArbitratorProcessesReserveTx;
|
import bisq.core.trade.protocol.tasks.ArbitratorProcessesReserveTx;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
||||||
|
@ -42,7 +42,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
ProcessInitTradeRequest.class,
|
ProcessInitTradeRequest.class,
|
||||||
ArbitratorProcessesReserveTx.class,
|
ArbitratorProcessesReserveTx.class,
|
||||||
ArbitratorSendsInitTradeAndMultisigRequests.class)
|
ArbitratorSendsInitTradeOrMultisigRequests.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
startTimeout(TRADE_TIMEOUT);
|
startTimeout(TRADE_TIMEOUT);
|
||||||
|
|
|
@ -26,9 +26,8 @@ import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.protocol.tasks.MakerSendsInitTradeRequestIfUnreserved;
|
import bisq.core.trade.protocol.tasks.MakerSendsInitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
@ -65,7 +64,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)
|
MakerSendsInitTradeRequest.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
startTimeout(TRADE_TIMEOUT);
|
startTimeout(TRADE_TIMEOUT);
|
||||||
|
|
|
@ -90,7 +90,7 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
BuyerEvent event = BuyerEvent.PAYMENT_SENT;
|
BuyerEvent event = BuyerEvent.PAYMENT_SENT;
|
||||||
expect(phase(Trade.Phase.DEPOSIT_UNLOCKED)
|
expect(phase(Trade.Phase.DEPOSITS_UNLOCKED)
|
||||||
.with(event)
|
.with(event)
|
||||||
.preCondition(trade.confirmPermitted()))
|
.preCondition(trade.confirmPermitted()))
|
||||||
.setup(tasks(ApplyFilter.class,
|
.setup(tasks(ApplyFilter.class,
|
||||||
|
|
|
@ -58,7 +58,7 @@ public abstract class DisputeProtocol extends TradeProtocol {
|
||||||
// Trader has not yet received the peer's signature but has clicked the accept button.
|
// Trader has not yet received the peer's signature but has clicked the accept button.
|
||||||
public void onAcceptMediationResult(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
public void onAcceptMediationResult(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
DisputeEvent event = DisputeEvent.MEDIATION_RESULT_ACCEPTED;
|
DisputeEvent event = DisputeEvent.MEDIATION_RESULT_ACCEPTED;
|
||||||
expect(anyPhase(Trade.Phase.DEPOSIT_UNLOCKED,
|
expect(anyPhase(Trade.Phase.DEPOSITS_UNLOCKED,
|
||||||
Trade.Phase.PAYMENT_SENT,
|
Trade.Phase.PAYMENT_SENT,
|
||||||
Trade.Phase.PAYMENT_RECEIVED)
|
Trade.Phase.PAYMENT_RECEIVED)
|
||||||
.with(event)
|
.with(event)
|
||||||
|
@ -85,7 +85,7 @@ public abstract class DisputeProtocol extends TradeProtocol {
|
||||||
// Trader has already received the peer's signature and has clicked the accept button as well.
|
// Trader has already received the peer's signature and has clicked the accept button as well.
|
||||||
public void onFinalizeMediationResultPayout(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
public void onFinalizeMediationResultPayout(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
DisputeEvent event = DisputeEvent.MEDIATION_RESULT_ACCEPTED;
|
DisputeEvent event = DisputeEvent.MEDIATION_RESULT_ACCEPTED;
|
||||||
expect(anyPhase(Trade.Phase.DEPOSIT_UNLOCKED,
|
expect(anyPhase(Trade.Phase.DEPOSITS_UNLOCKED,
|
||||||
Trade.Phase.PAYMENT_SENT,
|
Trade.Phase.PAYMENT_SENT,
|
||||||
Trade.Phase.PAYMENT_RECEIVED)
|
Trade.Phase.PAYMENT_RECEIVED)
|
||||||
.with(event)
|
.with(event)
|
||||||
|
@ -113,7 +113,7 @@ public abstract class DisputeProtocol extends TradeProtocol {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
protected void handle(MediatedPayoutTxSignatureMessage message, NodeAddress peer) {
|
protected void handle(MediatedPayoutTxSignatureMessage message, NodeAddress peer) {
|
||||||
expect(anyPhase(Trade.Phase.DEPOSIT_UNLOCKED,
|
expect(anyPhase(Trade.Phase.DEPOSITS_UNLOCKED,
|
||||||
Trade.Phase.PAYMENT_SENT,
|
Trade.Phase.PAYMENT_SENT,
|
||||||
Trade.Phase.PAYMENT_RECEIVED)
|
Trade.Phase.PAYMENT_RECEIVED)
|
||||||
.with(message)
|
.with(message)
|
||||||
|
@ -123,7 +123,7 @@ public abstract class DisputeProtocol extends TradeProtocol {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handle(MediatedPayoutTxPublishedMessage message, NodeAddress peer) {
|
protected void handle(MediatedPayoutTxPublishedMessage message, NodeAddress peer) {
|
||||||
expect(anyPhase(Trade.Phase.DEPOSIT_UNLOCKED,
|
expect(anyPhase(Trade.Phase.DEPOSITS_UNLOCKED,
|
||||||
Trade.Phase.PAYMENT_SENT,
|
Trade.Phase.PAYMENT_SENT,
|
||||||
Trade.Phase.PAYMENT_RECEIVED)
|
Trade.Phase.PAYMENT_RECEIVED)
|
||||||
.with(message)
|
.with(message)
|
||||||
|
|
|
@ -160,9 +160,6 @@ public class ProcessModel implements Model, PersistablePayload {
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private String makerSignature;
|
private String makerSignature;
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private NodeAddress backupArbitrator;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -231,7 +228,6 @@ public class ProcessModel implements Model, PersistablePayload {
|
||||||
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(backupArbitrator).ifPresent(e -> builder.setBackupArbitrator(backupArbitrator.toProtoMessage()));
|
|
||||||
Optional.ofNullable(multisigAddress).ifPresent(e -> builder.setMultisigAddress(multisigAddress));
|
Optional.ofNullable(multisigAddress).ifPresent(e -> builder.setMultisigAddress(multisigAddress));
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
@ -260,7 +256,6 @@ 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.setBackupArbitrator(proto.hasBackupArbitrator() ? NodeAddress.fromProto(proto.getBackupArbitrator()) : null);
|
|
||||||
processModel.setMultisigAddress(ProtoUtil.stringOrNullFromProto(proto.getMultisigAddress()));
|
processModel.setMultisigAddress(ProtoUtil.stringOrNullFromProto(proto.getMultisigAddress()));
|
||||||
|
|
||||||
String paymentStartedMessageStateString = ProtoUtil.stringOrNullFromProto(proto.getPaymentStartedMessageState());
|
String paymentStartedMessageStateString = ProtoUtil.stringOrNullFromProto(proto.getPaymentStartedMessageState());
|
||||||
|
|
|
@ -28,7 +28,7 @@ import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.tasks.MakerSendsInitTradeRequestIfUnreserved;
|
import bisq.core.trade.protocol.tasks.MakerSendsInitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
|
@ -66,7 +66,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)
|
MakerSendsInitTradeRequest.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
startTimeout(TRADE_TIMEOUT);
|
startTimeout(TRADE_TIMEOUT);
|
||||||
|
@ -123,7 +123,6 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
||||||
@Override
|
@Override
|
||||||
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
||||||
super.onTradeMessage(message, peer);
|
super.onTradeMessage(message, peer);
|
||||||
log.info("Received {} from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peer, message.getTradeId(), message.getUid());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -139,6 +139,5 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
||||||
@Override
|
@Override
|
||||||
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
||||||
super.onTradeMessage(message, peer);
|
super.onTradeMessage(message, peer);
|
||||||
log.info("Received {} from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peer, message.getTradeId(), message.getUid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
latchTrade();
|
latchTrade();
|
||||||
expect(anyPhase(Trade.Phase.DEPOSIT_UNLOCKED, Trade.Phase.DEPOSITS_PUBLISHED)
|
expect(anyPhase(Trade.Phase.DEPOSITS_UNLOCKED, Trade.Phase.DEPOSITS_PUBLISHED)
|
||||||
.with(message)
|
.with(message)
|
||||||
.from(peer)
|
.from(peer)
|
||||||
.preCondition(trade.getPayoutTx() == null,
|
.preCondition(trade.getPayoutTx() == null,
|
||||||
|
|
|
@ -17,16 +17,24 @@
|
||||||
|
|
||||||
package bisq.core.trade.protocol;
|
package bisq.core.trade.protocol;
|
||||||
|
|
||||||
|
import bisq.core.offer.availability.DisputeAgentSelection;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
|
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
|
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.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
|
import bisq.common.handlers.ResultHandler;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -42,41 +50,42 @@ public class TakerSendsInitTradeRequestToArbitrator extends TradeTask {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// send request to arbitrator
|
// send request to signing arbitrator then least used arbitrators until success
|
||||||
sendInitTradeRequest(trade.getOffer().getOfferPayload().getArbitratorSigner(), new SendDirectMessageListener() {
|
sendInitTradeRequests(trade.getOffer().getOfferPayload().getArbitratorSigner(), new HashSet<NodeAddress>(), () -> {
|
||||||
@Override
|
complete();
|
||||||
public void onArrived() {
|
}, (errorMessage) -> {
|
||||||
log.info("{} arrived at arbitrator: offerId={}", InitTradeRequest.class.getSimpleName(), trade.getId());
|
log.warn("Cannot initialize trade with arbitrators: " + errorMessage);
|
||||||
}
|
failed(errorMessage);
|
||||||
|
|
||||||
// send request to backup arbitrator if signer unavailable
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.info("Sending {} to signing arbitrator {} failed, using backup arbitrator {}. error={}", InitTradeRequest.class.getSimpleName(), trade.getOffer().getOfferPayload().getArbitratorSigner(), processModel.getBackupArbitrator(), errorMessage);
|
|
||||||
if (processModel.getBackupArbitrator() == null) {
|
|
||||||
log.warn("Cannot take offer because signing arbitrator is offline and backup arbitrator is null");
|
|
||||||
failed();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sendInitTradeRequest(processModel.getBackupArbitrator(), new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived at backup arbitrator: offerId={}", InitTradeRequest.class.getSimpleName(), trade.getId());
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) { // TODO (woodser): distinguish nack from offline
|
|
||||||
log.warn("Cannot take offer because arbitrators are unavailable: error={}.", InitTradeRequest.class.getSimpleName(), errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
|
||||||
complete(); // TODO (woodser): onArrived() doesn't get called if arbitrator rejects concurrent requests. always complete before onArrived()?
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendInitTradeRequests(NodeAddress arbitratorNodeAddress, Set<NodeAddress> excludedArbitrators, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
sendInitTradeRequest(arbitratorNodeAddress, new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived at arbitrator: offerId={}", InitTradeRequest.class.getSimpleName(), trade.getId());
|
||||||
|
resultHandler.handleResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if unavailable, try alternative arbitrator
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.warn("Arbitrator {} unavailable: {}", arbitratorNodeAddress, errorMessage);
|
||||||
|
excludedArbitrators.add(arbitratorNodeAddress);
|
||||||
|
Arbitrator altArbitrator = DisputeAgentSelection.getLeastUsedArbitrator(processModel.getTradeStatisticsManager(), processModel.getArbitratorManager(), excludedArbitrators);
|
||||||
|
if (altArbitrator == null) {
|
||||||
|
errorMessageHandler.handleErrorMessage("Cannot take offer because no arbitrators are available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info("Using alternative arbitrator {}", altArbitrator.getNodeAddress());
|
||||||
|
sendInitTradeRequests(altArbitrator.getNodeAddress(), excludedArbitrators, resultHandler, errorMessageHandler);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void sendInitTradeRequest(NodeAddress arbitratorNodeAddress, SendDirectMessageListener listener) {
|
private void sendInitTradeRequest(NodeAddress arbitratorNodeAddress, SendDirectMessageListener listener) {
|
||||||
|
|
||||||
// get registered arbitrator
|
// get registered arbitrator
|
||||||
|
|
|
@ -638,8 +638,9 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void unlatchTrade() {
|
protected void unlatchTrade() {
|
||||||
if (tradeLatch != null) tradeLatch.countDown();
|
CountDownLatch lastLatch = tradeLatch;
|
||||||
tradeLatch = null;
|
tradeLatch = null;
|
||||||
|
if (lastLatch != null) lastLatch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void awaitTradeLatch() {
|
protected void awaitTradeLatch() {
|
||||||
|
|
|
@ -22,7 +22,6 @@ import bisq.common.app.Version;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
import bisq.common.crypto.Sig;
|
import bisq.common.crypto.Sig;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.btc.wallet.XmrWalletService;
|
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferDirection;
|
import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
|
@ -84,13 +83,8 @@ public class ArbitratorProcessesDepositRequest extends TradeTask {
|
||||||
else if (trader == processModel.getTaker()) tradeFee = ParsingUtils.coinToAtomicUnits(trade.getTakerFee());
|
else if (trader == processModel.getTaker()) tradeFee = ParsingUtils.coinToAtomicUnits(trade.getTakerFee());
|
||||||
else throw new RuntimeException("DepositRequest is not from maker or taker");
|
else throw new RuntimeException("DepositRequest is not from maker or taker");
|
||||||
|
|
||||||
// flush reserve tx from pool
|
|
||||||
XmrWalletService xmrWalletService = trade.getXmrWalletService();
|
|
||||||
MoneroDaemon daemon = xmrWalletService.getDaemon();
|
|
||||||
daemon.flushTxPool(trader.getReserveTxHash());
|
|
||||||
|
|
||||||
// verify deposit tx
|
// verify deposit tx
|
||||||
xmrWalletService.verifyTradeTx(depositAddress,
|
trade.getXmrWalletService().verifyTradeTx(depositAddress,
|
||||||
depositAmount,
|
depositAmount,
|
||||||
tradeFee,
|
tradeFee,
|
||||||
trader.getDepositTxHash(),
|
trader.getDepositTxHash(),
|
||||||
|
@ -108,6 +102,7 @@ public class ArbitratorProcessesDepositRequest extends TradeTask {
|
||||||
if (processModel.getMaker().getDepositTxHex() != null && processModel.getTaker().getDepositTxHex() != null) {
|
if (processModel.getMaker().getDepositTxHex() != null && processModel.getTaker().getDepositTxHex() != null) {
|
||||||
|
|
||||||
// relay txs
|
// relay txs
|
||||||
|
MoneroDaemon daemon = trade.getXmrWalletService().getDaemon();
|
||||||
daemon.submitTxHex(processModel.getMaker().getDepositTxHex()); // TODO (woodser): check that result is good. will need to release funds if one is submitted
|
daemon.submitTxHex(processModel.getMaker().getDepositTxHex()); // TODO (woodser): check that result is good. will need to release funds if one is submitted
|
||||||
daemon.submitTxHex(processModel.getTaker().getDepositTxHex());
|
daemon.submitTxHex(processModel.getTaker().getDepositTxHex());
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,7 @@ package bisq.core.trade.protocol.tasks;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferDirection;
|
import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.offer.OfferPayload;
|
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeUtils;
|
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.protocol.TradingPeer;
|
import bisq.core.trade.protocol.TradingPeer;
|
||||||
import bisq.core.util.ParsingUtils;
|
import bisq.core.util.ParsingUtils;
|
||||||
|
|
|
@ -27,7 +27,6 @@ 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;
|
||||||
|
@ -42,10 +41,10 @@ import monero.wallet.MoneroWallet;
|
||||||
* Arbitrator sends InitMultisigRequests after the maker acks.
|
* Arbitrator sends InitMultisigRequests after the maker acks.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
public class ArbitratorSendsInitTradeOrMultisigRequests extends TradeTask {
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public ArbitratorSendsInitTradeAndMultisigRequests(TaskRunner taskHandler, Trade trade) {
|
public ArbitratorSendsInitTradeOrMultisigRequests(TaskRunner taskHandler, Trade trade) {
|
||||||
super(taskHandler, trade);
|
super(taskHandler, trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,22 +52,13 @@ public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
protected void run() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// skip if request not from taker
|
|
||||||
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(), processModel.getOfferId().getBytes(Charsets.UTF_8));
|
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), processModel.getOfferId().getBytes(Charsets.UTF_8));
|
||||||
|
|
||||||
// save pub keys
|
// handle request from taker
|
||||||
processModel.getArbitrator().setPubKeyRing(processModel.getPubKeyRing()); // TODO (woodser): why duplicating field in process model
|
if (request.getSenderNodeAddress().equals(trade.getTakerNodeAddress())) {
|
||||||
trade.setArbitratorPubKeyRing(processModel.getPubKeyRing());
|
|
||||||
trade.setMakerPubKeyRing(trade.getOffer().getPubKeyRing());
|
|
||||||
trade.setTakerPubKeyRing(request.getPubKeyRing());
|
|
||||||
|
|
||||||
// create request to initialize trade with maker
|
// create request to initialize trade with maker
|
||||||
InitTradeRequest makerRequest = new InitTradeRequest(
|
InitTradeRequest makerRequest = new InitTradeRequest(
|
||||||
|
@ -94,20 +84,6 @@ public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
|
|
||||||
// send init multisig requests on ack // TODO (woodser): only send InitMultisigRequests if arbitrator has maker reserve tx, else wait for that
|
|
||||||
TradeListener listener = new TradeListener() {
|
|
||||||
@Override
|
|
||||||
public void onAckMessage(AckMessage ackMessage, NodeAddress sender) {
|
|
||||||
if (sender.equals(trade.getMakerNodeAddress()) &&
|
|
||||||
ackMessage.getSourceMsgClassName().equals(InitTradeRequest.class.getSimpleName()) &&
|
|
||||||
ackMessage.getSourceUid().equals(makerRequest.getUid())) {
|
|
||||||
trade.removeListener(this);
|
|
||||||
if (ackMessage.isSuccess()) sendInitMultisigRequests();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
trade.addListener(listener);
|
|
||||||
|
|
||||||
// send request to maker
|
// send request to maker
|
||||||
log.info("Send {} with offerId {} and uid {} to maker {} with pub key ring", makerRequest.getClass().getSimpleName(), makerRequest.getTradeId(), makerRequest.getUid(), trade.getMakerNodeAddress(), trade.getMakerPubKeyRing());
|
log.info("Send {} with offerId {} and uid {} to maker {} with pub key ring", makerRequest.getClass().getSimpleName(), makerRequest.getTradeId(), makerRequest.getUid(), trade.getMakerNodeAddress(), trade.getMakerPubKeyRing());
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(
|
processModel.getP2PService().sendEncryptedDirectMessage(
|
||||||
|
@ -124,11 +100,19 @@ public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
public void onFault(String errorMessage) {
|
public void onFault(String errorMessage) {
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", makerRequest.getClass().getSimpleName(), makerRequest.getUid(), trade.getArbitratorNodeAddress(), errorMessage);
|
log.error("Sending {} failed: uid={}; peer={}; error={}", makerRequest.getClass().getSimpleName(), makerRequest.getUid(), trade.getArbitratorNodeAddress(), errorMessage);
|
||||||
appendToErrorMessage("Sending message failed: message=" + makerRequest + "\nerrorMessage=" + errorMessage);
|
appendToErrorMessage("Sending message failed: message=" + makerRequest + "\nerrorMessage=" + errorMessage);
|
||||||
trade.removeListener(listener);
|
|
||||||
failed();
|
failed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle request from maker
|
||||||
|
else if (request.getSenderNodeAddress().equals(trade.getMakerNodeAddress())) {
|
||||||
|
sendInitMultisigRequests();
|
||||||
|
complete(); // TODO: wait for InitMultisigRequest arrivals?
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Request is not from maker or taker");
|
||||||
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
|
@ -138,9 +122,7 @@ public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
|
|
||||||
// ensure arbitrator has maker's reserve tx
|
// ensure arbitrator has maker's reserve tx
|
||||||
if (processModel.getMaker().getReserveTxHash() == null) {
|
if (processModel.getMaker().getReserveTxHash() == null) {
|
||||||
log.warn("Arbitrator {} does not have maker's reserve tx after initializing trade", P2PService.getMyNodeAddress());
|
throw new RuntimeException("Arbitrator does not have maker's reserve tx after initializing trade");
|
||||||
failed();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// create wallet for multisig
|
// create wallet for multisig
|
||||||
|
@ -176,8 +158,6 @@ public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
@Override
|
@Override
|
||||||
public void onFault(String errorMessage) {
|
public void onFault(String errorMessage) {
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getUid(), trade.getMakerNodeAddress(), 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -196,8 +176,6 @@ public class ArbitratorSendsInitTradeAndMultisigRequests extends TradeTask {
|
||||||
@Override
|
@Override
|
||||||
public void onFault(String errorMessage) {
|
public void onFault(String errorMessage) {
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", initMultisigRequest.getClass().getSimpleName(), initMultisigRequest.getUid(), trade.getTakerNodeAddress(), 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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
|
@ -37,9 +37,9 @@ import static bisq.core.util.Validator.checkTradeId;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class MakerSendsInitTradeRequestIfUnreserved extends TradeTask {
|
public class MakerSendsInitTradeRequest extends TradeTask {
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public MakerSendsInitTradeRequestIfUnreserved(TaskRunner taskHandler, Trade trade) {
|
public MakerSendsInitTradeRequest(TaskRunner taskHandler, Trade trade) {
|
||||||
super(taskHandler, trade);
|
super(taskHandler, trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,19 +48,13 @@ public class MakerSendsInitTradeRequestIfUnreserved extends TradeTask {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// 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 makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // arbitrator's InitTradeRequest to maker
|
InitTradeRequest makerRequest = (InitTradeRequest) processModel.getTradeMessage(); // arbitrator's InitTradeRequest to maker
|
||||||
checkNotNull(makerRequest);
|
checkNotNull(makerRequest);
|
||||||
checkTradeId(processModel.getOfferId(), makerRequest);
|
checkTradeId(processModel.getOfferId(), makerRequest);
|
||||||
|
|
||||||
// maker signs offer id as nonce to avoid challenge protocol // TODO (woodser): is this necessary?
|
// maker signs offer id as nonce to avoid challenge protocol // TODO (woodser): is this necessary?
|
||||||
|
Offer offer = processModel.getOffer();
|
||||||
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), offer.getId().getBytes(Charsets.UTF_8));
|
byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), offer.getId().getBytes(Charsets.UTF_8));
|
||||||
|
|
||||||
// create request to arbitrator
|
// create request to arbitrator
|
||||||
|
@ -69,7 +63,7 @@ public class MakerSendsInitTradeRequestIfUnreserved extends TradeTask {
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
processModel.getPubKeyRing(),
|
processModel.getPubKeyRing(),
|
||||||
offer.getAmount().value,
|
offer.getAmount().value,
|
||||||
offer.getPrice().getValue(),
|
trade.getPrice().getValue(),
|
||||||
offer.getMakerFee().value,
|
offer.getMakerFee().value,
|
||||||
trade.getProcessModel().getAccountId(),
|
trade.getProcessModel().getAccountId(),
|
||||||
offer.getMakerPaymentAccountId(),
|
offer.getMakerPaymentAccountId(),
|
|
@ -60,8 +60,8 @@ public class ProcessDepositResponse extends TradeTask {
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradingPeerNodeAddress(), trade.getTradingPeerPubKeyRing(), request, new SendDirectMessageListener() {
|
processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradingPeerNodeAddress(), trade.getTradingPeerPubKeyRing(), request, new SendDirectMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onArrived() {
|
public void onArrived() {
|
||||||
complete();
|
|
||||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getTradingPeerNodeAddress(), trade.getId());
|
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getTradingPeerNodeAddress(), trade.getId());
|
||||||
|
complete();
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public void onFault(String errorMessage) {
|
public void onFault(String errorMessage) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.TradeUtils;
|
import bisq.core.trade.TradeUtils;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.protocol.TradingPeer;
|
import bisq.core.trade.protocol.TradingPeer;
|
||||||
import bisq.core.user.User;
|
|
||||||
|
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
@ -62,6 +61,9 @@ public class ProcessInitTradeRequest extends TradeTask {
|
||||||
// handle request as arbitrator
|
// handle request as arbitrator
|
||||||
TradingPeer multisigParticipant;
|
TradingPeer multisigParticipant;
|
||||||
if (trade instanceof ArbitratorTrade) {
|
if (trade instanceof ArbitratorTrade) {
|
||||||
|
trade.setMakerPubKeyRing((trade.getOffer().getPubKeyRing()));
|
||||||
|
trade.setArbitratorPubKeyRing(processModel.getPubKeyRing());
|
||||||
|
processModel.getArbitrator().setPubKeyRing(processModel.getPubKeyRing()); // TODO (woodser): why duplicating field in process model
|
||||||
|
|
||||||
// handle request from taker
|
// handle request from taker
|
||||||
if (request.getSenderNodeAddress().equals(request.getTakerNodeAddress())) {
|
if (request.getSenderNodeAddress().equals(request.getTakerNodeAddress())) {
|
||||||
|
@ -70,6 +72,17 @@ public class ProcessInitTradeRequest extends TradeTask {
|
||||||
if (trade.getTakerPubKeyRing() != null) throw new RuntimeException("Pub key ring should not be initialized before processing InitTradeRequest");
|
if (trade.getTakerPubKeyRing() != null) throw new RuntimeException("Pub key ring should not be initialized before processing InitTradeRequest");
|
||||||
trade.setTakerPubKeyRing(request.getPubKeyRing());
|
trade.setTakerPubKeyRing(request.getPubKeyRing());
|
||||||
if (!TradeUtils.isMakerSignatureValid(request, request.getMakerSignature(), offer.getPubKeyRing())) throw new RuntimeException("Maker signature is invalid for the trade request"); // verify maker signature
|
if (!TradeUtils.isMakerSignatureValid(request, request.getMakerSignature(), offer.getPubKeyRing())) throw new RuntimeException("Maker signature is invalid for the trade request"); // verify maker signature
|
||||||
|
|
||||||
|
// check trade price
|
||||||
|
try {
|
||||||
|
long tradePrice = request.getTradePrice();
|
||||||
|
offer.verifyTakersTradePrice(tradePrice);
|
||||||
|
trade.setPrice(tradePrice);
|
||||||
|
} catch (TradePriceOutOfToleranceException e) {
|
||||||
|
failed(e.getMessage());
|
||||||
|
} catch (Throwable e2) {
|
||||||
|
failed(e2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle request from maker
|
// handle request from maker
|
||||||
|
@ -79,6 +92,7 @@ public class ProcessInitTradeRequest extends TradeTask {
|
||||||
if (trade.getMakerPubKeyRing() == null) trade.setMakerPubKeyRing(request.getPubKeyRing());
|
if (trade.getMakerPubKeyRing() == null) trade.setMakerPubKeyRing(request.getPubKeyRing());
|
||||||
else if (!trade.getMakerPubKeyRing().equals(request.getPubKeyRing())) throw new RuntimeException("Init trade requests from maker and taker do not agree"); // TODO (woodser): proper handling
|
else if (!trade.getMakerPubKeyRing().equals(request.getPubKeyRing())) throw new RuntimeException("Init trade requests from maker and taker do not agree"); // TODO (woodser): proper handling
|
||||||
trade.setMakerPubKeyRing(request.getPubKeyRing());
|
trade.setMakerPubKeyRing(request.getPubKeyRing());
|
||||||
|
if (trade.getPrice().getValue() != request.getTradePrice()) throw new RuntimeException("Maker and taker price do not agree");
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("Sender is not trade's maker or taker");
|
throw new RuntimeException("Sender is not trade's maker or taker");
|
||||||
}
|
}
|
||||||
|
@ -89,6 +103,17 @@ public class ProcessInitTradeRequest extends TradeTask {
|
||||||
multisigParticipant = processModel.getTaker();
|
multisigParticipant = processModel.getTaker();
|
||||||
trade.setTakerNodeAddress(request.getSenderNodeAddress()); // arbitrator sends maker InitTradeRequest with taker's node address and pub key ring
|
trade.setTakerNodeAddress(request.getSenderNodeAddress()); // arbitrator sends maker InitTradeRequest with taker's node address and pub key ring
|
||||||
trade.setTakerPubKeyRing(request.getPubKeyRing());
|
trade.setTakerPubKeyRing(request.getPubKeyRing());
|
||||||
|
|
||||||
|
// check trade price
|
||||||
|
try {
|
||||||
|
long tradePrice = request.getTradePrice();
|
||||||
|
offer.verifyTakersTradePrice(tradePrice);
|
||||||
|
trade.setPrice(tradePrice);
|
||||||
|
} catch (TradePriceOutOfToleranceException e) {
|
||||||
|
failed(e.getMessage());
|
||||||
|
} catch (Throwable e2) {
|
||||||
|
failed(e2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle invalid trade type
|
// handle invalid trade type
|
||||||
|
@ -106,17 +131,6 @@ public class ProcessInitTradeRequest extends TradeTask {
|
||||||
multisigParticipant.setAccountAgeWitnessSignature(request.getAccountAgeWitnessSignatureOfOfferId());
|
multisigParticipant.setAccountAgeWitnessSignature(request.getAccountAgeWitnessSignatureOfOfferId());
|
||||||
multisigParticipant.setCurrentDate(request.getCurrentDate());
|
multisigParticipant.setCurrentDate(request.getCurrentDate());
|
||||||
|
|
||||||
// check trade price
|
|
||||||
try {
|
|
||||||
long tradePrice = request.getTradePrice();
|
|
||||||
offer.verifyTakersTradePrice(tradePrice);
|
|
||||||
trade.setPrice(tradePrice);
|
|
||||||
} catch (TradePriceOutOfToleranceException e) {
|
|
||||||
failed(e.getMessage());
|
|
||||||
} catch (Throwable e2) {
|
|
||||||
failed(e2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check trade amount
|
// check trade amount
|
||||||
checkArgument(request.getTradeAmount() > 0);
|
checkArgument(request.getTradeAmount() > 0);
|
||||||
trade.setAmount(Coin.valueOf(request.getTradeAmount()));
|
trade.setAmount(Coin.valueOf(request.getTradeAmount()));
|
||||||
|
|
|
@ -18,7 +18,10 @@
|
||||||
package bisq.core.trade.protocol.tasks;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
import bisq.common.crypto.Hash;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
import bisq.common.crypto.Sig;
|
import bisq.common.crypto.Sig;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
@ -77,6 +80,7 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
// save contract and signature
|
// save contract and signature
|
||||||
trade.setContract(contract);
|
trade.setContract(contract);
|
||||||
trade.setContractAsJson(contractAsJson);
|
trade.setContractAsJson(contractAsJson);
|
||||||
|
trade.setContractHash(Hash.getSha256Hash(checkNotNull(contractAsJson)));
|
||||||
trade.getSelf().setContractSignature(signature);
|
trade.getSelf().setContractSignature(signature);
|
||||||
|
|
||||||
// create response with contract signature
|
// create response with contract signature
|
||||||
|
@ -87,6 +91,7 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
UUID.randomUUID().toString(),
|
UUID.randomUUID().toString(),
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
|
contractAsJson,
|
||||||
signature);
|
signature);
|
||||||
|
|
||||||
// get response recipients. only arbitrator sends response to both peers
|
// get response recipients. only arbitrator sends response to both peers
|
||||||
|
|
|
@ -44,10 +44,13 @@ public class ProcessSignContractResponse extends TradeTask {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// get contract and signature
|
// compare contracts
|
||||||
String contractAsJson = trade.getContractAsJson();
|
String contractAsJson = trade.getContractAsJson();
|
||||||
SignContractResponse response = (SignContractResponse) processModel.getTradeMessage(); // TODO (woodser): verify response
|
SignContractResponse response = (SignContractResponse) processModel.getTradeMessage();
|
||||||
String signature = response.getContractSignature();
|
if (!contractAsJson.equals(response.getContractAsJson())) {
|
||||||
|
trade.getContract().printDiff(response.getContractAsJson());
|
||||||
|
failed("Contracts are not matching");
|
||||||
|
}
|
||||||
|
|
||||||
// get peer info
|
// get peer info
|
||||||
// TODO (woodser): make these utilities / refactor model
|
// TODO (woodser): make these utilities / refactor model
|
||||||
|
@ -60,6 +63,7 @@ public class ProcessSignContractResponse extends TradeTask {
|
||||||
|
|
||||||
// verify signature
|
// verify signature
|
||||||
// TODO (woodser): transfer contract for convenient comparison?
|
// TODO (woodser): transfer contract for convenient comparison?
|
||||||
|
String signature = response.getContractSignature();
|
||||||
if (!Sig.verify(peerPubKeyRing.getSignaturePubKey(), contractAsJson, signature)) throw new RuntimeException("Peer's contract signature is invalid");
|
if (!Sig.verify(peerPubKeyRing.getSignaturePubKey(), contractAsJson, signature)) throw new RuntimeException("Peer's contract signature is invalid");
|
||||||
|
|
||||||
// set peer's signature
|
// set peer's signature
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class ArbitratorManagerTest {
|
||||||
User user = mock(User.class);
|
User user = mock(User.class);
|
||||||
ArbitratorService arbitratorService = mock(ArbitratorService.class);
|
ArbitratorService arbitratorService = mock(ArbitratorService.class);
|
||||||
|
|
||||||
ArbitratorManager manager = new ArbitratorManager(null, arbitratorService, user, null, false);
|
ArbitratorManager manager = new ArbitratorManager(null, arbitratorService, user, null);
|
||||||
|
|
||||||
ArrayList<String> languagesOne = new ArrayList<String>() {{
|
ArrayList<String> languagesOne = new ArrayList<String>() {{
|
||||||
add("en");
|
add("en");
|
||||||
|
@ -80,7 +80,7 @@ public class ArbitratorManagerTest {
|
||||||
User user = mock(User.class);
|
User user = mock(User.class);
|
||||||
ArbitratorService arbitratorService = mock(ArbitratorService.class);
|
ArbitratorService arbitratorService = mock(ArbitratorService.class);
|
||||||
|
|
||||||
ArbitratorManager manager = new ArbitratorManager(null, arbitratorService, user, null, false);
|
ArbitratorManager manager = new ArbitratorManager(null, arbitratorService, user, null);
|
||||||
|
|
||||||
ArrayList<String> languagesOne = new ArrayList<String>() {{
|
ArrayList<String> languagesOne = new ArrayList<String>() {{
|
||||||
add("en");
|
add("en");
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package bisq.daemon.grpc;
|
package bisq.daemon.grpc;
|
||||||
|
|
||||||
import bisq.core.api.CoreApi;
|
import bisq.core.api.CoreApi;
|
||||||
|
|
||||||
import bisq.proto.grpc.RegisterDisputeAgentReply;
|
import bisq.proto.grpc.RegisterDisputeAgentReply;
|
||||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||||
|
import bisq.proto.grpc.UnregisterDisputeAgentReply;
|
||||||
|
import bisq.proto.grpc.UnregisterDisputeAgentRequest;
|
||||||
|
|
||||||
import io.grpc.ServerInterceptor;
|
import io.grpc.ServerInterceptor;
|
||||||
import io.grpc.stub.StreamObserver;
|
import io.grpc.stub.StreamObserver;
|
||||||
|
@ -18,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||||
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor;
|
||||||
import static bisq.proto.grpc.DisputeAgentsGrpc.DisputeAgentsImplBase;
|
import static bisq.proto.grpc.DisputeAgentsGrpc.DisputeAgentsImplBase;
|
||||||
import static bisq.proto.grpc.DisputeAgentsGrpc.getRegisterDisputeAgentMethod;
|
import static bisq.proto.grpc.DisputeAgentsGrpc.getRegisterDisputeAgentMethod;
|
||||||
|
import static bisq.proto.grpc.DisputeAgentsGrpc.getUnregisterDisputeAgentMethod;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,10 +43,39 @@ class GrpcDisputeAgentsService extends DisputeAgentsImplBase {
|
||||||
public void registerDisputeAgent(RegisterDisputeAgentRequest req,
|
public void registerDisputeAgent(RegisterDisputeAgentRequest req,
|
||||||
StreamObserver<RegisterDisputeAgentReply> responseObserver) {
|
StreamObserver<RegisterDisputeAgentReply> responseObserver) {
|
||||||
try {
|
try {
|
||||||
coreApi.registerDisputeAgent(req.getDisputeAgentType(), req.getRegistrationKey());
|
GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getRegisterDisputeAgentMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
|
||||||
|
coreApi.registerDisputeAgent(
|
||||||
|
req.getDisputeAgentType(),
|
||||||
|
req.getRegistrationKey(),
|
||||||
|
() -> {
|
||||||
var reply = RegisterDisputeAgentReply.newBuilder().build();
|
var reply = RegisterDisputeAgentReply.newBuilder().build();
|
||||||
responseObserver.onNext(reply);
|
responseObserver.onNext(reply);
|
||||||
responseObserver.onCompleted();
|
responseObserver.onCompleted();
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (Throwable cause) {
|
||||||
|
exceptionHandler.handleException(log, cause, responseObserver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterDisputeAgent(UnregisterDisputeAgentRequest req,
|
||||||
|
StreamObserver<UnregisterDisputeAgentReply> responseObserver) {
|
||||||
|
try {
|
||||||
|
GrpcErrorMessageHandler errorMessageHandler = new GrpcErrorMessageHandler(getUnregisterDisputeAgentMethod().getFullMethodName(), responseObserver, exceptionHandler, log);
|
||||||
|
coreApi.unregisterDisputeAgent(
|
||||||
|
req.getDisputeAgentType(),
|
||||||
|
() -> {
|
||||||
|
var reply = UnregisterDisputeAgentReply.newBuilder().build();
|
||||||
|
responseObserver.onNext(reply);
|
||||||
|
responseObserver.onCompleted();
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
if (!errorMessageHandler.isErrorHandled()) errorMessageHandler.handleErrorMessage(errorMessage);
|
||||||
|
});
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
exceptionHandler.handleException(log, cause, responseObserver);
|
exceptionHandler.handleException(log, cause, responseObserver);
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,7 +415,7 @@ class GrpcWalletsService extends WalletsImplBase {
|
||||||
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
|
||||||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||||
new HashMap<>() {{
|
new HashMap<>() {{
|
||||||
put(getGetBalancesMethod().getFullMethodName(), new GrpcCallRateMeter(20, SECONDS));
|
put(getGetBalancesMethod().getFullMethodName(), new GrpcCallRateMeter(50, SECONDS));
|
||||||
put(getGetAddressBalanceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getGetAddressBalanceMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||||
put(getGetFundingAddressesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
put(getGetFundingAddressesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
|
||||||
put(getSendBtcMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
|
put(getSendBtcMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES));
|
||||||
|
|
|
@ -192,7 +192,7 @@ public class NotificationCenter {
|
||||||
message = Res.get("notification.trade.accepted", role);
|
message = Res.get("notification.trade.accepted", role);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trade instanceof BuyerTrade && phase.ordinal() == Trade.Phase.DEPOSIT_UNLOCKED.ordinal())
|
if (trade instanceof BuyerTrade && phase.ordinal() == Trade.Phase.DEPOSITS_UNLOCKED.ordinal())
|
||||||
message = Res.get("notification.trade.confirmed");
|
message = Res.get("notification.trade.confirmed");
|
||||||
else if (trade instanceof SellerTrade && phase.ordinal() == Trade.Phase.PAYMENT_SENT.ordinal())
|
else if (trade instanceof SellerTrade && phase.ordinal() == Trade.Phase.PAYMENT_SENT.ordinal())
|
||||||
message = Res.get("notification.trade.paymentStarted");
|
message = Res.get("notification.trade.paymentStarted");
|
||||||
|
|
|
@ -162,7 +162,7 @@ public class BuyerStep2View extends TradeStepView {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case BUYER_CONFIRMED_IN_UI_PAYMENT_SENT:
|
case BUYER_CONFIRMED_IN_UI_PAYMENT_SENT:
|
||||||
busyAnimation.play();
|
busyAnimation.play();
|
||||||
statusLabel.setText("Confirming payment sent. This can take up to a few minutes depending on connection speed. Please wait...");
|
statusLabel.setText("Confirming payment sent. This can take up to a few minutes. Please wait...");
|
||||||
break;
|
break;
|
||||||
case BUYER_SENT_PAYMENT_SENT_MSG:
|
case BUYER_SENT_PAYMENT_SENT_MSG:
|
||||||
busyAnimation.play();
|
busyAnimation.play();
|
||||||
|
|
|
@ -121,7 +121,7 @@ public class SellerStep3View extends TradeStepView {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT:
|
case SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT:
|
||||||
busyAnimation.play();
|
busyAnimation.play();
|
||||||
statusLabel.setText(Res.get("Confirming payment received. This can take up to a few minutes depending on connection speed. Please wait..."));
|
statusLabel.setText(Res.get("Confirming payment received. This can take up to a few minutes. Please wait..."));
|
||||||
break;
|
break;
|
||||||
case SELLER_PUBLISHED_PAYOUT_TX:
|
case SELLER_PUBLISHED_PAYOUT_TX:
|
||||||
case SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG:
|
case SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG:
|
||||||
|
|
|
@ -845,14 +845,6 @@
|
||||||
<sha256 value="4728eddd64e6ae3e1f205a775c6a327b24bd990b86d528584a17450a8b5f00d6" origin="Generated by Gradle"/>
|
<sha256 value="4728eddd64e6ae3e1f205a775c6a327b24bd990b86d528584a17450a8b5f00d6" origin="Generated by Gradle"/>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</component>
|
||||||
<component group="io.github.monero-ecosystem" name="monero-java" version="0.7.0">
|
|
||||||
<artifact name="monero-java-0.7.0.jar">
|
|
||||||
<sha256 value="dbd9249df77c4d313b385bc5352a9e61ad930fae79acb5178813be19c2bb4771" origin="Generated by Gradle"/>
|
|
||||||
</artifact>
|
|
||||||
<artifact name="monero-java-0.7.0.pom">
|
|
||||||
<sha256 value="ead76a2facdfaa55fcc2bc4aa706e3c6eebd5df4b9dcb153a9ff01f8f0324596" origin="Generated by Gradle"/>
|
|
||||||
</artifact>
|
|
||||||
</component>
|
|
||||||
<component group="io.github.monero-ecosystem" name="monero-java" version="0.7.2">
|
<component group="io.github.monero-ecosystem" name="monero-java" version="0.7.2">
|
||||||
<artifact name="monero-java-0.7.2.jar">
|
<artifact name="monero-java-0.7.2.jar">
|
||||||
<sha256 value="166903729f2f554f2c7a9c908bc79e5940a96510852d9f9673494d346cec3c82" origin="Generated by Gradle"/>
|
<sha256 value="166903729f2f554f2c7a9c908bc79e5940a96510852d9f9673494d346cec3c82" origin="Generated by Gradle"/>
|
||||||
|
|
|
@ -220,6 +220,8 @@ message SendDisputeChatMessageReply {
|
||||||
service DisputeAgents {
|
service DisputeAgents {
|
||||||
rpc RegisterDisputeAgent (RegisterDisputeAgentRequest) returns (RegisterDisputeAgentReply) {
|
rpc RegisterDisputeAgent (RegisterDisputeAgentRequest) returns (RegisterDisputeAgentReply) {
|
||||||
}
|
}
|
||||||
|
rpc UnregisterDisputeAgent (UnregisterDisputeAgentRequest) returns (UnregisterDisputeAgentReply) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message RegisterDisputeAgentRequest {
|
message RegisterDisputeAgentRequest {
|
||||||
|
@ -230,6 +232,13 @@ message RegisterDisputeAgentRequest {
|
||||||
message RegisterDisputeAgentReply {
|
message RegisterDisputeAgentReply {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message UnregisterDisputeAgentRequest {
|
||||||
|
string dispute_agent_type = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message UnregisterDisputeAgentReply {
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Notifications
|
// Notifications
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -530,6 +539,7 @@ message OfferInfo {
|
||||||
string pub_key_ring = 26;
|
string pub_key_ring = 26;
|
||||||
string version_nr = 27;
|
string version_nr = 27;
|
||||||
int32 protocol_version = 28;
|
int32 protocol_version = 28;
|
||||||
|
string arbitrator_signer = 29;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AvailabilityResultWithDescription {
|
message AvailabilityResultWithDescription {
|
||||||
|
@ -822,19 +832,20 @@ message TradeInfo {
|
||||||
string payout_tx_id = 11;
|
string payout_tx_id = 11;
|
||||||
uint64 amount_as_long = 12;
|
uint64 amount_as_long = 12;
|
||||||
string price = 13;
|
string price = 13;
|
||||||
string trading_peer_node_address = 14;
|
string arbitrator_node_address = 14;
|
||||||
string state = 15;
|
string trading_peer_node_address = 15;
|
||||||
string phase = 16;
|
string state = 16;
|
||||||
string period_state = 17;
|
string phase = 17;
|
||||||
bool is_deposit_published = 18;
|
string period_state = 18;
|
||||||
bool is_deposit_unlocked = 19;
|
bool is_deposit_published = 19;
|
||||||
bool is_payment_sent = 20;
|
bool is_deposit_unlocked = 20;
|
||||||
bool is_payment_received = 21;
|
bool is_payment_sent = 21;
|
||||||
bool is_payout_published = 22;
|
bool is_payment_received = 22;
|
||||||
bool is_completed = 23;
|
bool is_payout_published = 23;
|
||||||
string contract_as_json = 24;
|
bool is_completed = 24;
|
||||||
ContractInfo contract = 25;
|
string contract_as_json = 25;
|
||||||
string trade_volume = 26;
|
ContractInfo contract = 26;
|
||||||
|
string trade_volume = 27;
|
||||||
|
|
||||||
string maker_deposit_tx_id = 100;
|
string maker_deposit_tx_id = 100;
|
||||||
string taker_deposit_tx_id = 101;
|
string taker_deposit_tx_id = 101;
|
||||||
|
|
|
@ -184,7 +184,6 @@ 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 backup_arbitrator = 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message RefreshOfferMessage {
|
message RefreshOfferMessage {
|
||||||
|
@ -328,7 +327,8 @@ message SignContractResponse {
|
||||||
PubKeyRing pub_key_ring = 3;
|
PubKeyRing pub_key_ring = 3;
|
||||||
string uid = 4;
|
string uid = 4;
|
||||||
int64 current_date = 5;
|
int64 current_date = 5;
|
||||||
string contract_signature = 6;
|
string contract_as_json = 6;
|
||||||
|
string contract_signature = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DepositRequest {
|
message DepositRequest {
|
||||||
|
@ -1598,14 +1598,13 @@ message OpenOffer {
|
||||||
|
|
||||||
Offer offer = 1;
|
Offer offer = 1;
|
||||||
State state = 2;
|
State state = 2;
|
||||||
NodeAddress backup_arbitrator = 3;
|
int64 trigger_price = 3;
|
||||||
int64 trigger_price = 4;
|
bool auto_split = 4;
|
||||||
bool auto_split = 5;
|
repeated string scheduled_tx_hashes = 5;
|
||||||
repeated string scheduled_tx_hashes = 6;
|
string scheduled_amount = 6; // BigInteger
|
||||||
string scheduled_amount = 7; // BigInteger
|
string reserve_tx_hash = 7;
|
||||||
string reserve_tx_hash = 8;
|
string reserve_tx_hex = 8;
|
||||||
string reserve_tx_hex = 9;
|
string reserve_tx_key = 9;
|
||||||
string reserve_tx_key = 10;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Tradable {
|
message Tradable {
|
||||||
|
@ -1665,7 +1664,7 @@ message Trade {
|
||||||
INIT = 1;
|
INIT = 1;
|
||||||
DEPOSIT_REQUESTED = 2;
|
DEPOSIT_REQUESTED = 2;
|
||||||
DEPOSITS_PUBLISHED = 3;
|
DEPOSITS_PUBLISHED = 3;
|
||||||
DEPOSITS_CONFIRMED = 4;
|
DEPOSITS_UNLOCKED = 4;
|
||||||
PAYMENT_SENT = 5;
|
PAYMENT_SENT = 5;
|
||||||
PAYMENT_RECEIVED = 6;
|
PAYMENT_RECEIVED = 6;
|
||||||
PAYOUT_PUBLISHED = 7;
|
PAYOUT_PUBLISHED = 7;
|
||||||
|
@ -1778,12 +1777,11 @@ 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 backup_arbitrator = 1002;
|
TradingPeer maker = 1002;
|
||||||
TradingPeer maker = 1003;
|
TradingPeer taker = 1003;
|
||||||
TradingPeer taker = 1004;
|
TradingPeer arbitrator = 1004;
|
||||||
TradingPeer arbitrator = 1005;
|
NodeAddress temp_trading_peer_node_address = 1005;
|
||||||
NodeAddress temp_trading_peer_node_address = 1006;
|
string multisig_address = 1006;
|
||||||
string multisig_address = 1007;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message TradingPeer {
|
message TradingPeer {
|
||||||
|
|
Loading…
Reference in a new issue