Changed trade period to time based instead of block based

This commit is contained in:
Manfred Karrer 2016-04-05 01:20:27 +02:00
parent 86405a4b88
commit a78163c735
32 changed files with 220 additions and 240 deletions

View File

@ -37,7 +37,7 @@ public final class PaymentMethod implements Persistable, Comparable {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
// time in blocks (average 10 min for one block confirmation
private static final long HOUR = 3600;
private static final long HOUR = 3600 * 1000;
private static final long DAY = HOUR * 24;
public static final String OK_PAY_ID = "OK_PAY";
@ -89,12 +89,12 @@ public final class PaymentMethod implements Persistable, Comparable {
/**
* @param id
* @param lockTime lock time when seller release BTC until the payout tx gets valid (bitcoin tx lockTime). Serves as protection
* against charge back risk. If Bank do the charge back quickly the Arbitrator and the seller can push another
* double spend tx to invalidate the time locked payout tx. For the moment we set all to 0 but will have it in
* place when needed.
* @param maxTradePeriod The min. period a trader need to wait until he gets displayed the contact form for opening a dispute.
* @param maxTradeLimit The max. allowed trade amount in Bitcoin for that payment method (depending on charge back risk)
* @param lockTime lock time when seller release BTC until the payout tx gets valid (bitcoin tx lockTime). Serves as protection
* against charge back risk. If Bank do the charge back quickly the Arbitrator and the seller can push another
* double spend tx to invalidate the time locked payout tx. For the moment we set all to 0 but will have it in
* place when needed.
* @param maxTradePeriod The min. period a trader need to wait until he gets displayed the contact form for opening a dispute.
* @param maxTradeLimit The max. allowed trade amount in Bitcoin for that payment method (depending on charge back risk)
*/
public PaymentMethod(String id, long lockTime, long maxTradePeriod, Coin maxTradeLimit) {
this.id = id;

View File

@ -160,8 +160,6 @@ public abstract class Trade implements Tradable, Model {
private String offererContractSignature;
private Transaction payoutTx;
private long lockTimeAsBlockHeight;
private int openDisputeTimeAsBlockHeight;
private int checkPaymentTimeAsBlockHeight;
private NodeAddress arbitratorNodeAddress;
private String takerPaymentAccountId;
private String errorMessage;
@ -176,6 +174,7 @@ public abstract class Trade implements Tradable, Model {
// Constructor, initialization
///////////////////////////////////////////////////////////////////////////////////////////
// offerer
protected Trade(Offer offer, Storage<? extends TradableList> storage) {
this.offer = offer;
this.storage = storage;
@ -464,22 +463,6 @@ public abstract class Trade implements Tradable, Model {
return lockTimeAsBlockHeight;
}
public int getOpenDisputeTimeAsBlockHeight() {
return openDisputeTimeAsBlockHeight;
}
public void setOpenDisputeTimeAsBlockHeight(int openDisputeTimeAsBlockHeight) {
this.openDisputeTimeAsBlockHeight = openDisputeTimeAsBlockHeight;
}
public int getCheckPaymentTimeAsBlockHeight() {
return checkPaymentTimeAsBlockHeight;
}
public void setCheckPaymentTimeAsBlockHeight(int checkPaymentTimeAsBlockHeight) {
this.checkPaymentTimeAsBlockHeight = checkPaymentTimeAsBlockHeight;
}
public void setTakerContractSignature(String takerSignature) {
this.takerContractSignature = takerSignature;
}
@ -633,8 +616,6 @@ public abstract class Trade implements Tradable, Model {
"\n\toffererContractSignature.hashCode()='" + (offererContractSignature != null ? offererContractSignature.hashCode() : "") + '\'' +
"\n\tpayoutTx=" + payoutTx +
"\n\tlockTimeAsBlockHeight=" + lockTimeAsBlockHeight +
"\n\topenDisputeTimeAsBlockHeight=" + openDisputeTimeAsBlockHeight +
"\n\tcheckPaymentTimeAsBlockHeight=" + checkPaymentTimeAsBlockHeight +
"\n\tarbitratorNodeAddress=" + arbitratorNodeAddress +
"\n\ttakerPaymentAccountId='" + takerPaymentAccountId + '\'' +
"\n\terrorMessage='" + errorMessage + '\'' +

View File

@ -31,7 +31,6 @@ import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
import io.bitsquare.trade.protocol.trade.tasks.buyer.*;
import io.bitsquare.trade.protocol.trade.tasks.offerer.*;
import io.bitsquare.trade.protocol.trade.tasks.shared.BroadcastAfterLockTime;
import io.bitsquare.trade.protocol.trade.tasks.shared.InitWaitPeriodForOpenDispute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -111,7 +110,6 @@ public class BuyerAsOffererProtocol extends TradeProtocol implements BuyerProtoc
LoadTakeOfferFeeTx.class,
CreateAndSignContract.class,
OffererCreatesAndSignsDepositTxAsBuyer.class,
InitWaitPeriodForOpenDispute.class,
SetupDepositBalanceListener.class,
SendPublishDepositTxRequest.class
);

View File

@ -28,6 +28,7 @@ import io.bitsquare.common.crypto.PubKeyRing;
import io.bitsquare.common.taskrunner.Model;
import io.bitsquare.p2p.NodeAddress;
import io.bitsquare.p2p.P2PService;
import io.bitsquare.payment.PaymentAccount;
import io.bitsquare.payment.PaymentAccountContractData;
import io.bitsquare.trade.OffererTrade;
import io.bitsquare.trade.Trade;
@ -181,11 +182,14 @@ public class ProcessModel implements Model, Serializable {
return tradeMessage;
}
@Nullable
public PaymentAccountContractData getPaymentAccountContractData(Trade trade) {
PaymentAccount paymentAccount;
if (trade instanceof OffererTrade)
return user.getPaymentAccount(offer.getOffererPaymentAccountId()).getContractData();
paymentAccount = user.getPaymentAccount(offer.getOffererPaymentAccountId());
else
return user.getPaymentAccount(trade.getTakerPaymentAccountId()).getContractData();
paymentAccount = user.getPaymentAccount(trade.getTakerPaymentAccountId());
return paymentAccount != null ? paymentAccount.getContractData() : null;
}
public String getAccountId() {

View File

@ -29,7 +29,6 @@ import io.bitsquare.trade.protocol.trade.messages.*;
import io.bitsquare.trade.protocol.trade.tasks.offerer.*;
import io.bitsquare.trade.protocol.trade.tasks.seller.*;
import io.bitsquare.trade.protocol.trade.tasks.shared.BroadcastAfterLockTime;
import io.bitsquare.trade.protocol.trade.tasks.shared.InitWaitPeriodForOpenDispute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -109,7 +108,6 @@ public class SellerAsOffererProtocol extends TradeProtocol implements SellerProt
VerifyArbitrationSelection.class,
VerifyTakerAccount.class,
LoadTakeOfferFeeTx.class,
InitWaitPeriodForOpenDispute.class,
CreateAndSignContract.class,
OffererCreatesAndSignsDepositTxAsSeller.class,
SetupDepositBalanceListener.class,

View File

@ -43,8 +43,6 @@ public final class PublishDepositTxRequest extends TradeMessage {
public final String offererPayoutAddressString;
public final byte[] preparedDepositTx;
public final ArrayList<RawTransactionInput> offererInputs;
public final int openDisputeTimeAsBlockHeight;
public final int checkPaymentTimeAsBlockHeight;
public final byte[] offererTradeWalletPubKey;
public PublishDepositTxRequest(String tradeId,
@ -55,9 +53,7 @@ public final class PublishDepositTxRequest extends TradeMessage {
String offererContractSignature,
String offererPayoutAddressString,
byte[] preparedDepositTx,
ArrayList<RawTransactionInput> offererInputs,
int openDisputeTimeAsBlockHeight,
int checkPaymentTimeAsBlockHeight) {
ArrayList<RawTransactionInput> offererInputs) {
super(tradeId);
this.offererPaymentAccountContractData = offererPaymentAccountContractData;
this.offererAccountId = offererAccountId;
@ -67,8 +63,6 @@ public final class PublishDepositTxRequest extends TradeMessage {
this.offererPayoutAddressString = offererPayoutAddressString;
this.preparedDepositTx = preparedDepositTx;
this.offererInputs = offererInputs;
this.openDisputeTimeAsBlockHeight = openDisputeTimeAsBlockHeight;
this.checkPaymentTimeAsBlockHeight = checkPaymentTimeAsBlockHeight;
log.trace("offererPaymentAccount size " + Utilities.serialize(offererPaymentAccountContractData).length);
log.trace("offererTradeWalletPubKey size " + offererTradeWalletPubKey.length);

View File

@ -47,6 +47,7 @@ public class CreateAndSignContract extends TradeTask {
TradingPeer taker = processModel.tradingPeer;
PaymentAccountContractData offererPaymentAccountContractData = processModel.getPaymentAccountContractData(trade);
checkNotNull(offererPaymentAccountContractData, "offererPaymentAccountContractData must not be null");
PaymentAccountContractData takerPaymentAccountContractData = taker.getPaymentAccountContractData();
boolean isBuyerOffererAndSellerTaker = trade instanceof BuyerAsOffererTrade;

View File

@ -45,9 +45,7 @@ public class SendPublishDepositTxRequest extends TradeTask {
trade.getOffererContractSignature(),
processModel.getAddressEntry().getAddressString(),
processModel.getPreparedDepositTx(),
processModel.getRawTransactionInputs(),
trade.getOpenDisputeTimeAsBlockHeight(),
trade.getCheckPaymentTimeAsBlockHeight()
processModel.getRawTransactionInputs()
);
processModel.getP2PService().sendEncryptedDirectMessage(

View File

@ -1,50 +0,0 @@
/*
* This file is part of Bitsquare.
*
* Bitsquare 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.
*
* Bitsquare 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 Bitsquare. If not, see <http://www.gnu.org/licenses/>.
*/
package io.bitsquare.trade.protocol.trade.tasks.shared;
import io.bitsquare.common.taskrunner.TaskRunner;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.protocol.trade.tasks.TradeTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InitWaitPeriodForOpenDispute extends TradeTask {
private static final Logger log = LoggerFactory.getLogger(InitWaitPeriodForOpenDispute.class);
public InitWaitPeriodForOpenDispute(TaskRunner taskHandler, Trade trade) {
super(taskHandler, trade);
}
@Override
protected void run() {
try {
runInterceptHook();
int openDisputeTimeAsBlockHeight = processModel.getTradeWalletService().getLastBlockSeenHeight()
+ trade.getOffer().getPaymentMethod().getMaxTradePeriod();
trade.setOpenDisputeTimeAsBlockHeight(openDisputeTimeAsBlockHeight);
int checkPaymentTimeAsBlockHeight = processModel.getTradeWalletService().getLastBlockSeenHeight()
+ trade.getOffer().getPaymentMethod().getMaxTradePeriod() / 2;
trade.setCheckPaymentTimeAsBlockHeight(checkPaymentTimeAsBlockHeight);
complete();
} catch (Throwable t) {
failed(t);
}
}
}

View File

@ -66,7 +66,7 @@ public class ProcessPublishDepositTxRequest extends TradeTask {
processModel.tradingPeer.setRawTransactionInputs(checkNotNull(publishDepositTxRequest.offererInputs));
processModel.setPreparedDepositTx(checkNotNull(publishDepositTxRequest.preparedDepositTx));
checkArgument(publishDepositTxRequest.offererInputs.size() > 0);
if (publishDepositTxRequest.openDisputeTimeAsBlockHeight != 0) {
/*if (publishDepositTxRequest.openDisputeTimeAsBlockHeight != 0) {
trade.setOpenDisputeTimeAsBlockHeight(publishDepositTxRequest.openDisputeTimeAsBlockHeight);
} else {
failed("waitPeriodForOpenDisputeAsBlockHeight = 0");
@ -76,7 +76,7 @@ public class ProcessPublishDepositTxRequest extends TradeTask {
trade.setCheckPaymentTimeAsBlockHeight(publishDepositTxRequest.checkPaymentTimeAsBlockHeight);
} else {
failed("notificationTimeAsBlockHeight = 0");
}
}*/
// update to the latest peer address of our peer if the message is correct
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());

View File

@ -201,6 +201,7 @@ public final class User implements Persistable {
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
@Nullable
public PaymentAccount getPaymentAccount(String paymentAccountId) {
Optional<PaymentAccount> optional = paymentAccounts.stream().filter(e -> e.getId().equals(paymentAccountId)).findAny();
if (optional.isPresent())

View File

@ -76,7 +76,7 @@ import static io.bitsquare.app.BitsquareEnvironment.APP_NAME_KEY;
public class BitsquareApp extends Application {
private static final Logger log = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(BitsquareApp.class);
public static final boolean DEV_MODE = false;
public static final boolean DEV_MODE = true;
public static final boolean IS_RELEASE_VERSION = !DEV_MODE && true;
private static Environment env;

View File

@ -108,23 +108,12 @@ public abstract class PaymentMethodForm {
public static void addAllowedPeriod(GridPane gridPane, int gridRow,
@Nullable PaymentAccountContractData paymentAccountContractData, String dateFromBlocks) {
if (paymentAccountContractData != null) {
long hours = paymentAccountContractData.getMaxTradePeriod() / 3600;
String displayText;
if (hours == 1)
displayText = hours + " hour";
else
displayText = hours + " hours";
if (hours == 24)
displayText = "1 day";
if (hours > 24)
displayText = hours / 24 + " days";
addLabelTextField(gridPane, gridRow, "Max. allowed trade period / date:", displayText + " / " + dateFromBlocks);
long hours = paymentAccountContractData.getMaxTradePeriod() / 3600_000;
addLabelTextField(gridPane, gridRow, "Max. allowed trade period / date:", getTimeText(hours) + " / " + dateFromBlocks);
}
}
protected void addAllowedPeriod() {
long hours = paymentAccount.getPaymentMethod().getMaxTradePeriod() / 6;
protected static String getTimeText(long hours) {
String time = hours + " hours";
if (hours == 1)
time = "1 hour";
@ -133,7 +122,12 @@ public abstract class PaymentMethodForm {
else if (hours > 24)
time = hours / 24 + " days";
String displayText = "Max. trade duration: " + time + " / Max. trade limit: " +
return time;
}
protected void addAllowedPeriod() {
long hours = paymentAccount.getPaymentMethod().getMaxTradePeriod() / 3600_000;
String displayText = "Max. trade duration: " + getTimeText(hours) + " / Max. trade limit: " +
formatter.formatCoinWithCode(paymentAccount.getPaymentMethod().getMaxTradeLimit());
addLabelTextField(gridPane, ++gridRow, "Limitations:", displayText);

View File

@ -66,7 +66,8 @@ import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
import javafx.beans.property.*;
import javafx.collections.ListChangeListener;
import org.bitcoinj.core.*;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.store.BlockStoreException;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
@ -75,10 +76,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
@ -529,75 +527,66 @@ public class MainViewModel implements ViewModel {
private void applyTradePeriodState() {
updateTradePeriodState();
tradeWalletService.addBlockChainListener(new BlockChainListener() {
clock.addListener(new Clock.Listener() {
@Override
public void notifyNewBestBlock(StoredBlock block) throws VerificationException {
public void onSecondTick() {
updateTradePeriodState();
}
@Override
public void reorganize(StoredBlock splitPoint, List<StoredBlock> oldBlocks, List<StoredBlock> newBlocks)
throws VerificationException {
public void onMinuteTick() {
updateTradePeriodState();
}
@Override
public boolean isTransactionRelevant(Transaction tx) throws ScriptException {
return false;
}
@Override
public void receiveFromBlock(Transaction tx, StoredBlock block, AbstractBlockChain.NewBlockType blockType, int relativityOffset)
throws VerificationException {
}
@Override
public boolean notifyTransactionIsInBlock(Sha256Hash txHash, StoredBlock block, AbstractBlockChain.NewBlockType blockType,
int relativityOffset) throws VerificationException {
return false;
public void onMissedSecondTick(long missed) {
}
});
}
private void updateTradePeriodState() {
tradeManager.getTrades().stream().forEach(trade -> {
int bestChainHeight = tradeWalletService.getBestChainHeight();
if (trade.getState().getPhase().ordinal() < Trade.Phase.PAYOUT_PAID.ordinal()) {
long maxTradePeriod = trade.getOffer().getPaymentMethod().getMaxTradePeriod();
Date maxTradePeriodDate = new Date(trade.getDate().getTime() + maxTradePeriod);
Date halfTradePeriodDate = new Date(trade.getDate().getTime() + maxTradePeriod / 2);
Date now = new Date();
if (trade.getOpenDisputeTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getOpenDisputeTimeAsBlockHeight())
trade.setTradePeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER);
else if (trade.getCheckPaymentTimeAsBlockHeight() > 0 && bestChainHeight >= trade.getCheckPaymentTimeAsBlockHeight())
trade.setTradePeriodState(Trade.TradePeriodState.HALF_REACHED);
if (now.after(maxTradePeriodDate))
trade.setTradePeriodState(Trade.TradePeriodState.TRADE_PERIOD_OVER);
else if (now.after(halfTradePeriodDate))
trade.setTradePeriodState(Trade.TradePeriodState.HALF_REACHED);
String key;
String limitDate = formatter.addBlocksToNowDateFormatted(trade.getOpenDisputeTimeAsBlockHeight() - tradeWalletService.getBestChainHeight());
switch (trade.getTradePeriodState()) {
case NORMAL:
break;
case HALF_REACHED:
key = "displayHalfTradePeriodOver" + trade.getId();
if (preferences.showAgain(key)) {
preferences.dontShowAgain(key, true);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the half of the max. allowed trading period and " +
"is still not completed.\n\n" +
"The trade period ends on " + limitDate + "\n\n" +
"Please check your trade state at \"Portfolio/Open trades\" for further information.")
.show();
}
break;
case TRADE_PERIOD_OVER:
key = "displayTradePeriodOver" + trade.getId();
if (preferences.showAgain(key)) {
preferences.dontShowAgain(key, true);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the max. allowed trading period and is " +
"not completed.\n\n" +
"The trade period ended on " + limitDate + "\n\n" +
"Please check your trade at \"Portfolio/Open trades\" for contacting " +
"the arbitrator.")
.show();
}
break;
String key;
switch (trade.getTradePeriodState()) {
case NORMAL:
break;
case HALF_REACHED:
key = "displayHalfTradePeriodOver" + trade.getId();
if (preferences.showAgain(key)) {
preferences.dontShowAgain(key, true);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the half of the max. allowed trading period and " +
"is still not completed.\n\n" +
"The trade period ends on " + formatter.formatDateTime(maxTradePeriodDate) + "\n\n" +
"Please check your trade state at \"Portfolio/Open trades\" for further information.")
.show();
}
break;
case TRADE_PERIOD_OVER:
key = "displayTradePeriodOver" + trade.getId();
if (preferences.showAgain(key)) {
preferences.dontShowAgain(key, true);
new Popup().warning("Your trade with ID " + trade.getShortId() +
" has reached the max. allowed trading period and is " +
"not completed.\n\n" +
"The trade period ended on " + formatter.formatDateTime(maxTradePeriodDate) + "\n\n" +
"Please check your trade at \"Portfolio/Open trades\" for contacting " +
"the arbitrator.")
.show();
}
break;
}
}
});
}

View File

@ -18,7 +18,6 @@
package io.bitsquare.gui.main.account.arbitratorregistration;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.arbitration.Arbitrator;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Tuple2;
@ -131,9 +130,6 @@ public class ArbitratorRegistrationView extends ActivatableViewAndModel<VBox, Ar
pubKeyTextField = FormBuilder.addLabelTextField(gridPane, gridRow, "Public key:",
model.registrationPubKeyAsHex.get(), Layout.FIRST_ROW_DISTANCE).second;
if (BitsquareApp.DEV_MODE)
pubKeyTextField.setText("6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a");
pubKeyTextField.textProperty().bind(model.registrationPubKeyAsHex);
Tuple2<Label, ListView> tuple = addLabelListView(gridPane, ++gridRow, "Your languages:");

View File

@ -36,7 +36,6 @@ import io.bitsquare.trade.protocol.trade.tasks.buyer.*;
import io.bitsquare.trade.protocol.trade.tasks.offerer.*;
import io.bitsquare.trade.protocol.trade.tasks.seller.*;
import io.bitsquare.trade.protocol.trade.tasks.shared.BroadcastAfterLockTime;
import io.bitsquare.trade.protocol.trade.tasks.shared.InitWaitPeriodForOpenDispute;
import io.bitsquare.trade.protocol.trade.tasks.taker.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@ -85,7 +84,6 @@ public class DebugView extends InitializableView {
CreateAndSignContract.class,
OffererCreatesAndSignsDepositTxAsBuyer.class,
LoadTakeOfferFeeTx.class,
InitWaitPeriodForOpenDispute.class,
SetupDepositBalanceListener.class,
SendPublishDepositTxRequest.class,
@ -157,7 +155,6 @@ public class DebugView extends InitializableView {
ProcessPayDepositRequest.class,
VerifyArbitrationSelection.class,
VerifyTakerAccount.class,
InitWaitPeriodForOpenDispute.class,
CreateAndSignContract.class,
OffererCreatesAndSignsDepositTxAsBuyer.class,
SetupDepositBalanceListener.class,

View File

@ -145,6 +145,7 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
tableView.setPlaceholder(placeholder);
tableView.getSelectionModel().clearSelection();
tableView.getColumns().add(getSelectColumn());
TableColumn<Dispute, Dispute> tradeIdColumn = getTradeIdColumn();
tableView.getColumns().add(tradeIdColumn);
TableColumn<Dispute, Dispute> roleColumn = getRoleColumn();
@ -714,6 +715,49 @@ public class TraderDisputeView extends ActivatableView<VBox, Void> {
// Table
///////////////////////////////////////////////////////////////////////////////////////////
private TableColumn<Dispute, Dispute> getSelectColumn() {
TableColumn<Dispute, Dispute> column = new TableColumn<Dispute, Dispute>("Select") {
{
setMinWidth(110);
setMaxWidth(110);
}
};
column.setCellValueFactory((addressListItem) ->
new ReadOnlyObjectWrapper<>(addressListItem.getValue()));
column.setCellFactory(
new Callback<TableColumn<Dispute, Dispute>, TableCell<Dispute,
Dispute>>() {
@Override
public TableCell<Dispute, Dispute> call(TableColumn<Dispute,
Dispute> column) {
return new TableCell<Dispute, Dispute>() {
Button button;
@Override
public void updateItem(final Dispute item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
if (button == null) {
button = new Button("Select");
button.setOnAction(e -> tableView.getSelectionModel().select(item));
setGraphic(button);
}
} else {
setGraphic(null);
if (button != null) {
button.setOnAction(null);
button = null;
}
}
}
};
}
});
return column;
}
private TableColumn<Dispute, Dispute> getTradeIdColumn() {
TableColumn<Dispute, Dispute> column = new TableColumn<Dispute, Dispute>("Trade ID") {
{

View File

@ -804,7 +804,7 @@ public class CreateOfferView extends ActivatableViewAndModel<AnchorPane, CreateO
fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
Label label = new Label("OR");
label.setPadding(new Insets(5, 0, 0, 0));
fundFromExternalWalletButton = new Button("Pay in funds from external wallet");
fundFromExternalWalletButton = new Button("Open your external wallet for funding");
fundFromExternalWalletButton.setDefaultButton(false);
fundFromExternalWalletButton.setOnAction(e -> {
try {

View File

@ -727,7 +727,7 @@ public class TakeOfferView extends ActivatableViewAndModel<AnchorPane, TakeOffer
fundFromSavingsWalletButton.setOnAction(e -> model.fundFromSavingsWallet());
Label label = new Label("OR");
label.setPadding(new Insets(5, 0, 0, 0));
fundFromExternalWalletButton = new Button("Pay in funds from external wallet");
fundFromExternalWalletButton = new Button("Open your external wallet for funding");
fundFromExternalWalletButton.setDefaultButton(false);
fundFromExternalWalletButton.setOnAction(e -> {
try {

View File

@ -17,7 +17,6 @@
package io.bitsquare.gui.main.overlays.windows;
import io.bitsquare.app.BitsquareApp;
import io.bitsquare.gui.components.InputTextField;
import io.bitsquare.gui.main.overlays.Overlay;
import io.bitsquare.gui.main.overlays.popups.Popup;
@ -94,8 +93,6 @@ public class EnterPrivKeyWindow extends Overlay<EnterPrivKeyWindow> {
GridPane.setRowIndex(label, ++rowIndex);
keyInputTextField = new InputTextField();
if (BitsquareApp.DEV_MODE)
keyInputTextField.setText("6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a");
GridPane.setMargin(keyInputTextField, new Insets(3, 0, 0, 0));
GridPane.setRowIndex(keyInputTextField, rowIndex);
GridPane.setColumnIndex(keyInputTextField, 1);

View File

@ -220,10 +220,6 @@ public class PendingTradesDataModel extends ActivatableDataModel {
return getTrade() != null ? getTrade().getLockTimeAsBlockHeight() : 0;
}
public long getOpenDisputeTimeAsBlockHeight() {
return getTrade() != null ? getTrade().getOpenDisputeTimeAsBlockHeight() : 0;
}
public int getBestChainHeight() {
return tradeWalletService.getBestChainHeight();
}

View File

@ -28,6 +28,7 @@
<TableView fx:id="tableView" VBox.vgrow="SOMETIMES">
<columns>
<TableColumn text="Select" fx:id="selectColumn" minWidth="70" maxWidth="70" sortable="false"/>
<TableColumn text="Trade ID" fx:id="idColumn" minWidth="100"/>
<TableColumn text="Date/Time" fx:id="dateColumn" minWidth="130"/>
<TableColumn text="Trade amount in BTC" fx:id="tradeAmountColumn" minWidth="130"/>

View File

@ -52,7 +52,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@FXML
TableView<PendingTradesListItem> tableView;
@FXML
TableColumn<PendingTradesListItem, PendingTradesListItem> priceColumn, tradeVolumeColumn, tradeAmountColumn, avatarColumn, roleColumn, paymentMethodColumn, idColumn, dateColumn;
TableColumn<PendingTradesListItem, PendingTradesListItem> selectColumn, priceColumn, tradeVolumeColumn, tradeAmountColumn, avatarColumn, roleColumn, paymentMethodColumn, idColumn, dateColumn;
@FXML
private SortedList<PendingTradesListItem> sortedList;
@ -77,6 +77,7 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
@Override
public void initialize() {
setSelectColumnCellFactory();
setTradeIdColumnCellFactory();
setDateColumnCellFactory();
setAmountColumnCellFactory();
@ -228,6 +229,39 @@ public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTrad
// CellFactories
///////////////////////////////////////////////////////////////////////////////////////////
private void setSelectColumnCellFactory() {
selectColumn.setCellValueFactory((pendingTradesListItem) -> new ReadOnlyObjectWrapper<>(pendingTradesListItem.getValue()));
selectColumn.setCellFactory(
new Callback<TableColumn<PendingTradesListItem, PendingTradesListItem>, TableCell<PendingTradesListItem, PendingTradesListItem>>() {
@Override
public TableCell<PendingTradesListItem, PendingTradesListItem> call(TableColumn<PendingTradesListItem,
PendingTradesListItem> column) {
return new TableCell<PendingTradesListItem, PendingTradesListItem>() {
Button button;
@Override
public void updateItem(final PendingTradesListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && !empty) {
if (button == null) {
button = new Button("Select");
button.setOnAction(e -> tableView.getSelectionModel().select(item));
setGraphic(button);
}
} else {
setGraphic(null);
if (button != null) {
button.setOnAction(null);
button = null;
}
}
}
};
}
});
}
private void setTradeIdColumnCellFactory() {
idColumn.setCellValueFactory((pendingTradesListItem) -> new ReadOnlyObjectWrapper<>(pendingTradesListItem.getValue()));
idColumn.setCellFactory(

View File

@ -39,6 +39,7 @@ import org.bitcoinj.core.BlockChainListener;
import org.fxmisc.easybind.EasyBind;
import org.fxmisc.easybind.Subscription;
import java.util.Date;
import java.util.stream.Collectors;
import static io.bitsquare.gui.main.portfolio.pendingtrades.PendingTradesViewModel.SellerState.*;
@ -148,36 +149,60 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return dataModel.getTrade() != null ? formatter.formatCoinWithCode(dataModel.getTrade().getPayoutAmount()) : "";
}
// columns
public String getRemainingTime() {
if (dataModel.getTrade() != null)
return formatter.getPeriodBetweenBlockHeights(getBestChainHeight(),
dataModel.getTrade().getOpenDisputeTimeAsBlockHeight());
else
return "";
// trade period
private long getMaxTradePeriod() {
return dataModel.getOffer() != null ? dataModel.getOffer().getPaymentMethod().getMaxTradePeriod() : 0;
}
public double getRemainingTimeAsPercentage() {
if (dataModel.getTrade() != null && dataModel.getOffer() != null) {
double remainingBlocks = dataModel.getTrade().getOpenDisputeTimeAsBlockHeight() - getBestChainHeight();
double maxPeriod = dataModel.getOffer().getPaymentMethod().getMaxTradePeriod();
if (maxPeriod != 0)
return 1 - remainingBlocks / maxPeriod;
else
return 0;
} else {
private long getTimeWhenDisputeOpens() {
return dataModel.getTrade() != null ? dataModel.getTrade().getDate().getTime() + getMaxTradePeriod() : 0;
}
private long getTimeWhenHalfPeriodReached() {
return dataModel.getTrade() != null ? dataModel.getTrade().getDate().getTime() + getMaxTradePeriod() / 2 : 0;
}
private Date getDateWhenDisputeOpens() {
return new Date(getTimeWhenDisputeOpens());
}
private Date getDateWhenHalfPeriodReached() {
return new Date(getTimeWhenHalfPeriodReached());
}
private long getRemainingTradeDuration() {
return getDateWhenDisputeOpens().getTime() - new Date().getTime();
}
public String getRemainingTradeDurationAsWords() {
return formatter.getDurationAsWords(Math.max(0, getRemainingTradeDuration()));
}
public double getRemainingTradeDurationAsPercentage() {
long maxPeriod = getMaxTradePeriod();
long remaining = getRemainingTradeDuration();
if (maxPeriod != 0) {
double v = 1 - (double) remaining / (double) maxPeriod;
return v;
} else
return 0;
}
}
public boolean showWarning(Trade trade) {
return getBestChainHeight() >= trade.getCheckPaymentTimeAsBlockHeight();
public String getDateForOpenDispute() {
return formatter.formatDateTime(new Date(new Date().getTime() + getRemainingTradeDuration()));
}
public boolean showDispute(Trade trade) {
return getBestChainHeight() >= trade.getOpenDisputeTimeAsBlockHeight();
public boolean showWarning() {
return new Date().after(getDateWhenHalfPeriodReached());
}
public boolean showDispute() {
return new Date().after(getDateWhenDisputeOpens());
}
//
String getMyRole(PendingTradesListItem item) {
Trade trade = item.getTrade();
Contract contract = trade.getContract();
@ -214,22 +239,10 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
return dataModel.getLockTime();
}
private long getOpenDisputeTimeAsBlockHeight() {
return dataModel.getOpenDisputeTimeAsBlockHeight();
}
public int getBestChainHeight() {
return dataModel.getBestChainHeight();
}
public String getOpenDisputeTimeAsFormattedDate() {
if (dataModel.getOffer() != null)
return formatter.addBlocksToNowDateFormatted(getOpenDisputeTimeAsBlockHeight() - getBestChainHeight() +
(dataModel.getOffer().getPaymentMethod().getLockTime()));
else
return "";
}
public String getPaymentMethod() {
if (dataModel.getTrade() != null && dataModel.getTrade().getContract() != null)
return BSResources.get(dataModel.getTrade().getContract().getPaymentMethodName());

View File

@ -113,6 +113,7 @@ public abstract class TradeStepView extends AnchorPane {
clockListener = new Clock.Listener() {
@Override
public void onSecondTick() {
updateTimeLeft();
}
@Override
@ -165,7 +166,7 @@ public abstract class TradeStepView extends AnchorPane {
txIdTextField = addLabelTxIdTextField(gridPane, gridRow, "Deposit transaction ID:", Layout.FIRST_ROW_DISTANCE).second;
PaymentMethodForm.addAllowedPeriod(gridPane, ++gridRow, model.dataModel.getSellersPaymentAccountContractData(),
model.getOpenDisputeTimeAsFormattedDate());
model.getDateForOpenDispute());
timeLeftTextField = addLabelTextField(gridPane, ++gridRow, "Remaining time:").second;
@ -198,16 +199,16 @@ public abstract class TradeStepView extends AnchorPane {
private void updateTimeLeft() {
if (timeLeftTextField != null) {
String remainingTime = model.getRemainingTime();
timeLeftProgressBar.setProgress(model.getRemainingTimeAsPercentage());
String remainingTime = model.getRemainingTradeDurationAsWords();
timeLeftProgressBar.setProgress(model.getRemainingTradeDurationAsPercentage());
if (remainingTime != null) {
timeLeftTextField.setText(remainingTime);
if (model.showWarning(trade) || model.showDispute(trade)) {
if (model.showWarning() || model.showDispute()) {
timeLeftTextField.setStyle("-fx-text-fill: -bs-error-red");
timeLeftProgressBar.setStyle("-fx-accent: -bs-error-red;");
}
} else {
timeLeftTextField.setText("Trade not completed in time (" + model.getOpenDisputeTimeAsFormattedDate() + ")");
timeLeftTextField.setText("Trade not completed in time (" + model.getDateForOpenDispute() + ")");
timeLeftTextField.setStyle("-fx-text-fill: -bs-error-red");
timeLeftProgressBar.setStyle("-fx-accent: -bs-error-red;");
}

View File

@ -191,7 +191,7 @@ public class BuyerStep2View extends TradeStepView {
setWarningHeadline();
return "You still have not done your " + model.dataModel.getCurrencyCode() + " payment!\n" +
"Please note that the trade has to be completed until " +
model.getOpenDisputeTimeAsFormattedDate() +
model.getDateForOpenDispute() +
" otherwise the trade will be investigated by the arbitrator.";
}

View File

@ -60,7 +60,7 @@ public class BuyerStep3View extends TradeStepView {
return "The seller still has not confirmed your payment!\n" +
"Please check " + substitute + " if the payment sending was successful.\n" +
"If the seller does not confirm the receipt of your payment until " +
model.getOpenDisputeTimeAsFormattedDate() +
model.getDateForOpenDispute() +
" the trade will be investigated by the arbitrator.";
}

View File

@ -138,7 +138,7 @@ public class BuyerStep4View extends TradeStepView {
setInformationHeadline();
return "The payout transaction is still blocked by the lock time!\n" +
"If the trade has not been completed on " +
model.getOpenDisputeTimeAsFormattedDate() +
model.getDateForOpenDispute() +
" the arbitrator will investigate.";
}
@ -151,7 +151,7 @@ public class BuyerStep4View extends TradeStepView {
long missingBlocks = model.getLockTime() - bestBlocKHeight;
blockTextField.setText(String.valueOf(missingBlocks));
timeTextField.setText(model.getOpenDisputeTimeAsFormattedDate());
timeTextField.setText(model.getDateForOpenDispute());
}
}

View File

@ -57,7 +57,7 @@ public class SellerStep2View extends TradeStepView {
return "The buyer still has not done the " + model.dataModel.getCurrencyCode() + " payment.\n" +
"You need to wait until he starts the payment.\n" +
"If the trade has not been completed on " +
model.getOpenDisputeTimeAsFormattedDate() +
model.getDateForOpenDispute() +
" the arbitrator will investigate.";
}

View File

@ -197,7 +197,7 @@ public class SellerStep3View extends TradeStepView {
return "You still have not confirmed the receipt of the payment!\n" +
"Please check " + substitute + " if you have received the payment.\n" +
"If you do not confirm receipt until " +
model.getOpenDisputeTimeAsFormattedDate() +
model.getDateForOpenDispute() +
" the trade will be investigated by the arbitrator.";
}

View File

@ -58,7 +58,7 @@ public class SellerStep3bView extends TradeStepView {
return "The trading peer has not finalized the payout transaction!\n" +
"He might be offline. You need to wait until he finalizes the payout transaction.\n" +
"If the trade has not been completed on " +
model.getOpenDisputeTimeAsFormattedDate() +
model.getDateForOpenDispute() +
" the arbitrator will investigate.";
}

View File

@ -356,15 +356,8 @@ public class BSFormatter {
return new Date(new Date().getTime() + blocks * TimeUnit.MINUTES.toMillis(10));
}
public String addBlocksToNowDateFormatted(long blocks) {
DateFormat dateFormatter = DateFormat.getDateInstance(DateFormat.DEFAULT, locale);
DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT, locale);
Date date = addBlocksToNowDate(blocks);
return dateFormatter.format(date) + " " + timeFormatter.format(date);
}
public String getPeriodBetweenBlockHeights(long startBlockHeight, long endBlockHeight) {
return getDaysHoursMinutes(addBlocksToNowDate(startBlockHeight), addBlocksToNowDate(endBlockHeight));
public String getDurationAsWords(long duration) {
return DurationFormatUtils.formatDurationWords(duration, true, true);
}
public String getDaysHoursMinutes(Date startDate, Date endDate) {