Merge remote-tracking branch 'origin/DAO' into disk-protobuffer

Conflicts:
	core/src/main/java/io/bisq/core/arbitration/DisputeManager.java
	core/src/main/java/io/bisq/core/payment/PaymentAccount.java
	core/src/main/java/io/bisq/core/trade/TradeManager.java
	core/src/main/java/io/bisq/core/trade/protocol/ProcessModel.java
	core/src/main/java/io/bisq/core/trade/protocol/tasks/taker/TakerPublishTakerFeeTx.java
	network/src/main/java/io/bisq/network/p2p/DecryptedMsgWithPubKey.java
	network/src/main/java/io/bisq/network/p2p/storage/P2PDataStorage.java
	protobuffer/src/main/java/io/bisq/protobuffer/message/offer/OfferAvailabilityResponse.java
	protobuffer/src/main/java/io/bisq/protobuffer/payload/arbitration/DisputeResult.java
	protobuffer/src/main/java/io/bisq/protobuffer/payload/offer/OfferPayload.java
	protobuffer/src/main/java/io/bisq/protobuffer/payload/trade/statistics/TradeStatistics.java
	protobuffer/src/main/proto/pb.proto
This commit is contained in:
Mike Rosseel 2017-03-27 10:56:53 +02:00
commit a05ae36acc
375 changed files with 5918 additions and 5778 deletions

View file

@ -17,14 +17,25 @@
package io.bisq.common.crypto;
import org.jetbrains.annotations.NotNull;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class Util {
public class CryptoUtils {
public static String pubKeyToString(PublicKey publicKey) {
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
}
public static PublicKey getPubKeyFromBytes(@NotNull byte[] publicKeyBytes)
throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
return KeyFactory.getInstance(Sig.KEY_ALGO, "BC").generatePublic(new X509EncodedKeySpec(publicKeyBytes));
}
}

View file

@ -52,6 +52,7 @@ public class Sig {
return keyPair;
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
e.printStackTrace();
log.error(e.toString());
throw new RuntimeException("Could not create key.");
}
}

View file

@ -17,6 +17,7 @@
package io.bisq.common.locale;
import io.bisq.common.app.DevEnv;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@ -71,6 +72,9 @@ public class Res {
return resourceBundle.getString(key);
} catch (MissingResourceException e) {
log.warn("Missing resource for key: " + key);
if (DevEnv.DEV_MODE)
throw new RuntimeException("Missing resource for key: " + key);
return key;
}
}

View file

