mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 15:10:44 +01:00
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:
commit
a05ae36acc
375 changed files with 5918 additions and 5778 deletions
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
||||
|
|
88
core/src/main/java/io/bisq/core/alert/Alert.java
Normal file
88
core/src/main/java/io/bisq/core/alert/Alert.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() + '\'' +
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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) {
|
|
@ -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) {
|
|
@ -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) {
|
|
@ -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();
|
|
@ -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(),
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
Loading…
Add table
Reference in a new issue