Merged seedpeer branch with master branch

Conflicts:
	src/main/java/io/bitsquare/BitSquare.java
	src/main/java/io/bitsquare/gui/main/MainModel.java
This commit is contained in:
Steve Myers 2014-10-12 13:07:28 -07:00
commit f8b9408514
27 changed files with 560 additions and 122 deletions

View File

@ -14,7 +14,7 @@ There are no central points of control or failure in the Bitsquare network. Ther
Because the fiat money portion of any trade must be transferred via traditional means such as a wire transfer, Bitsquare incorporates first-class support for human arbitration to resolve any errors or disputes.
You can read about all of this and more in the [overview](http://bitsquare.io/images/overview.png), [whitepaper](https://docs.google.com/document/d/1d3EiWZdaM89-P6MVhS53unXv2-pDpSFsN3W4kCGXKgY/edit#), [arbitration](https://docs.google.com/document/d/1LJCRFdtM2Jn2Oiv49qRXwBDG8HZD0Hiedy8tNjErHps/edit) and [risk analysis](https://docs.google.com/document/d/1sHwU7K7C8Nl-fS4Z6X88L-NVJ_WBiKnsSpYpYfyqUXA/edit) documents. Several [screencasts](https://docs.google.com/document/d/1d3EiWZdaM89-P6MVhS53unXv2-pDpSFsN3W4kCGXKgY/edit#) are available as well.
You can read about all of this and more in the [overview](http://bitsquare.io/images/overview.png), [whitepaper](https://docs.google.com/document/d/1d3EiWZdaM89-P6MVhS53unXv2-pDpSFsN3W4kCGXKgY/edit#), [arbitration](https://docs.google.com/document/d/1LJCRFdtM2Jn2Oiv49qRXwBDG8HZD0Hiedy8tNjErHps/edit) and [risk analysis](https://docs.google.com/document/d/1sHwU7K7C8Nl-fS4Z6X88L-NVJ_WBiKnsSpYpYfyqUXA/edit) documents. Several [screencasts](https://www.youtube.com/playlist?list=PLXvC3iNe_di9bL1A5xyAKI2PzNg8jU092) are available as well.
Status

View File

@ -1,5 +1,3 @@
apply plugin: 'java'
apply plugin: 'application'
apply from: 'gradle/javafx.gradle'
version = '0.1.0-SNAPSHOT'

View File

@ -23,7 +23,6 @@ import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.core.TransactionConfidence;
import org.bitcoinj.core.TransactionOutput;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.wallet.CoinSelection;
import org.bitcoinj.wallet.DefaultCoinSelector;
@ -95,14 +94,20 @@ class AddressBasedCoinSelector extends DefaultCoinSelector {
// TODO It might be risky to accept 0 confirmation tx from the network with only > 1 numBroadcastPeers
// Need to be tested in testnet and mainnet
// We need to handle cases when malleability happens or tx get lost and have not been successful propagated
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
/* return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
// we accept network tx without confirmations and numBroadcastPeers > 0
/*confidence.getSource().equals(TransactionConfidence.Source.SELF) &&*/
//confidence.getSource().equals(TransactionConfidence.Source.SELF) &&
// In regtest mode we expect to have only one peer, so we won't see transactions propagate.
// TODO: The value 1 below dates from a time when transactions we broadcast *to* were
// counted, set to 0
(confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());
(confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());*/
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
// TODO at testnet we got confidence.numBroadcastPeers()=0 -> probably because we use chained unconfirmed tx
// investigate further
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
type.equals(TransactionConfidence.ConfidenceType.PENDING);
}
private static boolean isInBlockChain(Transaction tx) {

View File

@ -22,6 +22,9 @@ import org.bitcoinj.core.AddressFormatException;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.params.TestNet3Params;
import javax.inject.Inject;
@ -46,14 +49,29 @@ public class FeePolicy {
// Not used at the moment
// private static final String registrationFeeAddress = "mvkDXt4QmN4Nq9dRUsRigBCaovde9nLkZR";
private static final String createOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg";
private static final String takeOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg";
//
private static String createOfferFeeAddress;
private static String takeOfferFeeAddress;
private final NetworkParameters params;
@Inject
public FeePolicy(NetworkParameters params) {
this.params = params;
if (params.equals(TestNet3Params.get())) {
createOfferFeeAddress = "n3LLZ9ngKdxms3gCbHhNPibHTh7DmHigtE";
takeOfferFeeAddress = "n3LLZ9ngKdxms3gCbHhNPibHTh7DmHigtE";
}
else if (params.equals(MainNetParams.get())) {
// bitsquare donation address used for the moment...
createOfferFeeAddress = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7";
takeOfferFeeAddress = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7";
}
else if (params.equals(RegTestParams.get())) {
createOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg";
takeOfferFeeAddress = "n2upbsaKAe4PD3cc4JfS7UCqPC5oNd7Ckg";
}
}
//TODO who is receiver? other users or dev address? use donation option list?

View File

@ -46,6 +46,7 @@ import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.kits.WalletAppKit;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.utils.Threading;
@ -54,13 +55,13 @@ import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.Service;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
@ -102,7 +103,7 @@ public class WalletFacade {
private final FeePolicy feePolicy;
private final CryptoFacade cryptoFacade;
private final Persistence persistence;
private final List<DownloadListener> downloadListeners = new CopyOnWriteArrayList<>();
// private final List<DownloadListener> downloadListeners = new CopyOnWriteArrayList<>();
private final List<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArrayList<>();
private final List<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArrayList<>();
private final List<BalanceListener> balanceListeners = new CopyOnWriteArrayList<>();
@ -132,7 +133,7 @@ public class WalletFacade {
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void initialize(StartupListener startupListener) {
public void initialize(org.bitcoinj.core.DownloadListener downloadListener, StartupListener startupListener) {
// Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
@ -164,7 +165,7 @@ public class WalletFacade {
// Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the
// last months worth or more (takes a few seconds).
try {
walletAppKit.setCheckpoints(getClass().getClassLoader().getResourceAsStream("wallet/checkpoints"));
walletAppKit.setCheckpoints(getClass().getResourceAsStream("/wallet/checkpoints"));
} catch (Exception e) {
e.printStackTrace();
log.error(e.toString());
@ -172,24 +173,40 @@ public class WalletFacade {
// As an example!
// walletAppKit.useTor();
}
walletAppKit.setDownloadListener(new BlockChainDownloadListener())
else if (params == TestNet3Params.get()) {
walletAppKit.setCheckpoints(getClass().getResourceAsStream("/wallet/checkpoints.testnet"));
//walletAppKit.useTor();
}
walletAppKit.setDownloadListener(downloadListener)
.setBlockingStartup(false)
.restoreWalletFromSeed(null)
.setUserAgent("BitSquare", "0.1");
/*
// TODO restore from DeterministicSeed
if (seed != null)
walletAppKit.restoreWalletFromSeed(seed);
*/
walletAppKit.addListener(new Service.Listener() {
@Override
public void failed(Service.State from, Throwable failure) {
walletAppKit = null;
// TODO show error popup
//crashAlert(failure);
}
}, Threading.SAME_THREAD);
walletAppKit.startAsync();
}
private void initWallet() {
wallet = walletAppKit.wallet();
wallet.allowSpendingUnconfirmedTransactions();
//walletAppKit.peerGroup().setMaxConnections(11);
if (params == RegTestParams.get())
/* if (params == RegTestParams.get())
walletAppKit.peerGroup().setMinBroadcastConnections(1);
else
walletAppKit.peerGroup().setMinBroadcastConnections(3);
walletAppKit.peerGroup().setMinBroadcastConnections(3);*/
walletEventListener = new WalletEventListener() {
@ -266,14 +283,14 @@ public class WalletFacade {
// Listener
///////////////////////////////////////////////////////////////////////////////////////////
public DownloadListener addDownloadListener(DownloadListener listener) {
/* public DownloadListener addDownloadListener(DownloadListener listener) {
downloadListeners.add(listener);
return listener;
}
public void removeDownloadListener(DownloadListener listener) {
downloadListeners.remove(listener);
}
}*/
public AddressConfidenceListener addAddressConfidenceListener(AddressConfidenceListener listener) {
addressConfidenceListeners.add(listener);
@ -1140,7 +1157,7 @@ public class WalletFacade {
void downloadComplete();
}
private class BlockChainDownloadListener extends org.bitcoinj.core.DownloadListener {
/* private class BlockChainDownloadListener extends org.bitcoinj.core.DownloadListener {
@Override
protected void progress(double percent, int blocksSoFar, Date date) {
super.progress(percent, blocksSoFar, date);
@ -1164,6 +1181,6 @@ public class WalletFacade {
downloadListener.downloadComplete();
}
}
}
}*/
}

View File

@ -146,7 +146,7 @@ class NetworkParametersProvider implements Provider<NetworkParameters> {
// Set default
// String networkType= WalletFacade.MAIN_NET;
// String networkType = WalletFacade.TEST_NET;
//String networkType = WalletFacade.TEST_NET;
String networkType = WalletFacade.REG_TEST_NET;
if (networkTypeFromConfig != null)

View File

@ -23,14 +23,20 @@ import io.bitsquare.gui.UIModel;
import io.bitsquare.gui.util.Profiler;
import io.bitsquare.msg.DHTSeedService;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.actor.event.PeerInitialized;
import io.bitsquare.msg.listeners.BootstrapListener;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.User;
import org.bitcoinj.core.DownloadListener;
import com.google.inject.Inject;
import java.util.Date;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
@ -57,11 +63,10 @@ class MainModel extends UIModel {
private boolean messageFacadeInited;
private boolean walletFacadeInited;
final BooleanProperty backendInited = new SimpleBooleanProperty();
final DoubleProperty networkSyncProgress = new SimpleDoubleProperty();
final BooleanProperty networkSyncComplete = new SimpleBooleanProperty();
// final ObjectProperty<Coin> balance = new SimpleObjectProperty<>();
final BooleanProperty backendReady = new SimpleBooleanProperty();
final DoubleProperty networkSyncProgress = new SimpleDoubleProperty(-1);
final IntegerProperty numPendingTrades = new SimpleIntegerProperty(0);
private boolean facadesInitialised;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -120,19 +125,34 @@ class MainModel extends UIModel {
Profiler.printMsgWithTime("MainModel.initFacades");
walletFacade.initialize(() -> {
DownloadListener downloadListener = new DownloadListener() {
@Override
protected void progress(double percent, int blocksLeft, Date date) {
super.progress(percent, blocksLeft, date);
Platform.runLater(() -> {
networkSyncProgress.set(percent / 100.0);
if (facadesInitialised && percent >= 100.0)
backendReady.set(true);
});
}
@Override
protected void doneDownload() {
super.doneDownload();
Platform.runLater(() -> {
networkSyncProgress.set(1.0);
if (facadesInitialised)
backendReady.set(true);
});
}
};
walletFacade.initialize(downloadListener, () -> {
walletFacadeInited = true;
if (messageFacadeInited)
onFacadesInitialised();
/* walletFacade.addBalanceListener(new BalanceListener() {
@Override
public void onBalanceChanged(Coin balance) {
updateBalance(balance);
}
});
updateBalance(walletFacade.getWalletBalance());*/
});
}

View File

@ -24,9 +24,11 @@ import io.bitsquare.gui.util.BSFormatter;
import com.google.inject.Inject;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
@ -40,15 +42,14 @@ import org.slf4j.LoggerFactory;
class MainPM extends PresentationModel<MainModel> {
private static final Logger log = LoggerFactory.getLogger(MainPM.class);
final BooleanProperty backendInited = new SimpleBooleanProperty();
// final StringProperty balance = new SimpleStringProperty();
private BSFormatter formatter;
final BooleanProperty backendReady = new SimpleBooleanProperty();
final StringProperty bankAccountsComboBoxPrompt = new SimpleStringProperty();
final BooleanProperty bankAccountsComboBoxDisable = new SimpleBooleanProperty();
final StringProperty splashScreenInfoText = new SimpleStringProperty();
final BooleanProperty networkSyncComplete = new SimpleBooleanProperty();
final IntegerProperty numPendingTrades = new SimpleIntegerProperty();
private BSFormatter formatter;
final DoubleProperty networkSyncProgress = new SimpleDoubleProperty();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@ -70,22 +71,20 @@ class MainPM extends PresentationModel<MainModel> {
public void initialize() {
super.initialize();
backendInited.bind(model.backendInited);
networkSyncComplete.bind(model.networkSyncComplete);
backendReady.bind(model.backendReady);
networkSyncProgress.bind(model.networkSyncProgress);
numPendingTrades.bind(model.numPendingTrades);
model.networkSyncProgress.addListener((ov, oldValue, newValue) -> {
if ((double) newValue > 0)
splashScreenInfoText.set("Synchronise with network " + formatter.formatToPercent((double) newValue));
if ((double) newValue > 0.0)
splashScreenInfoText.set("Synchronise with network " + formatter.formatToPercent((double)
newValue));
else if ((double) newValue == 1)
splashScreenInfoText.set("Synchronise with network completed.");
else
splashScreenInfoText.set("Synchronise with network...");
});
/*model.balance.addListener((ov, oldValue, newValue) -> balance.set(formatter.formatCoinWithCode
(newValue)));*/
splashScreenInfoText.set("Synchronise with network...");
model.getBankAccounts().addListener((ListChangeListener<BankAccount>) change -> {
bankAccountsComboBoxDisable.set(change.getList().isEmpty());

View File

@ -22,7 +22,6 @@ import io.bitsquare.bank.BankAccount;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.OverlayManager;
import io.bitsquare.gui.ViewCB;
import io.bitsquare.gui.components.NetworkSyncPane;
import io.bitsquare.gui.components.Popups;
import io.bitsquare.gui.components.SystemNotification;
import io.bitsquare.gui.util.Profiler;
@ -39,6 +38,7 @@ import java.util.ResourceBundle;
import javax.inject.Inject;
import javafx.animation.Interpolator;
import javafx.application.Platform;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@ -61,10 +61,9 @@ public class MainViewCB extends ViewCB<MainPM> {
private final ToggleGroup navButtonsGroup = new ToggleGroup();
private BorderPane baseApplicationContainer;
private VBox baseOverlayContainer;
private VBox splashScreen;
private AnchorPane contentContainer;
private HBox leftNavPane, rightNavPane;
private NetworkSyncPane networkSyncPane;
private ToggleButton buyButton, sellButton, homeButton, msgButton, ordersButton, fundsButton, settingsButton,
accountButton;
private Pane ordersButtonButtonPane;
@ -166,27 +165,22 @@ public class MainViewCB extends ViewCB<MainPM> {
private void startup() {
baseApplicationContainer = getBaseApplicationContainer();
baseOverlayContainer = getSplashScreen();
((StackPane) root).getChildren().addAll(baseApplicationContainer, baseOverlayContainer);
splashScreen = getSplashScreen();
((StackPane) root).getChildren().addAll(baseApplicationContainer, splashScreen);
baseApplicationContainer.setCenter(getApplicationContainer());
onBaseContainersCreated();
Platform.runLater(() -> onSplashScreenAdded());
}
private void onBaseContainersCreated() {
Profiler.printMsgWithTime("MainController.onBaseContainersCreated");
AnchorPane applicationContainer = getApplicationContainer();
baseApplicationContainer.setCenter(applicationContainer);
presentationModel.backendInited.addListener((ov, oldValue, newValue) -> {
private void onSplashScreenAdded() {
presentationModel.backendReady.addListener((ov, oldValue, newValue) -> {
if (newValue)
onBackendInited();
onBackendReady();
});
presentationModel.initBackend();
}
private void onBackendInited() {
private void onBackendReady() {
Profiler.printMsgWithTime("MainController.onBackendInited");
addMainNavigation();
}
@ -238,7 +232,7 @@ public class MainViewCB extends ViewCB<MainPM> {
private void onContentAdded() {
Profiler.printMsgWithTime("MainController.onContentAdded");
Transitions.fadeOutAndRemove(baseOverlayContainer, 1500).setInterpolator(Interpolator.EASE_IN);
Transitions.fadeOutAndRemove(splashScreen, 1500).setInterpolator(Interpolator.EASE_IN);
}
@ -302,7 +296,12 @@ public class MainViewCB extends ViewCB<MainPM> {
loadingLabel.setPadding(new Insets(80, 0, 0, 0));
loadingLabel.textProperty().bind(presentationModel.splashScreenInfoText);
vBox.getChildren().addAll(logo, subTitle, loadingLabel);
ProgressBar progressBar = new ProgressBar();
progressBar.setPrefWidth(200);
progressBar.progressProperty().bind(presentationModel.networkSyncProgress);
vBox.getChildren().addAll(logo, subTitle, loadingLabel, progressBar);
return vBox;
}
@ -327,22 +326,7 @@ public class MainViewCB extends ViewCB<MainPM> {
AnchorPane.setTopAnchor(contentContainer, 60d);
AnchorPane.setBottomAnchor(contentContainer, 25d);
networkSyncPane = new NetworkSyncPane();
networkSyncPane.setSpacing(10);
networkSyncPane.setPrefHeight(20);
AnchorPane.setLeftAnchor(networkSyncPane, 0d);
AnchorPane.setBottomAnchor(networkSyncPane, 5d);
// TODO sometimes it keeps running... deactivate ti for the moment and replace it with the notification pane
// from Mike Hearn later
networkSyncPane.setVisible(false);
presentationModel.networkSyncComplete.addListener((ov, old, newValue) -> {
if (newValue)
networkSyncPane.downloadComplete();
});
anchorPane.getChildren().addAll(leftNavPane, rightNavPane, contentContainer, networkSyncPane);
anchorPane.getChildren().addAll(leftNavPane, rightNavPane, contentContainer);
return anchorPane;
}
@ -361,8 +345,6 @@ public class MainViewCB extends ViewCB<MainPM> {
msgButton = addNavButton(msgButtonHolder, "Messages", Navigation.Item.MSG);
leftNavPane.getChildren().add(msgButtonHolder);
//addBalanceInfo(rightNavPane);
addBankAccountComboBox(rightNavPane);
settingsButton = addNavButton(rightNavPane, "Preferences", Navigation.Item.SETTINGS);
@ -408,28 +390,6 @@ public class MainViewCB extends ViewCB<MainPM> {
return toggleButton;
}
/*private void addBalanceInfo(Pane parent) {
final TextField balanceTextField = new TextField();
balanceTextField.setEditable(false);
balanceTextField.setPrefWidth(110);
balanceTextField.setId("nav-balance-label");
balanceTextField.textProperty().bind(presentationModel.balance);
final Label titleLabel = new Label("Balance");
titleLabel.setMouseTransparent(true);
titleLabel.setId("nav-button-label");
balanceTextField.widthProperty().addListener((ov, o, n) ->
titleLabel.setLayoutX(((double) n - titleLabel.getWidth()) / 2));
final VBox vBox = new VBox();
vBox.setPadding(new Insets(12, 5, 0, 0));
vBox.setSpacing(2);
vBox.getChildren().setAll(balanceTextField, titleLabel);
vBox.setAlignment(Pos.CENTER);
parent.getChildren().add(vBox);
}*/
private void addBankAccountComboBox(Pane parent) {
final ComboBox<BankAccount> comboBox = new ComboBox<>(presentationModel.getBankAccounts());
comboBox.setLayoutY(12);
@ -460,4 +420,4 @@ public class MainViewCB extends ViewCB<MainPM> {
vBox.getChildren().setAll(comboBox, titleLabel);
parent.getChildren().add(vBox);
}
}
}

View File

@ -24,6 +24,6 @@
xmlns:fx="http://javafx.com/fxml">
<Tab fx:id="withdrawalTab" text="Open for withdrawal" closable="false"/>
<Tab fx:id="transactionsTab" text="History" closable="false"/>
<Tab fx:id="transactionsTab" text="Transactions" closable="false"/>
</TabPane>

View File

@ -17,7 +17,6 @@
package io.bitsquare.gui.main.orders.pending;
import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.gui.PresentationModel;
import io.bitsquare.gui.components.Popups;
@ -151,7 +150,7 @@ public class PendingTradesPM extends PresentationModel<PendingTradesModel> {
}
String getAmountToWithdraw() {
return formatter.formatCoinWithCode(model.getAmountToWithdraw().subtract(FeePolicy.TX_FEE));
return formatter.formatCoinWithCode(model.getAmountToWithdraw()); //.subtract(FeePolicy.TX_FEE));
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -285,6 +285,8 @@ public class CreateOfferViewCB extends CachedViewCB<CreateOfferPM> {
private void close() {
TabPane tabPane = ((TabPane) (root.getParent().getParent()));
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
navigation.navigationTo(Navigation.Item.MAIN, Navigation.Item.ORDERS, Navigation.Item.OFFERS);
}

View File

@ -23,7 +23,7 @@ import io.bitsquare.trade.Offer;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
class OrderBookListItem {
public class OrderBookListItem {
private final Offer offer;
private final ObjectProperty<Country> bankAccountCountry = new SimpleObjectProperty<>();
@ -32,7 +32,7 @@ class OrderBookListItem {
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
OrderBookListItem(Offer offer, Country bankAccountCountry) {
public OrderBookListItem(Offer offer, Country bankAccountCountry) {
this.offer = offer;
setBankAccountCountry(bankAccountCountry);
}
@ -51,7 +51,7 @@ class OrderBookListItem {
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
Offer getOffer() {
public Offer getOffer() {
return offer;
}

View File

@ -22,6 +22,7 @@ import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.btc.listeners.BalanceListener;
import io.bitsquare.gui.UIModel;
import io.bitsquare.persistence.Persistence;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
@ -56,6 +57,7 @@ class TakeOfferModel extends UIModel {
private final TradeManager tradeManager;
private final WalletFacade walletFacade;
private final Settings settings;
private Persistence persistence;
private Offer offer;
private AddressEntry addressEntry;
@ -81,10 +83,11 @@ class TakeOfferModel extends UIModel {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
TakeOfferModel(TradeManager tradeManager, WalletFacade walletFacade, Settings settings) {
TakeOfferModel(TradeManager tradeManager, WalletFacade walletFacade, Settings settings, Persistence persistence) {
this.tradeManager = tradeManager;
this.walletFacade = walletFacade;
this.settings = settings;
this.persistence = persistence;
}
@ -168,7 +171,7 @@ class TakeOfferModel extends UIModel {
requestTakeOfferErrorMessage.set("Take offer request got rejected.");
break;
default:
log.error("Unhandled trade state: " + newValue);
log.warn("Unhandled trade state: " + newValue);
break;
}
});
@ -227,6 +230,13 @@ class TakeOfferModel extends UIModel {
return true;
}
Boolean displaySecurityDepositInfo() {
Object securityDepositInfoDisplayedObject = persistence.read("displaySecurityDepositInfo");
if (securityDepositInfoDisplayedObject instanceof Boolean)
return (Boolean) securityDepositInfoDisplayedObject;
else
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////
// Setter

View File

@ -303,6 +303,10 @@ class TakeOfferPM extends PresentationModel<TakeOfferModel> {
return paymentLabel;
}
Boolean displaySecurityDepositInfo() {
return model.displaySecurityDepositInfo();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private

View File

@ -201,6 +201,23 @@ public class TakeOfferViewCB extends CachedViewCB<TakeOfferPM> {
@FXML
void onShowPayFundsScreen() {
if (presentationModel.displaySecurityDepositInfo()) {
overlayManager.blurContent();
List<Action> actions = new ArrayList<>();
actions.add(new AbstractAction(BSResources.get("shared.close")) {
@Override
public void handle(ActionEvent actionEvent) {
Dialog.Actions.CLOSE.handle(actionEvent);
overlayManager.removeBlurContent();
}
});
Popups.openInfo("To ensure that both traders behave fair they need to pay a security deposit.",
"The deposit will stay in your local trading wallet until the offer gets accepted by " +
"another trader. " +
"\nIt will be refunded to you after the trade has successfully completed.",
actions);
}
priceAmountPane.setInactive();
showPaymentInfoScreenButton.setVisible(false);

View File

@ -309,7 +309,7 @@ public class BSFormatter {
decimalFormat.setMinimumFractionDigits(1);
decimalFormat.setMaximumFractionDigits(1);
decimalFormat.setGroupingUsed(false);
return decimalFormat.format(value / 100) + " %";
return decimalFormat.format(value * 100.0) + " %";
}
private String cleanInput(String input) {

View File

@ -48,12 +48,12 @@ public class BSResources {
try {
return BSResources.getResourceBundle().getString(key);
} catch (MissingResourceException e) {
log.warn("MissingResourceException for key: " + key);
log.warn("Missing resource for key: " + key);
return key;
}
}
public static String get(String key, String... arguments) {
public static String get(String key, Object... arguments) {
return MessageFormat.format(BSResources.get(key), arguments);
}
}

View File

@ -35,6 +35,10 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import net.tomp2p.connection.Ports;
import net.tomp2p.dht.PeerBuilderDHT;
import net.tomp2p.dht.PeerDHT;
@ -76,6 +80,7 @@ public class BootstrappedPeerFactory {
private final Persistence persistence;
private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create();
public final StringProperty connectionState = new SimpleStringProperty();
///////////////////////////////////////////////////////////////////////////////////////////
@ -194,6 +199,9 @@ public class BootstrappedPeerFactory {
log.debug("We are not behind a NAT and reachable to other peers: My address visible to the " +
"outside is " + futureDiscover.peerAddress());
requestBootstrapPeerMap();
setConnectionState("We are not behind a NAT and reachable to other peers: My address visible to " +
"the " +
"outside is " + futureDiscover.peerAddress());
settableFuture.set(peerDHT);
persistence.write(ref, "lastSuccessfulBootstrap", "default");
@ -203,6 +211,10 @@ public class BootstrappedPeerFactory {
log.warn("We are probably behind a NAT and not reachable to other peers. We try port forwarding " +
"as next step.");
setConnectionState("We are probably behind a NAT and not reachable to other peers. We try port " +
"forwarding " +
"as next step.");
bootstrapWithPortForwarding(peerDHT, futureDiscover);
}
}
@ -230,6 +242,8 @@ public class BootstrappedPeerFactory {
log.debug("Port forwarding was successful. My address visible to the outside is " +
futureNAT.peerAddress());
requestBootstrapPeerMap();
setConnectionState("Port forwarding was successful. My address visible to the outside is " +
futureNAT.peerAddress());
settableFuture.set(peerDHT);
persistence.write(ref, "lastSuccessfulBootstrap", "portForwarding");
@ -238,6 +252,8 @@ public class BootstrappedPeerFactory {
log.warn("Port forwarding has failed. Reason: " + futureNAT.failedReason());
log.warn("We try to use a relay as next step.");
setConnectionState("We try to use a relay as next step.");
bootstrapWithRelay(peerDHT, nodeBehindNat);
}
}
@ -324,6 +340,8 @@ public class BootstrappedPeerFactory {
if (future.isSuccess()) {
log.debug("Final bootstrap was successful. bootstrapTo = " + futureBootstrap2.bootstrapTo());
requestBootstrapPeerMap();
setConnectionState("Final bootstrap was successful. bootstrapTo = " + futureBootstrap2
.bootstrapTo());
settableFuture.set(peerDHT);
persistence.write(ref, "lastSuccessfulBootstrap", "relay");
@ -348,4 +366,8 @@ public class BootstrappedPeerFactory {
private void requestBootstrapPeerMap() {
log.debug("getBootstrapPeerMap");
}
private void setConnectionState(String state) {
Platform.runLater(() -> connectionState.set(state));
}
}

View File

@ -177,6 +177,7 @@ public class MessageFacade implements MessageBroker {
try {
Object offerDataObject = offerData.object();
if (offerDataObject instanceof Offer) {
log.error("Added offer to DHT with ID: " + ((Offer) offerDataObject).getId());
listener.onOfferAdded((Offer) offerDataObject);
}
} catch (ClassNotFoundException | IOException e) {

View File

@ -373,7 +373,7 @@ public class TradeManager {
// probably not needed
@Override
public void onWaitingForPeerResponse(SellerTakesOfferProtocol.State state) {
log.error("onWaitingForPeerResponse");
log.debug("onWaitingForPeerResponse");
}
};

View File

@ -71,7 +71,9 @@ createOffer.advancedBox.county=Payments account country:
createOffer.advancedBox.info=Your trading partners must fulfill your offer restrictions. You can edit the accepted countries, languages and arbitrators in the settings. The payments account details are used from your current selected payments account (if you have multiple payments accounts).
createOffer.success.headline=Your offer has been successfully published to the distributed offerbook.
createOffer.success.info=The Transaction ID for the offer payment is: {0}
createOffer.success.info=In the Offers screen you can manage your open offers.\n\nThe Transaction ID for the offer \
payment is: {0}
createOffer.error.message=An error occurred when placing the offer.\n{0}

View File

@ -36,6 +36,10 @@
<logger name="io.bitsquare.gui.CachedViewController" level="OFF"/>
<logger name="io.bitsquare.gui.util.Profiler" level="TRACE"/>
<logger name=" org.bitcoinj.core.BitcoinSerializer" level="WARN"/>
<logger name=" org.bitcoinj.core.AbstractBlockChain" level="WARN"/>
<!--
<logger name="org.bitcoinj.core.Wallet" level="OFF"/>
<logger name="org.bitcoinj.core.MemoryPool" level="OFF"/>

Binary file not shown.

View File

@ -0,0 +1,182 @@
/*
* 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.msg.dhttest;
import io.bitsquare.bank.BankAccountType;
import io.bitsquare.gui.main.trade.orderbook.OrderBookListItem;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.msg.BootstrappedPeerFactory;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.AddOfferListener;
import io.bitsquare.msg.listeners.BootstrapListener;
import io.bitsquare.msg.listeners.OrderBookListener;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.user.User;
import org.bitcoinj.core.Coin;
import java.net.URL;
import java.util.ArrayList;
import java.util.Currency;
import java.util.List;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DHTTestController implements Initializable {
private static final Logger log = LoggerFactory.getLogger(DHTTestController.class);
private final MessageFacade messageFacade;
private BootstrappedPeerFactory bootstrappedPeerFactory;
private User user;
private final ObservableList<OrderBookListItem> orderBookListItems = FXCollections.observableArrayList();
@FXML TableView table;
@FXML TextArea stateLabel;
@FXML TextField toSaveTextField;
@FXML TableColumn<OrderBookListItem, OrderBookListItem> idColumn;
@Inject
private DHTTestController(MessageFacade messageFacade, User user,
BootstrappedPeerFactory bootstrappedPeerFactory) {
this.user = user;
this.messageFacade = messageFacade;
this.bootstrappedPeerFactory = bootstrappedPeerFactory;
user.applyPersistedUser(null);
}
@Override
public void initialize(URL url, ResourceBundle rb) {
messageFacade.init(new BootstrapListener() {
@Override
public void onCompleted() {
onMessageFacadeInitialised();
}
@Override
public void onFailed(Throwable throwable) {
log.error(throwable.toString());
}
});
messageFacade.addOrderBookListener(new OrderBookListener() {
@Override
public void onOfferAdded(Offer offer) {
log.debug("offer added " + offer.getId());
}
@Override
public void onOffersReceived(List<Offer> offers) {
//TODO use deltas instead replacing the whole list
orderBookListItems.clear();
offers.stream().forEach(offer -> {
if (offer != null) {
orderBookListItems.add(new OrderBookListItem(offer, CountryUtil.getDefaultCountry()));
}
});
}
@Override
public void onOfferRemoved(Offer offer) {
orderBookListItems.removeIf(item -> item.getOffer().getId().equals(offer.getId()));
}
});
setIDColumnCellFactory();
table.setItems(orderBookListItems);
bootstrappedPeerFactory.connectionState.addListener((ov, oldValue, newValue) -> {
stateLabel.setText(newValue);
});
}
@FXML
public void onAddOffer() {
Offer offer = new Offer(toSaveTextField.getText(),
user.getMessagePublicKey(),
Direction.BUY,
500,
Coin.COIN,
Coin.COIN,
BankAccountType.SEPA,
Currency.getInstance("EUR"),
CountryUtil.getDefaultCountry(),
"bankAccountUID",
new ArrayList<>(),
10,
new ArrayList<>(),
new ArrayList<>());
messageFacade.addOffer(offer, new AddOfferListener() {
@Override
public void onComplete() {
log.debug("onAddOffer onComplete");
}
@Override
public void onFailed(String reason, Throwable throwable) {
log.debug("onAddOffer onFailed");
}
});
}
public void onGetOffers() {
log.debug("onLoad");
messageFacade.getOffers("EUR");
}
private void onMessageFacadeInitialised() {
log.debug("onMessageFacadeInitialised");
}
private void setIDColumnCellFactory() {
idColumn.setCellValueFactory((offer) -> new ReadOnlyObjectWrapper<>(offer.getValue()));
idColumn.setCellFactory(
new Callback<TableColumn<OrderBookListItem, OrderBookListItem>, TableCell<OrderBookListItem,
OrderBookListItem>>() {
@Override
public TableCell<OrderBookListItem, OrderBookListItem> call(
TableColumn<OrderBookListItem, OrderBookListItem> column) {
return new TableCell<OrderBookListItem, OrderBookListItem>() {
@Override
public void updateItem(final OrderBookListItem item, boolean empty) {
super.updateItem(item, empty);
if (item != null && item.getOffer() != null && item.getOffer().getAmount() != null)
setText(item.getOffer().getId());
}
};
}
});
}
}

View File

@ -0,0 +1,102 @@
/*
* 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.msg.dhttest;
import io.bitsquare.di.BitSquareModule;
import io.bitsquare.util.ViewLoader;
import com.google.inject.Guice;
import com.google.inject.Injector;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.input.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DHTTestRunner extends Application {
private static final Logger log = LoggerFactory.getLogger(DHTTestRunner.class);
private Scene scene;
private Parent view;
private Pane pane;
private boolean devTest = true;
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws IOException {
Injector injector = Guice.createInjector(new BitSquareModule());
ViewLoader.setInjector(injector);
pane = new StackPane();
scene = new Scene(pane, 1000, 630);
scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+S"), this::loadMainWindow);
loadMainWindow();
primaryStage.setScene(scene);
primaryStage.show();
}
public void loadMainWindow() {
log.debug("re load");
pane.getChildren().removeAll();
ViewLoader loader = new ViewLoader(
getUrl("/io/bitsquare/msg/dhttest/DHTTestView.fxml")
, false);
try {
view = loader.load();
} catch (IOException e) {
e.printStackTrace();
}
pane.getChildren().setAll(view);
refreshStylesheets();
}
private void refreshStylesheets() {
scene.getStylesheets().clear();
scene.getStylesheets().setAll(getUrl("/io/bitsquare/gui/bitsquare.css").toExternalForm());
}
private URL getUrl(String subPath) {
if (devTest) {
try {
// load from file system location to make a reload possible. makes dev process easier with hot reload
return new URL("file:///Users/mk/Documents/_intellij/bitsquare/src/test/java" + subPath);
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}
else {
return getClass().getResource(subPath);
}
}
}

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ 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/>.
-->
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="root" prefHeight="630.0" prefWidth="1000.0" style="-fx-background-color: f4f4f4;"
AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0"
AnchorPane.topAnchor="10.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.msg.dhttest.DHTTestController">
<children>
<GridPane fx:id="gridPane" hgap="5.0" vgap="5.0" AnchorPane.leftAnchor="10" AnchorPane.topAnchor="10">
<children>
<Label text="State:" GridPane.rowIndex="0">
<GridPane.margin>
<Insets bottom="20.0"/>
</GridPane.margin>
</Label>
<TextArea fx:id="stateLabel" text="connecting..." editable="false" focusTraversable="false"
GridPane.rowIndex="0" prefWidth="750"
GridPane.columnIndex="1">
<GridPane.margin>
<Insets bottom="20.0"/>
</GridPane.margin>
</TextArea>
<Label text="Store offer to DHT with ID:" GridPane.rowIndex="1"/>
<TextField fx:id="toSaveTextField" text="test value" GridPane.columnIndex="1"
GridPane.rowIndex="1" prefWidth="300"/>
<Button text="Save offer" onAction="#onAddOffer" GridPane.columnIndex="1"
GridPane.rowIndex="2">
<GridPane.margin>
<Insets bottom="20.0"/>
</GridPane.margin>
</Button>
<Label text="Offerbook:" GridPane.rowIndex="3"/>
<Button text="Load offers" onAction="#onGetOffers" GridPane.columnIndex="1"
GridPane.rowIndex="3"/>
<TableView fx:id="table" GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2"
prefHeight="300">
<columns>
<TableColumn text="ID" fx:id="idColumn" minWidth="130"/>
</columns>
</TableView>
</children>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="200"/>
<ColumnConstraints hgrow="ALWAYS"/>
</columnConstraints>
</GridPane>
</children>
</AnchorPane>