@ -19,16 +19,14 @@ package io.bisq.common.taskrunner;
import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
@Slf4j
public class TaskRunner<T extends Model> {
private static final Logger log = LoggerFactory.getLogger(TaskRunner.class);
private final Queue<Class<? extends Task>> tasks = new LinkedBlockingQueue<>();
private final T sharedModel;
private final Class<T> sharedModelClass;
@ -64,7 +62,7 @@ public class TaskRunner<T extends Model> {
if (tasks.size() > 0) {
try {
currentTask = tasks.poll();
log.trace("Run task: " + currentTask.getSimpleName());
log.info("Run task: " + currentTask.getSimpleName());
currentTask.getDeclaredConstructor(TaskRunner.class, sharedModelClass).newInstance(this, sharedModel).run();
} catch (Throwable throwable) {
throwable.printStackTrace();

View file

@ -51,7 +51,6 @@ public class Utilities {
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
private static long lastTimeStamp = System.currentTimeMillis();
public static final String LB = System.getProperty("line.separator");
public static final String LB2 = LB + LB;
// TODO check out Jackson lib
public static String objectToJson(Object object) {

View file

@ -185,6 +185,7 @@ shared.volume=Volume
shared.makerTxFee=Maker: {0}
shared.takerTxFee=Taker: {0}
shared.securityDepositBox.description=Security deposit for BTC {0}
shared.iConfirm=I confirm
####################################################################
@ -292,7 +293,7 @@ offerbook.filterByPaymentMethod=Filter by payment method
offerbook.nrOffers=No. of offers: {0}
offerbook.volume={0} (min - max)
# e.g: Create new offer to buy BTC - {0} buy or sell, {1} BTC or other currency like ETH
offerbook.createOfferTo=Create new offer to {0} {1}
offerbook.createOfferTo=Create new offer to {0} {1}
# postfix to previous. e.g.: Create new offer to buy BTC with ETH or Create new offer to sell BTC for ETH
offerbook.buyWithOtherCurrency=with {0}
offerbook.sellForOtherCurrency=for {0}
@ -400,7 +401,7 @@ takeOffer.paymentInfo=Payment info
takeOffer.setAmountPrice=Set amount and price
takeOffer.alreadyFunded.askCancel=You have already funded that offer.\nIf you cancel now, your funds will be moved to your local bisq wallet and are available for withdrawal in the \"Funds/Available for withdrawal\" screen.\nAre you sure you want to cancel?
takeOffer.failed.offerNotAvailable=Take offer request failed because the offer is not available anymore. Maybe another trader has taken the offer in the meantime.
takeOffer.failed.offerTaken=TYou cannot take that offer because the offer was already taken by another trader.
takeOffer.failed.offerTaken=You cannot take that offer because the offer was already taken by another trader.
takeOffer.failed.offerRemoved=You cannot take that offer because the offer has been removed in the meantime.
takeOffer.failed.offererNotOnline=Take offer request failed because maker is not online anymore.
takeOffer.failed.offererOffline=You cannot take that offer because the maker is offline.
@ -410,7 +411,6 @@ takeOffer.error.noFundsLost=\n\nNo funds have left your wallet yet.\nPlease try
takeOffer.error.feePaid=\n\nThe taker fee is already paid. In the worst case you have lost that fee. We are sorry about that but keep in mind it is a very small amount.\nPlease try to restart you application and check your network connection to see if you can resolve the issue.
takeOffer.error.depositPublished=\n\nThe deposit transaction is already published.\nPlease try to restart you application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the developers for support.
takeOffer.error.payoutPublished=\n\nThe payout transaction is already published.\nPlease try to restart you application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the developers for support.
takeOffer.error.disputed=\n\nThe trade is handled already by an arbitrator.\nPlease try to restart you application and check your network connection to see if you can resolve the issue.\nIf the problem still remains please contact the arbitrator or the developers for support.
takeOffer.tac=With taking that offer I agree to the trade conditions as defined above.
@ -428,7 +428,6 @@ portfolio.pending.step2_buyer.startPayment=Start payment
portfolio.pending.step2_seller.waitPaymentStarted=Wait until payment has started
portfolio.pending.step3_buyer.waitPaymentArrived=Wait until payment arrived
portfolio.pending.step3_seller.confirmPaymentReceived=Confirm payment received
portfolio.pending.step4.waitPaymentUnlocked=Wait for payout unlock
portfolio.pending.step5.completed=Completed
portfolio.pending.step1.info=Deposit transaction has been published.\n{0} needs to wait for at least one blockchain confirmation before starting the payment.
@ -442,7 +441,7 @@ portfolio.pending.step2_buyer.copyPaste=(You can copy & paste the values from th
portfolio.pending.step2_buyer.refTextWarn=DO NOT use any additional notice in the \"reason for payment\" text like Bitcoin, BTC or bisq.
# suppress inspection "TrailingSpacesInProperty"
portfolio.pending.step2_buyer.accountDetails=Here are the trading account details of the BTC seller:\n
portfolio.pending.step2_buyer.tradeId=Please don't forget to add the trade ID \"
portfolio.pending.step2_buyer.tradeId=Please don't forget to add the trade ID
# suppress inspection "TrailingSpacesInProperty"
portfolio.pending.step2_buyer.assign= as \"reason for payment\" so the receiver can assign your payment to this trade.\n\n
portfolio.pending.step2_buyer.fees=If your bank charges fees you have to cover those fees.
@ -521,19 +520,6 @@ portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon
portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirm that you have received the payment
portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=Yes, I have received the payment
portfolio.pending.step3b_seller.waitFinalize=Wait until peer finalizes the payout transaction
portfolio.pending.step3b_seller.info=We requested from the trading peer to sign and finalize the payout transaction.\nIt might be that the other peer is offline, so we need to wait until he finalizes the transaction when he goes online again.
portfolio.pending.step3b_seller.warn=The trading peer has not finalized the payout transaction!\nHe might be offline. You need to wait until he finalizes the payout transaction.\nIf the trade has not been completed on {0} the arbitrator will investigate.
portfolio.pending.step3b_seller.openForDispute=The trading peer has not finalized the payout transaction!\nThe max. period for the trade has elapsed.\nPlease contact the arbitrator for opening a dispute.
portfolio.pending.step4_buyer.blocks=Block(s) to wait until lock time elapsed:
portfolio.pending.step4_buyer.date=Approx. date when payout gets unlocked:
portfolio.pending.step4_buyer.wait=Wait until payout lock time is over
portfolio.pending.step4_buyer.send=Sending payout transaction to peer
portfolio.pending.step4_buyer.info=The payout transaction is signed and finalized by both parties.\nFor reducing bank chargeback risks the payout transaction is blocked by a lock time.\nAfter that lock time is over the payout transaction gets published and you receive your bitcoin.
portfolio.pending.step4_buyer.sending=We are sending the payout transaction to the other peer.
portfolio.pending.step4_buyer.warn=The payout transaction is still blocked by the lock time!\nIf the trade has not been completed on {0} the arbitrator will investigate.
portfolio.pending.step5_buyer.groupTitle=Summary of completed trade
portfolio.pending.step5_buyer.totalPaid=Total fees paid:
portfolio.pending.step5_buyer.refunded=Refunded security deposit:
@ -1303,11 +1289,6 @@ formatter.asTaker={0} {1} as taker
# Domain specific
####################################################################
# dynamic values are not recognized by IntelliJ
marketPrice.ask=Ask
marketPrice.bid=Bid
marketPrice.last=Last
# we use enum values here
# dynamic values are not recognized by IntelliJ
# suppress inspection "UnusedProperty"
@ -1424,7 +1405,21 @@ payment.accountType=Account type:
payment.checking=Checking
payment.savings=Savings
payment.personalId=Personal ID:
payment.clearXchange.selected=Your selected payment method is ClearXchange.
payment.clearXchange.info=Please be sure that you fulfill the requirements for the usage of ClearXchange.\n\n\
1. You need to have your ClearXchange account verified at their platform \
before starting a trade or creating an offer.\n\n\
2. You need to have a bank account at one of the following member banks:\n\
Bank of America\n\
Capital One P2P Payments\n\
Chase QuickPay\n\
FirstBank Person to Person Transfers\n\
Frost Send Money\n\
U.S. Bank Send Money\n\
Wells Fargo SurePay\n\n\
Please use ClearXchange only if you fulfill those requirements, \
otherwise it is very likely that the ClearXchange transfer fails and the trade ends up in a dispute.\n\
If you have not fulfilled the above requirements you would lose your security deposit in such a case.
# We use constants from the code so we do not use our normal naming convention

View file

@ -400,7 +400,7 @@ takeOffer.paymentInfo=Payment info
takeOffer.setAmountPrice=Set amount and price
takeOffer.alreadyFunded.askCancel=You have already funded that offer.\nIf you cancel now, your funds will be moved to your local bisq wallet and are available for withdrawal in the \"Funds/Available for withdrawal\" screen.\nAre you sure you want to cancel?
takeOffer.failed.offerNotAvailable=Take offer request failed because the offer is not available anymore. Maybe another trader has taken the offer in the meantime.
takeOffer.failed.offerTaken=TYou cannot take that offer because the offer was already taken by another trader.
takeOffer.failed.offerTaken=You cannot take that offer because the offer was already taken by another trader.
takeOffer.failed.offerRemoved=You cannot take that offer because the offer has been removed in the meantime.
takeOffer.failed.offererNotOnline=Take offer request failed because maker is not online anymore.
takeOffer.failed.offererOffline=You cannot take that offer because the maker is offline.
@ -521,19 +521,6 @@ portfolio.pending.step3_seller.onPaymentReceived.note=Please note, that as soon
portfolio.pending.step3_seller.onPaymentReceived.confirm.headline=Confirm that you have received the payment
portfolio.pending.step3_seller.onPaymentReceived.confirm.yes=Yes, I have received the payment
portfolio.pending.step3b_seller.waitFinalize=Wait until peer finalizes the payout transaction
portfolio.pending.step3b_seller.info=We requested from the trading peer to sign and finalize the payout transaction.\nIt might be that the other peer is offline, so we need to wait until he finalizes the transaction when he goes online again.
portfolio.pending.step3b_seller.warn=The trading peer has not finalized the payout transaction!\nHe might be offline. You need to wait until he finalizes the payout transaction.\nIf the trade has not been completed on {0} the arbitrator will investigate.
portfolio.pending.step3b_seller.openForDispute=The trading peer has not finalized the payout transaction!\nThe max. period for the trade has elapsed.\nPlease contact the arbitrator for opening a dispute.
portfolio.pending.step4_buyer.blocks=Block(s) to wait until lock time elapsed:
portfolio.pending.step4_buyer.date=Approx. date when payout gets unlocked:
portfolio.pending.step4_buyer.wait=Wait until payout lock time is over
portfolio.pending.step4_buyer.send=Sending payout transaction to peer
portfolio.pending.step4_buyer.info=The payout transaction is signed and finalized by both parties.\nFor reducing bank chargeback risks the payout transaction is blocked by a lock time.\nAfter that lock time is over the payout transaction gets published and you receive your bitcoin.
portfolio.pending.step4_buyer.sending=We are sending the payout transaction to the other peer.
portfolio.pending.step4_buyer.warn=The payout transaction is still blocked by the lock time!\nIf the trade has not been completed on {0} the arbitrator will investigate.
portfolio.pending.step5_buyer.groupTitle=Summary of completed trade
portfolio.pending.step5_buyer.totalPaid=Total fees paid:
portfolio.pending.step5_buyer.refunded=Refunded security deposit:

View file

@ -17,6 +17,11 @@
<artifactId>common</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>io.bisq</groupId>
<artifactId>vo</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>io.bisq</groupId>
<artifactId>network</artifactId>

View file

@ -0,0 +1,88 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.alert;
import com.google.common.annotations.VisibleForTesting;
import io.bisq.common.app.Version;
import io.bisq.vo.alert.AlertVO;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.Delegate;
import lombok.extern.slf4j.Slf4j;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
@EqualsAndHashCode
@ToString
@Slf4j
public final class Alert {
@Delegate
@Getter
private AlertVO alertVO;
public Alert(String message,
boolean isUpdateInfo,
String version) {
//TODO builder pattern might be better
// Or refactor client code so it is not using a half baked object
this.alertVO = new AlertVO(message, isUpdateInfo, version, null, null, null);
}
public Alert(AlertVO alertVO) {
this.alertVO = alertVO;
}
public void setSigAndPubKey(String signatureAsBase64, PublicKey storagePublicKey) {
this.alertVO = new AlertVO(alertVO.getMessage(),
alertVO.isUpdateInfo(),
alertVO.getVersion(),
new X509EncodedKeySpec(storagePublicKey.getEncoded()).getEncoded(),
signatureAsBase64,
alertVO.getExtraDataMap());
}
public boolean isNewVersion() {
return isNewVersion(Version.VERSION);
}
@VisibleForTesting
protected boolean isNewVersion(String myVersion) {
// We need to support different version usages (0.5, 0.5.1, 0.5.1.1.1, 0.5.10.1)
// So we fill up the right part up to 8 digits 0.5 -> 05000000, 0.5.1.1.1 -> 05111000
// that should handle all cases.
// TODO make it more elegant and add tests :-)
// In case the input comes in a corrupted format we don't want to screw up teh app
try {
String myVersionString = myVersion.replace(".", "");
while (myVersionString.length() < 9)
myVersionString += "0";
int myVersionNum = Integer.valueOf(myVersionString);
String alertVersionString = alertVO.getVersion().replace(".", "");
while (alertVersionString.length() < 9)
alertVersionString += "0";
int alertVersionNum = Integer.valueOf(alertVersionString);
return myVersionNum < alertVersionNum;
} catch (Throwable t) {
return false;
}
}
}

View file

@ -24,9 +24,10 @@ import io.bisq.core.app.AppOptionKeys;
import io.bisq.core.user.User;
import io.bisq.network.p2p.storage.HashMapChangedListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.payload.alert.Alert;
import io.bisq.wire.payload.p2p.storage.ProtectedStorageEntry;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.payload.StoragePayload;
import io.bisq.protobuffer.payload.alert.AlertPayload;
import io.bisq.protobuffer.payload.p2p.storage.ProtectedStorageEntry;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
@ -69,8 +70,9 @@ public class AlertManager {
p2PService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
public void onAdded(ProtectedStorageEntry data) {
if (data.getStoragePayload() instanceof Alert) {
Alert alert = (Alert) data.getStoragePayload();
final StoragePayload storagePayload = data.getStoragePayload();
if (storagePayload instanceof AlertPayload) {
Alert alert = new Alert(((AlertPayload) storagePayload).getAlertVO());
if (verifySignature(alert))
alertMessageProperty.set(alert);
}
@ -78,8 +80,9 @@ public class AlertManager {
@Override
public void onRemoved(ProtectedStorageEntry data) {
if (data.getStoragePayload() instanceof Alert) {
Alert alert = (Alert) data.getStoragePayload();
final StoragePayload storagePayload = data.getStoragePayload();
if (storagePayload instanceof AlertPayload) {
Alert alert = new Alert(((AlertPayload) storagePayload).getAlertVO());
if (verifySignature(alert))
alertMessageProperty.set(null);
}
@ -106,7 +109,7 @@ public class AlertManager {
if (isKeyValid) {
signAndAddSignatureToAlertMessage(alert);
user.setDevelopersAlert(alert);
boolean result = p2PService.addData(alert, true);
boolean result = p2PService.addData(new AlertPayload(alert.getAlertVO()), true);
if (result) {
log.trace("Add alertMessage to network was successful. AlertMessage = " + alert);
}
@ -118,7 +121,7 @@ public class AlertManager {
public boolean removeAlertMessageIfKeyIsValid(String privKeyString) {
Alert alert = user.getDevelopersAlert();
if (isKeyValid(privKeyString) && alert != null) {
if (p2PService.removeData(alert, true))
if (p2PService.removeData(new AlertPayload(alert.getAlertVO()), true))
log.trace("Remove alertMessage from network was successful. AlertMessage = " + alert);
user.setDevelopersAlert(null);
@ -138,13 +141,13 @@ public class AlertManager {
}
private void signAndAddSignatureToAlertMessage(Alert alert) {
String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes());
String alertMessageAsHex = Utils.HEX.encode(alert.getMessage().getBytes());
String signatureAsBase64 = alertSigningKey.signMessage(alertMessageAsHex);
alert.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
}
private boolean verifySignature(Alert alert) {
String alertMessageAsHex = Utils.HEX.encode(alert.message.getBytes());
String alertMessageAsHex = Utils.HEX.encode(alert.getMessage().getBytes());
try {
ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(alertMessageAsHex, alert.getSignatureAsBase64());
return true;

View file

@ -24,12 +24,12 @@ import io.bisq.core.app.AppOptionKeys;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.SendMailboxMessageListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.alert.PrivateNotificationMessage;
import io.bisq.wire.payload.alert.PrivateNotification;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.alert.PrivateNotificationMessage;
import io.bisq.protobuffer.payload.alert.PrivateNotificationPayload;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.security.SignatureException;
import java.util.UUID;
import static org.bitcoinj.core.Utils.HEX;
@ -48,7 +49,7 @@ public class PrivateNotificationManager {
private final P2PService p2PService;
private final KeyRing keyRing;
private final ObjectProperty<PrivateNotification> privateNotificationMessageProperty = new SimpleObjectProperty<>();
private final ObjectProperty<PrivateNotificationPayload> privateNotificationMessageProperty = new SimpleObjectProperty<>();
// Pub key for developer global privateNotification message
private static final String pubKeyAsHex = DevEnv.USE_DEV_PRIVILEGE_KEYS ?
@ -81,7 +82,7 @@ public class PrivateNotificationManager {
PrivateNotificationMessage privateNotificationMessage = (PrivateNotificationMessage) message;
log.trace("Received privateNotificationMessage: " + privateNotificationMessage);
if (privateNotificationMessage.getSenderNodeAddress().equals(senderNodeAddress)) {
final PrivateNotification privateNotification = privateNotificationMessage.privateNotification;
final PrivateNotificationPayload privateNotification = privateNotificationMessage.privateNotificationPayload;
if (verifySignature(privateNotification))
privateNotificationMessageProperty.set(privateNotification);
} else {
@ -95,18 +96,20 @@ public class PrivateNotificationManager {
// API
///////////////////////////////////////////////////////////////////////////////////////////
public ReadOnlyObjectProperty<PrivateNotification> privateNotificationProperty() {
public ReadOnlyObjectProperty<PrivateNotificationPayload> privateNotificationProperty() {
return privateNotificationMessageProperty;
}
public boolean sendPrivateNotificationMessageIfKeyIsValid(PrivateNotification privateNotification, PubKeyRing pubKeyRing, NodeAddress nodeAddress,
public boolean sendPrivateNotificationMessageIfKeyIsValid(PrivateNotificationPayload privateNotification, PubKeyRing pubKeyRing, NodeAddress nodeAddress,
String privKeyString, SendMailboxMessageListener sendMailboxMessageListener) {
boolean isKeyValid = isKeyValid(privKeyString);
if (isKeyValid) {
signAndAddSignatureToPrivateNotificationMessage(privateNotification);
p2PService.sendEncryptedMailboxMessage(nodeAddress,
pubKeyRing,
new PrivateNotificationMessage(privateNotification, p2PService.getNetworkNode().getNodeAddress()),
new PrivateNotificationMessage(privateNotification,
p2PService.getNetworkNode().getNodeAddress(),
UUID.randomUUID().toString()),
sendMailboxMessageListener);
}
@ -126,13 +129,13 @@ public class PrivateNotificationManager {
}
}
private void signAndAddSignatureToPrivateNotificationMessage(PrivateNotification privateNotification) {
private void signAndAddSignatureToPrivateNotificationMessage(PrivateNotificationPayload privateNotification) {
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
String signatureAsBase64 = privateNotificationSigningKey.signMessage(privateNotificationMessageAsHex);
privateNotification.setSigAndPubKey(signatureAsBase64, keyRing.getSignatureKeyPair().getPublic());
}
private boolean verifySignature(PrivateNotification privateNotification) {
private boolean verifySignature(PrivateNotificationPayload privateNotification) {
String privateNotificationMessageAsHex = Utils.HEX.encode(privateNotification.message.getBytes());
try {
ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(privateNotificationMessageAsHex, privateNotification.getSignatureAsBase64());

View file

@ -28,7 +28,7 @@ import io.bisq.core.btc.UserAgent;
import io.bisq.core.dao.RpcOptionKeys;
import io.bisq.core.exceptions.BisqException;
import io.bisq.network.NetworkOptionKeys;
import io.bisq.wire.crypto.KeyStorage;
import io.bisq.protobuffer.crypto.KeyStorage;
import joptsimple.OptionSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -28,14 +28,16 @@ import io.bisq.core.user.User;
import io.bisq.network.p2p.BootstrapListener;
import io.bisq.network.p2p.storage.HashMapChangedListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.payload.arbitration.Arbitrator;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.p2p.storage.ProtectedStorageEntry;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.payload.arbitration.Arbitrator;
import io.bisq.protobuffer.payload.arbitration.Mediator;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.p2p.storage.ProtectedStorageEntry;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Utils;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -109,6 +111,9 @@ public class ArbitratorManager {
persistedAcceptedArbitrators = new ArrayList<>(user.getAcceptedArbitrators());
user.clearAcceptedArbitrators();
// TODO we mirror arbitrator data for mediator as long we have not impl. it in the UI
user.clearAcceptedMediators();
arbitratorService.addHashSetChangedListener(new HashMapChangedListener() {
@Override
public void onAdded(ProtectedStorageEntry data) {
@ -165,17 +170,25 @@ public class ArbitratorManager {
Map<NodeAddress, Arbitrator> filtered = map.values().stream()
.filter(e -> isPublicKeyInList(Utils.HEX.encode(e.getRegistrationPubKey()))
&& verifySignature(e.getPubKeyRing().getSignaturePubKey(), e.getRegistrationPubKey(), e.getRegistrationSignature()))
.collect(Collectors.toMap(Arbitrator::getArbitratorNodeAddress, Function.identity()));
.collect(Collectors.toMap(Arbitrator::getNodeAddress, Function.identity()));
arbitratorsObservableMap.putAll(filtered);
arbitratorsObservableMap.values().stream()
.filter(arbitrator -> persistedAcceptedArbitrators.contains(arbitrator))
.forEach(user::addAcceptedArbitrator);
.forEach(a -> {
user.addAcceptedArbitrator(a);
user.addAcceptedMediator(getMediator(a)
);
});
if (preferences.getAutoSelectArbitrators()) {
arbitratorsObservableMap.values().stream()
.filter(user::hasMatchingLanguage)
.forEach(user::addAcceptedArbitrator);
.forEach(a -> {
user.addAcceptedArbitrator(a);
user.addAcceptedMediator(getMediator(a)
);
});
} else {
// if we don't have any arbitrator we set all matching
// we use a delay as we might get our matching arbitrator a bit delayed (first we get one we did not selected
@ -184,15 +197,32 @@ public class ArbitratorManager {
if (user.getAcceptedArbitrators().isEmpty()) {
arbitratorsObservableMap.values().stream()
.filter(user::hasMatchingLanguage)
.forEach(user::addAcceptedArbitrator);
.forEach(a -> {
user.addAcceptedArbitrator(a);
user.addAcceptedMediator(getMediator(a)
);
});
}
}, 100, TimeUnit.MILLISECONDS);
}
}
// TODO we mirror arbitrator data for mediator as long we have not impl. it in the UI
@NotNull
public static Mediator getMediator(Arbitrator arbitrator) {
return new Mediator(arbitrator.getNodeAddress(),
arbitrator.getPubKeyRing(),
arbitrator.getLanguageCodes(),
new Date(arbitrator.getRegistrationDate()),
arbitrator.getRegistrationPubKey(),
arbitrator.getRegistrationSignature(),
arbitrator.getEmailAddress(),
arbitrator.getExtraDataMap());
}
public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
user.setRegisteredArbitrator(arbitrator);
arbitratorsObservableMap.put(arbitrator.getArbitratorNodeAddress(), arbitrator);
arbitratorsObservableMap.put(arbitrator.getNodeAddress(), arbitrator);
arbitratorService.addArbitrator(arbitrator,
() -> {
log.debug("Arbitrator successfully saved in P2P network");
@ -208,7 +238,7 @@ public class ArbitratorManager {
Arbitrator registeredArbitrator = user.getRegisteredArbitrator();
if (registeredArbitrator != null) {
user.setRegisteredArbitrator(null);
arbitratorsObservableMap.remove(registeredArbitrator.getArbitratorNodeAddress());
arbitratorsObservableMap.remove(registeredArbitrator.getNodeAddress());
arbitratorService.removeArbitrator(registeredArbitrator,
() -> {
log.debug("Arbitrator successfully removed from P2P network");

View file

@ -21,8 +21,8 @@ import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.network.p2p.storage.HashMapChangedListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.payload.arbitration.Arbitrator;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.arbitration.Arbitrator;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -91,7 +91,7 @@ public class ArbitratorService {
Map<NodeAddress, Arbitrator> map = new HashMap<>();
for (Arbitrator arbitrator : arbitratorSet) {
NodeAddress arbitratorNodeAddress = arbitrator.getArbitratorNodeAddress();
NodeAddress arbitratorNodeAddress = arbitrator.getNodeAddress();
if (!map.containsKey(arbitratorNodeAddress))
map.put(arbitratorNodeAddress, arbitrator);
else

View file

@ -41,16 +41,16 @@ import io.bisq.network.p2p.BootstrapListener;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.SendMailboxMessageListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.persisted.payload.arbitration.DisputeList;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.arbitration.*;
import io.bisq.wire.payload.arbitration.Attachment;
import io.bisq.wire.payload.arbitration.Dispute;
import io.bisq.wire.payload.arbitration.DisputeResult;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.trade.Contract;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.arbitration.*;
import io.bisq.protobuffer.payload.arbitration.Attachment;
import io.bisq.protobuffer.payload.arbitration.Dispute;
import io.bisq.protobuffer.payload.arbitration.DisputeResult;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.trade.Contract;
import io.bisq.protobuffer.persistence.arbitration.DisputeList;
import javafx.collections.ObservableList;
import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Transaction;
@ -215,12 +215,20 @@ public class DisputeManager {
String sysMsg = dispute.isSupportTicket() ?
Res.get("support.youOpenedTicket")
: Res.get("support.youOpenedDispute", disputeInfo);
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(
dispute.getTradeId(),
keyRing.getPubKeyRing().hashCode(),
true,
false,
Res.get("support.systemMsg", sysMsg),
p2PService.getAddress());
disputeCommunicationMessage.setIsSystemMessage(true);
null,
p2PService.getAddress(),
new Date().getTime(),
false,
false,
UUID.randomUUID().toString()
);
disputeCommunicationMessage.setSystemMessage(true);
dispute.addDisputeMessage(disputeCommunicationMessage);
if (!reOpen) {
disputes.add(dispute);
@ -228,7 +236,8 @@ public class DisputeManager {
p2PService.sendEncryptedMailboxMessage(dispute.getContract().arbitratorNodeAddress,
dispute.getArbitratorPubKeyRing(),
new OpenNewDisputeMessage(dispute, p2PService.getAddress()),
new OpenNewDisputeMessage(dispute, p2PService.getAddress(),
UUID.randomUUID().toString()),
new SendMailboxMessageListener() {
@Override
public void onArrived() {
@ -271,7 +280,7 @@ public class DisputeManager {
disputeFromOpener.getTradeId(),
pubKeyRing.hashCode(),
!disputeFromOpener.isDisputeOpenerIsBuyer(),
!disputeFromOpener.isDisputeOpenerIsOfferer(),
!disputeFromOpener.isDisputeOpenerIsMaker(),
pubKeyRing,
disputeFromOpener.getTradeDate(),
contractFromOpener,
@ -281,7 +290,7 @@ public class DisputeManager {
disputeFromOpener.getDepositTxId(),
disputeFromOpener.getPayoutTxId(),
disputeFromOpener.getContractAsJson(),
disputeFromOpener.getOffererContractSignature(),
disputeFromOpener.getMakerContractSignature(),
disputeFromOpener.getTakerContractSignature(),
disputeFromOpener.getArbitratorPubKeyRing(),
disputeFromOpener.isSupportTicket()
@ -291,12 +300,19 @@ public class DisputeManager {
String sysMsg = dispute.isSupportTicket() ?
Res.get("support.peerOpenedTicket")
: Res.get("support.peerOpenedDispute", disputeInfo);
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(
dispute.getTradeId(),
keyRing.getPubKeyRing().hashCode(),
true,
false,
Res.get("support.systemMsg", sysMsg),
p2PService.getAddress());
disputeCommunicationMessage.setIsSystemMessage(true);
null,
p2PService.getAddress(),
new Date().getTime(),
false,
false,
UUID.randomUUID().toString()
);
disputeCommunicationMessage.setSystemMessage(true);
dispute.addDisputeMessage(disputeCommunicationMessage);
disputes.add(dispute);
@ -307,7 +323,9 @@ public class DisputeManager {
log.trace("sendPeerOpenedDisputeMessage to peerAddress " + peerNodeAddress);
p2PService.sendEncryptedMailboxMessage(peerNodeAddress,
peersPubKeyRing,
new PeerOpenedDisputeMessage(dispute, p2PService.getAddress()),
new PeerOpenedDisputeMessage(dispute,
p2PService.getAddress(),
UUID.randomUUID().toString()),
new SendMailboxMessageListener() {
@Override
public void onArrived() {
@ -333,11 +351,19 @@ public class DisputeManager {
// traders send msg to the arbitrator or arbitrator to 1 trader (trader to trader is not allowed)
public DisputeCommunicationMessage sendDisputeDirectMessage(Dispute dispute, String text, ArrayList<Attachment> attachments) {
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(
dispute.getTradeId(),
dispute.getTraderPubKeyRing().hashCode(),
isTrader(dispute),
text,
p2PService.getAddress());
null,
p2PService.getAddress(),
new Date().getTime(),
false,
false,
UUID.randomUUID().toString()
);
disputeCommunicationMessage.addAllAttachments(attachments);
PubKeyRing receiverPubKeyRing = null;
NodeAddress peerNodeAddress = null;
@ -386,11 +412,19 @@ public class DisputeManager {
// arbitrator send result to trader
public void sendDisputeResultMessage(DisputeResult disputeResult, Dispute dispute, String text) {
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(dispute.getTradeId(),
DisputeCommunicationMessage disputeCommunicationMessage = new DisputeCommunicationMessage(
dispute.getTradeId(),
dispute.getTraderPubKeyRing().hashCode(),
false,
text,
p2PService.getAddress());
null,
p2PService.getAddress(),
new Date().getTime(),
false,
false,
UUID.randomUUID().toString()
);
dispute.addDisputeMessage(disputeCommunicationMessage);
disputeResult.setDisputeCommunicationMessage(disputeCommunicationMessage);
@ -402,7 +436,8 @@ public class DisputeManager {
peerNodeAddress = contract.getSellerNodeAddress();
p2PService.sendEncryptedMailboxMessage(peerNodeAddress,
dispute.getTraderPubKeyRing(),
new DisputeResultMessage(disputeResult, p2PService.getAddress()),
new DisputeResultMessage(disputeResult, p2PService.getAddress(),
UUID.randomUUID().toString()),
new SendMailboxMessageListener() {
@Override
public void onArrived() {
@ -429,7 +464,10 @@ public class DisputeManager {
log.trace("sendPeerPublishedPayoutTxMessage to peerAddress " + peerNodeAddress);
p2PService.sendEncryptedMailboxMessage(peerNodeAddress,
peersPubKeyRing,
new PeerPublishedPayoutTxMessage(transaction.bitcoinSerialize(), dispute.getTradeId(), p2PService.getAddress()),
new PeerPublishedPayoutTxMessage(transaction.bitcoinSerialize(),
dispute.getTradeId(),
p2PService.getAddress(),
UUID.randomUUID().toString()),
new SendMailboxMessageListener() {
@Override
public void onArrived() {
@ -604,7 +642,7 @@ public class DisputeManager {
contract.getSellerMultiSigPubKey(),
disputeResult.getArbitratorPubKey()
);
Transaction committedDisputedPayoutTx = tradeWalletService.addTransactionToWallet(signedDisputedPayoutTx);
Transaction committedDisputedPayoutTx = tradeWalletService.addTxToWallet(signedDisputedPayoutTx);
log.debug("broadcast committedDisputedPayoutTx");
tradeWalletService.broadcastTx(committedDisputedPayoutTx, new FutureCallback<Transaction>() {
@Override
@ -678,7 +716,7 @@ public class DisputeManager {
if (disputeOptional.isPresent()) {
cleanupRetryMap(uid);
Transaction walletTx = tradeWalletService.addTransactionToWallet(peerPublishedPayoutTxMessage.transaction);
Transaction walletTx = tradeWalletService.addTxToWallet(peerPublishedPayoutTxMessage.transaction);
disputeOptional.get().setDisputePayoutTxId(walletTx.getHashAsString());
BtcWalletService.printTx("Disputed payoutTx received from peer", walletTx);
tradeManager.closeDisputedTrade(tradeId);

View file

@ -17,7 +17,7 @@
package io.bisq.core.btc.data;
import io.bisq.wire.payload.btc.RawTransactionInput;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import javax.annotation.Nullable;
import java.util.ArrayList;

View file

@ -17,16 +17,16 @@
package io.bisq.core.btc.data;
import io.bisq.wire.payload.btc.RawTransactionInput;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import java.util.ArrayList;
public class PreparedDepositTxAndOffererInputs {
public final ArrayList<RawTransactionInput> rawOffererInputs;
public class PreparedDepositTxAndMakerInputs {
public final ArrayList<RawTransactionInput> rawMakerInputs;
public final byte[] depositTransaction;
public PreparedDepositTxAndOffererInputs(ArrayList<RawTransactionInput> rawOffererInputs, byte[] depositTransaction) {
this.rawOffererInputs = rawOffererInputs;
public PreparedDepositTxAndMakerInputs(ArrayList<RawTransactionInput> rawMakerInputs, byte[] depositTransaction) {
this.rawMakerInputs = rawMakerInputs;
this.depositTransaction = depositTransaction;
}
}

View file

@ -292,7 +292,8 @@ public class BtcWalletService extends WalletService {
if (addressEntry.isPresent()) {
return addressEntry.get();
} else {
AddressEntry entry = addressEntryList.addAddressEntry(new AddressEntry(wallet.freshReceiveKey(), wallet.getParams(), context, offerId));
AddressEntry entry = addressEntryList.addAddressEntry(new AddressEntry(wallet.freshReceiveKey(),
wallet.getParams(), context, offerId));
saveAddressEntryList();
return entry;
}

View file

@ -26,12 +26,12 @@ import io.bisq.common.app.Log;
import io.bisq.common.util.Utilities;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.data.InputsAndChangeOutput;
import io.bisq.core.btc.data.PreparedDepositTxAndOffererInputs;
import io.bisq.core.btc.data.PreparedDepositTxAndMakerInputs;
import io.bisq.core.btc.exceptions.SigningException;
import io.bisq.core.btc.exceptions.TransactionVerificationException;
import io.bisq.core.btc.exceptions.WalletException;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.btc.RawTransactionInput;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import org.bitcoinj.core.*;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.TransactionSignature;
@ -151,8 +151,10 @@ public class TradeWalletService {
* @throws InsufficientMoneyException
* @throws AddressFormatException
*/
public Transaction createTradingFeeTx(Address fundingAddress, Address reservedForTradeAddress, Address changeAddress, Coin reservedFundsForOffer,
boolean useSavingsWallet, Coin tradingFee, Coin txFee, String feeReceiverAddresses)
public Transaction createTradingFeeTx(Address fundingAddress, Address reservedForTradeAddress,
Address changeAddress, Coin reservedFundsForOffer,
boolean useSavingsWallet, Coin tradingFee, Coin txFee,
String feeReceiverAddresses)
throws InsufficientMoneyException, AddressFormatException {
Transaction tradingFeeTx = new Transaction(params);
tradingFeeTx.addOutput(tradingFee, new Address(params, feeReceiverAddresses));
@ -189,14 +191,14 @@ public class TradeWalletService {
///////////////////////////////////////////////////////////////////////////////////////////
// We construct the deposit transaction in the way that the buyer is always the first entry (inputs, outputs, MS keys) and then the seller.
// In the creation of the deposit tx the taker/offerer roles are the determining roles instead of buyer/seller.
// In the creation of the deposit tx the taker/maker roles are the determining roles instead of buyer/seller.
// In the payout tx is is the buyer/seller role. We keep the buyer/seller ordering over all transactions to not get confusion with ordering,
// which is important to follow correctly specially for the order of the MS keys.
/**
* The taker creates a dummy transaction to get the input(s) and optional change output for the amount and the takersAddress for that trade.
* That will be used to send to the offerer for creating the deposit transaction.
* That will be used to send to the maker for creating the deposit transaction.
*
* @param inputAmount Amount of takers input
* @param txFee Mining fee
@ -276,47 +278,47 @@ public class TradeWalletService {
}
/**
* The offerer creates the deposit transaction using the takers input(s) and optional output and signs his input(s).
* The maker creates the deposit transaction using the takers input(s) and optional output and signs his input(s).
*
* @param offererIsBuyer The flag indicating if we are in the offerer as buyer role or the opposite.
* @param makerIsBuyer The flag indicating if we are in the maker as buyer role or the opposite.
* @param contractHash The hash of the contract to be added to the OP_RETURN output.
* @param offererInputAmount The input amount of the offerer.
* @param makerInputAmount The input amount of the maker.
* @param msOutputAmount The output amount to our MS output.
* @param takerRawTransactionInputs Raw data for the connected outputs for all inputs of the taker (normally 1 input)
* @param takerChangeOutputValue Optional taker change output value
* @param takerChangeAddressString Optional taker change address
* @param offererAddress The offerer's address.
* @param offererChangeAddress The offerer's change address.
* @param makerAddress The maker's address.
* @param makerChangeAddress The maker's change address.
* @param buyerPubKey The public key of the buyer.
* @param sellerPubKey The public key of the seller.
* @param arbitratorPubKey The public key of the arbitrator.
* @return A data container holding the serialized transaction and the offerer raw inputs
* @return A data container holding the serialized transaction and the maker raw inputs
* @throws SigningException
* @throws TransactionVerificationException
* @throws WalletException
*/
public PreparedDepositTxAndOffererInputs offererCreatesAndSignsDepositTx(boolean offererIsBuyer,
byte[] contractHash,
Coin offererInputAmount,
Coin msOutputAmount,
List<RawTransactionInput> takerRawTransactionInputs,
long takerChangeOutputValue,
@Nullable String takerChangeAddressString,
Address offererAddress,
Address offererChangeAddress,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
public PreparedDepositTxAndMakerInputs makerCreatesAndSignsDepositTx(boolean makerIsBuyer,
byte[] contractHash,
Coin makerInputAmount,
Coin msOutputAmount,
List<RawTransactionInput> takerRawTransactionInputs,
long takerChangeOutputValue,
@Nullable String takerChangeAddressString,
Address makerAddress,
Address makerChangeAddress,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
throws SigningException, TransactionVerificationException, WalletException, AddressFormatException {
log.trace("offererCreatesAndSignsDepositTx called");
log.trace("offererIsBuyer " + offererIsBuyer);
log.trace("offererInputAmount " + offererInputAmount.toFriendlyString());
log.trace("makerCreatesAndSignsDepositTx called");
log.trace("makerIsBuyer " + makerIsBuyer);
log.trace("makerInputAmount " + makerInputAmount.toFriendlyString());
log.trace("msOutputAmount " + msOutputAmount.toFriendlyString());
log.trace("takerRawInputs " + takerRawTransactionInputs.toString());
log.trace("takerChangeOutputValue " + takerChangeOutputValue);
log.trace("takerChangeAddressString " + takerChangeAddressString);
log.trace("offererAddress " + offererAddress);
log.trace("offererChangeAddress " + offererChangeAddress);
log.trace("makerAddress " + makerAddress);
log.trace("makerChangeAddress " + makerChangeAddress);
log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
@ -326,29 +328,29 @@ public class TradeWalletService {
// First we construct a dummy TX to get the inputs and outputs we want to use for the real deposit tx.
// Similar to the way we did in the createTakerDepositTxInputs method.
Transaction dummyTx = new Transaction(params);
TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, offererInputAmount, new ECKey().toAddress(params));
TransactionOutput dummyOutput = new TransactionOutput(params, dummyTx, makerInputAmount, new ECKey().toAddress(params));
dummyTx.addOutput(dummyOutput);
addAvailableInputsAndChangeOutputs(dummyTx, offererAddress, offererChangeAddress, Coin.ZERO);
addAvailableInputsAndChangeOutputs(dummyTx, makerAddress, makerChangeAddress, Coin.ZERO);
// Normally we have only 1 input but we support multiple inputs if the user has paid in with several transactions.
List<TransactionInput> offererInputs = dummyTx.getInputs();
TransactionOutput offererOutput = null;
List<TransactionInput> makerInputs = dummyTx.getInputs();
TransactionOutput makerOutput = null;
// We don't support more then 1 optional change output
Preconditions.checkArgument(dummyTx.getOutputs().size() < 3, "dummyTx.getOutputs().size() >= 3");
// Only save change outputs, the dummy output is ignored (that's why we start with index 1)
if (dummyTx.getOutputs().size() > 1)
offererOutput = dummyTx.getOutput(1);
makerOutput = dummyTx.getOutput(1);
// Now we construct the real deposit tx
Transaction preparedDepositTx = new Transaction(params);
ArrayList<RawTransactionInput> offererRawTransactionInputs = new ArrayList<>();
if (offererIsBuyer) {
ArrayList<RawTransactionInput> makerRawTransactionInputs = new ArrayList<>();
if (makerIsBuyer) {
// Add buyer inputs
for (TransactionInput input : offererInputs) {
for (TransactionInput input : makerInputs) {
preparedDepositTx.addInput(input);
offererRawTransactionInputs.add(getRawInputFromTransactionInput(input));
makerRawTransactionInputs.add(getRawInputFromTransactionInput(input));
}
// Add seller inputs
@ -364,9 +366,9 @@ public class TradeWalletService {
preparedDepositTx.addInput(getTransactionInput(preparedDepositTx, new byte[]{}, rawTransactionInput));
// Add seller inputs
for (TransactionInput input : offererInputs) {
for (TransactionInput input : makerInputs) {
preparedDepositTx.addInput(input);
offererRawTransactionInputs.add(getRawInputFromTransactionInput(input));
makerRawTransactionInputs.add(getRawInputFromTransactionInput(input));
}
}
@ -388,10 +390,10 @@ public class TradeWalletService {
takerTransactionOutput = new TransactionOutput(params, preparedDepositTx, Coin.valueOf(takerChangeOutputValue),
new Address(params, takerChangeAddressString));
if (offererIsBuyer) {
if (makerIsBuyer) {
// Add optional buyer outputs
if (offererOutput != null)
preparedDepositTx.addOutput(offererOutput);
if (makerOutput != null)
preparedDepositTx.addOutput(makerOutput);
// Add optional seller outputs
if (takerTransactionOutput != null)
@ -404,13 +406,13 @@ public class TradeWalletService {
preparedDepositTx.addOutput(takerTransactionOutput);
// Add optional buyer outputs
if (offererOutput != null)
preparedDepositTx.addOutput(offererOutput);
if (makerOutput != null)
preparedDepositTx.addOutput(makerOutput);
}
// Sign inputs
int start = offererIsBuyer ? 0 : takerRawTransactionInputs.size();
int end = offererIsBuyer ? offererInputs.size() : preparedDepositTx.getInputs().size();
int start = makerIsBuyer ? 0 : takerRawTransactionInputs.size();
int end = makerIsBuyer ? makerInputs.size() : preparedDepositTx.getInputs().size();
for (int i = start; i < end; i++) {
TransactionInput input = preparedDepositTx.getInput(i);
signInput(preparedDepositTx, input, i);
@ -421,15 +423,15 @@ public class TradeWalletService {
verifyTransaction(preparedDepositTx);
return new PreparedDepositTxAndOffererInputs(offererRawTransactionInputs, preparedDepositTx.bitcoinSerialize());
return new PreparedDepositTxAndMakerInputs(makerRawTransactionInputs, preparedDepositTx.bitcoinSerialize());
}
/**
* The taker signs the deposit transaction he received from the offerer and publishes it.
* The taker signs the deposit transaction he received from the maker and publishes it.
*
* @param takerIsSeller The flag indicating if we are in the taker as seller role or the opposite.
* @param contractHash The hash of the contract to be added to the OP_RETURN output.
* @param offerersDepositTxSerialized The prepared deposit transaction signed by the offerer.
* @param makersDepositTxSerialized The prepared deposit transaction signed by the maker.
* @param buyerInputs The connected outputs for all inputs of the buyer.
* @param sellerInputs The connected outputs for all inputs of the seller.
* @param buyerPubKey The public key of the buyer.
@ -442,7 +444,7 @@ public class TradeWalletService {
*/
public Transaction takerSignsAndPublishesDepositTx(boolean takerIsSeller,
byte[] contractHash,
byte[] offerersDepositTxSerialized,
byte[] makersDepositTxSerialized,
List<RawTransactionInput> buyerInputs,
List<RawTransactionInput> sellerInputs,
byte[] buyerPubKey,
@ -450,11 +452,11 @@ public class TradeWalletService {
byte[] arbitratorPubKey,
FutureCallback<Transaction> callback) throws SigningException, TransactionVerificationException,
WalletException {
Transaction offerersDepositTx = new Transaction(params, offerersDepositTxSerialized);
Transaction makersDepositTx = new Transaction(params, makersDepositTxSerialized);
log.trace("signAndPublishDepositTx called");
log.trace("takerIsSeller " + takerIsSeller);
log.trace("offerersDepositTx " + offerersDepositTx.toString());
log.trace("makersDepositTx " + makersDepositTx.toString());
log.trace("buyerConnectedOutputsForAllInputs " + buyerInputs.toString());
log.trace("sellerConnectedOutputsForAllInputs " + sellerInputs.toString());
log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
@ -464,20 +466,20 @@ public class TradeWalletService {
checkArgument(!buyerInputs.isEmpty());
checkArgument(!sellerInputs.isEmpty());
// Check if offerer's Multisig script is identical to the takers
// Check if maker's Multisig script is identical to the takers
Script p2SHMultiSigOutputScript = getP2SHMultiSigOutputScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
if (!offerersDepositTx.getOutput(0).getScriptPubKey().equals(p2SHMultiSigOutputScript))
throw new TransactionVerificationException("Offerer's p2SHMultiSigOutputScript does not match to takers p2SHMultiSigOutputScript");
if (!makersDepositTx.getOutput(0).getScriptPubKey().equals(p2SHMultiSigOutputScript))
throw new TransactionVerificationException("Maker's p2SHMultiSigOutputScript does not match to takers p2SHMultiSigOutputScript");
// The outpoints are not available from the serialized offerersDepositTx, so we cannot use that tx directly, but we use it to construct a new
// The outpoints are not available from the serialized makersDepositTx, so we cannot use that tx directly, but we use it to construct a new
// depositTx
Transaction depositTx = new Transaction(params);
if (takerIsSeller) {
// Add buyer inputs and apply signature
// We grab the signature from the offerersDepositTx and apply it to the new tx input
// We grab the signature from the makersDepositTx and apply it to the new tx input
for (int i = 0; i < buyerInputs.size(); i++)
depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(offerersDepositTx, i), buyerInputs.get(i)));
depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(makersDepositTx, i), buyerInputs.get(i)));
// Add seller inputs
for (RawTransactionInput rawTransactionInput : sellerInputs)
@ -489,23 +491,23 @@ public class TradeWalletService {
depositTx.addInput(getTransactionInput(depositTx, new byte[]{}, rawTransactionInput));
// Add seller inputs
// We grab the signature from the offerersDepositTx and apply it to the new tx input
for (int i = buyerInputs.size(), k = 0; i < offerersDepositTx.getInputs().size(); i++, k++)
depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(offerersDepositTx, i), sellerInputs.get(k)));
// We grab the signature from the makersDepositTx and apply it to the new tx input
for (int i = buyerInputs.size(), k = 0; i < makersDepositTx.getInputs().size(); i++, k++)
depositTx.addInput(getTransactionInput(depositTx, getScriptProgram(makersDepositTx, i), sellerInputs.get(k)));
}
// Check if OP_RETURN output with contract hash matches the one from the offerer
TransactionOutput contractHashOutput = new TransactionOutput(params, offerersDepositTx, Coin.ZERO,
// Check if OP_RETURN output with contract hash matches the one from the maker
TransactionOutput contractHashOutput = new TransactionOutput(params, makersDepositTx, Coin.ZERO,
ScriptBuilder.createOpReturnScript(contractHash).getProgram());
log.debug("contractHashOutput " + contractHashOutput);
TransactionOutput offerersContractHashOutput = offerersDepositTx.getOutputs().get(1);
log.debug("offerersContractHashOutput " + offerersContractHashOutput);
if (!offerersContractHashOutput.getScriptPubKey().equals(contractHashOutput.getScriptPubKey()))
throw new TransactionVerificationException("Offerer's transaction output for the contract hash is not matching takers version.");
TransactionOutput makersContractHashOutput = makersDepositTx.getOutputs().get(1);
log.debug("makersContractHashOutput " + makersContractHashOutput);
if (!makersContractHashOutput.getScriptPubKey().equals(contractHashOutput.getScriptPubKey()))
throw new TransactionVerificationException("Maker's transaction output for the contract hash is not matching takers version.");
// Add all outputs from offerersDepositTx to depositTx
offerersDepositTx.getOutputs().forEach(depositTx::addOutput);
//BtcWalletService.printTx("offerersDepositTx", offerersDepositTx);
// Add all outputs from makersDepositTx to depositTx
makersDepositTx.getOutputs().forEach(depositTx::addOutput);
//BtcWalletService.printTx("makersDepositTx", makersDepositTx);
// Sign inputs
int start = takerIsSeller ? buyerInputs.size() : 0;
@ -539,7 +541,6 @@ public class TradeWalletService {
* @param buyerPayoutAddressString Address for buyer
* @param sellerPayoutAddressString Address for seller
* @param multiSigKeyPair DeterministicKey for MultiSig from seller
* @param lockTime Lock time
* @param buyerPubKey The public key of the buyer.
* @param sellerPubKey The public key of the seller.
* @param arbitratorPubKey The public key of the arbitrator.
@ -547,16 +548,15 @@ public class TradeWalletService {
* @throws AddressFormatException
* @throws TransactionVerificationException
*/
public byte[] sellerSignsPayoutTx(Transaction depositTx,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
String buyerPayoutAddressString,
String sellerPayoutAddressString,
DeterministicKey multiSigKeyPair,
long lockTime,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
public byte[] buyerSignsPayoutTx(Transaction depositTx,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
String buyerPayoutAddressString,
String sellerPayoutAddressString,
DeterministicKey multiSigKeyPair,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
throws AddressFormatException, TransactionVerificationException {
log.trace("sellerSignsPayoutTx called");
log.trace("depositTx " + depositTx.toString());
@ -565,7 +565,6 @@ public class TradeWalletService {
log.trace("buyerPayoutAddressString " + buyerPayoutAddressString);
log.trace("sellerPayoutAddressString " + sellerPayoutAddressString);
log.trace("multiSigKeyPair (not displayed for security reasons)");
log.trace("lockTime " + lockTime);
log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
@ -573,9 +572,7 @@ public class TradeWalletService {
buyerPayoutAmount,
sellerPayoutAmount,
buyerPayoutAddressString,
sellerPayoutAddressString,
lockTime
);
sellerPayoutAddressString);
// MS redeemScript
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
// MS output from prev. tx is index 0
@ -584,26 +581,26 @@ public class TradeWalletService {
if (multiSigKeyPair.isEncrypted())
checkNotNull(aesKey);
ECKey.ECDSASignature sellerSignature = multiSigKeyPair.sign(sigHash, aesKey).toCanonicalised();
ECKey.ECDSASignature buyerSignature = multiSigKeyPair.sign(sigHash, aesKey).toCanonicalised();
BtcWalletService.printTx("prepared payoutTx", preparedPayoutTx);
verifyTransaction(preparedPayoutTx);
return sellerSignature.encodeToDER();
return buyerSignature.encodeToDER();
}
/**
* Buyer creates and signs payout transaction and adds signature of seller to complete the transaction
*
* @param depositTx Deposit transaction
* @param sellerSignature DER encoded canonical signature of seller
* @param buyerSignature DER encoded canonical signature of seller
* @param buyerPayoutAmount Payout amount for buyer
* @param sellerPayoutAmount Payout amount for seller
* @param buyerPayoutAddressString Address for buyer
* @param sellerPayoutAddressString Address for seller
* @param multiSigKeyPair Buyer's keypair for MultiSig
* @param lockTime Lock time
* @param buyerPubKey The public key of the buyer.
* @param sellerPubKey The public key of the seller.
* @param arbitratorPubKey The public key of the arbitrator.
@ -612,28 +609,26 @@ public class TradeWalletService {
* @throws TransactionVerificationException
* @throws WalletException
*/
public Transaction buyerSignsAndFinalizesPayoutTx(Transaction depositTx,
byte[] sellerSignature,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
String buyerPayoutAddressString,
String sellerPayoutAddressString,
DeterministicKey multiSigKeyPair,
long lockTime,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
public Transaction sellerSignsAndFinalizesPayoutTx(Transaction depositTx,
byte[] buyerSignature,
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
String buyerPayoutAddressString,
String sellerPayoutAddressString,
DeterministicKey multiSigKeyPair,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey)
throws AddressFormatException, TransactionVerificationException, WalletException {
log.trace("buyerSignsAndFinalizesPayoutTx called");
log.trace("depositTx " + depositTx.toString());
log.trace("sellerSignature r " + ECKey.ECDSASignature.decodeFromDER(sellerSignature).r.toString());
log.trace("sellerSignature s " + ECKey.ECDSASignature.decodeFromDER(sellerSignature).s.toString());
log.trace("buyerSignature r " + ECKey.ECDSASignature.decodeFromDER(buyerSignature).r.toString());
log.trace("buyerSignature s " + ECKey.ECDSASignature.decodeFromDER(buyerSignature).s.toString());
log.trace("buyerPayoutAmount " + buyerPayoutAmount.toFriendlyString());
log.trace("sellerPayoutAmount " + sellerPayoutAmount.toFriendlyString());
log.trace("buyerPayoutAddressString " + buyerPayoutAddressString);
log.trace("sellerPayoutAddressString " + sellerPayoutAddressString);
log.trace("multiSigKeyPair (not displayed for security reasons)");
log.trace("lockTime " + lockTime);
log.info("buyerPubKey " + ECKey.fromPublicOnly(buyerPubKey).toString());
log.info("sellerPubKey " + ECKey.fromPublicOnly(sellerPubKey).toString());
log.info("arbitratorPubKey " + ECKey.fromPublicOnly(arbitratorPubKey).toString());
@ -642,8 +637,7 @@ public class TradeWalletService {
buyerPayoutAmount,
sellerPayoutAmount,
buyerPayoutAddressString,
sellerPayoutAddressString,
lockTime);
sellerPayoutAddressString);
// MS redeemScript
Script redeemScript = getMultiSigRedeemScript(buyerPubKey, sellerPubKey, arbitratorPubKey);
// MS output from prev. tx is index 0
@ -652,10 +646,12 @@ public class TradeWalletService {
if (multiSigKeyPair.isEncrypted())
checkNotNull(aesKey);
ECKey.ECDSASignature buyerSignature = multiSigKeyPair.sign(sigHash, aesKey).toCanonicalised();
TransactionSignature sellerTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(sellerSignature), Transaction.SigHash.ALL, false);
TransactionSignature buyerTxSig = new TransactionSignature(buyerSignature, Transaction.SigHash.ALL, false);
ECKey.ECDSASignature sellerSignature = multiSigKeyPair.sign(sigHash, aesKey).toCanonicalised();
TransactionSignature buyerTxSig = new TransactionSignature(ECKey.ECDSASignature.decodeFromDER(buyerSignature),
Transaction.SigHash.ALL, false);
TransactionSignature sellerTxSig = new TransactionSignature(sellerSignature, Transaction.SigHash.ALL, false);
// Take care of order of signatures. Need to be reversed here. See comment below at getMultiSigRedeemScript (arbitrator, seller, buyer)
Script inputScript = ScriptBuilder.createP2SHMultiSigInputScript(ImmutableList.of(sellerTxSig, buyerTxSig), redeemScript);
@ -669,12 +665,10 @@ public class TradeWalletService {
checkScriptSig(payoutTx, input, 0);
checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null");
input.verify(input.getConnectedOutput());
// As we use lockTime the tx will not be relayed as it is not considered standard.
// We need to broadcast on our own when we reahced the block height. Both peers will do the broadcast.
return payoutTx;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Dispute
///////////////////////////////////////////////////////////////////////////////////////////
@ -818,9 +812,6 @@ public class TradeWalletService {
checkScriptSig(payoutTx, input, 0);
checkNotNull(input.getConnectedOutput(), "input.getConnectedOutput() must not be null");
input.verify(input.getConnectedOutput());
// As we use lockTime the tx will not be relayed as it is not considered standard.
// We need to broadcast on our own when we reached the block height. Both peers will do the broadcast.
return payoutTx;
}
@ -944,7 +935,7 @@ public class TradeWalletService {
* @return The transaction we added to the wallet, which is different as the one we passed as argument!
* @throws VerificationException
*/
public Transaction addTransactionToWallet(Transaction transaction) throws VerificationException {
public Transaction addTxToWallet(Transaction transaction) throws VerificationException {
Log.traceCall("transaction " + transaction.toString());
// We need to recreate the transaction otherwise we get a null pointer...
@ -961,7 +952,7 @@ public class TradeWalletService {
* @return The transaction we added to the wallet, which is different as the one we passed as argument!
* @throws VerificationException
*/
public Transaction addTransactionToWallet(byte[] serializedTransaction) throws VerificationException {
public Transaction addTxToWallet(byte[] serializedTransaction) throws VerificationException {
Log.traceCall();
// We need to recreate the tx otherwise we get a null pointer...
@ -984,35 +975,6 @@ public class TradeWalletService {
return wallet.getTransaction(txId);
}
/**
* Returns the height of the last seen best-chain block. Can be 0 if a wallet is brand new or -1 if the wallet
* is old and doesn't have that data.
*/
public int getLastBlockSeenHeight() {
checkNotNull(wallet);
return wallet.getLastBlockSeenHeight();
}
public ListenableFuture<StoredBlock> getBlockHeightFuture(Transaction transaction) {
checkNotNull(walletConfig);
return walletConfig.chain().getHeightFuture((int) transaction.getLockTime());
}
public int getBestChainHeight() {
checkNotNull(walletConfig);
return walletConfig.chain().getBestChainHeight();
}
public void addBlockChainListener(BlockChainListener blockChainListener) {
checkNotNull(walletConfig);
walletConfig.chain().addListener(blockChainListener);
}
public void removeBlockChainListener(BlockChainListener blockChainListener) {
checkNotNull(walletConfig);
walletConfig.chain().removeListener(blockChainListener);
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private methods
@ -1027,10 +989,10 @@ public class TradeWalletService {
return new RawTransactionInput(input.getOutpoint().getIndex(), input.getConnectedOutput().getParentTransaction().bitcoinSerialize(), input.getValue().value);
}
private byte[] getScriptProgram(Transaction offerersDepositTx, int i) throws TransactionVerificationException {
byte[] scriptProgram = offerersDepositTx.getInputs().get(i).getScriptSig().getProgram();
private byte[] getScriptProgram(Transaction makersDepositTx, int i) throws TransactionVerificationException {
byte[] scriptProgram = makersDepositTx.getInputs().get(i).getScriptSig().getProgram();
if (scriptProgram.length == 0)
throw new TransactionVerificationException("Inputs from offerer not signed.");
throw new TransactionVerificationException("Inputs from maker not signed.");
return scriptProgram;
}
@ -1071,19 +1033,12 @@ public class TradeWalletService {
Coin buyerPayoutAmount,
Coin sellerPayoutAmount,
String buyerAddressString,
String sellerAddressString,
long lockTime) throws AddressFormatException {
String sellerAddressString) throws AddressFormatException {
TransactionOutput p2SHMultiSigOutput = depositTx.getOutput(0);
Transaction transaction = new Transaction(params);
transaction.addInput(p2SHMultiSigOutput);
transaction.addOutput(buyerPayoutAmount, new Address(params, buyerAddressString));
transaction.addOutput(sellerPayoutAmount, new Address(params, sellerAddressString));
if (lockTime != 0) {
log.debug("We use a lockTime of " + lockTime);
// When using lockTime we need to set sequenceNumber to 0
transaction.getInputs().stream().forEach(i -> i.setSequenceNumber(0));
transaction.setLockTime(lockTime);
}
return transaction;
}

View file

@ -205,8 +205,7 @@ public class WalletConfig extends AbstractIdleService {
*/
public WalletConfig connectToLocalHost() {
try {
final InetAddress localHost = InetAddress.getLocalHost();
return setPeerNodes(new PeerAddress(localHost, params.getPort()));
return setPeerNodes(new PeerAddress(InetAddress.getLocalHost(), params.getPort()));
} catch (UnknownHostException e) {
// Borked machine with no loopback adapter configured properly.
throw new RuntimeException(e);
@ -476,8 +475,9 @@ public class WalletConfig extends AbstractIdleService {
Futures.addCallback(vPeerGroup.startAsync(), new FutureCallback() {
@Override
public void onSuccess(@Nullable Object result) {
final PeerEventListener l = downloadListener == null ? new DownloadProgressTracker() : downloadListener;
vPeerGroup.startBlockChainDownload(l);
final PeerEventListener listener = downloadListener == null ?
new DownloadProgressTracker() : downloadListener;
vPeerGroup.startBlockChainDownload(listener);
}
@Override

View file

@ -303,6 +303,7 @@ public abstract class WalletService {
// TransactionConfidence
///////////////////////////////////////////////////////////////////////////////////////////
@Nullable
public TransactionConfidence getConfidenceForAddress(Address address) {
List<TransactionConfidence> transactionConfidenceList = new ArrayList<>();
if (wallet != null) {
@ -361,7 +362,7 @@ public abstract class WalletService {
return mergedOutputs;
}
@Nullable
protected TransactionConfidence getMostRecentConfidence(List<TransactionConfidence> transactionConfidenceList) {
TransactionConfidence transactionConfidence = null;
for (TransactionConfidence confidence : transactionConfidenceList) {
@ -459,7 +460,7 @@ public abstract class WalletService {
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
public Transaction getTransactionFromSerializedTx(byte[] tx) {
public Transaction getTxFromSerializedTx(byte[] tx) {
return new Transaction(params, tx);
}

View file

@ -154,8 +154,7 @@ public class WalletsSetup {
final PeerGroup peerGroup = walletConfig.peerGroup();
// We don't want to get our node white list polluted with nodes from AddressMessage calls.
if (preferences.getBitcoinNodes() != null && !preferences.getBitcoinNodes().isEmpty()
)
if (preferences.getBitcoinNodes() != null && !preferences.getBitcoinNodes().isEmpty())
peerGroup.setAddPeersFromAddressMessage(false);
peerGroup.addEventListener(new PeerEventListener() {
@ -423,7 +422,7 @@ public class WalletsSetup {
checkNotNull(seed, "Seed must be not be null.");
backupWallets();
Context ctx = Context.get();
new Thread(() -> {
try {

View file

@ -19,7 +19,7 @@ package io.bisq.core.dao.compensation;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Persistable;
import io.bisq.wire.payload.dao.compensation.CompensationRequestPayload;
import io.bisq.protobuffer.payload.dao.compensation.CompensationRequestPayload;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -26,9 +26,9 @@ import io.bisq.core.dao.DaoPeriodService;
import io.bisq.core.dao.vote.VotingDefaultValues;
import io.bisq.network.p2p.storage.HashMapChangedListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.payload.StoragePayload;
import io.bisq.wire.payload.dao.compensation.CompensationRequestPayload;
import io.bisq.wire.payload.p2p.storage.ProtectedStorageEntry;
import io.bisq.protobuffer.payload.StoragePayload;
import io.bisq.protobuffer.payload.dao.compensation.CompensationRequestPayload;
import io.bisq.protobuffer.payload.p2p.storage.ProtectedStorageEntry;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.bitcoinj.core.Coin;

View file

@ -28,7 +28,7 @@ import io.bisq.core.dao.DaoPeriodService;
import io.bisq.core.dao.compensation.CompensationRequest;
import io.bisq.core.dao.compensation.CompensationRequestManager;
import io.bisq.core.provider.fee.FeeService;
import io.bisq.wire.payload.dao.compensation.CompensationRequestPayload;
import io.bisq.protobuffer.payload.dao.compensation.CompensationRequestPayload;
import org.apache.commons.lang3.StringUtils;
import org.bitcoinj.core.Utils;
import org.slf4j.Logger;

View file

@ -22,13 +22,13 @@ import com.google.inject.name.Named;
import io.bisq.common.app.DevEnv;
import io.bisq.core.app.AppOptionKeys;
import io.bisq.core.user.User;
import io.bisq.generated.protobuffer.PB;
import io.bisq.network.p2p.storage.HashMapChangedListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.payload.filter.Filter;
import io.bisq.wire.payload.filter.PaymentAccountFilter;
import io.bisq.wire.payload.p2p.storage.ProtectedStorageEntry;
import io.bisq.wire.proto.Messages;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.payload.filter.Filter;
import io.bisq.protobuffer.payload.filter.PaymentAccountFilter;
import io.bisq.protobuffer.payload.p2p.storage.ProtectedStorageEntry;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
@ -164,7 +164,7 @@ public class FilterManager {
}
private String getHexFromData(Filter filter) {
Messages.Filter.Builder builder = Messages.Filter.newBuilder().addAllBannedNodeAddress(filter.bannedNodeAddress)
PB.Filter.Builder builder = PB.Filter.newBuilder().addAllBannedNodeAddress(filter.bannedNodeAddress)
.addAllBannedOfferIds(filter.bannedOfferIds)
.addAllBannedPaymentAccounts(filter.bannedPaymentAccounts.stream()
.map(PaymentAccountFilter::toProtoBuf)

View file

@ -14,11 +14,11 @@ import io.bisq.core.offer.availability.OfferAvailabilityModel;
import io.bisq.core.offer.availability.OfferAvailabilityProtocol;
import io.bisq.core.provider.price.MarketPrice;
import io.bisq.core.provider.price.PriceFeedService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.offer.OfferPayload;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.offer.OfferPayload;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import javafx.beans.property.*;
import lombok.Getter;
import lombok.Setter;
@ -52,7 +52,7 @@ public class Offer implements Serializable {
AVAILABLE,
NOT_AVAILABLE,
REMOVED,
OFFERER_OFFLINE
MAKER_OFFLINE
}
@ -135,7 +135,7 @@ public class Offer implements Serializable {
if (offerPayload.isUseMarketBasedPrice()) {
checkNotNull(priceFeedService, "priceFeed must not be null");
MarketPrice marketPrice = priceFeedService.getMarketPrice(currencyCode);
if (marketPrice != null) {
if (marketPrice != null && marketPrice.isValid()) {
double factor;
double marketPriceMargin = offerPayload.getMarketPriceMargin();
if (CurrencyUtil.isCryptoCurrency(currencyCode)) {
@ -171,16 +171,16 @@ public class Offer implements Serializable {
public void checkTradePriceTolerance(long takersTradePrice) throws TradePriceOutOfToleranceException,
MarketPriceNotAvailableException, IllegalArgumentException {
checkArgument(takersTradePrice > 0, "takersTradePrice must be positive");
Price tradePrice = Price.valueOf(getCurrencyCode(), takersTradePrice);
Price offerPrice = getPrice();
if (offerPrice == null)
throw new MarketPriceNotAvailableException("Market price required for calculating trade price is not available.");
checkArgument(takersTradePrice > 0, "takersTradePrice must be positive");
double factor = (double) takersTradePrice / (double) offerPrice.getValue();
// We allow max. 1 % difference between own offerPayload price calculation and takers calculation.
// Market price might be different at offerer's and takers side so we need a bit of tolerance.
// Market price might be different at maker's and takers side so we need a bit of tolerance.
// The tolerance will get smaller once we have multiple price feeds avoiding fast price fluctuations
// from one provider.
if (Math.abs(1 - factor) > 0.01) {
@ -272,7 +272,9 @@ public class Offer implements Serializable {
}
public PaymentMethod getPaymentMethod() {
return PaymentMethod.getPaymentMethodById(offerPayload.getPaymentMethodId());
return new PaymentMethod(offerPayload.getPaymentMethodId(),
offerPayload.getMaxTradePeriod(),
Coin.valueOf(offerPayload.getMaxTradeLimit()));
}
// utils
@ -312,6 +314,10 @@ public class Offer implements Serializable {
return errorMessageProperty;
}
public String getErrorMessage() {
return errorMessageProperty.get();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Delegate Getter (boilerplate code generated via IntelliJ generate delegate feature)
@ -329,6 +335,10 @@ public class Offer implements Serializable {
return offerPayload.getArbitratorNodeAddresses();
}
public List<NodeAddress> getMediatorNodeAddresses() {
return offerPayload.getMediatorNodeAddresses();
}
@Nullable
public List<String> getAcceptedBankIds() {
return offerPayload.getAcceptedBankIds();
@ -367,16 +377,16 @@ public class Offer implements Serializable {
return offerPayload.getMarketPriceMargin();
}
public NodeAddress getOffererNodeAddress() {
return offerPayload.getOffererNodeAddress();
public NodeAddress getMakerNodeAddress() {
return offerPayload.getMakerNodeAddress();
}
public PubKeyRing getPubKeyRing() {
return offerPayload.getPubKeyRing();
}
public String getOffererPaymentAccountId() {
return offerPayload.getOffererPaymentAccountId();
public String getMakerPaymentAccountId() {
return offerPayload.getMakerPaymentAccountId();
}
public String getOfferFeePaymentTxId() {
@ -443,7 +453,7 @@ public class Offer implements Serializable {
if (offerPayload != null ? !offerPayload.equals(offer.offerPayload) : offer.offerPayload != null) return false;
if (state != offer.state) return false;
return !(errorMessageProperty != null ? !errorMessageProperty.equals(offer.errorMessageProperty) : offer.errorMessageProperty != null);
return !(getErrorMessage() != null ? !getErrorMessage().equals(offer.getErrorMessage()) : offer.getErrorMessage() != null);
}
@ -451,16 +461,16 @@ public class Offer implements Serializable {
public int hashCode() {
int result = offerPayload != null ? offerPayload.hashCode() : 0;
result = 31 * result + (state != null ? state.hashCode() : 0);
result = 31 * result + (errorMessageProperty != null ? errorMessageProperty.hashCode() : 0);
result = 31 * result + (getErrorMessage() != null ? getErrorMessage().hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Offer{" +
"offerPayload=" + offerPayload +
"getErrorMessage()='" + getErrorMessage() + '\'' +
", state=" + state +
", errorMessageProperty=" + errorMessageProperty +
", offerPayload=" + offerPayload +
'}';
}
}

View file

@ -29,8 +29,8 @@ import io.bisq.core.provider.price.PriceFeedService;
import io.bisq.network.p2p.BootstrapListener;
import io.bisq.network.p2p.storage.HashMapChangedListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.payload.offer.OfferPayload;
import io.bisq.wire.payload.p2p.storage.ProtectedStorageEntry;
import io.bisq.protobuffer.payload.offer.OfferPayload;
import io.bisq.protobuffer.payload.p2p.storage.ProtectedStorageEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -3,7 +3,7 @@ package io.bisq.core.offer;
import io.bisq.common.locale.CurrencyUtil;
import io.bisq.common.monetary.Price;
import io.bisq.common.monetary.Volume;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import org.bitcoinj.core.Coin;
import org.bitcoinj.utils.MonetaryFormat;
import org.slf4j.Logger;

View file

@ -44,12 +44,12 @@ import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.SendDirectMessageListener;
import io.bisq.network.p2p.peers.PeerManager;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.offer.OfferAvailabilityRequest;
import io.bisq.wire.message.offer.OfferAvailabilityResponse;
import io.bisq.wire.payload.offer.AvailabilityResult;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.offer.OfferAvailabilityRequest;
import io.bisq.protobuffer.message.offer.OfferAvailabilityResponse;
import io.bisq.protobuffer.payload.offer.AvailabilityResult;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import javafx.collections.ObservableList;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
@ -368,27 +368,26 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
if (openOfferOptional.isPresent()) {
if (openOfferOptional.get().getState() == OpenOffer.State.AVAILABLE) {
final Offer offer = openOfferOptional.get().getOffer();
if (!preferences.getIgnoreTradersList().stream().filter(i -> i.equals(offer.getOffererNodeAddress().getHostNameWithoutPostFix())).findAny().isPresent()) {
if (!preferences.getIgnoreTradersList().stream().filter(i -> i.equals(offer.getMakerNodeAddress().getHostNameWithoutPostFix())).findAny().isPresent()) {
availabilityResult = AvailabilityResult.AVAILABLE;
// TODO mediators not impl yet
List<NodeAddress> acceptedArbitrators = user.getAcceptedArbitratorAddresses();
if (acceptedArbitrators != null && !acceptedArbitrators.isEmpty()) {
// We need to be backward compatible. takersTradePrice was not used before 0.4.9.
if (message.takersTradePrice > 0) {
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
// in trade price between the peers. Also here poor connectivity might cause market price API connection
// losses and therefore an outdated market price.
try {
offer.checkTradePriceTolerance(message.takersTradePrice);
} catch (TradePriceOutOfToleranceException e) {
log.warn("Trade price check failed because takers price is outside out tolerance.");
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
} catch (MarketPriceNotAvailableException e) {
log.warn(e.getMessage());
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
} catch (Throwable e) {
log.warn("Trade price check failed. " + e.getMessage());
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
}
// Check also tradePrice to avoid failures after taker fee is paid caused by a too big difference
// in trade price between the peers. Also here poor connectivity might cause market price API connection
// losses and therefore an outdated market price.
try {
offer.checkTradePriceTolerance(message.takersTradePrice);
} catch (TradePriceOutOfToleranceException e) {
log.warn("Trade price check failed because takers price is outside out tolerance.");
availabilityResult = AvailabilityResult.PRICE_OUT_OF_TOLERANCE;
} catch (MarketPriceNotAvailableException e) {
log.warn(e.getMessage());
availabilityResult = AvailabilityResult.MARKET_PRICE_NOT_AVAILABLE;
} catch (Throwable e) {
log.warn("Trade price check failed. " + e.getMessage());
availabilityResult = AvailabilityResult.UNKNOWN_FAILURE;
}
} else {
log.warn("acceptedArbitrators is null or empty: acceptedArbitrators=" + acceptedArbitrators);

View file

@ -20,9 +20,9 @@ package io.bisq.core.offer.availability;
import io.bisq.common.taskrunner.Model;
import io.bisq.core.offer.Offer;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.message.offer.OfferAvailabilityResponse;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.message.offer.OfferAvailabilityResponse;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -27,9 +27,9 @@ import io.bisq.core.offer.availability.tasks.ProcessOfferAvailabilityResponse;
import io.bisq.core.offer.availability.tasks.SendOfferAvailabilityRequest;
import io.bisq.core.util.Validator;
import io.bisq.network.p2p.DecryptedDirectMessageListener;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.offer.OfferAvailabilityResponse;
import io.bisq.wire.message.offer.OfferMessage;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.offer.OfferAvailabilityResponse;
import io.bisq.protobuffer.message.offer.OfferMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -85,7 +85,7 @@ public class OfferAvailabilityProtocol {
model.offer.setState(Offer.State.UNDEFINED);
model.p2PService.addDecryptedDirectMessageListener(decryptedDirectMessageListener);
model.setPeerNodeAddress(model.offer.getOffererNodeAddress());
model.setPeerNodeAddress(model.offer.getMakerNodeAddress());
taskRunner = new TaskRunner<>(model,
() -> log.debug("sequence at sendOfferAvailabilityRequest completed"),
@ -135,7 +135,7 @@ public class OfferAvailabilityProtocol {
if (timeoutTimer == null) {
timeoutTimer = UserThread.runAfter(() -> {
log.debug("Timeout reached at " + this);
model.offer.setState(Offer.State.OFFERER_OFFLINE);
model.offer.setState(Offer.State.MAKER_OFFLINE);
errorMessageHandler.handleErrorMessage("Timeout reached: Peer has not responded.");
}, TIMEOUT_SEC);
} else {

View file

@ -21,8 +21,8 @@ import io.bisq.common.taskrunner.Task;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.offer.Offer;
import io.bisq.core.offer.availability.OfferAvailabilityModel;
import io.bisq.wire.message.offer.OfferAvailabilityResponse;
import io.bisq.wire.payload.offer.AvailabilityResult;
import io.bisq.protobuffer.message.offer.OfferAvailabilityResponse;
import io.bisq.protobuffer.payload.offer.AvailabilityResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,8 +40,7 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
OfferAvailabilityResponse offerAvailabilityResponse = model.getMessage();
if (model.offer.getState() != Offer.State.REMOVED) {
// TODO: isAvailable is kept for backward compatibility. Can be removed once everyone is on v0.4.9
if (offerAvailabilityResponse.isAvailable || offerAvailabilityResponse.availabilityResult == AvailabilityResult.AVAILABLE) {
if (offerAvailabilityResponse.availabilityResult == AvailabilityResult.AVAILABLE) {
model.offer.setState(Offer.State.AVAILABLE);
} else {
model.offer.setState(Offer.State.NOT_AVAILABLE);

View file

@ -22,7 +22,7 @@ import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.offer.Offer;
import io.bisq.core.offer.availability.OfferAvailabilityModel;
import io.bisq.network.p2p.SendDirectMessageListener;
import io.bisq.wire.message.offer.OfferAvailabilityRequest;
import io.bisq.protobuffer.message.offer.OfferAvailabilityRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -49,7 +49,7 @@ public class SendOfferAvailabilityRequest extends Task<OfferAvailabilityModel> {
@Override
public void onFault() {
model.offer.setState(Offer.State.OFFERER_OFFLINE);
model.offer.setState(Offer.State.MAKER_OFFLINE);
}
}
);

View file

@ -23,9 +23,9 @@ import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.offer.Offer;
import io.bisq.core.offer.placeoffer.PlaceOfferModel;
import io.bisq.core.trade.protocol.ArbitrationSelectionRule;
import io.bisq.wire.payload.arbitration.Arbitrator;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.core.trade.protocol.ArbitratorSelectionRule;
import io.bisq.protobuffer.payload.arbitration.Arbitrator;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
@ -47,7 +47,8 @@ public class CreateOfferFeeTx extends Task<PlaceOfferModel> {
try {
runInterceptHook();
NodeAddress selectedArbitratorNodeAddress = ArbitrationSelectionRule.select(model.user.getAcceptedArbitratorAddresses(), model.offer);
NodeAddress selectedArbitratorNodeAddress = ArbitratorSelectionRule.select(model.user.getAcceptedArbitratorAddresses(),
model.offer);
log.debug("selectedArbitratorAddress " + selectedArbitratorNodeAddress);
Arbitrator selectedArbitrator = model.user.getAcceptedArbitratorByAddress(selectedArbitratorNodeAddress);
checkNotNull(selectedArbitrator, "selectedArbitrator must not be null at CreateOfferFeeTx");

View file

@ -24,7 +24,7 @@ import io.bisq.core.btc.Restrictions;
import io.bisq.core.offer.Offer;
import io.bisq.core.offer.placeoffer.PlaceOfferModel;
import io.bisq.core.provider.fee.FeeService;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.protobuffer.message.trade.TradeMessage;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -75,13 +75,11 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
checkArgument(offer.getMinAmount().compareTo(Restrictions.MIN_TRADE_AMOUNT) >= 0,
"MinAmount is less then "
+ Restrictions.MIN_TRADE_AMOUNT.toFriendlyString());
Preconditions.checkArgument(offer.getAmount().compareTo(offer.getPaymentMethod().getMaxTradeLimit()) <= 0,
Preconditions.checkArgument(offer.getAmount().compareTo(offer.getPaymentMethod().getMaxTradeLimitAsCoin()) <= 0,
"Amount is larger then "
+ offer.getPaymentMethod().getMaxTradeLimit().toFriendlyString());
+ offer.getPaymentMethod().getMaxTradeLimitAsCoin().toFriendlyString());
checkArgument(offer.getAmount().compareTo(offer.getMinAmount()) >= 0, "MinAmount is larger then Amount");
//
checkNotNull(offer.getPrice(), "Price is null");
checkArgument(offer.getPrice().isPositive(),
"Price must be positive. price=" + offer.getPrice().toFriendlyString());
@ -90,6 +88,7 @@ public class ValidateOffer extends Task<PlaceOfferModel> {
"Date must not be 0. date=" + offer.getDate().toString());
checkNotNull(offer.getArbitratorNodeAddresses(), "Arbitrator is null");
checkNotNull(offer.getMediatorNodeAddresses(), "Mediator is null");
checkNotNull(offer.getCurrencyCode(), "Currency is null");
checkNotNull(offer.getDirection(), "Direction is null");
checkNotNull(offer.getId(), "Id is null");

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.AliPayAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.AliPayAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class AliPayAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class AliPayAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new AliPayAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -18,9 +18,9 @@
package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.wire.payload.payment.CashDepositAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.CashDepositAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import javax.annotation.Nullable;
@ -33,7 +33,7 @@ public final class CashDepositAccount extends CountryBasedPaymentAccount impleme
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new CashDepositAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.ChaseQuickPayAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.ChaseQuickPayAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class ChaseQuickPayAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class ChaseQuickPayAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new ChaseQuickPayAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.ClearXchangeAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.ClearXchangeAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class ClearXchangeAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class ClearXchangeAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new ClearXchangeAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -19,8 +19,8 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.Country;
import io.bisq.wire.payload.payment.CountryBasedPaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.CountryBasedPaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -18,9 +18,9 @@
package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.wire.payload.payment.CryptoCurrencyAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.CryptoCurrencyAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class CryptoCurrencyAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -33,7 +33,7 @@ public final class CryptoCurrencyAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new CryptoCurrencyAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.FasterPaymentsAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.FasterPaymentsAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class FasterPaymentsAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class FasterPaymentsAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new FasterPaymentsAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.InteracETransferAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.InteracETransferAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class InteracETransferAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class InteracETransferAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new InteracETransferAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -18,10 +18,10 @@
package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.wire.payload.payment.BankAccountPayload;
import io.bisq.wire.payload.payment.NationalBankAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.BankAccountPayload;
import io.bisq.protobuffer.payload.payment.NationalBankAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public final class NationalBankAccount extends CountryBasedPaymentAccount implements SameCountryRestrictedBankAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -32,7 +32,7 @@ public final class NationalBankAccount extends CountryBasedPaymentAccount implem
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new NationalBankAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.CurrencyUtil;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.OKPayAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.OKPayAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -39,7 +39,7 @@ public final class OKPayAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new OKPayAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,8 +20,8 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.TradeCurrency;
import io.bisq.common.persistance.Persistable;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
@ -34,8 +34,8 @@ import java.util.Date;
import java.util.List;
import java.util.UUID;
@ToString
@EqualsAndHashCode
@ToString
@Slf4j
public abstract class PaymentAccount implements Persistable {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -67,7 +67,7 @@ public abstract class PaymentAccount implements Persistable {
this.paymentMethod = paymentMethod;
id = UUID.randomUUID().toString();
creationDate = new Date();
paymentAccountPayload = setPayload();
paymentAccountPayload = getPayload();
}
@ -103,9 +103,10 @@ public abstract class PaymentAccount implements Persistable {
return null;
}
protected abstract PaymentAccountPayload setPayload();
public String getPaymentDetails() {
return paymentAccountPayload.getPaymentDetails();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Getter, Setter
///////////////////////////////////////////////////////////////////////////////////////////
protected abstract PaymentAccountPayload getPayload();
}

View file

@ -17,13 +17,9 @@
package io.bisq.core.payment;
import io.bisq.wire.payload.payment.PaymentMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
public class PaymentAccountFactory {
private static final Logger log = LoggerFactory.getLogger(PaymentAccountFactory.class);
public static PaymentAccount getPaymentAccount(PaymentMethod paymentMethod) {
switch (paymentMethod.getId()) {
case PaymentMethod.OK_PAY_ID:

View file

@ -2,7 +2,7 @@ package io.bisq.core.payment;
import io.bisq.common.locale.TradeCurrency;
import io.bisq.core.offer.Offer;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
@ -35,6 +35,14 @@ public class PaymentAccountUtil {
return result;
}
// TODO might be used to show more details if we get payment methods updates with diff. limits
public static String getInfoForMismatchingPaymentMethodLimits(Offer offer, PaymentAccount paymentAccount) {
// dont translate atm as it is not used so far in the UI just for logs
return "Payment methods have different trade limits or trade periods.\n" +
"Our local Payment method: " + paymentAccount.getPaymentMethod().toString() + "\n" +
"Payment method from offer: " + offer.getPaymentMethod().toString();
}
//TODO not tested with all combinations yet....
public static boolean isPaymentAccountValidForOffer(Offer offer, PaymentAccount paymentAccount) {
// check if we have a matching currency
@ -44,6 +52,12 @@ public class PaymentAccountUtil {
return false;
// check if we have a matching payment method or if its a bank account payment method which is treated special
final boolean arePaymentMethodsEqual = paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod());
if (!arePaymentMethodsEqual &&
paymentAccount.getPaymentMethod().getId().equals(offer.getPaymentMethod().getId()))
log.warn(getInfoForMismatchingPaymentMethodLimits(offer, paymentAccount));
if (paymentAccount instanceof CountryBasedPaymentAccount) {
CountryBasedPaymentAccount countryBasedPaymentAccount = (CountryBasedPaymentAccount) paymentAccount;
@ -54,7 +68,7 @@ public class PaymentAccountUtil {
return false;
if (paymentAccount instanceof SepaAccount || offer.getPaymentMethod().equals(PaymentMethod.SEPA)) {
return paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod());
return arePaymentMethodsEqual;
} else if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK) ||
offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) {
@ -86,7 +100,7 @@ public class PaymentAccountUtil {
}
} else {
return paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod());
return arePaymentMethodsEqual;
}
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.wire.payload.payment.PerfectMoneyAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.PerfectMoneyAccountPayload;
public final class PerfectMoneyAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class PerfectMoneyAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new PerfectMoneyAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -18,10 +18,10 @@
package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.wire.payload.payment.BankAccountPayload;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.wire.payload.payment.SameBankAccountPayload;
import io.bisq.protobuffer.payload.payment.BankAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.SameBankAccountPayload;
public final class SameBankAccount extends CountryBasedPaymentAccount implements BankNameRestrictedBankAccount, SameCountryRestrictedBankAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -32,7 +32,7 @@ public final class SameBankAccount extends CountryBasedPaymentAccount implements
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new SameBankAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.CountryUtil;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.wire.payload.payment.SepaAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.SepaAccountPayload;
import java.util.List;
@ -35,7 +35,7 @@ public final class SepaAccount extends CountryBasedPaymentAccount implements Ban
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new SepaAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod(),
CountryUtil.getAllSepaCountries(Preferences.getDefaultLocale()));
}

View file

@ -18,9 +18,9 @@
package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.wire.payload.payment.SpecificBanksAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.SpecificBanksAccountPayload;
import java.util.ArrayList;
@ -33,7 +33,7 @@ public final class SpecificBanksAccount extends CountryBasedPaymentAccount imple
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new SpecificBanksAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.wire.payload.payment.SwishAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.SwishAccountPayload;
public final class SwishAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class SwishAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new SwishAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -20,9 +20,9 @@ package io.bisq.core.payment;
import io.bisq.common.app.Version;
import io.bisq.common.locale.FiatCurrency;
import io.bisq.core.user.Preferences;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.payment.PaymentMethod;
import io.bisq.wire.payload.payment.USPostalMoneyOrderAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.payment.PaymentMethod;
import io.bisq.protobuffer.payload.payment.USPostalMoneyOrderAccountPayload;
public final class USPostalMoneyOrderAccount extends PaymentAccount {
// That object is saved to disc. We need to take care of changes to not break deserialization.
@ -34,7 +34,7 @@ public final class USPostalMoneyOrderAccount extends PaymentAccount {
}
@Override
protected PaymentAccountPayload setPayload() {
protected PaymentAccountPayload getPayload() {
return new USPostalMoneyOrderAccountPayload(paymentMethod.getId(), id, paymentMethod.getMaxTradePeriod());
}

View file

@ -1,49 +1,25 @@
package io.bisq.core.provider.price;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.Value;
import java.time.Instant;
@Value
public class MarketPrice {
private static final Logger log = LoggerFactory.getLogger(MarketPrice.class);
public final String currencyCode;
public final double last;
private static final long MARKET_PRICE_MAX_AGE_SEC = 1800; // 30 min
public MarketPrice(String currencyCode, double last) {
private final String currencyCode;
private final double price;
private final long timestampSec;
public MarketPrice(String currencyCode, double price, long timestampSec) {
this.currencyCode = currencyCode;
this.last = last;
this.price = price;
this.timestampSec = timestampSec;
}
public double getPrice() {
return last;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof MarketPrice)) return false;
MarketPrice that = (MarketPrice) o;
if (Double.compare(that.last, last) != 0) return false;
return !(currencyCode != null ? !currencyCode.equals(that.currencyCode) : that.currencyCode != null);
}
@Override
public int hashCode() {
int result;
long temp;
result = currencyCode != null ? currencyCode.hashCode() : 0;
temp = Double.doubleToLongBits(last);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "MarketPrice{" +
"currencyCode='" + currencyCode + '\'' +
", last='" + last + '\'' +
'}';
public boolean isValid() {
long limit = Instant.now().getEpochSecond() - MARKET_PRICE_MAX_AGE_SEC;
return timestampSec > limit && price > 0;
}
}

View file

@ -165,7 +165,8 @@ public class PriceFeedService {
if (cache.containsKey(currencyCode)) {
try {
MarketPrice marketPrice = cache.get(currencyCode);
priceConsumer.accept(marketPrice.getPrice());
if (marketPrice.isValid())
priceConsumer.accept(marketPrice.getPrice());
} catch (Throwable t) {
log.warn("Error at applyPriceToConsumer " + t.getMessage());
}

View file

@ -3,6 +3,7 @@ package io.bisq.core.provider.price;
import com.google.gson.Gson;
import com.google.gson.internal.LinkedTreeMap;
import io.bisq.common.app.Version;
import io.bisq.common.util.MathUtils;
import io.bisq.common.util.Tuple2;
import io.bisq.core.provider.HttpClientProvider;
import io.bisq.network.http.HttpClient;
@ -25,7 +26,8 @@ public class PriceProvider extends HttpClientProvider {
public Tuple2<Map<String, Long>, Map<String, MarketPrice>> getAll() throws IOException, HttpException {
Map<String, MarketPrice> marketPriceMap = new HashMap<>();
String json = httpClient.requestWithGET("getAllMarketPrices", "User-Agent", "bisq/" + Version.VERSION + ", uid:" + httpClient.getUid());
String json = httpClient.requestWithGET("getAllMarketPrices", "User-Agent", "bisq/"
+ Version.VERSION + ", uid:" + httpClient.getUid());
LinkedTreeMap<String, Object> map = new Gson().fromJson(json, LinkedTreeMap.class);
Map<String, Long> tsMap = new HashMap<>();
tsMap.put("btcAverageTs", ((Double) map.get("btcAverageTs")).longValue());
@ -34,8 +36,17 @@ public class PriceProvider extends HttpClientProvider {
List<LinkedTreeMap<String, Object>> list = (ArrayList<LinkedTreeMap<String, Object>>) map.get("data");
list.stream().forEach(treeMap -> {
marketPriceMap.put((String) treeMap.get("c"),
new MarketPrice((String) treeMap.get("c"), (double) treeMap.get("l")));
try {
final String currencyCode = (String) treeMap.get("currencyCode");
final double price = (double) treeMap.get("price");
// json uses double for our timestampSec long value...
final long timestampSec = MathUtils.doubleToLong((double) treeMap.get("timestampSec"));
marketPriceMap.put(currencyCode, new MarketPrice(currencyCode, price, timestampSec));
} catch (Throwable t) {
log.error(t.toString());
t.printStackTrace();
}
});
return new Tuple2<>(tsMap, marketPriceMap);
}

View file

@ -20,10 +20,10 @@ package io.bisq.core.trade;
import io.bisq.common.app.Version;
import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.protocol.BuyerAsOffererProtocol;
import io.bisq.core.trade.protocol.OffererProtocol;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.core.trade.protocol.BuyerAsMakerProtocol;
import io.bisq.core.trade.protocol.MakerProtocol;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -31,18 +31,18 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
public final class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade {
public final class BuyerAsMakerTrade extends BuyerTrade implements MakerTrade {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class);
private static final Logger log = LoggerFactory.getLogger(BuyerAsMakerTrade.class);
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, initialization
///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsOffererTrade(Offer offer, Coin txFee, Coin takeOfferFee, Storage<? extends TradableList> storage) {
public BuyerAsMakerTrade(Offer offer, Coin txFee, Coin takeOfferFee, Storage<? extends TradableList> storage) {
super(offer, txFee, takeOfferFee, storage);
}
@ -58,7 +58,7 @@ public final class BuyerAsOffererTrade extends BuyerTrade implements OffererTrad
@Override
protected void createProtocol() {
tradeProtocol = new BuyerAsOffererProtocol(this);
tradeProtocol = new BuyerAsMakerProtocol(this);
}
@ -68,6 +68,6 @@ public final class BuyerAsOffererTrade extends BuyerTrade implements OffererTrad
@Override
public void handleTakeOfferRequest(TradeMessage message, NodeAddress taker) {
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
((MakerProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
}
}

View file

@ -22,7 +22,7 @@ import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.protocol.BuyerAsTakerProtocol;
import io.bisq.core.trade.protocol.TakerProtocol;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -23,7 +23,7 @@ import io.bisq.common.handlers.ResultHandler;
import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.protocol.BuyerProtocol;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -35,7 +35,7 @@ public abstract class BuyerTrade extends Trade {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class);
private static final Logger log = LoggerFactory.getLogger(BuyerAsMakerTrade.class);
BuyerTrade(Offer offer, Coin tradeAmount, Coin txFee, Coin takeOfferFee, long tradePrice, NodeAddress tradingPeerNodeAddress, Storage<? extends TradableList> storage) {
super(offer, tradeAmount, txFee, takeOfferFee, tradePrice, tradingPeerNodeAddress, storage);
@ -63,16 +63,4 @@ public abstract class BuyerTrade extends Trade {
return getOffer().getBuyerSecurityDeposit().add(getTradeAmount());
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setter for Mutable objects
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void setState(State state) {
super.setState(state);
if (state == State.WITHDRAW_COMPLETED && tradeProtocol != null)
tradeProtocol.completed();
}
}

View file

@ -18,9 +18,9 @@
package io.bisq.core.trade;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
public interface OffererTrade {
public interface MakerTrade {
void handleTakeOfferRequest(TradeMessage message, NodeAddress peerNodeAddress);
}

View file

@ -20,10 +20,10 @@ package io.bisq.core.trade;
import io.bisq.common.app.Version;
import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.protocol.OffererProtocol;
import io.bisq.core.trade.protocol.SellerAsOffererProtocol;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.core.trade.protocol.MakerProtocol;
import io.bisq.core.trade.protocol.SellerAsMakerProtocol;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -31,18 +31,18 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.ObjectInputStream;
public final class SellerAsOffererTrade extends SellerTrade implements OffererTrade {
public final class SellerAsMakerTrade extends SellerTrade implements MakerTrade {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
private static final Logger log = LoggerFactory.getLogger(SellerAsOffererTrade.class);
private static final Logger log = LoggerFactory.getLogger(SellerAsMakerTrade.class);
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, initialization
///////////////////////////////////////////////////////////////////////////////////////////
public SellerAsOffererTrade(Offer offer, Coin txFee, Coin takeOfferFee, Storage<? extends TradableList> storage) {
public SellerAsMakerTrade(Offer offer, Coin txFee, Coin takeOfferFee, Storage<? extends TradableList> storage) {
super(offer, txFee, takeOfferFee, storage);
}
@ -58,7 +58,7 @@ public final class SellerAsOffererTrade extends SellerTrade implements OffererTr
@Override
protected void createProtocol() {
tradeProtocol = new SellerAsOffererProtocol(this);
tradeProtocol = new SellerAsMakerProtocol(this);
}
@ -68,6 +68,6 @@ public final class SellerAsOffererTrade extends SellerTrade implements OffererTr
@Override
public void handleTakeOfferRequest(TradeMessage message, NodeAddress taker) {
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
((MakerProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
}
}

View file

@ -22,7 +22,7 @@ import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.protocol.SellerAsTakerProtocol;
import io.bisq.core.trade.protocol.TakerProtocol;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -23,7 +23,7 @@ import io.bisq.common.handlers.ResultHandler;
import io.bisq.common.storage.Storage;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.protocol.SellerProtocol;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -59,16 +59,5 @@ public abstract class SellerTrade extends Trade {
public Coin getPayoutAmount() {
return getOffer().getSellerSecurityDeposit();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setter for Mutable objects
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public void setState(State state) {
super.setState(state);
if (state == State.WITHDRAW_COMPLETED && tradeProtocol != null)
tradeProtocol.completed();
}
}

View file

@ -21,6 +21,7 @@ import com.google.common.base.Throwables;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import io.bisq.common.app.DevEnv;
import io.bisq.common.app.Log;
import io.bisq.common.app.Version;
import io.bisq.common.monetary.Price;
@ -38,10 +39,11 @@ import io.bisq.core.trade.protocol.TradeProtocol;
import io.bisq.core.user.User;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.payload.arbitration.Arbitrator;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.trade.Contract;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.payload.arbitration.Arbitrator;
import io.bisq.protobuffer.payload.arbitration.Mediator;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.trade.Contract;
import javafx.beans.property.*;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
@ -70,53 +72,98 @@ public abstract class Trade implements Tradable, Model {
private static final Logger log = LoggerFactory.getLogger(Trade.class);
public enum State {
// #################### Phase PREPARATION
// When trade protocol starts no funds are on stake
PREPARATION(Phase.PREPARATION),
TAKER_FEE_PAID(Phase.TAKER_FEE_PAID),
// At first part maker/taker have different roles
// taker perspective
// #################### Phase TAKER_FEE_PAID
TAKER_PUBLISHED_TAKER_FEE_TX(Phase.TAKER_FEE_PUBLISHED),
OFFERER_SENT_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
TAKER_PUBLISHED_DEPOSIT_TX(Phase.DEPOSIT_PAID),
DEPOSIT_SEEN_IN_NETWORK(Phase.DEPOSIT_PAID), // triggered by balance update, used only in error cases
TAKER_SENT_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PAID),
OFFERER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PAID),
DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN(Phase.DEPOSIT_PAID),
// PUBLISH_DEPOSIT_TX_REQUEST
// maker perspective
MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST(Phase.TAKER_FEE_PUBLISHED),
MAKER_SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.TAKER_FEE_PUBLISHED),
MAKER_STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST(Phase.TAKER_FEE_PUBLISHED),
MAKER_SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.TAKER_FEE_PUBLISHED),
BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED(Phase.FIAT_SENT),
// taker perspective
TAKER_RECEIVED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.TAKER_FEE_PUBLISHED),
// #################### Phase DEPOSIT_PAID
TAKER_PUBLISHED_DEPOSIT_TX(Phase.DEPOSIT_PUBLISHED),
// DEPOSIT_TX_PUBLISHED_MSG
// taker perspective
TAKER_SENT_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PUBLISHED),
TAKER_SAW_ARRIVED_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PUBLISHED),
TAKER_STORED_IN_MAILBOX_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PUBLISHED),
TAKER_SEND_FAILED_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PUBLISHED),
// maker perspective
MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG(Phase.DEPOSIT_PUBLISHED),
// Alternatively the maker could have seen the deposit tx earlier before he received the DEPOSIT_TX_PUBLISHED_MSG
MAKER_SAW_DEPOSIT_TX_IN_NETWORK(Phase.DEPOSIT_PUBLISHED),
// #################### Phase DEPOSIT_CONFIRMED
DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN(Phase.DEPOSIT_CONFIRMED),
// #################### Phase FIAT_SENT
BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED(Phase.FIAT_SENT),
BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG(Phase.FIAT_SENT),
BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG(Phase.FIAT_SENT),
BUYER_STORED_IN_MAILBOX_FIAT_PAYMENT_INITIATED_MSG(Phase.FIAT_SENT),
BUYER_SEND_FAILED_FIAT_PAYMENT_INITIATED_MSG(Phase.FIAT_SENT),
SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG(Phase.FIAT_SENT),
SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT(Phase.FIAT_RECEIVED),
SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG(Phase.FIAT_RECEIVED),
BUYER_RECEIVED_FIAT_PAYMENT_RECEIPT_MSG(Phase.FIAT_RECEIVED),
// #################### Phase FIAT_RECEIVED
SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT(Phase.FIAT_RECEIVED),
BUYER_COMMITTED_PAYOUT_TX(Phase.PAYOUT_PAID), //TODO needed?
BUYER_STARTED_SEND_PAYOUT_TX(Phase.PAYOUT_PAID), // not from the success/arrived handler!
SELLER_RECEIVED_AND_COMMITTED_PAYOUT_TX(Phase.PAYOUT_PAID),
PAYOUT_BROAD_CASTED(Phase.PAYOUT_PAID),
// #################### Phase PAYOUT_PAID
SELLER_PUBLISHED_PAYOUT_TX(Phase.PAYOUT_PUBLISHED),
SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
// Alternatively the maker could have seen the payout tx earlier before he received the PAYOUT_TX_PUBLISHED_MSG
BUYER_SAW_PAYOUT_TX_IN_NETWORK(Phase.PAYOUT_PUBLISHED),
// #################### Phase WITHDRAWN
WITHDRAW_COMPLETED(Phase.WITHDRAWN);
public Phase getPhase() {
return phase;
}
@NotNull
private final Phase phase;
State(Phase phase) {
State(@NotNull Phase phase) {
this.phase = phase;
}
}
public enum Phase {
PREPARATION,
TAKER_FEE_PAID,
DEPOSIT_REQUESTED,
DEPOSIT_PAID,
TAKER_FEE_PUBLISHED,
DEPOSIT_PUBLISHED,
DEPOSIT_CONFIRMED,
FIAT_SENT,
FIAT_RECEIVED,
PAYOUT_PAID,
WITHDRAWN,
DISPUTE
PAYOUT_PUBLISHED,
WITHDRAWN
}
public enum DisputeState {
@ -139,6 +186,7 @@ public abstract class Trade implements Tradable, Model {
// Transient/Immutable
transient private ObjectProperty<State> stateProperty;
transient private ObjectProperty<Phase> statePhaseProperty;
transient private ObjectProperty<DisputeState> disputeStateProperty;
transient private ObjectProperty<TradePeriodState> tradePeriodStateProperty;
// Trades are saved in the TradeList
@ -169,10 +217,10 @@ public abstract class Trade implements Tradable, Model {
private String contractAsJson;
private byte[] contractHash;
private String takerContractSignature;
private String offererContractSignature;
private String makerContractSignature;
private Transaction payoutTx;
private long lockTimeAsBlockHeight;
private NodeAddress arbitratorNodeAddress;
private NodeAddress mediatorNodeAddress;
private byte[] arbitratorBtcPubKey;
private String takerPaymentAccountId;
private String errorMessage;
@ -186,7 +234,7 @@ public abstract class Trade implements Tradable, Model {
// Constructor, initialization
///////////////////////////////////////////////////////////////////////////////////////////
// offerer
// maker
protected Trade(Offer offer, Coin txFee, Coin takeOfferFee, Storage<? extends TradableList> storage) {
this.offer = offer;
this.txFee = txFee;
@ -264,6 +312,7 @@ public abstract class Trade implements Tradable, Model {
protected void initStateProperties() {
stateProperty = new SimpleObjectProperty<>(state);
statePhaseProperty = new SimpleObjectProperty<>(state.phase);
disputeStateProperty = new SimpleObjectProperty<>(disputeState);
tradePeriodStateProperty = new SimpleObjectProperty<>(tradePeriodState);
}
@ -326,11 +375,24 @@ public abstract class Trade implements Tradable, Model {
public void setState(State state) {
log.info("Trade.setState: " + state);
boolean changed = this.state != state;
this.state = state;
stateProperty.set(state);
if (changed)
persist();
if (state.getPhase().ordinal() >= this.state.getPhase().ordinal()) {
boolean changed = this.state != state;
this.state = state;
stateProperty.set(state);
statePhaseProperty.set(state.getPhase());
if (state == State.WITHDRAW_COMPLETED && tradeProtocol != null)
tradeProtocol.completed();
if (changed)
persist();
} else {
final String message = "we got a state change to a previous phase. that is likely a bug.\n" +
"old state is: " + this.state + ". New state is: " + state;
log.error(message);
if (DevEnv.DEV_MODE)
throw new RuntimeException(message);
}
}
public void setDisputeState(DisputeState disputeState) {
@ -358,12 +420,36 @@ public abstract class Trade implements Tradable, Model {
return tradePeriodState;
}
public boolean isTakerFeePaid() {
return state.getPhase() != null && state.getPhase().ordinal() >= Phase.TAKER_FEE_PAID.ordinal();
public boolean isInPreparation() {
return state.getPhase().ordinal() == Phase.PREPARATION.ordinal();
}
public boolean isDepositPaid() {
return state.getPhase() != null && state.getPhase().ordinal() >= Phase.DEPOSIT_PAID.ordinal();
public boolean isTakerFeePublished() {
return state.getPhase().ordinal() >= Phase.TAKER_FEE_PUBLISHED.ordinal();
}
public boolean isDepositPublished() {
return state.getPhase().ordinal() >= Phase.DEPOSIT_PUBLISHED.ordinal();
}
public boolean isDepositConfirmed() {
return state.getPhase().ordinal() >= Phase.DEPOSIT_CONFIRMED.ordinal();
}
public boolean isFiatSent() {
return state.getPhase().ordinal() >= Phase.FIAT_SENT.ordinal();
}
public boolean isFiatReceived() {
return state.getPhase().ordinal() >= Phase.FIAT_RECEIVED.ordinal();
}
public boolean isPayoutPublished() {
return state.getPhase().ordinal() >= Phase.PAYOUT_PUBLISHED.ordinal() || isWithdrawn();
}
public boolean isWithdrawn() {
return state.getPhase().ordinal() == Phase.WITHDRAWN.ordinal();
}
public State getState() {
@ -435,11 +521,18 @@ public abstract class Trade implements Tradable, Model {
return halfTradePeriodDate;
}
public boolean hasFailed() {
return errorMessageProperty().get() != null;
}
public ReadOnlyObjectProperty<? extends State> stateProperty() {
public ReadOnlyObjectProperty<State> stateProperty() {
return stateProperty;
}
public ReadOnlyObjectProperty<Phase> statePhaseProperty() {
return statePhaseProperty;
}
public ReadOnlyObjectProperty<Coin> tradeAmountProperty() {
return tradeAmountProperty;
}
@ -448,7 +541,6 @@ public abstract class Trade implements Tradable, Model {
return tradeVolumeProperty;
}
public ReadOnlyObjectProperty<DisputeState> disputeStateProperty() {
return disputeStateProperty;
}
@ -505,15 +597,6 @@ public abstract class Trade implements Tradable, Model {
return takeOfferFee;
}
public void setLockTimeAsBlockHeight(long lockTimeAsBlockHeight) {
this.lockTimeAsBlockHeight = lockTimeAsBlockHeight;
}
public long getLockTimeAsBlockHeight() {
return lockTimeAsBlockHeight;
}
public void setTakerContractSignature(String takerSignature) {
this.takerContractSignature = takerSignature;
}
@ -523,13 +606,13 @@ public abstract class Trade implements Tradable, Model {
return takerContractSignature;
}
public void setOffererContractSignature(String offererContractSignature) {
this.offererContractSignature = offererContractSignature;
public void setMakerContractSignature(String makerContractSignature) {
this.makerContractSignature = makerContractSignature;
}
@Nullable
public String getOffererContractSignature() {
return offererContractSignature;
public String getMakerContractSignature() {
return makerContractSignature;
}
public void setContractAsJson(String contractAsJson) {
@ -554,7 +637,6 @@ public abstract class Trade implements Tradable, Model {
this.payoutTx = payoutTx;
}
// Not used now, but will be used in some reporting UI
@Nullable
public Transaction getPayoutTx() {
return payoutTx;
@ -569,6 +651,10 @@ public abstract class Trade implements Tradable, Model {
return errorMessageProperty;
}
public String getErrorMessage() {
return errorMessageProperty.get();
}
public NodeAddress getArbitratorNodeAddress() {
return arbitratorNodeAddress;
}
@ -581,20 +667,27 @@ public abstract class Trade implements Tradable, Model {
arbitratorBtcPubKey = arbitrator.getBtcPubKey();
}
public byte[] getArbitratorPubKey() {
// Prior to v0.4.8.4 we did not store the arbitratorBtcPubKey in the trade object so we need to support the
// previously used version as well and request the arbitrator from the user object (but that caused sometimes a bug when
// the client did not get delivered an arbitrator from the P2P network).
if (arbitratorBtcPubKey == null) {
Arbitrator arbitrator = processModel.getUser().getAcceptedArbitratorByAddress(arbitratorNodeAddress);
checkNotNull(arbitrator, "arbitrator must not be null");
arbitratorBtcPubKey = arbitrator.getBtcPubKey();
}
public byte[] getArbitratorBtcPubKey() {
Arbitrator arbitrator = processModel.getUser().getAcceptedArbitratorByAddress(arbitratorNodeAddress);
checkNotNull(arbitrator, "arbitrator must not be null");
arbitratorBtcPubKey = arbitrator.getBtcPubKey();
checkNotNull(arbitratorBtcPubKey, "ArbitratorPubKey must not be null");
return arbitratorBtcPubKey;
}
public NodeAddress getMediatorNodeAddress() {
return mediatorNodeAddress;
}
public void applyMediatorNodeAddress(NodeAddress mediatorNodeAddress) {
this.mediatorNodeAddress = mediatorNodeAddress;
Mediator mediator = processModel.getUser().getAcceptedMediatorByAddress(mediatorNodeAddress);
checkNotNull(mediator, "mediator must not be null");
}
public String getTakerPaymentAccountId() {
return takerPaymentAccountId;
}
@ -629,7 +722,7 @@ public abstract class Trade implements Tradable, Model {
if (depositTx != null) {
TransactionConfidence transactionConfidence = depositTx.getConfidence();
log.debug("transactionConfidence " + transactionConfidence.getDepthInBlocks());
if (transactionConfidence.getDepthInBlocks() > 0) {
if (transactionConfidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) {
setConfirmedState();
} else {
ListenableFuture<TransactionConfidence> future = transactionConfidence.getDepthFuture(1);
@ -649,7 +742,6 @@ public abstract class Trade implements Tradable, Model {
}
});
}
} else {
log.error("depositTx == null. That must not happen.");
}
@ -658,8 +750,8 @@ public abstract class Trade implements Tradable, Model {
abstract protected void createProtocol();
private void setConfirmedState() {
// we oly apply the state if we are not already further in the process
if (state.ordinal() < State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN.ordinal())
// we only apply the state if we are not already further in the process
if (!isDepositConfirmed())
setState(State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN);
}
@ -681,11 +773,13 @@ public abstract class Trade implements Tradable, Model {
"\n\tdepositTx=" + depositTx +
"\n\ttakeOfferFeeTxId=" + takeOfferFeeTxId +
"\n\tcontract=" + contract +
"\n\ttakerContractSignature.hashCode()='" + (takerContractSignature != null ? takerContractSignature.hashCode() : "") + '\'' +
"\n\toffererContractSignature.hashCode()='" + (offererContractSignature != null ? offererContractSignature.hashCode() : "") + '\'' +
"\n\ttakerContractSignature.hashCode()='" + (takerContractSignature != null ?
takerContractSignature.hashCode() : "") + '\'' +
"\n\tmakerContractSignature.hashCode()='" + (makerContractSignature != null ?
makerContractSignature.hashCode() : "") + '\'' +
"\n\tpayoutTx=" + payoutTx +
"\n\tlockTimeAsBlockHeight=" + lockTimeAsBlockHeight +
"\n\tarbitratorNodeAddress=" + arbitratorNodeAddress +
"\n\tmediatorNodeAddress=" + mediatorNodeAddress +
"\n\ttakerPaymentAccountId='" + takerPaymentAccountId + '\'' +
"\n\ttxFee='" + txFee.toFriendlyString() + '\'' +
"\n\ttakeOfferFee='" + takeOfferFee.toFriendlyString() + '\'' +

View file

@ -46,12 +46,12 @@ import io.bisq.network.p2p.DecryptedDirectMessageListener;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.network.p2p.messaging.DecryptedMailboxListener;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.trade.PayDepositRequest;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.trade.statistics.TradeStatistics;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.trade.PayDepositRequest;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.trade.statistics.TradeStatistics;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.ObservableList;
@ -151,14 +151,7 @@ public class TradeManager {
log.trace("onMailboxMessageAdded decryptedMessageWithPubKey: " + decryptedMsgWithPubKey);
log.trace("onMailboxMessageAdded senderAddress: " + senderNodeAddress);
Message message = decryptedMsgWithPubKey.message;
if (message instanceof PayDepositRequest) {
PayDepositRequest payDepositRequest = (PayDepositRequest) message;
log.trace("Received payDepositRequest: " + payDepositRequest);
if (payDepositRequest.getSenderNodeAddress().equals(senderNodeAddress))
handleInitialTakeOfferRequest(payDepositRequest, senderNodeAddress);
else
log.warn("Peer address not matching for payDepositRequest");
} else if (message instanceof TradeMessage) {
if (message instanceof TradeMessage) {
log.trace("Received TradeMessage: " + message);
String tradeId = ((TradeMessage) message).tradeId;
Optional<Trade> tradeOptional = trades.stream().filter(e -> e.getId().equals(tradeId)).findAny();
@ -203,11 +196,13 @@ public class TradeManager {
for (Trade trade : trades) {
trade.setStorage(tradableListStorage);
if (trade.isDepositPaid() || (trade.isTakerFeePaid() && trade.errorMessageProperty().get() == null)) {
initTrade(trade, trade.getProcessModel().isUseSavingsWallet(), trade.getProcessModel().getFundsNeededForTrade());
if (trade.isDepositPublished() ||
(trade.isTakerFeePublished() && !trade.hasFailed())) {
initTrade(trade, trade.getProcessModel().getUseSavingsWallet(),
trade.getProcessModel().getFundsNeededForTrade());
trade.updateDepositTxFromWallet();
tradesForStatistics.add(trade);
} else if (trade.isTakerFeePaid()) {
} else if (trade.isTakerFeePublished()) {
addTradeToFailedTradesList.add(trade);
} else {
removePreparedTradeList.add(trade);
@ -276,14 +271,16 @@ public class TradeManager {
PayDepositRequest payDepositRequest = (PayDepositRequest) message;
Trade trade;
if (offer.isBuyOffer())
trade = new BuyerAsOffererTrade(offer, payDepositRequest.txFee, payDepositRequest.takeOfferFee, tradableListStorage);
trade = new BuyerAsMakerTrade(offer, Coin.valueOf(payDepositRequest.txFee),
Coin.valueOf(payDepositRequest.takeOfferFee), tradableListStorage);
else
trade = new SellerAsOffererTrade(offer, payDepositRequest.txFee, payDepositRequest.takeOfferFee, tradableListStorage);
trade = new SellerAsMakerTrade(offer, Coin.valueOf(payDepositRequest.txFee),
Coin.valueOf(payDepositRequest.takeOfferFee), tradableListStorage);
trade.setStorage(tradableListStorage);
initTrade(trade, trade.getProcessModel().isUseSavingsWallet(), trade.getProcessModel().getFundsNeededForTrade());
trades.add(trade);
((OffererTrade) trade).handleTakeOfferRequest(message, peerNodeAddress);
((MakerTrade) trade).handleTakeOfferRequest(message, peerNodeAddress);
} else {
// TODO respond
//(RequestDepositTxInputsMessage)message.
@ -387,8 +384,10 @@ public class TradeManager {
// Trade
///////////////////////////////////////////////////////////////////////////////////////////
public void onWithdrawRequest(String toAddress, Coin amount, Coin fee, KeyParameter aesKey, Trade trade, ResultHandler resultHandler, FaultHandler faultHandler) {
String fromAddress = walletService.getOrCreateAddressEntry(trade.getId(), AddressEntry.Context.TRADE_PAYOUT).getAddressString();
public void onWithdrawRequest(String toAddress, Coin amount, Coin fee, KeyParameter aesKey,
Trade trade, ResultHandler resultHandler, FaultHandler faultHandler) {
String fromAddress = walletService.getOrCreateAddressEntry(trade.getId(),
AddressEntry.Context.TRADE_PAYOUT).getAddressString();
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
@Override
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
@ -437,6 +436,8 @@ public class TradeManager {
private void removeTrade(Trade trade) {
trades.remove(trade);
// we only swap if we have not an open offer (in case the removeTrade happened at the trade preparation phase)
if (!openOfferManager.findOpenOffer(trade.getId()).isPresent())
walletService.swapAnyTradeEntryContextToAvailableEntry(trade.getId());
}
@ -472,7 +473,7 @@ public class TradeManager {
}
public boolean isBuyer(Offer offer) {
// If I am the offerer, we use the offer direction, otherwise the mirrored direction
// If I am the maker, we use the offer direction, otherwise the mirrored direction
if (isMyOffer(offer))
return offer.isBuyOffer();
else
@ -484,8 +485,10 @@ public class TradeManager {
}
public Stream<AddressEntry> getAddressEntriesForAvailableBalanceStream() {
Stream<AddressEntry> availableOrPayout = Stream.concat(walletService.getAddressEntries(AddressEntry.Context.TRADE_PAYOUT).stream(), walletService.getFundedAvailableAddressEntries().stream());
Stream<AddressEntry> available = Stream.concat(availableOrPayout, walletService.getAddressEntries(AddressEntry.Context.ARBITRATOR).stream());
Stream<AddressEntry> availableOrPayout = Stream.concat(walletService.getAddressEntries(AddressEntry.Context.TRADE_PAYOUT)
.stream(), walletService.getFundedAvailableAddressEntries().stream());
Stream<AddressEntry> available = Stream.concat(availableOrPayout,
walletService.getAddressEntries(AddressEntry.Context.ARBITRATOR).stream());
available = Stream.concat(available, walletService.getAddressEntries(AddressEntry.Context.OFFER_FUNDING).stream());
return available
.filter(addressEntry -> walletService.getBalanceForAddress(addressEntry.getAddress()).isPositive());
@ -493,7 +496,6 @@ public class TradeManager {
public Stream<Trade> getLockedTradeStream() {
return getTrades().stream()
.filter(trade -> trade.getState().getPhase().ordinal() >= Trade.Phase.DEPOSIT_PAID.ordinal() &&
trade.getState().getPhase().ordinal() < Trade.Phase.PAYOUT_PAID.ordinal());
.filter(trade -> trade.isDepositPublished() && !trade.isPayoutPublished());
}
}

View file

@ -23,7 +23,7 @@ import io.bisq.core.offer.Offer;
import io.bisq.core.provider.price.PriceFeedService;
import io.bisq.core.trade.Tradable;
import io.bisq.core.trade.TradableList;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.protobuffer.crypto.KeyRing;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -23,7 +23,7 @@ import io.bisq.core.offer.Offer;
import io.bisq.core.provider.price.PriceFeedService;
import io.bisq.core.trade.TradableList;
import io.bisq.core.trade.Trade;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.protobuffer.crypto.KeyRing;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View file

@ -18,10 +18,9 @@
package io.bisq.core.trade.protocol;
import io.bisq.core.offer.Offer;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Sha256Hash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
@ -30,9 +29,8 @@ import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
public class ArbitrationSelectionRule {
private static final Logger log = LoggerFactory.getLogger(ArbitrationSelectionRule.class);
@Slf4j
public class ArbitratorSelectionRule {
public static NodeAddress select(List<NodeAddress> acceptedArbitratorNodeAddresses, Offer offer) {
List<NodeAddress> candidates = new ArrayList<>();
for (NodeAddress offerArbitratorNodeAddress : offer.getArbitratorNodeAddresses()) {

View file

@ -19,53 +19,60 @@ package io.bisq.core.trade.protocol;
import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.core.trade.BuyerAsOffererTrade;
import io.bisq.core.trade.BuyerAsMakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.buyer.*;
import io.bisq.core.trade.protocol.tasks.offerer.*;
import io.bisq.core.trade.protocol.tasks.shared.BroadcastAfterLockTime;
import io.bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage;
import io.bisq.core.trade.protocol.tasks.buyer.BuyerSendFiatTransferStartedMessage;
import io.bisq.core.trade.protocol.tasks.buyer.BuyerSetupPayoutTxListener;
import io.bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerCreatesAndSignsDepositTx;
import io.bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerSignPayoutTx;
import io.bisq.core.trade.protocol.tasks.maker.*;
import io.bisq.core.util.Validator;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.p2p.MailboxMessage;
import io.bisq.wire.message.trade.DepositTxPublishedMessage;
import io.bisq.wire.message.trade.FinalizePayoutTxRequest;
import io.bisq.wire.message.trade.PayDepositRequest;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.p2p.MailboxMessage;
import io.bisq.protobuffer.message.trade.DepositTxPublishedMessage;
import io.bisq.protobuffer.message.trade.PayDepositRequest;
import io.bisq.protobuffer.message.trade.PayoutTxPublishedMessage;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtocol, OffererProtocol {
private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererProtocol.class);
private final BuyerAsOffererTrade buyerAsOffererTrade;
@Slf4j
public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol, MakerProtocol {
private final BuyerAsMakerTrade buyerAsMakerTrade;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public BuyerAsOffererProtocol(BuyerAsOffererTrade trade) {
public BuyerAsMakerProtocol(BuyerAsMakerTrade trade) {
super(trade);
this.buyerAsOffererTrade = trade;
// If we are after the time lock state we need to setup the listener again
Trade.State tradeState = trade.getState();
Trade.Phase phase = tradeState.getPhase();
if (trade.getPayoutTx() != null && (phase == Trade.Phase.FIAT_RECEIVED || phase == Trade.Phase.PAYOUT_PAID) &&
tradeState != Trade.State.PAYOUT_BROAD_CASTED) {
this.buyerAsMakerTrade = trade;
Trade.Phase phase = trade.getState().getPhase();
if (phase == Trade.Phase.TAKER_FEE_PUBLISHED) {
TradeTaskRunner taskRunner = new TradeTaskRunner(trade,
() -> {
handleTaskRunnerSuccess("SetupPayoutTxLockTimeReachedListener");
handleTaskRunnerSuccess("MakerSetupDepositTxListener");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(BroadcastAfterLockTime.class);
taskRunner.addTasks(MakerSetupDepositTxListener.class);
taskRunner.run();
} else if (trade.isFiatSent() && !trade.isPayoutPublished()) {
TradeTaskRunner taskRunner = new TradeTaskRunner(trade,
() -> {
handleTaskRunnerSuccess("BuyerSetupPayoutTxListener");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(BuyerSetupPayoutTxListener.class);
taskRunner.run();
}
}
@ -82,11 +89,12 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
if (message instanceof MailboxMessage) {
MailboxMessage mailboxMessage = (MailboxMessage) message;
NodeAddress peerNodeAddress = mailboxMessage.getSenderNodeAddress();
if (message instanceof FinalizePayoutTxRequest) {
handle((FinalizePayoutTxRequest) message, peerNodeAddress);
} else if (message instanceof DepositTxPublishedMessage) {
if (message instanceof DepositTxPublishedMessage)
handle((DepositTxPublishedMessage) message, peerNodeAddress);
}
else if (message instanceof PayoutTxPublishedMessage)
handle((PayoutTxPublishedMessage) message, peerNodeAddress);
else
log.error("We received an unhandled MailboxMessage" + message.toString());
}
}
@ -102,19 +110,21 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
processModel.setTradeMessage(message);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
() -> handleTaskRunnerSuccess("handleTakeOfferRequest"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessPayDepositRequest.class,
VerifyArbitrationSelection.class,
VerifyTakerAccount.class,
LoadTakeOfferFeeTx.class,
CreateAndSignContract.class,
OffererCreatesAndSignsDepositTxAsBuyer.class,
SetupDepositBalanceListener.class,
SendPublishDepositTxRequest.class
MakerProcessPayDepositRequest.class,
MakerVerifyArbitratorSelection.class,
MakerVerifyMediatorSelection.class,
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class,
MakerCreateAndSignContract.class,
BuyerAsMakerCreatesAndSignsDepositTx.class,
MakerSetupDepositTxListener.class,
MakerSendPublishDepositTxRequest.class
);
startTimeout();
taskRunner.run();
}
@ -129,11 +139,13 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
() -> handleTaskRunnerSuccess("handle DepositTxPublishedMessage"),
this::handleTaskRunnerFault);
taskRunner.addTasks(ProcessDepositTxPublishedMessage.class,
PublishTradeStatistics.class);
taskRunner.addTasks(MakerProcessDepositTxPublishedMessage.class,
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class,
MakerPublishTradeStatistics.class);
taskRunner.run();
}
@ -145,14 +157,9 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// User clicked the "bank transfer started" button
@Override
public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
if (buyerAsOffererTrade.getState().ordinal() <= Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG.ordinal()) {
if (buyerAsOffererTrade.getState() == Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG)
log.warn("onFiatPaymentStarted called twice. " +
"That is expected if the app starts up and the other peer has still not continued.");
buyerAsOffererTrade.setState(Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
if (trade.isDepositConfirmed() && !trade.isFiatSent()) {
buyerAsMakerTrade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
() -> {
resultHandler.handleResult();
handleTaskRunnerSuccess("onFiatPaymentStarted");
@ -162,14 +169,15 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
handleTaskRunnerFault(errorMessage);
});
taskRunner.addTasks(
VerifyTakeOfferFeePayment.class,
SendFiatTransferStartedMessage.class
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class,
BuyerAsMakerSignPayoutTx.class,
BuyerSendFiatTransferStartedMessage.class,
BuyerSetupPayoutTxListener.class
);
taskRunner.run();
} else {
log.warn("onFiatPaymentStarted called twice. " +
"That should not happen.\n" +
"state=" + buyerAsOffererTrade.getState());
log.warn("onFiatPaymentStarted called twice. tradeState=" + trade.getState());
}
}
@ -178,23 +186,20 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
// Incoming message handling
///////////////////////////////////////////////////////////////////////////////////////////
private void handle(FinalizePayoutTxRequest tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle RequestFinalizePayoutTxMessage called");
private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle PayoutTxPublishedMessage called");
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsOffererTrade,
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
() -> {
handleTaskRunnerSuccess("handle FinalizePayoutTxRequest");
handleTaskRunnerSuccess("handle PayoutTxPublishedMessage");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessFinalizePayoutTxRequest.class,
SignAndFinalizePayoutTx.class,
SendPayoutTxFinalizedMessage.class,
BroadcastAfterLockTime.class
BuyerProcessPayoutTxPublishedMessage.class
);
taskRunner.run();
}
@ -208,13 +213,13 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, NodeAddress peerNodeAddress) {
if (tradeMessage instanceof DepositTxPublishedMessage) {
handle((DepositTxPublishedMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof FinalizePayoutTxRequest) {
handle((FinalizePayoutTxRequest) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) tradeMessage, peerNodeAddress);
} else //noinspection StatementWithEmptyBody
if (tradeMessage instanceof PayDepositRequest) {
// do nothing as we get called the handleTakeOfferRequest method from outside
} else {
log.error("Incoming decrypted tradeMessage not supported. " + tradeMessage);
}
// do nothing as we get called the handleTakeOfferRequest method from outside
} else {
log.error("Incoming decrypted tradeMessage not supported. " + tradeMessage);
}
}
}

View file

@ -22,21 +22,23 @@ import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.core.trade.BuyerAsTakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.buyer.*;
import io.bisq.core.trade.protocol.tasks.shared.BroadcastAfterLockTime;
import io.bisq.core.trade.protocol.tasks.buyer.BuyerProcessPayoutTxPublishedMessage;
import io.bisq.core.trade.protocol.tasks.buyer.BuyerSendFiatTransferStartedMessage;
import io.bisq.core.trade.protocol.tasks.buyer.BuyerSetupPayoutTxListener;
import io.bisq.core.trade.protocol.tasks.buyer_as_maker.BuyerAsMakerSignPayoutTx;
import io.bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerCreatesDepositTxInputs;
import io.bisq.core.trade.protocol.tasks.buyer_as_taker.BuyerAsTakerSignAndPublishDepositTx;
import io.bisq.core.trade.protocol.tasks.taker.*;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.p2p.MailboxMessage;
import io.bisq.wire.message.trade.FinalizePayoutTxRequest;
import io.bisq.wire.message.trade.PublishDepositTxRequest;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.p2p.MailboxMessage;
import io.bisq.protobuffer.message.trade.PayoutTxPublishedMessage;
import io.bisq.protobuffer.message.trade.PublishDepositTxRequest;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol, TakerProtocol {
private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerProtocol.class);
private final BuyerAsTakerTrade buyerAsTakerTrade;
@ -51,19 +53,16 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing());
// If we are after the timeLock state we need to setup the listener again
Trade.State tradeState = trade.getState();
Trade.Phase phase = tradeState.getPhase();
if (trade.getPayoutTx() != null && (phase == Trade.Phase.FIAT_RECEIVED || phase == Trade.Phase.PAYOUT_PAID) &&
tradeState != Trade.State.PAYOUT_BROAD_CASTED) {
Trade.Phase phase = trade.getState().getPhase();
if (trade.isFiatSent() && !trade.isPayoutPublished()) {
TradeTaskRunner taskRunner = new TradeTaskRunner(trade,
() -> {
handleTaskRunnerSuccess("SetupPayoutTxLockTimeReachedListener");
handleTaskRunnerSuccess("BuyerSetupPayoutTxListener");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(BroadcastAfterLockTime.class);
taskRunner.addTasks(BuyerSetupPayoutTxListener.class);
taskRunner.run();
}
}
@ -76,9 +75,13 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
@Override
public void doApplyMailboxMessage(Message message, Trade trade) {
this.trade = trade;
if (message instanceof FinalizePayoutTxRequest)
handle((FinalizePayoutTxRequest) message, ((MailboxMessage) message).getSenderNodeAddress());
final NodeAddress senderNodeAddress = ((MailboxMessage) message).getSenderNodeAddress();
if (message instanceof PublishDepositTxRequest)
handle((PublishDepositTxRequest) message, senderNodeAddress);
else if (message instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) message, senderNodeAddress);
} else
log.error("We received an unhandled MailboxMessage" + message.toString());
}
@ -93,12 +96,14 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
this::handleTaskRunnerFault);
taskRunner.addTasks(
SelectArbitrator.class,
LoadCreateOfferFeeTx.class,
CreateTakeOfferFeeTx.class,
BroadcastTakeOfferFeeTx.class,
TakerCreatesDepositTxInputsAsBuyer.class,
SendPayDepositRequest.class
TakerSelectArbitrator.class,
TakerSelectMediator.class,
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class,
TakerCreateTakerFeeTx.class,
TakerPublishTakerFeeTx.class,
BuyerAsTakerCreatesDepositTxInputs.class,
TakerSendPayDepositRequest.class
);
startTimeout();
taskRunner.run();
@ -118,12 +123,13 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
() -> handleTaskRunnerSuccess("PublishDepositTxRequest"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessPublishDepositTxRequest.class,
VerifyOffererAccount.class,
VerifyAndSignContract.class,
SignAndPublishDepositTxAsBuyer.class,
SendDepositTxPublishedMessage.class,
PublishTradeStatistics.class
TakerProcessPublishDepositTxRequest.class,
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class,
TakerVerifyAndSignContract.class,
BuyerAsTakerSignAndPublishDepositTx.class,
TakerSendDepositTxPublishedMessage.class,
TakerPublishTradeStatistics.class
);
taskRunner.run();
}
@ -136,12 +142,8 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
// User clicked the "bank transfer started" button
@Override
public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
if (buyerAsTakerTrade.getState().ordinal() <= Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG.ordinal()) {
if (buyerAsTakerTrade.getState() == Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG)
log.warn("onFiatPaymentStarted called twice. " +
"That is expected if the app starts up and the other peer has still not continued.");
buyerAsTakerTrade.setState(Trade.State.BUYER_CONFIRMED_FIAT_PAYMENT_INITIATED);
if (!trade.isFiatSent()) {
buyerAsTakerTrade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> {
@ -153,14 +155,15 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
handleTaskRunnerFault(errorMessage);
});
taskRunner.addTasks(
VerifyOfferFeePayment.class,
SendFiatTransferStartedMessage.class
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class,
BuyerAsMakerSignPayoutTx.class,
BuyerSendFiatTransferStartedMessage.class,
BuyerSetupPayoutTxListener.class
);
taskRunner.run();
} else {
log.warn("onFiatPaymentStarted called twice. " +
"That should not happen.\n" +
"state=" + buyerAsTakerTrade.getState());
log.warn("onFiatPaymentStarted called twice. tradeState=" + trade.getState());
}
}
@ -169,22 +172,20 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
// Incoming message handling
///////////////////////////////////////////////////////////////////////////////////////////
private void handle(FinalizePayoutTxRequest tradeMessage, NodeAddress sender) {
private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle PayoutTxPublishedMessage called");
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> {
handleTaskRunnerSuccess("FinalizePayoutTxRequest");
handleTaskRunnerSuccess("handle PayoutTxPublishedMessage");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessFinalizePayoutTxRequest.class,
SignAndFinalizePayoutTx.class,
SendPayoutTxFinalizedMessage.class,
BroadcastAfterLockTime.class
BuyerProcessPayoutTxPublishedMessage.class
);
taskRunner.run();
}
@ -197,8 +198,8 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
protected void doHandleDecryptedMessage(TradeMessage tradeMessage, NodeAddress sender) {
if (tradeMessage instanceof PublishDepositTxRequest) {
handle((PublishDepositTxRequest) tradeMessage, sender);
} else if (tradeMessage instanceof FinalizePayoutTxRequest) {
handle((FinalizePayoutTxRequest) tradeMessage, sender);
} else if (tradeMessage instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) tradeMessage, sender);
} else {
log.error("Incoming message not supported. " + tradeMessage);
}

View file

@ -18,9 +18,9 @@
package io.bisq.core.trade.protocol;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
public interface OffererProtocol {
public interface MakerProtocol {
void handleTakeOfferRequest(TradeMessage message, NodeAddress taker);
}

View file

@ -0,0 +1,48 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol;
import io.bisq.core.offer.Offer;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Sha256Hash;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
@Slf4j
public class MediatorSelectionRule {
public static NodeAddress select(List<NodeAddress> acceptedMediatorNodeAddresses, Offer offer) {
List<NodeAddress> candidates = new ArrayList<>();
for (NodeAddress offerMediatorNodeAddress : offer.getMediatorNodeAddresses()) {
candidates.addAll(acceptedMediatorNodeAddresses.stream()
.filter(offerMediatorNodeAddress::equals)
.collect(Collectors.toList()));
}
checkArgument(candidates.size() > 0, "candidates.size() <= 0");
int index = Math.abs(Arrays.hashCode(Sha256Hash.hash(offer.getId().getBytes()))) % candidates.size();
NodeAddress selectedMediator = candidates.get(index);
log.debug("selectedMediator " + selectedMediator);
return selectedMediator;
}
}

View file

@ -26,18 +26,18 @@ import io.bisq.core.filter.FilterManager;
import io.bisq.core.offer.Offer;
import io.bisq.core.offer.OpenOfferManager;
import io.bisq.core.payment.PaymentAccount;
import io.bisq.core.trade.OffererTrade;
import io.bisq.core.trade.MakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.TradeManager;
import io.bisq.core.user.User;
import io.bisq.network.p2p.storage.P2PService;
import io.bisq.wire.crypto.KeyRing;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.btc.RawTransactionInput;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.filter.PaymentAccountFilter;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.crypto.KeyRing;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.filter.PaymentAccountFilter;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@ -88,6 +88,7 @@ public class ProcessModel implements Model, Serializable {
@Getter
@Setter
private List<NodeAddress> takerAcceptedArbitratorNodeAddresses;
private List<NodeAddress> takerAcceptedMediatorNodeAddresses;
// that is used to store temp. the peers address when we get an incoming message before the message is verified.
// After successful verified we copy that over to the trade.tradingPeerAddress
@ -179,8 +180,8 @@ public class ProcessModel implements Model, Serializable {
@Nullable
public PaymentAccountPayload getPaymentAccountPayload(Trade trade) {
PaymentAccount paymentAccount;
if (trade instanceof OffererTrade)
paymentAccount = user.getPaymentAccount(offer.getOffererPaymentAccountId());
if (trade instanceof MakerTrade)
paymentAccount = user.getPaymentAccount(offer.getMakerPaymentAccountId());
else
paymentAccount = user.getPaymentAccount(trade.getTakerPaymentAccountId());
return paymentAccount != null ? paymentAccount.getPaymentAccountPayload() : null;
@ -211,6 +212,62 @@ public class ProcessModel implements Model, Serializable {
return keyRing.getPubKeyRing();
}
public KeyRing getKeyRing() {
return keyRing;
}
public void setTakerAcceptedArbitratorNodeAddresses(List<NodeAddress> takerAcceptedArbitratorNodeAddresses) {
this.takerAcceptedArbitratorNodeAddresses = takerAcceptedArbitratorNodeAddresses;
}
public List<NodeAddress> getTakerAcceptedArbitratorNodeAddresses() {
return takerAcceptedArbitratorNodeAddresses;
}
public void setTakerAcceptedMediatorNodeAddresses(List<NodeAddress> takerAcceptedMediatorNodeAddresses) {
this.takerAcceptedMediatorNodeAddresses = takerAcceptedMediatorNodeAddresses;
}
public List<NodeAddress> getTakerAcceptedMediatorNodeAddresses() {
return takerAcceptedMediatorNodeAddresses;
}
public void setTempTradingPeerNodeAddress(NodeAddress tempTradingPeerNodeAddress) {
this.tempTradingPeerNodeAddress = tempTradingPeerNodeAddress;
}
public NodeAddress getTempTradingPeerNodeAddress() {
return tempTradingPeerNodeAddress;
}
public ArbitratorManager getArbitratorManager() {
return arbitratorManager;
}
public void setPreparedDepositTx(byte[] preparedDepositTx) {
this.preparedDepositTx = preparedDepositTx;
}
public byte[] getPreparedDepositTx() {
return preparedDepositTx;
}
public void setRawTransactionInputs(ArrayList<RawTransactionInput> rawTransactionInputs) {
this.rawTransactionInputs = rawTransactionInputs;
}
public ArrayList<RawTransactionInput> getRawTransactionInputs() {
return rawTransactionInputs;
}
public void setChangeOutputValue(long changeOutputValue) {
this.changeOutputValue = changeOutputValue;
}
public long getChangeOutputValue() {
return changeOutputValue;
}
public void setChangeOutputAddress(String changeOutputAddress) {
this.changeOutputAddress = changeOutputAddress;
}

View file

@ -20,50 +20,50 @@ package io.bisq.core.trade.protocol;
import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.core.trade.SellerAsOffererTrade;
import io.bisq.core.trade.SellerAsMakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.offerer.*;
import io.bisq.core.trade.protocol.tasks.seller.*;
import io.bisq.core.trade.protocol.tasks.shared.BroadcastAfterLockTime;
import io.bisq.core.trade.protocol.tasks.maker.*;
import io.bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
import io.bisq.core.trade.protocol.tasks.seller.SellerProcessFiatTransferStartedMessage;
import io.bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
import io.bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx;
import io.bisq.core.trade.protocol.tasks.seller_as_maker.SellerAsMakerCreatesAndSignsDepositTx;
import io.bisq.core.util.Validator;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.p2p.MailboxMessage;
import io.bisq.wire.message.trade.*;
import io.bisq.wire.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.p2p.MailboxMessage;
import io.bisq.protobuffer.message.trade.DepositTxPublishedMessage;
import io.bisq.protobuffer.message.trade.FiatTransferStartedMessage;
import io.bisq.protobuffer.message.trade.PayDepositRequest;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
public class SellerAsOffererProtocol extends TradeProtocol implements SellerProtocol, OffererProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerAsOffererProtocol.class);
private final SellerAsOffererTrade sellerAsOffererTrade;
@Slf4j
public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtocol, MakerProtocol {
private final SellerAsMakerTrade sellerAsMakerTrade;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public SellerAsOffererProtocol(SellerAsOffererTrade trade) {
public SellerAsMakerProtocol(SellerAsMakerTrade trade) {
super(trade);
this.sellerAsOffererTrade = trade;
this.sellerAsMakerTrade = trade;
// If we are after the time lock state we need to setup the listener again
//TODO not sure if that is not called already from the checkPayoutTxTimeLock at tradeProtocol
Trade.State tradeState = trade.getState();
Trade.Phase phase = tradeState.getPhase();
if (trade.getPayoutTx() != null && (phase == Trade.Phase.FIAT_RECEIVED || phase == Trade.Phase.PAYOUT_PAID) &&
tradeState != Trade.State.PAYOUT_BROAD_CASTED) {
Trade.Phase phase = trade.getState().getPhase();
if (phase == Trade.Phase.TAKER_FEE_PUBLISHED) {
TradeTaskRunner taskRunner = new TradeTaskRunner(trade,
() -> {
handleTaskRunnerSuccess("SetupPayoutTxLockTimeReachedListener");
handleTaskRunnerSuccess("MakerSetupDepositTxListener");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(BroadcastAfterLockTime.class);
taskRunner.addTasks(MakerSetupDepositTxListener.class);
taskRunner.run();
}
}
@ -78,15 +78,12 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
this.trade = trade;
NodeAddress peerNodeAddress = ((MailboxMessage) message).getSenderNodeAddress();
if (message instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) message, peerNodeAddress);
} else {
if (message instanceof FiatTransferStartedMessage) {
handle((FiatTransferStartedMessage) message, peerNodeAddress);
} else if (message instanceof DepositTxPublishedMessage) {
handle((DepositTxPublishedMessage) message, peerNodeAddress);
}
}
if (message instanceof DepositTxPublishedMessage)
handle((DepositTxPublishedMessage) message, peerNodeAddress);
else if (message instanceof FiatTransferStartedMessage)
handle((FiatTransferStartedMessage) message, peerNodeAddress);
else
log.error("We received an unhandled MailboxMessage" + message.toString());
}
@ -101,19 +98,20 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
processModel.setTradeMessage(message);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> handleTaskRunnerSuccess("handleTakeOfferRequest"),
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessPayDepositRequest.class,
VerifyArbitrationSelection.class,
VerifyTakerAccount.class,
LoadTakeOfferFeeTx.class,
CreateAndSignContract.class,
OffererCreatesAndSignsDepositTxAsSeller.class,
SetupDepositBalanceListener.class,
SendPublishDepositTxRequest.class
MakerProcessPayDepositRequest.class,
MakerVerifyArbitratorSelection.class,
MakerVerifyMediatorSelection.class,
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class,
MakerCreateAndSignContract.class,
SellerAsMakerCreatesAndSignsDepositTx.class,
MakerSetupDepositTxListener.class,
MakerSendPublishDepositTxRequest.class
);
startTimeout();
taskRunner.run();
@ -129,12 +127,14 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> handleTaskRunnerSuccess("DepositTxPublishedMessage"),
this::handleTaskRunnerFault);
taskRunner.addTasks(ProcessDepositTxPublishedMessage.class,
PublishTradeStatistics.class);
taskRunner.addTasks(MakerProcessDepositTxPublishedMessage.class,
MakerPublishTradeStatistics.class,
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class);
taskRunner.run();
}
@ -147,11 +147,13 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> handleTaskRunnerSuccess("FiatTransferStartedMessage"),
this::handleTaskRunnerFault);
taskRunner.addTasks(ProcessFiatTransferStartedMessage.class);
taskRunner.addTasks(SellerProcessFiatTransferStartedMessage.class,
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class);
taskRunner.run();
}
@ -163,14 +165,9 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
// User clicked the "bank transfer received" button, so we release the funds for pay out
@Override
public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
if (sellerAsOffererTrade.getState().ordinal() <= Trade.State.SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG.ordinal()) {
if (sellerAsOffererTrade.getState() == Trade.State.SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG)
log.warn("onFiatPaymentReceived called twice. " +
"That is expected if the app starts up and the other peer has still not continued.");
sellerAsOffererTrade.setState(Trade.State.SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
if (trade.isFiatSent() && !trade.isFiatReceived()) {
sellerAsMakerTrade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> {
resultHandler.handleResult();
handleTaskRunnerSuccess("onFiatPaymentReceived");
@ -181,36 +178,20 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
});
taskRunner.addTasks(
VerifyTakeOfferFeePayment.class,
SignPayoutTx.class,
SendFinalizePayoutTxRequest.class
MakerVerifyTakerAccount.class,
MakerVerifyTakerFeePayment.class,
SellerSignAndFinalizePayoutTx.class,
SellerBroadcastPayoutTx.class,
SellerSendPayoutTxPublishedMessage.class
);
taskRunner.run();
} else {
log.warn("onFiatPaymentReceived called twice. " +
"That should not happen.\n" +
"state=" + sellerAsOffererTrade.getState());
"state=" + sellerAsMakerTrade.getState());
}
}
private void handle(PayoutTxFinalizedMessage tradeMessage, NodeAddress sender) {
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsOffererTrade,
() -> {
handleTaskRunnerSuccess("PayoutTxFinalizedMessage");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessPayoutTxFinalizedMessage.class,
BroadcastAfterLockTime.class
);
taskRunner.run();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Massage dispatcher
///////////////////////////////////////////////////////////////////////////////////////////
@ -221,8 +202,6 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
handle((DepositTxPublishedMessage) tradeMessage, sender);
} else if (tradeMessage instanceof FiatTransferStartedMessage) {
handle((FiatTransferStartedMessage) tradeMessage, sender);
} else if (tradeMessage instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) tradeMessage, sender);
} else {
log.error("Incoming tradeMessage not supported. " + tradeMessage);
}

View file

@ -22,22 +22,23 @@ import io.bisq.common.handlers.ErrorMessageHandler;
import io.bisq.common.handlers.ResultHandler;
import io.bisq.core.trade.SellerAsTakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.seller.*;
import io.bisq.core.trade.protocol.tasks.shared.BroadcastAfterLockTime;
import io.bisq.core.trade.protocol.tasks.seller.SellerBroadcastPayoutTx;
import io.bisq.core.trade.protocol.tasks.seller.SellerProcessFiatTransferStartedMessage;
import io.bisq.core.trade.protocol.tasks.seller.SellerSendPayoutTxPublishedMessage;
import io.bisq.core.trade.protocol.tasks.seller.SellerSignAndFinalizePayoutTx;
import io.bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerCreatesDepositTxInputs;
import io.bisq.core.trade.protocol.tasks.seller_as_taker.SellerAsTakerSignAndPublishDepositTx;
import io.bisq.core.trade.protocol.tasks.taker.*;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.p2p.MailboxMessage;
import io.bisq.wire.message.trade.FiatTransferStartedMessage;
import io.bisq.wire.message.trade.PayoutTxFinalizedMessage;
import io.bisq.wire.message.trade.PublishDepositTxRequest;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.p2p.MailboxMessage;
import io.bisq.protobuffer.message.trade.FiatTransferStartedMessage;
import io.bisq.protobuffer.message.trade.PublishDepositTxRequest;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtocol, TakerProtocol {
private static final Logger log = LoggerFactory.getLogger(SellerAsTakerProtocol.class);
private final SellerAsTakerTrade sellerAsTakerTrade;
@ -51,23 +52,6 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
this.sellerAsTakerTrade = trade;
processModel.tradingPeer.setPubKeyRing(trade.getOffer().getPubKeyRing());
// If we are after the timeLock state we need to setup the listener again
//TODO not sure if that is not called already from the checkPayoutTxTimeLock at tradeProtocol
Trade.State tradeState = trade.getState();
Trade.Phase phase = tradeState.getPhase();
if (trade.getPayoutTx() != null && (phase == Trade.Phase.FIAT_RECEIVED || phase == Trade.Phase.PAYOUT_PAID) &&
tradeState != Trade.State.PAYOUT_BROAD_CASTED) {
TradeTaskRunner taskRunner = new TradeTaskRunner(trade,
() -> {
handleTaskRunnerSuccess("SetupPayoutTxLockTimeReachedListener");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(BroadcastAfterLockTime.class);
taskRunner.run();
}
}
@ -81,11 +65,12 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
if (message instanceof MailboxMessage) {
NodeAddress peerNodeAddress = ((MailboxMessage) message).getSenderNodeAddress();
if (message instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) message, peerNodeAddress);
} else if (message instanceof FiatTransferStartedMessage) {
if (message instanceof PublishDepositTxRequest)
handle((PublishDepositTxRequest) message, peerNodeAddress);
else if (message instanceof FiatTransferStartedMessage)
handle((FiatTransferStartedMessage) message, peerNodeAddress);
}
else
log.error("We received an unhandled MailboxMessage" + message.toString());
}
}
@ -101,12 +86,14 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
this::handleTaskRunnerFault);
taskRunner.addTasks(
SelectArbitrator.class,
LoadCreateOfferFeeTx.class,
CreateTakeOfferFeeTx.class,
BroadcastTakeOfferFeeTx.class,
TakerCreatesDepositTxInputsAsSeller.class,
SendPayDepositRequest.class
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class,
TakerSelectArbitrator.class,
TakerSelectMediator.class,
TakerCreateTakerFeeTx.class,
TakerPublishTakerFeeTx.class,
SellerAsTakerCreatesDepositTxInputs.class,
TakerSendPayDepositRequest.class
);
startTimeout();
taskRunner.run();
@ -128,12 +115,13 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessPublishDepositTxRequest.class,
VerifyOffererAccount.class,
VerifyAndSignContract.class,
SignAndPublishDepositTxAsSeller.class,
SendDepositTxPublishedMessage.class,
PublishTradeStatistics.class
TakerProcessPublishDepositTxRequest.class,
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class,
TakerVerifyAndSignContract.class,
SellerAsTakerSignAndPublishDepositTx.class,
TakerSendDepositTxPublishedMessage.class,
TakerPublishTradeStatistics.class
);
taskRunner.run();
}
@ -151,7 +139,9 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
() -> handleTaskRunnerSuccess("FiatTransferStartedMessage"),
this::handleTaskRunnerFault);
taskRunner.addTasks(ProcessFiatTransferStartedMessage.class);
taskRunner.addTasks(SellerProcessFiatTransferStartedMessage.class,
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class);
taskRunner.run();
}
@ -163,13 +153,8 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
// User clicked the "bank transfer received" button, so we release the funds for pay out
@Override
public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
if (sellerAsTakerTrade.getState().ordinal() <= Trade.State.SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG.ordinal()) {
if (sellerAsTakerTrade.getState() == Trade.State.SELLER_SENT_FIAT_PAYMENT_RECEIPT_MSG)
log.warn("onFiatPaymentReceived called twice. " +
"That is expected if the app starts up and the other peer has still not continued.");
sellerAsTakerTrade.setState(Trade.State.SELLER_CONFIRMED_FIAT_PAYMENT_RECEIPT);
if (trade.isFiatSent() && !trade.isFiatReceived()) {
sellerAsTakerTrade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> {
resultHandler.handleResult();
@ -181,9 +166,11 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
});
taskRunner.addTasks(
VerifyOfferFeePayment.class,
SignPayoutTx.class,
SendFinalizePayoutTxRequest.class
TakerVerifyMakerAccount.class,
TakerVerifyMakerFeePayment.class,
SellerSignAndFinalizePayoutTx.class,
SellerBroadcastPayoutTx.class,
SellerSendPayoutTxPublishedMessage.class
);
taskRunner.run();
} else {
@ -193,25 +180,6 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
}
}
private void handle(PayoutTxFinalizedMessage tradeMessage, NodeAddress sender) {
stopTimeout();
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> {
handleTaskRunnerSuccess("PayoutTxFinalizedMessage");
processModel.onComplete();
},
this::handleTaskRunnerFault);
taskRunner.addTasks(
ProcessPayoutTxFinalizedMessage.class,
BroadcastAfterLockTime.class
);
taskRunner.run();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Massage dispatcher
@ -223,8 +191,6 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
handle((PublishDepositTxRequest) tradeMessage, sender);
} else if (tradeMessage instanceof FiatTransferStartedMessage) {
handle((FiatTransferStartedMessage) tradeMessage, sender);
} else if (tradeMessage instanceof PayoutTxFinalizedMessage) {
handle((PayoutTxFinalizedMessage) tradeMessage, sender);
} else {
log.error("Incoming message not supported. " + tradeMessage);
}

View file

@ -19,28 +19,29 @@ package io.bisq.core.trade.protocol;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.core.trade.OffererTrade;
import io.bisq.core.trade.MakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.TradeManager;
import io.bisq.network.p2p.DecryptedDirectMessageListener;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.wire.message.Message;
import io.bisq.wire.message.trade.TradeMessage;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.p2p.NodeAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.Message;
import io.bisq.protobuffer.message.trade.TradeMessage;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import javafx.beans.value.ChangeListener;
import lombok.extern.slf4j.Slf4j;
import java.security.PublicKey;
import static io.bisq.core.util.Validator.nonEmptyStringOf;
@Slf4j
public abstract class TradeProtocol {
private static final Logger log = LoggerFactory.getLogger(TradeProtocol.class);
private static final long TIMEOUT_SEC = 75;
protected final ProcessModel processModel;
private final DecryptedDirectMessageListener decryptedDirectMessageListener;
private final ChangeListener<Trade.State> stateChangeListener;
protected Trade trade;
private Timer timeoutTimer;
@ -76,6 +77,12 @@ public abstract class TradeProtocol {
//}
};
processModel.getP2PService().addDecryptedDirectMessageListener(decryptedDirectMessageListener);
stateChangeListener = (observable, oldValue, newValue) -> {
if (newValue.getPhase() == Trade.Phase.TAKER_FEE_PUBLISHED && trade instanceof MakerTrade)
processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer());
};
trade.stateProperty().addListener(stateChangeListener);
}
public void completed() {
@ -89,6 +96,7 @@ public abstract class TradeProtocol {
private void cleanup() {
log.debug("cleanup " + this);
stopTimeout();
trade.stateProperty().removeListener(stateChangeListener);
// We removed that from here earlier as it broke the trade process in some non critical error cases.
// But it should be actually removed...
processModel.getP2PService().removeDecryptedDirectMessageListener(decryptedDirectMessageListener);
@ -112,7 +120,7 @@ public abstract class TradeProtocol {
timeoutTimer = UserThread.runAfter(() -> {
log.error("Timeout reached. TradeID=" + trade.getId());
trade.setErrorMessage("A timeout occurred.");
cleanupTradable();
cleanupTradableOnFault();
cleanup();
}, TIMEOUT_SEC);
}
@ -130,27 +138,28 @@ public abstract class TradeProtocol {
protected void handleTaskRunnerFault(String errorMessage) {
log.error(errorMessage);
cleanupTradable();
cleanupTradableOnFault();
cleanup();
}
private void cleanupTradable() {
Trade.State tradeState = trade.getState();
log.debug("cleanupTradable tradeState=" + tradeState);
boolean isOffererTrade = trade instanceof OffererTrade;
if (isOffererTrade && (tradeState == Trade.State.OFFERER_SENT_PUBLISH_DEPOSIT_TX_REQUEST || tradeState == Trade.State.DEPOSIT_SEEN_IN_NETWORK))
processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer());
//boolean isTakerTrade = trade instanceof TakerTrade;
// if (isTakerTrade) {
private void cleanupTradableOnFault() {
final Trade.State state = trade.getState();
log.debug("cleanupTradable tradeState=" + state);
TradeManager tradeManager = processModel.getTradeManager();
if (tradeState.getPhase() == Trade.Phase.PREPARATION) {
final Trade.Phase phase = state.getPhase();
if (trade.isInPreparation()) {
// no funds left. we just clean up the trade list
tradeManager.removePreparedTrade(trade);
} else if (tradeState.getPhase() == Trade.Phase.TAKER_FEE_PAID) {
tradeManager.addTradeToFailedTrades(trade);
processModel.getWalletService().swapAnyTradeEntryContextToAvailableEntry(trade.getId());
} else {
// we have either as taker the fee paid or as maker the publishDepositTx request sent,
// so the maker has his offer closed and therefor its for both a failed trade
if (trade.isTakerFeePublished() && !trade.isWithdrawn())
tradeManager.addTradeToFailedTrades(trade);
// if we have not the deposit already published we swap reserved funds to available funds
if (!trade.isDepositPublished())
processModel.getWalletService().swapAnyTradeEntryContextToAvailableEntry(trade.getId());
}
// }
}
}

View file

@ -19,23 +19,21 @@ package io.bisq.core.trade.protocol;
import io.bisq.common.app.Version;
import io.bisq.common.persistance.Persistable;
import io.bisq.wire.payload.btc.RawTransactionInput;
import io.bisq.wire.payload.crypto.PubKeyRing;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import io.bisq.protobuffer.payload.crypto.PubKeyRing;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;
@Slf4j
public final class TradingPeer implements Persistable {
// That object is saved to disc. We need to take care of changes to not break deserialization.
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
private static final Logger log = LoggerFactory.getLogger(TradingPeer.class);
// Mutable
private String accountId;
private PaymentAccountPayload paymentAccountPayload;
@ -154,7 +152,7 @@ public final class TradingPeer implements Persistable {
return changeOutputValue;
}
public void setChangeOutputAddress(String changeOutputAddress) {
public void setChangeOutputAddress(@Nullable String changeOutputAddress) {
this.changeOutputAddress = changeOutputAddress;
}

View file

@ -22,13 +22,11 @@ import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.ProcessModel;
import io.bisq.network.p2p.DecryptedMsgWithPubKey;
import io.bisq.wire.message.p2p.MailboxMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.p2p.MailboxMessage;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public abstract class TradeTask extends Task<Trade> {
private static final Logger log = LoggerFactory.getLogger(TradeTask.class);
protected final ProcessModel processModel;
protected final Trade trade;

View file

@ -15,26 +15,24 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.seller;
package io.bisq.core.trade.protocol.tasks.buyer;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.core.util.Validator;
import io.bisq.wire.message.trade.PayoutTxFinalizedMessage;
import io.bisq.protobuffer.message.trade.PayoutTxPublishedMessage;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class ProcessPayoutTxFinalizedMessage extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(ProcessPayoutTxFinalizedMessage.class);
@Slf4j
public class BuyerProcessPayoutTxPublishedMessage extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public ProcessPayoutTxFinalizedMessage(TaskRunner taskHandler, Trade trade) {
public BuyerProcessPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -43,21 +41,20 @@ public class ProcessPayoutTxFinalizedMessage extends TradeTask {
try {
runInterceptHook();
log.debug("current trade state " + trade.getState());
PayoutTxFinalizedMessage message = (PayoutTxFinalizedMessage) processModel.getTradeMessage();
PayoutTxPublishedMessage message = (PayoutTxPublishedMessage) processModel.getTradeMessage();
Validator.checkTradeId(processModel.getId(), message);
checkNotNull(message);
checkArgument(message.payoutTx != null);
Transaction walletTx = processModel.getTradeWalletService().addTransactionToWallet(message.payoutTx);
Transaction walletTx = processModel.getTradeWalletService().addTxToWallet(message.payoutTx);
trade.setPayoutTx(walletTx);
BtcWalletService.printTx("payoutTx received from peer", walletTx);
// update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.SELLER_RECEIVED_AND_COMMITTED_PAYOUT_TX);
trade.setState(Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG);
complete();
} catch (Throwable t) {

View file

@ -23,15 +23,15 @@ import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.network.p2p.SendMailboxMessageListener;
import io.bisq.wire.message.trade.FiatTransferStartedMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.message.trade.FiatTransferStartedMessage;
import lombok.extern.slf4j.Slf4j;
public class SendFiatTransferStartedMessage extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(SendFiatTransferStartedMessage.class);
import java.util.UUID;
@Slf4j
public class BuyerSendFiatTransferStartedMessage extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public SendFiatTransferStartedMessage(TaskRunner taskHandler, Trade trade) {
public BuyerSendFiatTransferStartedMessage(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -40,33 +40,41 @@ public class SendFiatTransferStartedMessage extends TradeTask {
try {
runInterceptHook();
BtcWalletService walletService = processModel.getWalletService();
AddressEntry payoutAddressEntry = walletService.getOrCreateAddressEntry(processModel.getOffer().getId(), AddressEntry.Context.TRADE_PAYOUT);
final String id = processModel.getId();
AddressEntry payoutAddressEntry = walletService.getOrCreateAddressEntry(id,
AddressEntry.Context.TRADE_PAYOUT);
final FiatTransferStartedMessage message = new FiatTransferStartedMessage(
id,
payoutAddressEntry.getAddressString(),
processModel.getMyNodeAddress(),
processModel.getPayoutTxSignature(),
UUID.randomUUID().toString()
);
log.info("Send message to peer. tradeId={}, message{}", id, message);
trade.setState(Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG);
processModel.getP2PService().sendEncryptedMailboxMessage(
trade.getTradingPeerNodeAddress(),
processModel.tradingPeer.getPubKeyRing(),
new FiatTransferStartedMessage(
processModel.getId(),
payoutAddressEntry.getAddressString(),
processModel.getMyNodeAddress()
),
message,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.debug("Message arrived at peer.");
trade.setState(Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG);
log.info("Message arrived at peer. tradeId={}, message{}", id, message);
trade.setState(Trade.State.BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG);
complete();
}
@Override
public void onStoredInMailbox() {
log.debug("Message stored in mailbox.");
trade.setState(Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG);
log.info("Message stored in mailbox. tradeId={}, message{}", id, message);
trade.setState(Trade.State.BUYER_STORED_IN_MAILBOX_FIAT_PAYMENT_INITIATED_MSG);
complete();
}
@Override
public void onFault(String errorMessage) {
appendToErrorMessage("FiatTransferStartedMessage sending failed");
trade.setState(Trade.State.BUYER_SEND_FAILED_FIAT_PAYMENT_INITIATED_MSG);
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
failed(errorMessage);
}
}

View file

@ -0,0 +1,95 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.buyer;
import io.bisq.common.UserThread;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.listeners.AddressConfidenceListener;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.TransactionConfidence;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
@Slf4j
public class BuyerSetupPayoutTxListener extends TradeTask {
// Use instance fields to not get eaten up by the GC
private Subscription tradeStateSubscription;
private AddressConfidenceListener listener;
@SuppressWarnings({"WeakerAccess", "unused"})
public BuyerSetupPayoutTxListener(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
if (!trade.isPayoutPublished()) {
BtcWalletService walletService = processModel.getWalletService();
Address address = walletService.getOrCreateAddressEntry(processModel.getOffer().getId(),
AddressEntry.Context.TRADE_PAYOUT).getAddress();
if (isInNetwork(walletService.getConfidenceForAddress(address))) {
trade.setState(Trade.State.BUYER_SAW_PAYOUT_TX_IN_NETWORK);
} else {
listener = new AddressConfidenceListener(address) {
@Override
public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
if (isInNetwork(confidence))
trade.setState(Trade.State.BUYER_SAW_PAYOUT_TX_IN_NETWORK);
}
};
walletService.addAddressConfidenceListener(listener);
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
log.debug("BuyerSetupListenerForPayoutTx tradeStateSubscription tradeState=" + newValue);
if (trade.isPayoutPublished()) {
walletService.removeAddressConfidenceListener(listener);
// hack to remove tradeStateSubscription at callback
UserThread.execute(this::unSubscribe);
}
});
}
}
// we complete immediately, our object stays alive because the balanceListener is stored in the WalletService
complete();
} catch (Throwable t) {
failed(t);
}
}
private boolean isInNetwork(TransactionConfidence confidence) {
log.debug("onTransactionConfidenceChanged " + confidence);
return confidence != null &&
(confidence.getConfidenceType().equals(TransactionConfidence.ConfidenceType.BUILDING) ||
confidence.getConfidenceType().equals(TransactionConfidence.ConfidenceType.PENDING));
}
private void unSubscribe() {
if (tradeStateSubscription != null)
tradeStateSubscription.unsubscribe();
}
}

View file

@ -1,102 +0,0 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.buyer;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.data.PreparedDepositTxAndOffererInputs;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.TradingPeer;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.wire.crypto.Hash;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class OffererCreatesAndSignsDepositTxAsBuyer extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(OffererCreatesAndSignsDepositTxAsBuyer.class);
@SuppressWarnings({"WeakerAccess", "unused"})
public OffererCreatesAndSignsDepositTxAsBuyer(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
Coin buyerInputAmount = trade.getOffer().getBuyerSecurityDeposit();
Coin msOutputAmount = buyerInputAmount
.add(trade.getTxFee())
.add(trade.getOffer().getSellerSecurityDeposit())
.add(trade.getTradeAmount());
log.debug("\n\n------------------------------------------------------------\n"
+ "Contract as json\n"
+ trade.getContractAsJson()
+ "\n------------------------------------------------------------\n");
byte[] contractHash = Hash.getHash(trade.getContractAsJson());
trade.setContractHash(contractHash);
BtcWalletService walletService = processModel.getWalletService();
String id = processModel.getOffer().getId();
Optional<AddressEntry> addressEntryOptional = walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG);
checkArgument(addressEntryOptional.isPresent(), "addressEntryOptional must be present");
AddressEntry buyerMultiSigAddressEntry = addressEntryOptional.get();
buyerMultiSigAddressEntry.setCoinLockedInMultiSig(buyerInputAmount);
walletService.saveAddressEntryList();
Address offererAddress = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.RESERVED_FOR_TRADE).getAddress();
Address offererChangeAddress = walletService.getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress();
TradingPeer tradingPeer = processModel.tradingPeer;
byte[] buyerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(buyerMultiSigPubKey, buyerMultiSigAddressEntry.getPubKey()),
"buyerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
PreparedDepositTxAndOffererInputs result = processModel.getTradeWalletService().offererCreatesAndSignsDepositTx(
true,
contractHash,
buyerInputAmount,
msOutputAmount,
tradingPeer.getRawTransactionInputs(),
tradingPeer.getChangeOutputValue(),
tradingPeer.getChangeOutputAddress(),
offererAddress,
offererChangeAddress,
buyerMultiSigPubKey,
tradingPeer.getMultiSigPubKey(),
trade.getArbitratorPubKey());
processModel.setPreparedDepositTx(result.depositTransaction);
processModel.setRawTransactionInputs(result.rawOffererInputs);
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -1,63 +0,0 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.buyer;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.core.util.Validator;
import io.bisq.wire.message.trade.FinalizePayoutTxRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkNotNull;
public class ProcessFinalizePayoutTxRequest extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(ProcessFinalizePayoutTxRequest.class);
@SuppressWarnings({"WeakerAccess", "unused"})
public ProcessFinalizePayoutTxRequest(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
log.debug("current trade state " + trade.getState());
FinalizePayoutTxRequest message = (FinalizePayoutTxRequest) processModel.getTradeMessage();
Validator.checkTradeId(processModel.getId(), message);
checkNotNull(message);
processModel.tradingPeer.setSignature(checkNotNull(message.sellerSignature));
processModel.tradingPeer.setPayoutAddressString(Validator.nonEmptyStringOf(message.sellerPayoutAddress));
trade.setLockTimeAsBlockHeight(Validator.nonNegativeLongOf(message.lockTimeAsBlockHeight));
// update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.BUYER_RECEIVED_FIAT_PAYMENT_RECEIPT_MSG);
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -1,81 +0,0 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.buyer;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.network.p2p.SendMailboxMessageListener;
import io.bisq.wire.message.trade.PayoutTxFinalizedMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SendPayoutTxFinalizedMessage extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(SendPayoutTxFinalizedMessage.class);
@SuppressWarnings({"WeakerAccess", "unused"})
public SendPayoutTxFinalizedMessage(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
if (trade.getPayoutTx() != null) {
processModel.getP2PService().sendEncryptedMailboxMessage(
trade.getTradingPeerNodeAddress(),
processModel.tradingPeer.getPubKeyRing(),
new PayoutTxFinalizedMessage(
processModel.getId(),
trade.getPayoutTx().bitcoinSerialize(),
processModel.getMyNodeAddress()
),
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.trace("Message arrived at peer.");
complete();
}
@Override
public void onStoredInMailbox() {
log.trace("Message stored in mailbox.");
complete();
}
@Override
public void onFault(String errorMessage) {
appendToErrorMessage("PayoutTxFinalizedMessage sending failed. errorMessage=" + errorMessage);
failed(errorMessage);
}
}
);
// state must not be set in onArrived or onStoredInMailbox handlers as we would get that
// called delayed and would overwrite the broad cast state set by the next task
trade.setState(Trade.State.BUYER_STARTED_SEND_PAYOUT_TX);
} else {
log.error("trade.getPayoutTx() = " + trade.getPayoutTx());
failed("PayoutTx is null");
}
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -0,0 +1,123 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.buyer_as_maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.data.PreparedDepositTxAndMakerInputs;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.offer.Offer;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.TradingPeer;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.protobuffer.crypto.Hash;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
public class BuyerAsMakerCreatesAndSignsDepositTx extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public BuyerAsMakerCreatesAndSignsDepositTx(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
BtcWalletService walletService = processModel.getWalletService();
String id = processModel.getOffer().getId();
TradingPeer tradingPeer = processModel.tradingPeer;
final Offer offer = trade.getOffer();
// params
final boolean makerIsBuyer = true;
final byte[] contractHash = Hash.getHash(trade.getContractAsJson());
trade.setContractHash(contractHash);
log.debug("\n\n------------------------------------------------------------\n"
+ "Contract as json\n"
+ trade.getContractAsJson()
+ "\n------------------------------------------------------------\n");
final Coin makerInputAmount = offer.getBuyerSecurityDeposit();
Optional<AddressEntry> addressEntryOptional = walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG);
checkArgument(addressEntryOptional.isPresent(), "addressEntryOptional must be present");
AddressEntry makerMultiSigAddressEntry = addressEntryOptional.get();
makerMultiSigAddressEntry.setCoinLockedInMultiSig(makerInputAmount);
walletService.saveAddressEntryList();
final Coin msOutputAmount = makerInputAmount
.add(trade.getTxFee())
.add(offer.getSellerSecurityDeposit())
.add(trade.getTradeAmount());
final List<RawTransactionInput> takerRawTransactionInputs = tradingPeer.getRawTransactionInputs();
final long takerChangeOutputValue = tradingPeer.getChangeOutputValue();
final String takerChangeAddressString = tradingPeer.getChangeOutputAddress();
final Address makerAddress = walletService.getOrCreateAddressEntry(id,
AddressEntry.Context.RESERVED_FOR_TRADE).getAddress();
final Address makerChangeAddress = walletService.getOrCreateAddressEntry(AddressEntry.Context.AVAILABLE).getAddress();
final byte[] buyerPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(buyerPubKey,
makerMultiSigAddressEntry.getPubKey()),
"buyerPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
final byte[] sellerPubKey = tradingPeer.getMultiSigPubKey();
final byte[] arbitratorBtcPubKey = trade.getArbitratorBtcPubKey();
PreparedDepositTxAndMakerInputs result = processModel.getTradeWalletService().makerCreatesAndSignsDepositTx(
makerIsBuyer,
contractHash,
makerInputAmount,
msOutputAmount,
takerRawTransactionInputs,
takerChangeOutputValue,
takerChangeAddressString,
makerAddress,
makerChangeAddress,
buyerPubKey,
sellerPubKey,
arbitratorBtcPubKey);
processModel.setPreparedDepositTx(result.depositTransaction);
processModel.setRawTransactionInputs(result.rawMakerInputs);
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View file

@ -15,7 +15,7 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.seller;
package io.bisq.core.trade.protocol.tasks.buyer_as_maker;
import com.google.common.base.Preconditions;
import io.bisq.common.taskrunner.TaskRunner;
@ -23,21 +23,19 @@ import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Coin;
import org.bitcoinj.crypto.DeterministicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkArgument;
public class SignPayoutTx extends TradeTask {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(SignPayoutTx.class);
@Slf4j
public class BuyerAsMakerSignPayoutTx extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public SignPayoutTx(TaskRunner taskHandler, Trade trade) {
public BuyerAsMakerSignPayoutTx(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -47,40 +45,42 @@ public class SignPayoutTx extends TradeTask {
runInterceptHook();
Preconditions.checkNotNull(trade.getTradeAmount(), "trade.getTradeAmount() must not be null");
Preconditions.checkNotNull(trade.getDepositTx(), "trade.getDepositTx() must not be null");
Coin sellerPayoutAmount = trade.getOffer().getSellerSecurityDeposit();
Coin buyerPayoutAmount = trade.getOffer().getBuyerSecurityDeposit()
.add(trade.getTradeAmount());
// We use the sellers LastBlockSeenHeight, which might be different to the buyers one.
// If lock time is 0 we set lockTimeAsBlockHeight to 0 to mark it as "not set".
// In the tradeWallet we apply the lockTime only if it is set, otherwise we use the default values for
// transaction lockTime and sequence number
long lockTime = trade.getOffer().getPaymentMethod().getLockTime();
long lockTimeAsBlockHeight = 0;
if (lockTime > 0)
lockTimeAsBlockHeight = processModel.getTradeWalletService().getLastBlockSeenHeight() + lockTime;
trade.setLockTimeAsBlockHeight(lockTimeAsBlockHeight);
BtcWalletService walletService = processModel.getWalletService();
String id = processModel.getOffer().getId();
String sellerPayoutAddressString = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT).getAddressString();
DeterministicKey multiSigKeyPair = walletService.getMultiSigKeyPair(id, processModel.getMyMultiSigPubKey());
byte[] sellerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(sellerMultiSigPubKey,
walletService.getOrCreateAddressEntry(id, AddressEntry.Context.MULTI_SIG).getPubKey()),
"sellerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
byte[] payoutTxSignature = processModel.getTradeWalletService().sellerSignsPayoutTx(
Coin buyerPayoutAmount = trade.getOffer().getBuyerSecurityDeposit().add(trade.getTradeAmount());
Coin sellerPayoutAmount = trade.getOffer().getSellerSecurityDeposit();
String buyerPayoutAddressString = walletService.getOrCreateAddressEntry(id,
AddressEntry.Context.TRADE_PAYOUT).getAddressString();
final String sellerPayoutAddressString = processModel.tradingPeer.getPayoutAddressString();
DeterministicKey buyerMultiSigKeyPair = walletService.getMultiSigKeyPair(id, processModel.getMyMultiSigPubKey());
byte[] buyerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(buyerMultiSigPubKey,
walletService.getOrCreateAddressEntry(id, AddressEntry.Context.MULTI_SIG).getPubKey()),
"buyerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
final byte[] sellerMultiSigPubKey = processModel.tradingPeer.getMultiSigPubKey();
byte[] payoutTxSignature = processModel.getTradeWalletService().buyerSignsPayoutTx(
trade.getDepositTx(),
buyerPayoutAmount,
sellerPayoutAmount,
processModel.tradingPeer.getPayoutAddressString(),
buyerPayoutAddressString,
sellerPayoutAddressString,
multiSigKeyPair,
lockTimeAsBlockHeight,
processModel.tradingPeer.getMultiSigPubKey(),
buyerMultiSigKeyPair,
buyerMultiSigPubKey,
sellerMultiSigPubKey,
trade.getArbitratorPubKey());
trade.getArbitratorBtcPubKey());
/*
DeterministicKey multiSigKeyPair,
byte[] buyerPubKey,
byte[] sellerPubKey,
byte[] arbitratorPubKey
*/
processModel.setPayoutTxSignature(payoutTxSignature);

View file

@ -15,7 +15,7 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.buyer;
package io.bisq.core.trade.protocol.tasks.buyer_as_taker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
@ -23,17 +23,15 @@ import io.bisq.core.btc.data.InputsAndChangeOutput;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TakerCreatesDepositTxInputsAsBuyer extends TradeTask {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(TakerCreatesDepositTxInputsAsBuyer.class);
@Slf4j
public class BuyerAsTakerCreatesDepositTxInputs extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public TakerCreatesDepositTxInputsAsBuyer(TaskRunner taskHandler, Trade trade) {
public BuyerAsTakerCreatesDepositTxInputs(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}

View file

@ -15,7 +15,7 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.taker;
package io.bisq.core.trade.protocol.tasks.buyer_as_taker;
import com.google.common.util.concurrent.FutureCallback;
import io.bisq.common.taskrunner.TaskRunner;
@ -24,13 +24,12 @@ import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.TradingPeer;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.wire.crypto.Hash;
import io.bisq.wire.payload.btc.RawTransactionInput;
import io.bisq.protobuffer.crypto.Hash;
import io.bisq.protobuffer.payload.btc.RawTransactionInput;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
@ -38,11 +37,10 @@ import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
public class SignAndPublishDepositTxAsBuyer extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(SignAndPublishDepositTxAsBuyer.class);
@Slf4j
public class BuyerAsTakerSignAndPublishDepositTx extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public SignAndPublishDepositTxAsBuyer(TaskRunner taskHandler, Trade trade) {
public BuyerAsTakerSignAndPublishDepositTx(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -67,7 +65,10 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
checkArgument(addressEntryOptional.isPresent(), "addressEntryOptional must be present");
AddressEntry buyerMultiSigAddressEntry = addressEntryOptional.get();
Coin buyerInput = Coin.valueOf(buyerInputs.stream().mapToLong(input -> input.value).sum());
buyerMultiSigAddressEntry.setCoinLockedInMultiSig(buyerInput.subtract(trade.getTxFee().multiply(2)));
walletService.saveAddressEntryList();
TradingPeer tradingPeer = processModel.tradingPeer;
byte[] buyerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(buyerMultiSigPubKey, buyerMultiSigAddressEntry.getPubKey()),
@ -81,7 +82,7 @@ public class SignAndPublishDepositTxAsBuyer extends TradeTask {
tradingPeer.getRawTransactionInputs(),
buyerMultiSigPubKey,
tradingPeer.getMultiSigPubKey(),
trade.getArbitratorPubKey(),
trade.getArbitratorBtcPubKey(),
new FutureCallback<Transaction>() {
@Override
public void onSuccess(Transaction transaction) {

View file

@ -15,7 +15,7 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import com.google.common.base.Preconditions;
import io.bisq.common.crypto.Sig;
@ -23,24 +23,22 @@ import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.common.util.Utilities;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.BuyerAsOffererTrade;
import io.bisq.core.trade.BuyerAsMakerTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.TradingPeer;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.wire.payload.p2p.NodeAddress;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.wire.payload.trade.Contract;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.protobuffer.payload.p2p.NodeAddress;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.payload.trade.Contract;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class CreateAndSignContract extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(CreateAndSignContract.class);
@Slf4j
public class MakerCreateAndSignContract extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public CreateAndSignContract(TaskRunner taskHandler, Trade trade) {
public MakerCreateAndSignContract(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -51,19 +49,22 @@ public class CreateAndSignContract extends TradeTask {
Preconditions.checkNotNull(trade.getTakeOfferFeeTxId(), "trade.getTakeOfferFeeTxId() must not be null");
TradingPeer taker = processModel.tradingPeer;
PaymentAccountPayload offererPaymentAccountPayload = processModel.getPaymentAccountPayload(trade);
checkNotNull(offererPaymentAccountPayload, "offererPaymentAccountPayload must not be null");
PaymentAccountPayload makerPaymentAccountPayload = processModel.getPaymentAccountPayload(trade);
checkNotNull(makerPaymentAccountPayload, "makerPaymentAccountPayload must not be null");
PaymentAccountPayload takerPaymentAccountPayload = taker.getPaymentAccountPayload();
boolean isBuyerOffererAndSellerTaker = trade instanceof BuyerAsOffererTrade;
boolean isBuyerMakerAndSellerTaker = trade instanceof BuyerAsMakerTrade;
NodeAddress buyerNodeAddress = isBuyerOffererAndSellerTaker ? processModel.getMyNodeAddress() : processModel.getTempTradingPeerNodeAddress();
NodeAddress sellerNodeAddress = isBuyerOffererAndSellerTaker ? processModel.getTempTradingPeerNodeAddress() : processModel.getMyNodeAddress();
NodeAddress buyerNodeAddress = isBuyerMakerAndSellerTaker ?
processModel.getMyNodeAddress() : processModel.getTempTradingPeerNodeAddress();
NodeAddress sellerNodeAddress = isBuyerMakerAndSellerTaker ?
processModel.getTempTradingPeerNodeAddress() : processModel.getMyNodeAddress();
BtcWalletService walletService = processModel.getWalletService();
String id = processModel.getOffer().getId();
AddressEntry takerAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT);
checkArgument(!walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG).isPresent(), "addressEntry must not be set here.");
AddressEntry offererAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.MULTI_SIG);
byte[] offererMultiSigPubKey = offererAddressEntry.getPubKey();
checkArgument(!walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG).isPresent(),
"addressEntry must not be set here.");
AddressEntry makerAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.MULTI_SIG);
byte[] makerMultiSigPubKey = makerAddressEntry.getPubKey();
Contract contract = new Contract(
processModel.getOffer().getOfferPayload(),
trade.getTradeAmount(),
@ -72,16 +73,17 @@ public class CreateAndSignContract extends TradeTask {
buyerNodeAddress,
sellerNodeAddress,
trade.getArbitratorNodeAddress(),
isBuyerOffererAndSellerTaker,
trade.getMediatorNodeAddress(),
isBuyerMakerAndSellerTaker,
processModel.getAccountId(),
taker.getAccountId(),
offererPaymentAccountPayload,
makerPaymentAccountPayload,
takerPaymentAccountPayload,
processModel.getPubKeyRing(),
taker.getPubKeyRing(),
takerAddressEntry.getAddressString(),
taker.getPayoutAddressString(),
offererMultiSigPubKey,
makerMultiSigPubKey,
taker.getMultiSigPubKey()
);
String contractAsJson = Utilities.objectToJson(contract);
@ -89,8 +91,8 @@ public class CreateAndSignContract extends TradeTask {
trade.setContract(contract);
trade.setContractAsJson(contractAsJson);
trade.setOffererContractSignature(signature);
processModel.setMyMultiSigPubKey(offererMultiSigPubKey);
trade.setMakerContractSignature(signature);
processModel.setMyMultiSigPubKey(makerMultiSigPubKey);
complete();
} catch (Throwable t) {

View file

@ -15,27 +15,24 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.OffererTrade;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.core.util.Validator;
import io.bisq.wire.message.trade.DepositTxPublishedMessage;
import io.bisq.protobuffer.message.trade.DepositTxPublishedMessage;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class ProcessDepositTxPublishedMessage extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(ProcessDepositTxPublishedMessage.class);
@Slf4j
public class MakerProcessDepositTxPublishedMessage extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public ProcessDepositTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
public MakerProcessDepositTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -50,21 +47,18 @@ public class ProcessDepositTxPublishedMessage extends TradeTask {
checkArgument(message.depositTx != null);
// To access tx confidence we need to add that tx into our wallet.
Transaction transactionFromSerializedTx = processModel.getWalletService().getTransactionFromSerializedTx(message.depositTx);
Transaction txFromSerializedTx = processModel.getWalletService().getTxFromSerializedTx(message.depositTx);
// update with full tx
Transaction walletTx = processModel.getTradeWalletService().addTransactionToWallet(transactionFromSerializedTx);
Transaction walletTx = processModel.getTradeWalletService().addTxToWallet(txFromSerializedTx);
trade.setDepositTx(walletTx);
BtcWalletService.printTx("depositTx received from peer", walletTx);
if (trade instanceof OffererTrade)
processModel.getOpenOfferManager().closeOpenOffer(trade.getOffer());
// update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing();
trade.setState(Trade.State.OFFERER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG);
trade.setState(Trade.State.MAKER_RECEIVED_DEPOSIT_TX_PUBLISHED_MSG);
complete();
} catch (Throwable t) {

View file

@ -15,29 +15,27 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.exceptions.TradePriceOutOfToleranceException;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.wire.message.trade.PayDepositRequest;
import io.bisq.wire.payload.filter.PaymentAccountFilter;
import io.bisq.wire.payload.payment.PaymentAccountPayload;
import io.bisq.protobuffer.message.trade.PayDepositRequest;
import io.bisq.protobuffer.payload.filter.PaymentAccountFilter;
import io.bisq.protobuffer.payload.payment.PaymentAccountPayload;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Coin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static io.bisq.core.util.Validator.checkTradeId;
import static io.bisq.core.util.Validator.nonEmptyStringOf;
public class ProcessPayDepositRequest extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(ProcessPayDepositRequest.class);
@Slf4j
public class MakerProcessPayDepositRequest extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public ProcessPayDepositRequest(TaskRunner taskHandler, Trade trade) {
public MakerProcessPayDepositRequest(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -64,8 +62,7 @@ public class ProcessPayDepositRequest extends TradeTask {
checkArgument(payDepositRequest.rawTransactionInputs.size() > 0);
processModel.tradingPeer.setChangeOutputValue(payDepositRequest.changeOutputValue);
if (payDepositRequest.changeOutputAddress != null)
processModel.tradingPeer.setChangeOutputAddress(payDepositRequest.changeOutputAddress);
processModel.tradingPeer.setChangeOutputAddress(payDepositRequest.changeOutputAddress);
processModel.tradingPeer.setMultiSigPubKey(checkNotNull(payDepositRequest.takerMultiSigPubKey));
processModel.tradingPeer.setPayoutAddressString(nonEmptyStringOf(payDepositRequest.takerPayoutAddressString));
@ -74,9 +71,11 @@ public class ProcessPayDepositRequest extends TradeTask {
processModel.tradingPeer.setAccountId(nonEmptyStringOf(payDepositRequest.takerAccountId));
trade.setTakeOfferFeeTxId(nonEmptyStringOf(payDepositRequest.takeOfferFeeTxId));
processModel.setTakerAcceptedArbitratorNodeAddresses(checkNotNull(payDepositRequest.acceptedArbitratorNodeAddresses));
processModel.setTakerAcceptedMediatorNodeAddresses(checkNotNull(payDepositRequest.acceptedMediatorNodeAddresses));
if (payDepositRequest.acceptedArbitratorNodeAddresses.isEmpty())
failed("acceptedArbitratorNames must not be empty");
trade.applyArbitratorNodeAddress(checkNotNull(payDepositRequest.arbitratorNodeAddress));
trade.applyMediatorNodeAddress(checkNotNull(payDepositRequest.mediatorNodeAddress));
try {
long takersTradePrice = payDepositRequest.tradePrice;
@ -91,8 +90,6 @@ public class ProcessPayDepositRequest extends TradeTask {
checkArgument(payDepositRequest.tradeAmount > 0);
trade.setTradeAmount(Coin.valueOf(payDepositRequest.tradeAmount));
// check and update to the latest peer address of our peer if the payDepositRequest is correct
checkArgument(payDepositRequest.getSenderNodeAddress().equals(processModel.getTempTradingPeerNodeAddress()));
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
removeMailboxMessageAfterProcessing();

View file

@ -15,15 +15,15 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.wire.payload.trade.statistics.TradeStatistics;
import io.bisq.protobuffer.payload.trade.statistics.TradeStatistics;
public class PublishTradeStatistics extends TradeTask {
public PublishTradeStatistics(TaskRunner taskHandler, Trade trade) {
public class MakerPublishTradeStatistics extends TradeTask {
public MakerPublishTradeStatistics(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -31,7 +31,7 @@ public class PublishTradeStatistics extends TradeTask {
protected void run() {
try {
runInterceptHook();
// Offerer is responsible for publishing. Only in case the offerer uses an old version the taker publishes.
// Maker is responsible for publishing. Only in case the maker uses an old version the taker publishes.
TradeStatistics tradeStatistics = new TradeStatistics(trade.getOffer().getOfferPayload(),
trade.getTradePrice(),
trade.getTradeAmount(),

View file

@ -15,28 +15,27 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import io.bisq.network.p2p.SendDirectMessageListener;
import io.bisq.wire.message.trade.PublishDepositTxRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.bisq.network.p2p.SendMailboxMessageListener;
import io.bisq.protobuffer.message.trade.PublishDepositTxRequest;
import lombok.extern.slf4j.Slf4j;
import java.util.Arrays;
import java.util.Optional;
import java.util.UUID;
import static com.google.common.base.Preconditions.checkArgument;
public class SendPublishDepositTxRequest extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(SendPublishDepositTxRequest.class);
@Slf4j
public class MakerSendPublishDepositTxRequest extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public SendPublishDepositTxRequest(TaskRunner taskHandler, Trade trade) {
public MakerSendPublishDepositTxRequest(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -49,43 +48,55 @@ public class SendPublishDepositTxRequest extends TradeTask {
Optional<AddressEntry> addressEntryOptional = walletService.getAddressEntry(id, AddressEntry.Context.MULTI_SIG);
checkArgument(addressEntryOptional.isPresent(), "addressEntry must be set here.");
AddressEntry offererPayoutAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT);
byte[] offererMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(offererMultiSigPubKey,
AddressEntry makerPayoutAddressEntry = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT);
byte[] makerMultiSigPubKey = processModel.getMyMultiSigPubKey();
checkArgument(Arrays.equals(makerMultiSigPubKey,
addressEntryOptional.get().getPubKey()),
"offererMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
"makerMultiSigPubKey from AddressEntry must match the one from the trade data. trade id =" + id);
PublishDepositTxRequest tradeMessage = new PublishDepositTxRequest(
PublishDepositTxRequest message = new PublishDepositTxRequest(
processModel.getId(),
processModel.getPaymentAccountPayload(trade),
processModel.getAccountId(),
offererMultiSigPubKey,
makerMultiSigPubKey,
trade.getContractAsJson(),
trade.getOffererContractSignature(),
offererPayoutAddressEntry.getAddressString(),
trade.getMakerContractSignature(),
makerPayoutAddressEntry.getAddressString(),
processModel.getPreparedDepositTx(),
processModel.getRawTransactionInputs()
processModel.getRawTransactionInputs(),
processModel.getMyNodeAddress(),
UUID.randomUUID().toString()
);
trade.setState(Trade.State.MAKER_SENT_PUBLISH_DEPOSIT_TX_REQUEST);
processModel.getP2PService().sendEncryptedDirectMessage(
processModel.getP2PService().sendEncryptedMailboxMessage(
trade.getTradingPeerNodeAddress(),
processModel.tradingPeer.getPubKeyRing(),
tradeMessage,
new SendDirectMessageListener() {
message,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.trace("Message arrived at peer.");
trade.setState(Trade.State.OFFERER_SENT_PUBLISH_DEPOSIT_TX_REQUEST);
log.info("Message arrived at peer. tradeId={}, message{}", id, message);
trade.setState(Trade.State.MAKER_SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST);
complete();
}
@Override
public void onFault() {
appendToErrorMessage("PublishDepositTxRequest sending failed");
failed();
public void onStoredInMailbox() {
log.info("Message stored in mailbox. tradeId={}, message{}", id, message);
trade.setState(Trade.State.MAKER_STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST);
complete();
}
@Override
public void onFault(String errorMessage) {
trade.setState(Trade.State.MAKER_SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST);
appendToErrorMessage("Sending message failed: message=" + message + "\nerrorMessage=" + errorMessage);
failed(errorMessage);
}
}
);
} catch (Throwable t) {
failed(t);
}

View file

@ -0,0 +1,90 @@
/*
* This file is part of bisq.
*
* bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.UserThread;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.btc.AddressEntry;
import io.bisq.core.btc.listeners.BalanceListener;
import io.bisq.core.btc.wallet.BtcWalletService;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import lombok.extern.slf4j.Slf4j;
import org.bitcoinj.core.Address;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
@Slf4j
public class MakerSetupDepositTxListener extends TradeTask {
// Use instance fields to not get eaten up by the GC
private Subscription tradeStateSubscription;
private BalanceListener listener;
@SuppressWarnings({"WeakerAccess", "unused"})
public MakerSetupDepositTxListener(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
if (trade.getState().getPhase() == Trade.Phase.TAKER_FEE_PUBLISHED) {
BtcWalletService walletService = processModel.getWalletService();
Address address = walletService.getOrCreateAddressEntry(trade.getId(),
AddressEntry.Context.RESERVED_FOR_TRADE).getAddress();
if (walletService.getBalanceForAddress(address).isZero()) {
trade.setState(Trade.State.MAKER_SAW_DEPOSIT_TX_IN_NETWORK);
} else {
listener = new BalanceListener(address) {
@Override
public void onBalanceChanged(Coin balance, Transaction tx) {
// dont store trade.getState().getPhase() as variable as we need it volatile!
if (balance.isZero() && trade.getState().getPhase() == Trade.Phase.TAKER_FEE_PUBLISHED)
trade.setState(Trade.State.MAKER_SAW_DEPOSIT_TX_IN_NETWORK);
}
};
walletService.addBalanceListener(listener);
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
log.error("MakerSetupDepositTxListener tradeStateSubscription tradeState=" + newValue);
if (newValue.getPhase() != Trade.Phase.TAKER_FEE_PUBLISHED) {
walletService.removeBalanceListener(listener);
// hack to remove tradeStateSubscription at callback
UserThread.execute(this::unSubscribe);
}
});
}
}
// we complete immediately, our object stays alive because the balanceListener is stored in the WalletService
complete();
} catch (Throwable t) {
failed(t);
}
}
private void unSubscribe() {
if (tradeStateSubscription != null)
tradeStateSubscription.unsubscribe();
}
}

View file

@ -15,21 +15,19 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.ArbitrationSelectionRule;
import io.bisq.core.trade.protocol.ArbitratorSelectionRule;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
public class VerifyArbitrationSelection extends TradeTask {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(VerifyArbitrationSelection.class);
@Slf4j
public class MakerVerifyArbitratorSelection extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public VerifyArbitrationSelection(TaskRunner taskHandler, Trade trade) {
public MakerVerifyArbitratorSelection(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -38,7 +36,8 @@ public class VerifyArbitrationSelection extends TradeTask {
try {
runInterceptHook();
if (trade.getArbitratorNodeAddress().equals(ArbitrationSelectionRule.select(processModel.getTakerAcceptedArbitratorNodeAddresses(),
if (trade.getArbitratorNodeAddress().equals(ArbitratorSelectionRule.select(
processModel.getTakerAcceptedArbitratorNodeAddresses(),
processModel.getOffer())))
complete();
else

View file

@ -15,21 +15,19 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.taker;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.ArbitrationSelectionRule;
import io.bisq.core.trade.protocol.MediatorSelectionRule;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
public class SelectArbitrator extends TradeTask {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(SelectArbitrator.class);
@Slf4j
public class MakerVerifyMediatorSelection extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public SelectArbitrator(TaskRunner taskHandler, Trade trade) {
public MakerVerifyMediatorSelection(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@ -38,11 +36,14 @@ public class SelectArbitrator extends TradeTask {
try {
runInterceptHook();
trade.applyArbitratorNodeAddress(ArbitrationSelectionRule.select(processModel.getUser().getAcceptedArbitratorAddresses(), processModel.getOffer()));
complete();
if (trade.getMediatorNodeAddress().equals(MediatorSelectionRule.select(
processModel.getTakerAcceptedMediatorNodeAddresses(),
processModel.getOffer())))
complete();
else
failed("Mediator selection verification failed");
} catch (Throwable t) {
failed(t);
}
}
}
}

View file

@ -15,20 +15,17 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class VerifyTakerAccount extends TradeTask {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(VerifyTakerAccount.class);
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class MakerVerifyTakerAccount extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public VerifyTakerAccount(TaskRunner taskHandler, Trade trade) {
public MakerVerifyTakerAccount(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}

View file

@ -15,20 +15,18 @@
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bisq.core.trade.protocol.tasks.offerer;
package io.bisq.core.trade.protocol.tasks.maker;
import io.bisq.common.taskrunner.TaskRunner;
import io.bisq.core.trade.Trade;
import io.bisq.core.trade.protocol.tasks.TradeTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.extern.slf4j.Slf4j;
public class VerifyTakeOfferFeePayment extends TradeTask {
@SuppressWarnings("unused")
private static final Logger log = LoggerFactory.getLogger(VerifyTakeOfferFeePayment.class);
@Slf4j
public class MakerVerifyTakerFeePayment extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public VerifyTakeOfferFeePayment(TaskRunner taskHandler, Trade trade) {
public MakerVerifyTakerFeePayment(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}

Some files were not shown because too many files have changed in this diff Show more