mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-03-15 10:26:37 -04:00
updated to master branch of tomp2p. refactored messaging. bootstrapping to seed node (server supported). right management.
This commit is contained in:
parent
222045ca5a
commit
57d5ddcc4f
@ -10,6 +10,10 @@ Then you can generate coins on demand with the Bitcoin qt client with that comma
|
||||
See: https://bitcoinj.github.io/testing
|
||||
You can change the network mode in the guice module: BitSquareModule.java
|
||||
|
||||
We use a fork of the actual TomP2P master branch: https://github.com/ManfredKarrer/TomP2P
|
||||
You need to check that out as well and deploy it to the local maven repository:
|
||||
mvn clean install -DskipTests
|
||||
|
||||
|
||||
### Resources:
|
||||
* Web: http://bitsquare.io
|
||||
|
6
pom.xml
6
pom.xml
@ -124,11 +124,13 @@
|
||||
<version>0.11.3</version>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>net.tomp2p</groupId>
|
||||
<artifactId>TomP2P</artifactId>
|
||||
<version>4.4</version>
|
||||
<artifactId>tomp2p-all</artifactId>
|
||||
<version>5.0-Alpha24-SNAPSHOT</version>
|
||||
</dependency>
|
||||
-->
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
@ -1,3 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Main-Class: io.bitsquare.Relay
|
||||
Main-Class: io.bitsquare.BootstrapNode_
|
||||
|
||||
|
@ -38,7 +38,7 @@ public class BitSquare extends Application
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BitSquare.class);
|
||||
|
||||
public static boolean fillFormsWithDummyData = false;
|
||||
public static boolean fillFormsWithDummyData = true;
|
||||
|
||||
private static String APP_NAME = "bitsquare";
|
||||
private static Stage primaryStage;
|
||||
|
@ -1,69 +0,0 @@
|
||||
package io.bitsquare;
|
||||
|
||||
import java.io.IOException;
|
||||
import javafx.application.Application;
|
||||
import javafx.stage.Stage;
|
||||
import net.tomp2p.p2p.Peer;
|
||||
import net.tomp2p.p2p.PeerMaker;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.peers.PeerMapChangeListener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
public class Relay extends Application
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(Relay.class);
|
||||
private static final Number160 ID = Number160.createHash(1);
|
||||
|
||||
private static Peer masterPeer = null;
|
||||
private static int port;
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
if (args != null && args.length == 1)
|
||||
{
|
||||
port = new Integer(args[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
port = 5001;
|
||||
}
|
||||
|
||||
launch(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws IOException
|
||||
{
|
||||
log.trace("Startup: start");
|
||||
if (masterPeer == null)
|
||||
{
|
||||
masterPeer = new PeerMaker(ID).setPorts(port).makeAndListen();
|
||||
// masterPeer = new PeerMaker(ID).setPorts(port).setBagSize(100).makeAndListen(); // setBagSize cause sync problems...
|
||||
masterPeer.getBroadcastRPC().getConnectionBean().getConnectionReservation().reserve(3).awaitUninterruptibly();
|
||||
masterPeer.getConnectionHandler().getPeerBean().getPeerMap().addPeerMapChangeListener(new PeerMapChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void peerInserted(PeerAddress peerAddress)
|
||||
{
|
||||
log.info("peerInserted " + peerAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void peerRemoved(PeerAddress peerAddress)
|
||||
{
|
||||
log.info("peerRemoved " + peerAddress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void peerUpdated(PeerAddress peerAddress)
|
||||
{
|
||||
log.info("peerUpdated " + peerAddress);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
174
src/main/java/io/bitsquare/SeedNode.java
Normal file
174
src/main/java/io/bitsquare/SeedNode.java
Normal file
@ -0,0 +1,174 @@
|
||||
package io.bitsquare;
|
||||
|
||||
import io.bitsquare.msg.SeedNodeAddress;
|
||||
import java.util.List;
|
||||
import net.tomp2p.dht.PeerBuilderDHT;
|
||||
import net.tomp2p.futures.BaseFuture;
|
||||
import net.tomp2p.futures.BaseFutureListener;
|
||||
import net.tomp2p.nat.PeerBuilderNAT;
|
||||
import net.tomp2p.nat.PeerNAT;
|
||||
import net.tomp2p.p2p.Peer;
|
||||
import net.tomp2p.p2p.PeerBuilder;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.peers.PeerMapChangeListener;
|
||||
import net.tomp2p.peers.PeerStatatistic;
|
||||
import net.tomp2p.relay.FutureRelay;
|
||||
import net.tomp2p.relay.RelayRPC;
|
||||
import net.tomp2p.tracker.PeerBuilderTracker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Well known node which is reachable for all peers for bootstrapping.
|
||||
* There will be several SeedNodes running on several servers.
|
||||
* <p>
|
||||
* TODO: Alternative bootstrap methods will follow later (save locally list of known nodes reported form other peers,...)
|
||||
*/
|
||||
public class SeedNode
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(SeedNode.class);
|
||||
|
||||
private static final List<SeedNodeAddress.StaticSeedNodeAddresses> staticSedNodeAddresses = SeedNodeAddress.StaticSeedNodeAddresses.getAllSeedNodeAddresses();
|
||||
|
||||
/**
|
||||
* @param args If no args passed we use localhost, otherwise the param is used as index for selecting an address from seedNodeAddresses
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
int index = 0;
|
||||
SeedNode seedNode = new SeedNode();
|
||||
if (args.length > 0)
|
||||
{
|
||||
// use host index passes as param
|
||||
int param = Integer.valueOf(args[0]);
|
||||
if (param < staticSedNodeAddresses.size())
|
||||
index = param;
|
||||
}
|
||||
try
|
||||
{
|
||||
seedNode.startupUsingAddress(new SeedNodeAddress(staticSedNodeAddresses.get(index)));
|
||||
} catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public SeedNode()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void startupUsingAddress(SeedNodeAddress seedNodeAddress)
|
||||
{
|
||||
try
|
||||
{
|
||||
Peer peer = new PeerBuilder(Number160.createHash(seedNodeAddress.getId())).ports(seedNodeAddress.getPort()).start();
|
||||
|
||||
// Need to add all features the clients will use (otherwise msg type is UNKNOWN_ID)
|
||||
new PeerBuilderDHT(peer).start();
|
||||
PeerNAT nodeBehindNat = new PeerBuilderNAT(peer).start();
|
||||
new RelayRPC(peer);
|
||||
new PeerBuilderTracker(peer);
|
||||
nodeBehindNat.startSetupRelay(new FutureRelay());
|
||||
|
||||
log.debug("Peer started. " + peer.peerAddress());
|
||||
|
||||
peer.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void peerInserted(PeerAddress peerAddress, boolean verified)
|
||||
{
|
||||
log.debug("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void peerRemoved(PeerAddress peerAddress, PeerStatatistic peerStatistics)
|
||||
{
|
||||
log.debug("Peer removed: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void peerUpdated(PeerAddress peerAddress, PeerStatatistic peerStatistics)
|
||||
{
|
||||
log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
||||
}
|
||||
});
|
||||
|
||||
// We keep server in endless loop
|
||||
for (; ; )
|
||||
{
|
||||
// Optional pinging
|
||||
boolean pingPeers = false;
|
||||
if (pingPeers)
|
||||
{
|
||||
for (PeerAddress peerAddress : peer.peerBean().peerMap().all())
|
||||
{
|
||||
BaseFuture future = peer.ping().peerAddress(peerAddress).tcpPing().start();
|
||||
future.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.debug("peer online (TCP):" + peerAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("offline " + peerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("exceptionCaught " + t);
|
||||
}
|
||||
});
|
||||
|
||||
future = peer.ping().peerAddress(peerAddress).start();
|
||||
future.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.debug("peer online (UDP):" + peerAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.debug("offline " + peerAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("exceptionCaught " + t);
|
||||
}
|
||||
});
|
||||
}
|
||||
Thread.sleep(1500);
|
||||
}
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
log.error("Exception: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -22,5 +22,4 @@ class BitSquareWallet extends Wallet implements Serializable
|
||||
super(params, keyCrypter);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -190,11 +190,9 @@ public class WalletFacade
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void shutDown()
|
||||
{
|
||||
wallet.removeEventListener(walletEventListener);
|
||||
|
||||
walletAppKit.stopAsync();
|
||||
}
|
||||
|
||||
@ -596,6 +594,7 @@ public class WalletFacade
|
||||
|
||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
|
||||
// we don't allow spending of unconfirmed tx as with fake registrations we would open up doors for spam and market manipulation with fake offers
|
||||
// so set includePending to false
|
||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, getRegistrationAddressInfo(), false);
|
||||
sendRequest.changeAddress = getRegistrationAddressInfo().getAddress();
|
||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||
@ -677,6 +676,8 @@ public class WalletFacade
|
||||
}
|
||||
|
||||
|
||||
// TODO: Trade process - use P2SH instead and optimize data exchange
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trade process
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -15,6 +15,7 @@ import io.bitsquare.btc.FeePolicy;
|
||||
import io.bitsquare.btc.WalletFacade;
|
||||
import io.bitsquare.crypto.CryptoFacade;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.SeedNodeAddress;
|
||||
import io.bitsquare.settings.Settings;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.Trading;
|
||||
@ -52,6 +53,11 @@ public class BitSquareModule extends AbstractModule
|
||||
//bind(String.class).annotatedWith(Names.named("networkType")).toInstance(WalletFacade.TEST_NET);
|
||||
bind(NetworkParameters.class).toProvider(NetworkParametersProvider.class).asEagerSingleton();
|
||||
bind(BitSquareWalletAppKit.class).toProvider(BitSquareWalletAppKitProvider.class).asEagerSingleton();
|
||||
|
||||
// bind(Boolean.class).annotatedWith(Names.named("useDiskStorage")).toInstance(new Boolean(true));
|
||||
bind(Boolean.class).annotatedWith(Names.named("useDiskStorage")).toInstance(new Boolean(false));
|
||||
bind(SeedNodeAddress.StaticSeedNodeAddresses.class).annotatedWith(Names.named("defaultSeedNode")).toInstance(SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST);
|
||||
// bind(SeedNodeAddress.StaticSeedNodeAddresses.class).annotatedWith(Names.named("defaultSeedNode")).toInstance(SeedNodeAddress.StaticSeedNodeAddresses.DIGITAL_OCEAN);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import io.bitsquare.gui.orders.OrdersController;
|
||||
import io.bitsquare.gui.util.Icons;
|
||||
import io.bitsquare.gui.util.Transitions;
|
||||
import io.bitsquare.locale.Localisation;
|
||||
import io.bitsquare.msg.BootstrapListener;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.trade.Direction;
|
||||
@ -176,8 +177,24 @@ public class MainController implements Initializable, NavigationController
|
||||
|
||||
private void init()
|
||||
{
|
||||
messageFacade.init(new BootstrapListener()
|
||||
{
|
||||
@Override
|
||||
public void onCompleted()
|
||||
{
|
||||
messageFacadeInited();
|
||||
}
|
||||
|
||||
messageFacade.init();
|
||||
@Override
|
||||
public void onFailed(Throwable throwable)
|
||||
{
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void messageFacadeInited()
|
||||
{
|
||||
|
||||
trading.addTakeOfferRequestListener(this::onTakeOfferRequested);
|
||||
|
||||
|
@ -26,9 +26,14 @@ import javafx.scene.layout.AnchorPane;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.stage.Stage;
|
||||
import javax.inject.Inject;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
/**
|
||||
* TODO remove tomp2p dependencies
|
||||
* import net.tomp2p.peers.Number160;
|
||||
* import net.tomp2p.storage.Data;
|
||||
*/
|
||||
@SuppressWarnings({"ALL", "UnusedParameters"})
|
||||
public class ArbitratorOverviewController implements Initializable, ChildController, NavigationController, ArbitratorListener
|
||||
{
|
||||
@ -135,7 +140,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success)
|
||||
public void onArbitratorsReceived(Map<Number640, Data> dataMap, boolean success)
|
||||
{
|
||||
if (success && dataMap != null)
|
||||
{
|
||||
@ -145,7 +150,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
|
||||
{
|
||||
try
|
||||
{
|
||||
Object arbitratorDataObject = arbitratorData.getObject();
|
||||
Object arbitratorDataObject = arbitratorData.object();
|
||||
if (arbitratorDataObject instanceof Arbitrator)
|
||||
{
|
||||
Arbitrator arbitrator = (Arbitrator) arbitratorDataObject;
|
||||
|
@ -22,8 +22,8 @@ import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.storage.Storage;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
import io.bitsquare.user.Reputation;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
@ -48,6 +48,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||
private final Storage storage;
|
||||
private final WalletFacade walletFacade;
|
||||
private final MessageFacade messageFacade;
|
||||
private User user;
|
||||
private Arbitrator arbitrator = new Arbitrator();
|
||||
private ArbitratorProfileController arbitratorProfileController;
|
||||
private boolean isEditMode;
|
||||
@ -92,11 +93,12 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
private ArbitratorRegistrationController(Storage storage, WalletFacade walletFacade, MessageFacade messageFacade)
|
||||
private ArbitratorRegistrationController(Storage storage, WalletFacade walletFacade, MessageFacade messageFacade, User user)
|
||||
{
|
||||
this.storage = storage;
|
||||
this.walletFacade = walletFacade;
|
||||
this.messageFacade = messageFacade;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
|
||||
@ -338,13 +340,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
messageFacade.addArbitrator(arbitrator);
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
messageFacade.addArbitrator(arbitrator);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@ -474,7 +470,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||
minArbitrationFeeTextField);
|
||||
|
||||
String pubKeyAsHex = walletFacade.getArbitratorDepositAddressInfo().getPubKeyAsHexString();
|
||||
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey());
|
||||
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey());
|
||||
String name = nameTextField.getText();
|
||||
|
||||
double maxTradeVolume = BitSquareConverter.stringToDouble(maxTradeVolumeTextField.getText());
|
||||
|
@ -205,11 +205,9 @@ public class CreateOfferController implements Initializable, ChildController, Hi
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("create offer pubkey " + user.getMessagePubKeyAsHex());
|
||||
|
||||
if (user.getCurrentBankAccount() != null)
|
||||
{
|
||||
offer = new Offer(user.getMessagePubKeyAsHex(),
|
||||
offer = new Offer(user.getMessagePublicKey(),
|
||||
direction,
|
||||
BitSquareConverter.stringToDouble(priceTextField.getText()),
|
||||
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
|
||||
|
@ -42,12 +42,13 @@
|
||||
<Label text="Balance:" GridPane.rowIndex="7"/>
|
||||
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||
|
||||
|
||||
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="8">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
<!--
|
||||
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="8">
|
||||
<GridPane.margin>
|
||||
<Insets top="10.0"/>
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
-->
|
||||
|
||||
<Label text="Bank account type:" GridPane.rowIndex="9"/>
|
||||
<TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="9"/>
|
||||
@ -67,17 +68,17 @@
|
||||
|
||||
<Button fx:id="placeOfferButton" defaultButton="true" onAction="#onPlaceOffer" text="Place offer" GridPane.columnIndex="1" GridPane.rowIndex="14"/>
|
||||
|
||||
<Label fx:id="txTitleLabel" text="Transaction ID:" visible="false" GridPane.rowIndex="15"/>
|
||||
<TextField fx:id="txTextField" visible="false" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="15"/>
|
||||
<Label fx:id="txTitleLabel" text="Transaction ID:" visible="false" GridPane.rowIndex="14"/>
|
||||
<TextField fx:id="txTextField" visible="false" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="14"/>
|
||||
<ConfidenceProgressIndicator fx:id="progressIndicator" visible="false" progress="0" GridPane.columnIndex="2" GridPane.halignment="LEFT"
|
||||
GridPane.rowIndex="15" GridPane.rowSpan="2" GridPane.valignment="TOP">
|
||||
GridPane.rowIndex="14" GridPane.rowSpan="2" GridPane.valignment="TOP">
|
||||
<GridPane.margin>
|
||||
<Insets top="2.0"/>
|
||||
</GridPane.margin>
|
||||
</ConfidenceProgressIndicator>
|
||||
<Label fx:id="confirmationLabel" text="Checking confirmations..." visible="false" GridPane.columnIndex="3" GridPane.rowIndex="15"/>
|
||||
<Label fx:id="confirmationLabel" text="Checking confirmations..." visible="false" GridPane.columnIndex="3" GridPane.rowIndex="14"/>
|
||||
|
||||
<Button fx:id="closeButton" visible="false" defaultButton="true" onAction="#onClose" text="Close" GridPane.columnIndex="1" GridPane.rowIndex="16"/>
|
||||
<Button fx:id="closeButton" visible="false" defaultButton="true" onAction="#onClose" text="Close" GridPane.columnIndex="1" GridPane.rowIndex="15"/>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
||||
|
@ -28,7 +28,6 @@ import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.orderbook.OrderBook;
|
||||
import io.bitsquare.trade.orderbook.OrderBookFilter;
|
||||
import io.bitsquare.user.User;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
@ -304,10 +303,6 @@ public class OrderBookController implements Initializable, ChildController
|
||||
{
|
||||
user.setAccountID(walletFacade.getRegistrationAddressInfo().toString());
|
||||
}
|
||||
if (messageFacade != null && messageFacade.getPubKey() != null)
|
||||
{
|
||||
user.setMessagePubKeyAsHex(DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey()));
|
||||
}
|
||||
|
||||
storage.write(user.getClass().getName(), user);
|
||||
} catch (InsufficientMoneyException e1)
|
||||
@ -437,7 +432,7 @@ public class OrderBookController implements Initializable, ChildController
|
||||
Image icon;
|
||||
Offer offer = orderBookListItem.getOffer();
|
||||
|
||||
if (offer.getMessagePubKeyAsHex().equals(user.getMessagePubKeyAsHex()))
|
||||
if (offer.getMessagePublicKey().equals(user.getMessagePublicKey()))
|
||||
{
|
||||
icon = Icons.getIconImage(Icons.REMOVE);
|
||||
title = "Remove";
|
||||
|
@ -191,7 +191,7 @@ public class TakerOfferController implements Initializable, ChildController
|
||||
public void onBankTransferInited(String tradeId)
|
||||
{
|
||||
setTradeId(tradeId);
|
||||
headLineLabel.setText("Bank transfer inited");
|
||||
headLineLabel.setText("Bank transfer initialised");
|
||||
infoLabel.setText("Check your bank account and continue \n" + "when you have received the money.");
|
||||
receivedFiatButton.setDisable(false);
|
||||
}
|
||||
|
@ -118,7 +118,7 @@
|
||||
</Label>
|
||||
|
||||
<!-- row 7 -->
|
||||
<Button fx:id="bankTransferInitedButton" defaultButton="true" onAction="#bankTransferInited" disable="true" text="Bank transfer inited"
|
||||
<Button fx:id="bankTransferInitedButton" defaultButton="true" onAction="#bankTransferInited" disable="true" text="Bank transfer initialised "
|
||||
GridPane.columnIndex="1"
|
||||
GridPane.rowIndex="7"/>
|
||||
|
||||
|
@ -139,7 +139,7 @@ public class SettingsController implements Initializable, ChildController, Navig
|
||||
if (settings.getAcceptedArbitrators().isEmpty())
|
||||
{
|
||||
String pubKeyAsHex = Utils.bytesToHexString(new ECKey().getPubKey());
|
||||
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey());
|
||||
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey());
|
||||
List<Locale> languages = new ArrayList<>();
|
||||
languages.add(LanguageUtil.getDefaultLanguageLocale());
|
||||
List<Arbitrator.METHOD> arbitrationMethods = new ArrayList<>();
|
||||
@ -168,13 +168,7 @@ public class SettingsController implements Initializable, ChildController, Navig
|
||||
settings.addAcceptedArbitrator(arbitrator);
|
||||
storage.write(settings.getClass().getName(), settings);
|
||||
|
||||
try
|
||||
{
|
||||
messageFacade.addArbitrator(arbitrator);
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
||||
}
|
||||
messageFacade.addArbitrator(arbitrator);
|
||||
}
|
||||
}
|
||||
|
||||
|
9
src/main/java/io/bitsquare/msg/BootstrapListener.java
Normal file
9
src/main/java/io/bitsquare/msg/BootstrapListener.java
Normal file
@ -0,0 +1,9 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
public interface BootstrapListener
|
||||
{
|
||||
public void onCompleted();
|
||||
|
||||
public void onFailed(Throwable throwable);
|
||||
|
||||
}
|
329
src/main/java/io/bitsquare/msg/BootstrappedPeerFactory.java
Normal file
329
src/main/java/io/bitsquare/msg/BootstrappedPeerFactory.java
Normal file
@ -0,0 +1,329 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.security.KeyPair;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import net.tomp2p.connection.Ports;
|
||||
import net.tomp2p.dht.PeerBuilderDHT;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.dht.StorageLayer;
|
||||
import net.tomp2p.futures.BaseFuture;
|
||||
import net.tomp2p.futures.BaseFutureListener;
|
||||
import net.tomp2p.futures.FutureBootstrap;
|
||||
import net.tomp2p.futures.FutureDiscover;
|
||||
import net.tomp2p.nat.FutureNAT;
|
||||
import net.tomp2p.nat.PeerBuilderNAT;
|
||||
import net.tomp2p.nat.PeerNAT;
|
||||
import net.tomp2p.p2p.Peer;
|
||||
import net.tomp2p.p2p.PeerBuilder;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.peers.PeerMapChangeListener;
|
||||
import net.tomp2p.peers.PeerStatatistic;
|
||||
import net.tomp2p.relay.DistributedRelay;
|
||||
import net.tomp2p.relay.FutureRelay;
|
||||
import net.tomp2p.storage.Storage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Creates a DHT peer and bootstrap to a seed node
|
||||
*/
|
||||
@Immutable
|
||||
public class BootstrappedPeerFactory
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(BootstrappedPeerFactory.class);
|
||||
|
||||
private final KeyPair keyPair;
|
||||
private final Storage storage;
|
||||
private final SeedNodeAddress seedNodeAddress;
|
||||
|
||||
private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* We use localhost as default seed node
|
||||
*/
|
||||
public BootstrappedPeerFactory(@NotNull KeyPair keyPair, @NotNull Storage storage)
|
||||
{
|
||||
this(keyPair, storage, new SeedNodeAddress(SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST));
|
||||
}
|
||||
|
||||
public BootstrappedPeerFactory(@NotNull KeyPair keyPair, @NotNull Storage storage, @NotNull SeedNodeAddress seedNodeAddress)
|
||||
{
|
||||
this.keyPair = keyPair;
|
||||
this.storage = storage;
|
||||
this.seedNodeAddress = seedNodeAddress;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public ListenableFuture<PeerDHT> start()
|
||||
{
|
||||
try
|
||||
{
|
||||
int randomPort = new Ports().tcpPort();
|
||||
Peer peer = new PeerBuilder(keyPair).ports(randomPort).start();
|
||||
PeerDHT peerDHT = new PeerBuilderDHT(peer).storageLayer(new StorageLayer(storage)).start();
|
||||
|
||||
peer.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void peerInserted(PeerAddress peerAddress, boolean verified)
|
||||
{
|
||||
log.debug("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified);
|
||||
|
||||
/* NavigableSet<PeerAddress> closePeers = peer.peerBean().peerMap().closePeers(2);
|
||||
log.debug("closePeers size = " + closePeers.size());
|
||||
log.debug("closePeers = " + closePeers);
|
||||
closePeers.forEach(e -> log.debug("forEach: " + e.toString()));
|
||||
|
||||
List<PeerAddress> allPeers = peer.peerBean().peerMap().all();
|
||||
log.debug("allPeers size = " + allPeers.size());
|
||||
log.debug("allPeers = " + allPeers);
|
||||
allPeers.forEach(e -> log.debug("forEach: " + e.toString()));*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void peerRemoved(PeerAddress peerAddress, PeerStatatistic peerStatistics)
|
||||
{
|
||||
log.debug("Peer removed: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void peerUpdated(PeerAddress peerAddress, PeerStatatistic peerStatistics)
|
||||
{
|
||||
// log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
||||
}
|
||||
});
|
||||
|
||||
discover(peerDHT);
|
||||
} catch (IOException e)
|
||||
{
|
||||
log.error("Exception: " + e);
|
||||
settableFuture.setException(e);
|
||||
}
|
||||
|
||||
return settableFuture;
|
||||
}
|
||||
|
||||
private void discover(PeerDHT peerDHT)
|
||||
{
|
||||
try
|
||||
{
|
||||
PeerAddress bootstrapAddress = new PeerAddress(Number160.createHash(seedNodeAddress.getId()),
|
||||
InetAddress.getByName(seedNodeAddress.getIp()),
|
||||
seedNodeAddress.getPort(),
|
||||
seedNodeAddress.getPort());
|
||||
// Check if peer is reachable from outside
|
||||
FutureDiscover futureDiscover = peerDHT.peer().discover().peerAddress(bootstrapAddress).start();
|
||||
futureDiscover.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
// We are not behind a NAT and reachable to other peers
|
||||
log.debug("We are not behind a NAT and reachable to other peers: My address visible to the outside is " + futureDiscover.peerAddress());
|
||||
getBootstrapPeerMap();
|
||||
settableFuture.set(peerDHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Discover has failed. Reason: " + futureDiscover.failedReason());
|
||||
log.warn("We are probably behind a NAT and not reachable to other peers. We try port forwarding as next step.");
|
||||
|
||||
startPortForwarding(peerDHT, bootstrapAddress, futureDiscover);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("Exception at discover: " + t);
|
||||
settableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
} catch (IOException e)
|
||||
{
|
||||
log.error("Exception: " + e);
|
||||
settableFuture.setException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void startPortForwarding(PeerDHT peerDHT, PeerAddress bootstrapAddress, FutureDiscover futureDiscover)
|
||||
{
|
||||
// Assume we are behind a NAT device
|
||||
PeerNAT nodeBehindNat = new PeerBuilderNAT(peerDHT.peer()).start();
|
||||
|
||||
// Try to set up port forwarding with UPNP and NATPMP if peer is not reachable
|
||||
FutureNAT futureNAT = nodeBehindNat.startSetupPortforwarding(futureDiscover);
|
||||
futureNAT.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
// Port forwarding has succeed
|
||||
log.debug("Port forwarding was successful. My address visible to the outside is " + futureNAT.peerAddress());
|
||||
getBootstrapPeerMap();
|
||||
settableFuture.set(peerDHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.warn("Port forwarding has failed. Reason: " + futureNAT.failedReason());
|
||||
log.warn("We try to use a relay as next step.");
|
||||
|
||||
prepareRelay(peerDHT, nodeBehindNat, bootstrapAddress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("Exception at port forwarding: " + t);
|
||||
settableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void prepareRelay(PeerDHT peerDHT, PeerNAT nodeBehindNat, PeerAddress bootstrapAddress)
|
||||
{
|
||||
// Last resort: we try to use other peers as relays
|
||||
|
||||
// The firewalled flags have to be set, so that other peers don’t add the unreachable peer to their peer maps.
|
||||
Peer peer = peerDHT.peer();
|
||||
PeerAddress serverPeerAddress = peer.peerBean().serverPeerAddress();
|
||||
serverPeerAddress = serverPeerAddress.changeFirewalledTCP(true).changeFirewalledUDP(true);
|
||||
peer.peerBean().serverPeerAddress(serverPeerAddress);
|
||||
|
||||
// Find neighbors
|
||||
FutureBootstrap futureBootstrap = peer.bootstrap().peerAddress(bootstrapAddress).start();
|
||||
futureBootstrap.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.debug("Bootstrap was successful. bootstrapTo = " + futureBootstrap.bootstrapTo());
|
||||
|
||||
setupRelay(peerDHT, nodeBehindNat, bootstrapAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Bootstrap failed. Reason:" + futureBootstrap.failedReason());
|
||||
settableFuture.setException(new Exception(futureBootstrap.failedReason()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("Exception at bootstrap: " + t);
|
||||
settableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupRelay(PeerDHT peerDHT, PeerNAT nodeBehindNat, PeerAddress bootstrapAddress)
|
||||
{
|
||||
FutureRelay futureRelay = new FutureRelay();
|
||||
futureRelay.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.debug("Start setup relay was successful.");
|
||||
futureRelay.relays().forEach(e -> log.debug("remotePeer = " + e.remotePeer()));
|
||||
|
||||
findNeighbors2(peerDHT, nodeBehindNat, bootstrapAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("setupRelay failed. Reason: " + futureRelay.failedReason());
|
||||
log.error("Bootstrap failed. We give up...");
|
||||
settableFuture.setException(new Exception(futureRelay.failedReason()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("Exception at setup relay: " + t);
|
||||
}
|
||||
});
|
||||
|
||||
DistributedRelay distributedRelay = nodeBehindNat.startSetupRelay(futureRelay);
|
||||
distributedRelay.addRelayListener((distributedRelay1, peerConnection) -> {
|
||||
log.error("startSetupRelay Failed");
|
||||
settableFuture.setException(new Exception("startSetupRelay Failed"));
|
||||
});
|
||||
}
|
||||
|
||||
private void findNeighbors2(PeerDHT peerDHT, PeerNAT nodeBehindNat, PeerAddress bootstrapAddress)
|
||||
{
|
||||
// find neighbors again
|
||||
FutureBootstrap futureBootstrap2 = peerDHT.peer().bootstrap().peerAddress(bootstrapAddress).start();
|
||||
futureBootstrap2.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.debug("Final bootstrap was successful. bootstrapTo = " + futureBootstrap2.bootstrapTo());
|
||||
getBootstrapPeerMap();
|
||||
settableFuture.set(peerDHT);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Bootstrap 2 failed. Reason:" + futureBootstrap2.failedReason());
|
||||
log.error("We give up...");
|
||||
settableFuture.setException(new Exception(futureBootstrap2.failedReason()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("Exception at bootstrap 2: " + t);
|
||||
settableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO we want to get a list of connected nodes form the seed node and save them locally for future bootstrapping
|
||||
// The seed node should only be used if no other known peers are available
|
||||
private void getBootstrapPeerMap()
|
||||
{
|
||||
log.debug("getBootstrapPeerMap");
|
||||
|
||||
/* NavigableSet<PeerAddress> closePeers = peer.peerBean().peerMap().closePeers(2);
|
||||
log.debug("closePeers size = " + closePeers.size());
|
||||
log.debug("closePeers = " + closePeers);
|
||||
closePeers.forEach(e -> log.debug("forEach: " + e.toString()));
|
||||
|
||||
List<PeerAddress> allPeers = peer.peerBean().peerMap().all();
|
||||
log.debug("allPeers size = " + allPeers.size());
|
||||
log.debug("allPeers = " + allPeers);
|
||||
allPeers.forEach(e -> log.debug("forEach: " + e.toString())); */
|
||||
}
|
||||
|
||||
|
||||
}
|
8
src/main/java/io/bitsquare/msg/MessageBroker.java
Normal file
8
src/main/java/io/bitsquare/msg/MessageBroker.java
Normal file
@ -0,0 +1,8 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
|
||||
public interface MessageBroker
|
||||
{
|
||||
void handleMessage(Object message, PeerAddress peerAddress);
|
||||
}
|
@ -1,66 +1,65 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import io.bitsquare.BitSquare;
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.inject.name.Named;
|
||||
import io.bitsquare.msg.listeners.*;
|
||||
import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import io.bitsquare.util.StorageDirectory;
|
||||
import io.bitsquare.user.User;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Currency;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import net.tomp2p.connection.Bindings;
|
||||
import net.tomp2p.connection.PeerConnection;
|
||||
import net.tomp2p.futures.*;
|
||||
import net.tomp2p.p2p.Peer;
|
||||
import net.tomp2p.p2p.PeerMaker;
|
||||
import net.tomp2p.dht.FutureGet;
|
||||
import net.tomp2p.dht.FuturePut;
|
||||
import net.tomp2p.dht.FutureRemove;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.BaseFuture;
|
||||
import net.tomp2p.futures.BaseFutureAdapter;
|
||||
import net.tomp2p.futures.BaseFutureListener;
|
||||
import net.tomp2p.futures.FutureDirect;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.storage.Data;
|
||||
import net.tomp2p.storage.StorageDisk;
|
||||
import net.tomp2p.utils.Utils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* That facade delivers messaging functionality from the TomP2P library
|
||||
* That facade delivers direct messaging and DHT functionality from the TomP2P library
|
||||
* It is the translating domain specific functionality to the messaging layer.
|
||||
* The TomP2P library codebase shall not be used outside that facade.
|
||||
* That way a change of the library will only affect that class.
|
||||
* That way we limit the dependency of the TomP2P library only to that class (and it's sub components).
|
||||
* <p>
|
||||
* TODO: improve callbacks that Platform.runLater is not necessary. We call usually that methods form teh UI thread.
|
||||
*/
|
||||
@SuppressWarnings({"EmptyMethod", "ConstantConditions"})
|
||||
public class MessageFacade
|
||||
public class MessageFacade implements MessageBroker
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
|
||||
// private static final String PING = "ping";
|
||||
// private static final String PONG = "pong";
|
||||
private static final int MASTER_PEER_PORT = 5001;
|
||||
private static final String ARBITRATORS_ROOT = "ArbitratorsRoot";
|
||||
|
||||
private final P2PNode p2pNode;
|
||||
|
||||
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
|
||||
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
|
||||
|
||||
private final List<IncomingTradeMessageListener> incomingTradeMessageListeners = new ArrayList<>();
|
||||
|
||||
|
||||
// private final List<PingPeerListener> pingPeerListeners = new ArrayList<>();
|
||||
private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
|
||||
private Peer myPeer;
|
||||
|
||||
private KeyPair keyPair;
|
||||
private Long lastTimeStamp = -3L;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Inject
|
||||
public MessageFacade()
|
||||
public MessageFacade(User user, @Named("useDiskStorage") Boolean useDiskStorage, @Named("defaultSeedNode") SeedNodeAddress.StaticSeedNodeAddresses defaultStaticSeedNodeAddresses)
|
||||
{
|
||||
this.p2pNode = new P2PNode(user.getMessageKeyPair(), useDiskStorage, defaultStaticSeedNodeAddresses, this);
|
||||
}
|
||||
|
||||
|
||||
@ -68,168 +67,190 @@ public class MessageFacade
|
||||
// Public Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void init()
|
||||
public void init(BootstrapListener bootstrapListener)
|
||||
{
|
||||
int port = Bindings.MAX_PORT - Math.abs(new Random().nextInt()) % (Bindings.MAX_PORT - Bindings.MIN_DYN_PORT);
|
||||
if (BitSquare.getAppName().contains("taker"))
|
||||
p2pNode.start(new FutureCallback<PeerDHT>()
|
||||
{
|
||||
port = 4501;
|
||||
}
|
||||
else if (BitSquare.getAppName().contains("offerer"))
|
||||
{
|
||||
port = 4500;
|
||||
}
|
||||
@Override
|
||||
public void onSuccess(@Nullable PeerDHT result)
|
||||
{
|
||||
log.debug("p2pNode.start success result = " + result);
|
||||
Platform.runLater(() -> bootstrapListener.onCompleted());
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
createMyPeerInstance(port);
|
||||
// setupStorage();
|
||||
//TODO save periodically or get informed if network address changes
|
||||
saveMyAddressToDHT();
|
||||
setupReplyHandler();
|
||||
} catch (IOException e)
|
||||
{
|
||||
shutDown();
|
||||
log.error("Error at init myPeerInstance" + e.getMessage());
|
||||
}
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.error(t.toString());
|
||||
Platform.runLater(() -> bootstrapListener.onFailed(t));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void shutDown()
|
||||
{
|
||||
if (myPeer != null)
|
||||
if (p2pNode != null)
|
||||
p2pNode.shutDown();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Find peer address by publicKey
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public void getPeerAddress(PublicKey publicKey, GetPeerAddressListener listener)
|
||||
{
|
||||
final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded());
|
||||
try
|
||||
{
|
||||
myPeer.shutdown();
|
||||
FutureGet futureGet = p2pNode.getDomainProtectedData(locationKey, publicKey);
|
||||
|
||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
if (baseFuture.isSuccess() && futureGet.data() != null)
|
||||
{
|
||||
final PeerAddress peerAddress = (PeerAddress) futureGet.data().object();
|
||||
Platform.runLater(() -> listener.onResult(peerAddress));
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.runLater(() -> listener.onFailed());
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Find peer address
|
||||
// Offer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
|
||||
public void addOffer(Offer offer)
|
||||
{
|
||||
final Number160 location = Number160.createHash(pubKeyAsHex);
|
||||
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
|
||||
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||
try
|
||||
{
|
||||
final Data data = new Data(offer);
|
||||
// the offer is default 30 days valid
|
||||
int defaultOfferTTL = 30 * 24 * 60 * 60 * 1000;
|
||||
data.ttlSeconds(defaultOfferTTL);
|
||||
|
||||
FuturePut futurePut = p2pNode.addProtectedData(locationKey, data);
|
||||
futurePut.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> {
|
||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(data, future.isSuccess()));
|
||||
setDirty(locationKey);
|
||||
});
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.trace("Add offer to DHT was successful. Stored data: [key: " + locationKey + ", value: " + data + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Add offer to DHT failed. Reason: " + future.failedReason());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error(t.toString());
|
||||
}
|
||||
});
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void removeOffer(Offer offer)
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||
try
|
||||
{
|
||||
final Data data = new Data(offer);
|
||||
FutureRemove futureRemove = p2pNode.removeFromDataMap(locationKey, data);
|
||||
futureRemove.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> {
|
||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferRemoved(data, future.isSuccess()));
|
||||
setDirty(locationKey);
|
||||
});
|
||||
if (future.isSuccess())
|
||||
{
|
||||
log.trace("Remove offer from DHT was successful. Stored data: [key: " + locationKey + ", value: " + data + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Remove offer from DHT failed. Reason: " + future.failedReason());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error(t.toString());
|
||||
}
|
||||
});
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void getOffers(String currencyCode)
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(currencyCode);
|
||||
FutureGet futureGet = p2pNode.getDataMap(locationKey);
|
||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
|
||||
Platform.runLater(() -> orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(futureGet.dataMap(), baseFuture.isSuccess())));
|
||||
if (baseFuture.isSuccess())
|
||||
{
|
||||
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
|
||||
Platform.runLater(() -> listener.onResult(peerAddress));
|
||||
log.trace("Get offers from DHT was successful. Stored data: [key: " + locationKey + ", values: " + futureGet.dataMap() + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.runLater(() -> listener.onFailed());
|
||||
log.error("Get offers from DHT failed with reason:" + baseFuture.failedReason());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Publish offer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addOffer(Offer offer) throws IOException
|
||||
{
|
||||
log.trace("addOffer");
|
||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||
final Number160 contentKey = Number160.createHash(offer.getId());
|
||||
final Data offerData = new Data(offer);
|
||||
//offerData.setTTLSeconds(5);
|
||||
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, offerData).start();
|
||||
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
|
||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> onOfferAdded(offerData, future.isSuccess(), locationKey));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onOfferAdded(Data offerData, boolean success, Number160 locationKey)
|
||||
{
|
||||
log.trace("onOfferAdded");
|
||||
setDirty(locationKey);
|
||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(offerData, success));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Get offers
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void getOffers(String currency)
|
||||
{
|
||||
log.trace("getOffers");
|
||||
final Number160 locationKey = Number160.createHash(currency);
|
||||
final FutureDHT getOffersFuture = myPeer.get(locationKey).setAll().start();
|
||||
getOffersFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
final Map<Number160, Data> dataMap = getOffersFuture.getDataMap();
|
||||
Platform.runLater(() -> onOffersReceived(dataMap, future.isSuccess()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
|
||||
{
|
||||
log.trace("onOffersReceived");
|
||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(dataMap, success));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Remove offer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void removeOffer(Offer offer)
|
||||
{
|
||||
log.trace("removeOffer");
|
||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||
Number160 contentKey = Number160.createHash(offer.getId());
|
||||
log.debug("removeOffer");
|
||||
FutureDHT removeFuture = myPeer.remove(locationKey).setReturnResults().setContentKey(contentKey).start();
|
||||
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> onOfferRemoved(removeFuture.getData(), future.isSuccess(), locationKey));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onOfferRemoved(Data data, boolean success, Number160 locationKey)
|
||||
{
|
||||
log.trace("onOfferRemoved");
|
||||
setDirty(locationKey);
|
||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferRemoved(data, success));
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Trade process
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, OutgoingTradeMessageListener listener)
|
||||
{
|
||||
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
|
||||
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
|
||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
FutureDirect futureDirect = p2pNode.sendData(peerAddress, tradeMessage);
|
||||
futureDirect.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (sendFuture.isSuccess())
|
||||
if (futureDirect.isSuccess())
|
||||
{
|
||||
Platform.runLater(() -> listener.onResult());
|
||||
}
|
||||
@ -238,316 +259,109 @@ public class MessageFacade
|
||||
Platform.runLater(() -> listener.onFailed());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> listener.onFailed());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reputation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void setupReputationRoot() throws IOException
|
||||
{
|
||||
String pubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(getPubKey()); // out message ID
|
||||
final Number160 locationKey = Number160.createHash("REPUTATION_" + pubKeyAsHex); // out reputation root storage location
|
||||
final Number160 contentKey = Utils.makeSHAHash(getPubKey().getEncoded()); // my pubKey -> i may only put in 1 reputation
|
||||
final Data reputationData = new Data(Number160.ZERO).setProtectedEntry().setPublicKey(getPubKey()); // at registration time we add a null value as data
|
||||
// we use a pubkey where we provable cannot own the private key.
|
||||
// the domain key must be verifiable by peers to be sure the reputation root was net deleted by the owner.
|
||||
// so we use the locationKey as it already meets our requirements (verifiable and impossible to create a private key out of it)
|
||||
myPeer.put(locationKey).setData(contentKey, reputationData).setDomainKey(locationKey).setProtectDomain().start();
|
||||
}
|
||||
|
||||
public void addReputation(String pubKeyAsHex) throws IOException
|
||||
{
|
||||
final Number160 locationKey = Number160.createHash("REPUTATION_" + pubKeyAsHex); // reputation root storage location ot the peer
|
||||
final Number160 contentKey = Utils.makeSHAHash(getPubKey().getEncoded()); // my pubKey -> i may only put in 1 reputation, I may update it later. eg. counter for 5 trades...
|
||||
final Data reputationData = new Data("TODO: some reputation data..., content signed and sig attached").setProtectedEntry().setPublicKey(getPubKey());
|
||||
myPeer.put(locationKey).setData(contentKey, reputationData).start();
|
||||
}
|
||||
|
||||
// At any offer or take offer fee payment the trader add the tx id and the pubKey and the signature of that tx to that entry.
|
||||
// That way he can prove with the signature that he is the payer of the offer fee.
|
||||
// It does not assure that the trade was really executed, but we can protect the traders privacy that way.
|
||||
// If we use the trade, we would link all trades together and would reveal the whole trading history.
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void addOfferFeePaymentToReputation(String txId, String pubKeyOfFeePayment) throws IOException
|
||||
{
|
||||
String pubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(getPubKey()); // out message ID
|
||||
final Number160 locationKey = Number160.createHash("REPUTATION_" + pubKeyAsHex); // reputation root storage location ot the peer
|
||||
final Number160 contentKey = Utils.makeSHAHash(getPubKey().getEncoded()); // my pubKey -> i may only put in 1 reputation, I may update it later. eg. counter for 5 trades...
|
||||
final Data reputationData = new Data("TODO: tx, btc_pubKey, sig(tx), content signed and sig attached").setProtectedEntry().setPublicKey(getPubKey());
|
||||
myPeer.put(locationKey).setData(contentKey, reputationData).start();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Arbitrators
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addArbitrator(Arbitrator arbitrator) throws IOException
|
||||
public void addArbitrator(Arbitrator arbitrator)
|
||||
{
|
||||
Number160 locationKey = Number160.createHash("Arbitrators");
|
||||
final Number160 contentKey = Number160.createHash(arbitrator.getId());
|
||||
final Data arbitratorData = new Data(arbitrator);
|
||||
//offerData.setTTLSeconds(5);
|
||||
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, arbitratorData).start();
|
||||
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
|
||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Platform.runLater(() -> onArbitratorAdded(arbitratorData, future.isSuccess(), locationKey));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
private void onArbitratorAdded(Data arbitratorData, boolean success, Number160 locationKey)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
public void getArbitrators(Locale languageLocale)
|
||||
{
|
||||
final Number160 locationKey = Number160.createHash("Arbitrators");
|
||||
final FutureDHT getArbitratorsFuture = myPeer.get(locationKey).setAll().start();
|
||||
getArbitratorsFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
final Map<Number160, Data> dataMap = getArbitratorsFuture.getDataMap();
|
||||
Platform.runLater(() -> onArbitratorsReceived(dataMap, future.isSuccess()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success)
|
||||
{
|
||||
for (ArbitratorListener arbitratorListener : arbitratorListeners)
|
||||
{
|
||||
arbitratorListener.onArbitratorsReceived(dataMap, success);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Check dirty flag for a location key
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// TODO just temp...
|
||||
public BooleanProperty getIsDirtyProperty()
|
||||
{
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
public void getDirtyFlag(Currency currency)
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(currency.getCurrencyCode());
|
||||
FutureDHT getFuture = myPeer.get(getDirtyLocationKey(locationKey)).start();
|
||||
getFuture.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Data data = getFuture.getData();
|
||||
if (data != null)
|
||||
{
|
||||
Object object = data.getObject();
|
||||
if (object instanceof Long)
|
||||
{
|
||||
Platform.runLater(() -> onGetDirtyFlag((Long) object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
System.out.println("getFuture exceptionCaught " + System.currentTimeMillis());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onGetDirtyFlag(long timeStamp)
|
||||
{
|
||||
// TODO don't get updates at first execute....
|
||||
if (lastTimeStamp != timeStamp)
|
||||
{
|
||||
isDirty.setValue(!isDirty.get());
|
||||
}
|
||||
if (lastTimeStamp > 0)
|
||||
{
|
||||
lastTimeStamp = timeStamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastTimeStamp++;
|
||||
}
|
||||
}
|
||||
|
||||
private Number160 getDirtyLocationKey(Number160 locationKey)
|
||||
{
|
||||
return Number160.createHash(locationKey + "Dirty");
|
||||
}
|
||||
|
||||
private void setDirty(Number160 locationKey)
|
||||
{
|
||||
// we don't want to get an update from dirty for own changes, so update the lastTimeStamp to omit a change trigger
|
||||
lastTimeStamp = System.currentTimeMillis();
|
||||
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
|
||||
try
|
||||
{
|
||||
FutureDHT putFuture = myPeer.put(getDirtyLocationKey(locationKey)).setData(new Data(lastTimeStamp)).start();
|
||||
putFuture.addListener(new BaseFutureListener<BaseFuture>()
|
||||
final Data arbitratorData = new Data(arbitrator);
|
||||
|
||||
FuturePut addFuture = p2pNode.addProtectedData(locationKey, arbitratorData);
|
||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
//System.out.println("operationComplete");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
System.err.println("exceptionCaught");
|
||||
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorAdded(arbitratorData, addFuture.isSuccess())));
|
||||
if (addFuture.isSuccess())
|
||||
{
|
||||
log.trace("Add arbitrator to DHT was successful. Stored data: [key: " + locationKey + ", values: " + arbitratorData + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Add arbitrator to DHT failed with reason:" + addFuture.failedReason());
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (IOException e)
|
||||
{
|
||||
log.warn("Error at writing dirty flag (timeStamp) " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Send message
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* public boolean sendMessage(Object message)
|
||||
{
|
||||
boolean result = false;
|
||||
if (otherPeerAddress != null)
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e)
|
||||
{
|
||||
if (peerConnection != null)
|
||||
peerConnection.close();
|
||||
|
||||
peerConnection = myPeer.createPeerConnection(otherPeerAddress, 20);
|
||||
if (!peerConnection.isClosed())
|
||||
{
|
||||
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(message).start();
|
||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
if (sendFuture.isSuccess())
|
||||
{
|
||||
final Object object = sendFuture.getObject();
|
||||
Platform.runLater(() -> onResponseFromSend(object));
|
||||
}
|
||||
else
|
||||
{
|
||||
Platform.runLater(() -> onSendFailed());
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
result = true;
|
||||
}
|
||||
e.printStackTrace();
|
||||
}
|
||||
return result;
|
||||
} */
|
||||
/*
|
||||
private void onResponseFromSend(Object response)
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onResponseFromSend(response);
|
||||
}
|
||||
|
||||
private void onSendFailed()
|
||||
public void removeArbitrator(Arbitrator arbitrator) throws IOException, ClassNotFoundException
|
||||
{
|
||||
for (MessageListener messageListener : messageListeners)
|
||||
messageListener.onSendFailed();
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Ping peer
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
//TODO not working anymore...
|
||||
/* public void pingPeer(String publicKeyAsHex)
|
||||
{
|
||||
Number160 location = Number160.createHash(publicKeyAsHex);
|
||||
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
|
||||
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
|
||||
final Data arbitratorData = new Data(arbitrator);
|
||||
FutureRemove removeFuture = p2pNode.removeFromDataMap(locationKey, arbitratorData);
|
||||
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
final Data data = getPeerAddressFuture.getData();
|
||||
if (data != null && data.getObject() instanceof PeerAddress)
|
||||
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorRemoved(arbitratorData, removeFuture.isSuccess())));
|
||||
if (removeFuture.isSuccess())
|
||||
{
|
||||
final PeerAddress peerAddress = (PeerAddress) data.getObject();
|
||||
Platform.runLater(() -> onAddressFoundPingPeer(peerAddress));
|
||||
log.trace("Remove arbitrator from DHT was successful. Stored data: [key: " + locationKey + ", values: " + arbitratorData + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Remove arbitrators from DHT failed with reason:" + removeFuture.failedReason());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onAddressFoundPingPeer(PeerAddress peerAddress)
|
||||
public void getArbitrators(Locale languageLocale)
|
||||
{
|
||||
try
|
||||
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
|
||||
FutureGet futureGet = p2pNode.getDataMap(locationKey);
|
||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
|
||||
if (!peerConnection.isClosed())
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(PING).start();
|
||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||
{
|
||||
if (sendFuture.isSuccess())
|
||||
{
|
||||
final String pong = (String) sendFuture.getObject();
|
||||
Platform.runLater(() -> onResponseFromPing(PONG.equals(pong)));
|
||||
}
|
||||
else
|
||||
{
|
||||
peerConnection.close();
|
||||
Platform.runLater(() -> onResponseFromPing(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorsReceived(futureGet.dataMap(), baseFuture.isSuccess())));
|
||||
if (baseFuture.isSuccess())
|
||||
{
|
||||
log.trace("Get arbitrators from DHT was successful. Stored data: [key: " + locationKey + ", values: " + futureGet.dataMap() + "]");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Get arbitrators from DHT failed with reason:" + baseFuture.failedReason());
|
||||
}
|
||||
}
|
||||
} catch (Exception e)
|
||||
{
|
||||
// ClosedChannelException can happen, check out if there is a better way to ping a myPeerInstance for online status
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onResponseFromPing(boolean success)
|
||||
{
|
||||
for (PingPeerListener pingPeerListener : pingPeerListeners)
|
||||
pingPeerListener.onPingPeerResult(success);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Event Listeners
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addMessageListener(OrderBookListener listener)
|
||||
public void addOrderBookListener(OrderBookListener listener)
|
||||
{
|
||||
orderBookListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeMessageListener(OrderBookListener listener)
|
||||
public void removeOrderBookListener(OrderBookListener listener)
|
||||
{
|
||||
orderBookListeners.remove(listener);
|
||||
}
|
||||
@ -584,12 +398,99 @@ public class MessageFacade
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Getters
|
||||
// Check dirty flag for a location key
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public PublicKey getPubKey()
|
||||
// TODO just temp...
|
||||
public BooleanProperty getIsDirtyProperty()
|
||||
{
|
||||
return keyPair.getPublic();
|
||||
return isDirty;
|
||||
}
|
||||
|
||||
public void getDirtyFlag(Currency currency)
|
||||
{
|
||||
Number160 locationKey = Number160.createHash(currency.getCurrencyCode());
|
||||
try
|
||||
{
|
||||
FutureGet getFuture = p2pNode.getData(getDirtyLocationKey(locationKey));
|
||||
getFuture.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
Data data = getFuture.data();
|
||||
if (data != null)
|
||||
{
|
||||
Object object = data.object();
|
||||
if (object instanceof Long)
|
||||
{
|
||||
Platform.runLater(() -> onGetDirtyFlag((Long) object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("getFuture exceptionCaught " + t.toString());
|
||||
}
|
||||
});
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private Long lastTimeStamp = -3L;
|
||||
private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
|
||||
|
||||
private void onGetDirtyFlag(long timeStamp)
|
||||
{
|
||||
// TODO don't get updates at first execute....
|
||||
if (lastTimeStamp != timeStamp)
|
||||
{
|
||||
isDirty.setValue(!isDirty.get());
|
||||
}
|
||||
if (lastTimeStamp > 0)
|
||||
{
|
||||
lastTimeStamp = timeStamp;
|
||||
}
|
||||
else
|
||||
{
|
||||
lastTimeStamp++;
|
||||
}
|
||||
}
|
||||
|
||||
private void setDirty(Number160 locationKey)
|
||||
{
|
||||
// we don't want to get an update from dirty for own changes, so update the lastTimeStamp to omit a change trigger
|
||||
lastTimeStamp = System.currentTimeMillis();
|
||||
try
|
||||
{
|
||||
FuturePut putFuture = p2pNode.putData(getDirtyLocationKey(locationKey), new Data(lastTimeStamp));
|
||||
putFuture.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
// log.trace("operationComplete");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.warn("Error at writing dirty flag (timeStamp) " + t.toString());
|
||||
}
|
||||
});
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
log.warn("Error at writing dirty flag (timeStamp) " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private Number160 getDirtyLocationKey(Number160 locationKey)
|
||||
{
|
||||
return Number160.createHash(locationKey + "Dirty");
|
||||
}
|
||||
|
||||
|
||||
@ -597,89 +498,13 @@ public class MessageFacade
|
||||
// Incoming message handler
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void onMessage(Object request, PeerAddress sender)
|
||||
@Override
|
||||
public void handleMessage(Object message, PeerAddress peerAddress)
|
||||
{
|
||||
if (request instanceof TradeMessage)
|
||||
if (message instanceof TradeMessage)
|
||||
{
|
||||
incomingTradeMessageListeners.stream().forEach(e -> e.onMessage((TradeMessage) request, sender));
|
||||
}
|
||||
/* else
|
||||
{
|
||||
for (OrderBookListener orderBookListener : orderBookListeners)
|
||||
orderBookListener.onMessage(request);
|
||||
} */
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void createMyPeerInstance(int port) throws IOException
|
||||
{
|
||||
keyPair = DSAKeyUtil.getKeyPair();
|
||||
myPeer = new PeerMaker(keyPair).setPorts(port).makeAndListen();
|
||||
final FutureBootstrap futureBootstrap = myPeer.bootstrap().setBroadcast().setPorts(MASTER_PEER_PORT).start();
|
||||
futureBootstrap.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (futureBootstrap.getBootstrapTo() != null)
|
||||
{
|
||||
PeerAddress masterPeerAddress = futureBootstrap.getBootstrapTo().iterator().next();
|
||||
final FutureDiscover futureDiscover = myPeer.discover().setPeerAddress(masterPeerAddress).start();
|
||||
futureDiscover.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
//System.out.println("operationComplete");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
System.err.println("exceptionCaught");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupReplyHandler()
|
||||
{
|
||||
myPeer.setObjectDataReply((sender, request) -> {
|
||||
if (!sender.equals(myPeer.getPeerAddress()))
|
||||
{
|
||||
Platform.runLater(() -> onMessage(request, sender));
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("Received msg from myself. That should never happen.");
|
||||
}
|
||||
//noinspection ReturnOfNull
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupStorage()
|
||||
{
|
||||
try
|
||||
{
|
||||
myPeer.getPeerBean().setStorage(new StorageDisk(StorageDirectory.getStorageDirectory().getCanonicalPath() + "/" + BitSquare.getAppName() + "_tomP2P"));
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error("####################");
|
||||
Platform.runLater(() -> incomingTradeMessageListeners.stream().forEach(e -> e.onMessage((TradeMessage) message, peerAddress)));
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMyAddressToDHT() throws IOException
|
||||
{
|
||||
Number160 location = Number160.createHash(DSAKeyUtil.getHexStringFromPublicKey(getPubKey()));
|
||||
//log.debug("saveMyAddressToDHT location "+location.toString());
|
||||
myPeer.put(location).setData(new Data(myPeer.getPeerAddress())).start();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
385
src/main/java/io/bitsquare/msg/P2PNode.java
Normal file
385
src/main/java/io/bitsquare/msg/P2PNode.java
Normal file
@ -0,0 +1,385 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import io.bitsquare.BitSquare;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import io.bitsquare.util.StorageDirectory;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import javax.annotation.Nullable;
|
||||
import net.tomp2p.connection.DSASignatureFactory;
|
||||
import net.tomp2p.dht.*;
|
||||
import net.tomp2p.futures.BaseFuture;
|
||||
import net.tomp2p.futures.BaseFutureListener;
|
||||
import net.tomp2p.futures.FutureDirect;
|
||||
import net.tomp2p.futures.FuturePeerConnection;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.storage.Data;
|
||||
import net.tomp2p.storage.Storage;
|
||||
import net.tomp2p.storage.StorageDisk;
|
||||
import net.tomp2p.utils.Utils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* The fully bootstrapped P2PNode which is responsible himself for his availability in the messaging system. It saves for instance the IP address periodically.
|
||||
* This class is offering generic functionality of TomP2P needed for Bitsquare, like data and domain protection.
|
||||
* It does not handle any domain aspects of Bitsquare.
|
||||
*/
|
||||
public class P2PNode
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(P2PNode.class);
|
||||
|
||||
private Thread bootstrapToLocalhostThread;
|
||||
private Thread bootstrapToServerThread;
|
||||
|
||||
// just for lightweight client test
|
||||
public static void main(String[] args)
|
||||
{
|
||||
P2PNode p2pNode = new P2PNode(DSAKeyUtil.getKeyPair(), false, SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST,
|
||||
(message, peerAddress) -> log.debug("handleMessage: message= " + message + "/ peerAddress=" + peerAddress));
|
||||
p2pNode.start(new FutureCallback<PeerDHT>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(@Nullable PeerDHT result)
|
||||
{
|
||||
log.debug("p2pNode.start success result = " + result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.error(t.toString());
|
||||
}
|
||||
});
|
||||
for (; ; )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private final KeyPair keyPair;
|
||||
private final Boolean useDiskStorage;
|
||||
private final SeedNodeAddress.StaticSeedNodeAddresses defaultStaticSeedNodeAddresses;
|
||||
private final MessageBroker messageBroker;
|
||||
|
||||
private PeerAddress storedPeerAddress;
|
||||
private PeerDHT peerDHT;
|
||||
private Storage storage;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public P2PNode(KeyPair keyPair, Boolean useDiskStorage, SeedNodeAddress.StaticSeedNodeAddresses defaultStaticSeedNodeAddresses, MessageBroker messageBroker)
|
||||
{
|
||||
this.keyPair = keyPair;
|
||||
this.useDiskStorage = useDiskStorage;
|
||||
this.defaultStaticSeedNodeAddresses = defaultStaticSeedNodeAddresses;
|
||||
this.messageBroker = messageBroker;
|
||||
}
|
||||
|
||||
// for unit testing
|
||||
P2PNode(KeyPair keyPair, PeerDHT peerDHT)
|
||||
{
|
||||
this.keyPair = keyPair;
|
||||
this.peerDHT = peerDHT;
|
||||
messageBroker = (message, peerAddress) -> {
|
||||
};
|
||||
useDiskStorage = false;
|
||||
defaultStaticSeedNodeAddresses = SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST;
|
||||
peerDHT.peerBean().keyPair(keyPair);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
public void start(FutureCallback<PeerDHT> callback)
|
||||
{
|
||||
useDiscStorage(useDiskStorage);
|
||||
setupTimerForIPCheck();
|
||||
|
||||
FutureCallback<PeerDHT> localCallback = new FutureCallback<PeerDHT>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(@Nullable PeerDHT result)
|
||||
{
|
||||
log.debug("p2pNode.start success result = " + result);
|
||||
callback.onSuccess(result);
|
||||
bootstrapThreadCompleted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t)
|
||||
{
|
||||
log.error(t.toString());
|
||||
callback.onFailure(t);
|
||||
}
|
||||
};
|
||||
|
||||
bootstrapToLocalhostThread = runBootstrapThread(localCallback, new SeedNodeAddress(defaultStaticSeedNodeAddresses));
|
||||
bootstrapToServerThread = runBootstrapThread(localCallback, new SeedNodeAddress(SeedNodeAddress.StaticSeedNodeAddresses.DIGITAL_OCEAN));
|
||||
}
|
||||
|
||||
public void bootstrapThreadCompleted()
|
||||
{
|
||||
if (bootstrapToLocalhostThread != null)
|
||||
bootstrapToLocalhostThread.interrupt();
|
||||
|
||||
if (bootstrapToServerThread != null)
|
||||
bootstrapToServerThread.interrupt();
|
||||
}
|
||||
|
||||
private Thread runBootstrapThread(FutureCallback<PeerDHT> callback, SeedNodeAddress seedNodeAddress)
|
||||
{
|
||||
Thread bootstrapThread = new Thread(new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
log.debug("runBootstrapThread");
|
||||
ListenableFuture<PeerDHT> bootstrapComplete = bootstrap(seedNodeAddress);
|
||||
Futures.addCallback(bootstrapComplete, callback);
|
||||
}
|
||||
});
|
||||
bootstrapThread.start();
|
||||
return bootstrapThread;
|
||||
}
|
||||
|
||||
public void shutDown()
|
||||
{
|
||||
if (peerDHT != null && peerDHT.peer() != null)
|
||||
peerDHT.peer().shutdown();
|
||||
|
||||
if (storage != null)
|
||||
storage.close();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Generic DHT methods
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// The data and the domain are protected by that key pair.
|
||||
public FuturePut putDomainProtectedData(Number160 locationKey, Data data) throws IOException, ClassNotFoundException
|
||||
{
|
||||
data.protectEntry(keyPair);
|
||||
final Number160 ownerKeyHash = Utils.makeSHAHash(keyPair.getPublic().getEncoded());
|
||||
return peerDHT.put(locationKey).data(data).keyPair(keyPair).domainKey(ownerKeyHash).protectDomain().start();
|
||||
}
|
||||
|
||||
// No protection, everybody can write.
|
||||
public FuturePut putData(Number160 locationKey, Data data) throws IOException, ClassNotFoundException
|
||||
{
|
||||
return peerDHT.put(locationKey).data(data).start();
|
||||
}
|
||||
|
||||
// Not public readable. Only users with the public key of the peer who stored the data can read that data
|
||||
public FutureGet getDomainProtectedData(Number160 locationKey, PublicKey publicKey) throws IOException, ClassNotFoundException
|
||||
{
|
||||
final Number160 ownerKeyHash = Utils.makeSHAHash(publicKey.getEncoded());
|
||||
return peerDHT.get(locationKey).domainKey(ownerKeyHash).start();
|
||||
}
|
||||
|
||||
// No protection, everybody can read.
|
||||
public FutureGet getData(Number160 locationKey) throws IOException, ClassNotFoundException
|
||||
{
|
||||
return peerDHT.get(locationKey).start();
|
||||
}
|
||||
|
||||
// No domain protection, but entry protection
|
||||
public FuturePut addProtectedData(Number160 locationKey, Data data) throws IOException, ClassNotFoundException
|
||||
{
|
||||
data.protectEntry(keyPair);
|
||||
log.trace("addProtectedData with contentKey " + data.hash().toString());
|
||||
return peerDHT.add(locationKey).data(data).keyPair(keyPair).start();
|
||||
}
|
||||
|
||||
// No domain protection, but entry protection
|
||||
public FutureRemove removeFromDataMap(Number160 locationKey, Data data) throws IOException, ClassNotFoundException
|
||||
{
|
||||
Number160 contentKey = data.hash();
|
||||
log.trace("removeFromDataMap with contentKey " + contentKey.toString());
|
||||
return peerDHT.remove(locationKey).contentKey(contentKey).keyPair(keyPair).start();
|
||||
}
|
||||
|
||||
// Public readable
|
||||
public FutureGet getDataMap(Number160 locationKey)
|
||||
{
|
||||
return peerDHT.get(locationKey).all().start();
|
||||
}
|
||||
|
||||
// Send signed payLoad to peer
|
||||
public FutureDirect sendData(PeerAddress peerAddress, Object payLoad)
|
||||
{
|
||||
// use 30 seconds as max idle time before connection get closed
|
||||
FuturePeerConnection futurePeerConnection = peerDHT.peer().createPeerConnection(peerAddress, 30000);
|
||||
FutureDirect futureDirect = peerDHT.peer().sendDirect(futurePeerConnection).object(payLoad).sign().start();
|
||||
futureDirect.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (futureDirect.isSuccess())
|
||||
{
|
||||
log.debug("sendMessage completed");
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("sendData failed with Reason " + futureDirect.failedReason());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error("Exception at sendData " + t.toString());
|
||||
}
|
||||
});
|
||||
|
||||
return futureDirect;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private ListenableFuture<PeerDHT> bootstrap(SeedNodeAddress seedNodeAddress)
|
||||
{
|
||||
BootstrappedPeerFactory bootstrappedPeerFactory = new BootstrappedPeerFactory(keyPair, storage, seedNodeAddress);
|
||||
ListenableFuture<PeerDHT> bootstrapComplete = bootstrappedPeerFactory.start();
|
||||
Futures.addCallback(bootstrapComplete, new FutureCallback<PeerDHT>()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(@Nullable PeerDHT peerDHT)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (peerDHT != null)
|
||||
{
|
||||
P2PNode.this.peerDHT = peerDHT;
|
||||
setupReplyHandler();
|
||||
FuturePut futurePut = storePeerAddress();
|
||||
futurePut.addListener(new BaseFutureListener<BaseFuture>()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(BaseFuture future) throws Exception
|
||||
{
|
||||
if (future.isSuccess())
|
||||
{
|
||||
storedPeerAddress = peerDHT.peerAddress();
|
||||
log.debug("storedPeerAddress = " + storedPeerAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(Throwable t) throws Exception
|
||||
{
|
||||
log.error(t.toString());
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
log.error("peerDHT is null");
|
||||
}
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NotNull Throwable t)
|
||||
{
|
||||
log.error(t.toString());
|
||||
}
|
||||
});
|
||||
return bootstrapComplete;
|
||||
}
|
||||
|
||||
private void setupReplyHandler()
|
||||
{
|
||||
peerDHT.peer().objectDataReply((sender, request) -> {
|
||||
if (!sender.equals(peerDHT.peer().peerAddress()))
|
||||
if (messageBroker != null) messageBroker.handleMessage(request, sender);
|
||||
else
|
||||
log.error("Received msg from myself. That should never happen.");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void setupTimerForIPCheck()
|
||||
{
|
||||
Timer timer = new Timer();
|
||||
long checkIfIPChangedPeriod = 600 * 1000;
|
||||
timer.scheduleAtFixedRate(new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (peerDHT != null && !storedPeerAddress.equals(peerDHT.peerAddress()))
|
||||
{
|
||||
try
|
||||
{
|
||||
storePeerAddress();
|
||||
} catch (IOException | ClassNotFoundException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
log.error(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}, checkIfIPChangedPeriod, checkIfIPChangedPeriod);
|
||||
}
|
||||
|
||||
private FuturePut storePeerAddress() throws IOException, ClassNotFoundException
|
||||
{
|
||||
Number160 locationKey = Utils.makeSHAHash(keyPair.getPublic().getEncoded());
|
||||
Data data = new Data(peerDHT.peerAddress());
|
||||
return putDomainProtectedData(locationKey, data);
|
||||
}
|
||||
|
||||
private void useDiscStorage(boolean useDiscStorage)
|
||||
{
|
||||
if (useDiscStorage)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
File path = new File(StorageDirectory.getStorageDirectory().getCanonicalPath() + "/" + BitSquare.getAppName() + "_tomP2P");
|
||||
if (!path.exists())
|
||||
{
|
||||
boolean created = path.mkdir();
|
||||
if (!created)
|
||||
throw new RuntimeException("Could not create the directory '" + path + "'");
|
||||
}
|
||||
storage = new StorageDisk(Number160.ZERO, path, new DSASignatureFactory());
|
||||
|
||||
} catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
storage = new StorageMemory();
|
||||
}
|
||||
}
|
||||
}
|
81
src/main/java/io/bitsquare/msg/SeedNodeAddress.java
Normal file
81
src/main/java/io/bitsquare/msg/SeedNodeAddress.java
Normal file
@ -0,0 +1,81 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class SeedNodeAddress
|
||||
{
|
||||
private final String id;
|
||||
private final String ip;
|
||||
private final int port;
|
||||
|
||||
public SeedNodeAddress(StaticSeedNodeAddresses staticSeedNodeAddresses)
|
||||
{
|
||||
this(staticSeedNodeAddresses.getId(), staticSeedNodeAddresses.getIp(), staticSeedNodeAddresses.getPort());
|
||||
}
|
||||
|
||||
public SeedNodeAddress(String id, String ip, int port)
|
||||
{
|
||||
this.id = id;
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getIp()
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Enum
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public enum StaticSeedNodeAddresses
|
||||
{
|
||||
LOCALHOST("localhost", "127.0.0.1", 5001),
|
||||
DIGITAL_OCEAN("digitalocean.bitsquare.io", "188.226.179.109", 5000);
|
||||
|
||||
private final String id;
|
||||
private final String ip;
|
||||
private final int port;
|
||||
|
||||
StaticSeedNodeAddresses(String id, String ip, int port)
|
||||
{
|
||||
this.id = id;
|
||||
this.ip = ip;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static List<StaticSeedNodeAddresses> getAllSeedNodeAddresses()
|
||||
{
|
||||
return new ArrayList<>(Arrays.asList(StaticSeedNodeAddresses.values()));
|
||||
}
|
||||
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getIp()
|
||||
{
|
||||
return ip;
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
{
|
||||
return port;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package io.bitsquare.msg.listeners;
|
||||
|
||||
import java.util.Map;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
|
||||
@ -9,7 +9,7 @@ public interface ArbitratorListener
|
||||
{
|
||||
void onArbitratorAdded(Data offerData, boolean success);
|
||||
|
||||
void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success);
|
||||
void onArbitratorsReceived(Map<Number640, Data> dataMap, boolean success);
|
||||
|
||||
void onArbitratorRemoved(Data data, boolean success);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package io.bitsquare.msg.listeners;
|
||||
|
||||
import java.util.Map;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.storage.Data;
|
||||
|
||||
public interface OrderBookListener
|
||||
@ -9,7 +9,7 @@ public interface OrderBookListener
|
||||
@SuppressWarnings("UnusedParameters")
|
||||
void onOfferAdded(Data offerData, boolean success);
|
||||
|
||||
void onOffersReceived(Map<Number160, Data> dataMap, boolean success);
|
||||
void onOffersReceived(Map<Number640, Data> dataMap, boolean success);
|
||||
|
||||
void onOfferRemoved(Data data, boolean success);
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
package io.bitsquare.trade;
|
||||
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class Contract implements Serializable
|
||||
{
|
||||
@ -15,8 +17,8 @@ public class Contract implements Serializable
|
||||
private final String takerAccountID;
|
||||
private final BankAccount offererBankAccount;
|
||||
private final BankAccount takerBankAccount;
|
||||
private final String offererMessagePubKeyAsHex;
|
||||
private final String takerMessagePubKeyAsHex;
|
||||
private final String offererMessagePublicKeyAsString;
|
||||
private final String takerMessagePublicKeyAsString;
|
||||
|
||||
public Contract(Offer offer,
|
||||
BigInteger tradeAmount,
|
||||
@ -25,8 +27,8 @@ public class Contract implements Serializable
|
||||
String takerAccountID,
|
||||
BankAccount offererBankAccount,
|
||||
BankAccount takerBankAccount,
|
||||
String offererMessagePubKeyAsHex,
|
||||
String takerMessagePubKeyAsHex)
|
||||
PublicKey offererMessagePublicKey,
|
||||
PublicKey takerMessagePublicKey)
|
||||
{
|
||||
this.offer = offer;
|
||||
this.tradeAmount = tradeAmount;
|
||||
@ -35,8 +37,8 @@ public class Contract implements Serializable
|
||||
this.takerAccountID = takerAccountID;
|
||||
this.offererBankAccount = offererBankAccount;
|
||||
this.takerBankAccount = takerBankAccount;
|
||||
this.offererMessagePubKeyAsHex = offererMessagePubKeyAsHex;
|
||||
this.takerMessagePubKeyAsHex = takerMessagePubKeyAsHex;
|
||||
this.offererMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(offererMessagePublicKey);
|
||||
this.takerMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(takerMessagePublicKey);
|
||||
}
|
||||
|
||||
|
||||
@ -79,17 +81,16 @@ public class Contract implements Serializable
|
||||
return takerBankAccount;
|
||||
}
|
||||
|
||||
public String getTakerMessagePubKeyAsHex()
|
||||
public String getTakerMessagePublicKey()
|
||||
{
|
||||
return takerMessagePubKeyAsHex;
|
||||
return takerMessagePublicKeyAsString;
|
||||
}
|
||||
|
||||
public String getOffererMessagePubKeyAsHex()
|
||||
public String getOffererMessagePublicKey()
|
||||
{
|
||||
return offererMessagePubKeyAsHex;
|
||||
return offererMessagePublicKeyAsString;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
@ -101,10 +102,8 @@ public class Contract implements Serializable
|
||||
", takerAccountID='" + takerAccountID + '\'' +
|
||||
", offererBankAccount=" + offererBankAccount +
|
||||
", takerBankAccount=" + takerBankAccount +
|
||||
", offererMessagePubKeyAsHex='" + offererMessagePubKeyAsHex + '\'' +
|
||||
", takerMessagePubKeyAsHex='" + takerMessagePubKeyAsHex + '\'' +
|
||||
", takerMessagePublicKeyAsString=" + takerMessagePublicKeyAsString +
|
||||
", offererMessagePublicKeyAsString=" + offererMessagePublicKeyAsString +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import io.bitsquare.locale.Country;
|
||||
import io.bitsquare.user.Arbitrator;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import java.util.*;
|
||||
|
||||
public class Offer implements Serializable
|
||||
@ -23,7 +24,7 @@ public class Offer implements Serializable
|
||||
private final double price;
|
||||
private final BigInteger amount;
|
||||
private final BigInteger minAmount;
|
||||
private final String messagePubKeyAsHex;
|
||||
private final PublicKey messagePublicKey;
|
||||
private final BankAccountType bankAccountType;
|
||||
private final Country bankAccountCountry;
|
||||
|
||||
@ -39,7 +40,7 @@ public class Offer implements Serializable
|
||||
// Constructor
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Offer(String messagePubKeyAsHex,
|
||||
public Offer(PublicKey messagePublicKey,
|
||||
Direction direction,
|
||||
double price,
|
||||
BigInteger amount,
|
||||
@ -53,7 +54,7 @@ public class Offer implements Serializable
|
||||
List<Country> acceptedCountries,
|
||||
List<Locale> acceptedLanguageLocales)
|
||||
{
|
||||
this.messagePubKeyAsHex = messagePubKeyAsHex;
|
||||
this.messagePublicKey = messagePublicKey;
|
||||
this.direction = direction;
|
||||
this.price = price;
|
||||
this.amount = amount;
|
||||
@ -77,9 +78,9 @@ public class Offer implements Serializable
|
||||
// Setters
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public String getMessagePubKeyAsHex()
|
||||
public PublicKey getMessagePublicKey()
|
||||
{
|
||||
return messagePubKeyAsHex;
|
||||
return messagePublicKey;
|
||||
}
|
||||
|
||||
|
||||
@ -183,7 +184,7 @@ public class Offer implements Serializable
|
||||
", price=" + price +
|
||||
", amount=" + amount +
|
||||
", minAmount=" + minAmount +
|
||||
", messagePubKey=" + messagePubKeyAsHex.hashCode() +
|
||||
", messagePubKey=" + messagePublicKey.hashCode() +
|
||||
", bankAccountTypeEnum=" + bankAccountType +
|
||||
", bankAccountCountryLocale=" + bankAccountCountry +
|
||||
", collateral=" + collateral +
|
||||
|
@ -136,11 +136,11 @@ public class Trading
|
||||
}
|
||||
|
||||
public void removeOffer(Offer offer)
|
||||
{
|
||||
if (!offers.containsKey(offer.getId()))
|
||||
{ //TODO
|
||||
/* if (!offers.containsKey(offer.getId()))
|
||||
{
|
||||
throw new IllegalStateException("offers does not contain the offer with the ID " + offer.getId());
|
||||
}
|
||||
}*/
|
||||
|
||||
offers.remove(offer.getId());
|
||||
saveOffers();
|
||||
|
@ -21,11 +21,17 @@ import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.collections.transformation.SortedList;
|
||||
import javax.inject.Inject;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.Number640;
|
||||
import net.tomp2p.storage.Data;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/*
|
||||
TODO
|
||||
remove dependencies to tomp2p
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.storage.Data;
|
||||
*/
|
||||
public class OrderBook implements OrderBookListener
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(OrderBook.class);
|
||||
@ -59,12 +65,12 @@ public class OrderBook implements OrderBookListener
|
||||
|
||||
public void init()
|
||||
{
|
||||
messageFacade.addMessageListener(this);
|
||||
messageFacade.addOrderBookListener(this);
|
||||
}
|
||||
|
||||
public void cleanup()
|
||||
{
|
||||
messageFacade.removeMessageListener(this);
|
||||
messageFacade.removeOrderBookListener(this);
|
||||
}
|
||||
|
||||
public void loadOffers()
|
||||
@ -177,7 +183,7 @@ public class OrderBook implements OrderBookListener
|
||||
{
|
||||
try
|
||||
{
|
||||
Object offerDataObject = offerData.getObject();
|
||||
Object offerDataObject = offerData.object();
|
||||
if (offerDataObject instanceof Offer)
|
||||
{
|
||||
Offer offer = (Offer) offerDataObject;
|
||||
@ -190,7 +196,7 @@ public class OrderBook implements OrderBookListener
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
|
||||
public void onOffersReceived(Map<Number640, Data> dataMap, boolean success)
|
||||
{
|
||||
if (success && dataMap != null)
|
||||
{
|
||||
@ -200,7 +206,7 @@ public class OrderBook implements OrderBookListener
|
||||
{
|
||||
try
|
||||
{
|
||||
Object offerDataObject = offerData.getObject();
|
||||
Object offerDataObject = offerData.object();
|
||||
if (offerDataObject instanceof Offer)
|
||||
{
|
||||
Offer offer = (Offer) offerDataObject;
|
||||
@ -226,7 +232,7 @@ public class OrderBook implements OrderBookListener
|
||||
{
|
||||
try
|
||||
{
|
||||
Object offerDataObject = offerData.getObject();
|
||||
Object offerDataObject = offerData.object();
|
||||
if (offerDataObject instanceof Offer)
|
||||
{
|
||||
Offer offer = (Offer) offerDataObject;
|
||||
|
@ -16,6 +16,7 @@ import io.bitsquare.trade.protocol.taker.RequestOffererPublishDepositTxMessage;
|
||||
import io.bitsquare.trade.protocol.taker.TakeOfferFeePayedMessage;
|
||||
import io.bitsquare.user.User;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
@ -76,7 +77,7 @@ public class ProtocolForOffererAsBuyer
|
||||
private final String arbitratorPubKey;
|
||||
private final BankAccount bankAccount;
|
||||
private final String accountId;
|
||||
private final String messagePubKey;
|
||||
private final PublicKey messagePublicKey;
|
||||
private final ECKey accountKey;
|
||||
private final String payoutAddress;
|
||||
|
||||
@ -90,7 +91,7 @@ public class ProtocolForOffererAsBuyer
|
||||
private String peersPayoutAddress;
|
||||
private String peersAccountId;
|
||||
private BankAccount peersBankAccount;
|
||||
private String peersMessagePubKey;
|
||||
private PublicKey peersMessagePublicKey;
|
||||
private String peersContractAsJson;
|
||||
private String signedTakerDepositTxAsHex;
|
||||
private String txConnOutAsHex;
|
||||
@ -129,7 +130,7 @@ public class ProtocolForOffererAsBuyer
|
||||
|
||||
bankAccount = user.getBankAccount(trade.getOffer().getBankAccountId());
|
||||
accountId = user.getAccountId();
|
||||
messagePubKey = user.getMessagePubKeyAsHex();
|
||||
messagePublicKey = user.getMessagePublicKey();
|
||||
|
||||
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
|
||||
payoutAddress = walletFacade.getAddressInfoByTradeID(tradeId).getAddressString();
|
||||
@ -241,7 +242,7 @@ public class ProtocolForOffererAsBuyer
|
||||
String peersPayoutAddress = nonEmptyStringOf(message.getTakerPayoutAddress());
|
||||
String peersAccountId = nonEmptyStringOf(message.getTakerAccountId());
|
||||
BankAccount peersBankAccount = checkNotNull(message.getTakerBankAccount());
|
||||
String peersMessagePubKey = nonEmptyStringOf(message.getTakerMessagePubKey());
|
||||
PublicKey peersMessagePublicKey = checkNotNull(message.getTakerMessagePublicKey());
|
||||
String peersContractAsJson = nonEmptyStringOf(message.getTakerContractAsJson());
|
||||
String signedTakerDepositTxAsHex = nonEmptyStringOf(message.getSignedTakerDepositTxAsHex());
|
||||
String txConnOutAsHex = nonEmptyStringOf(message.getTxConnOutAsHex());
|
||||
@ -253,7 +254,7 @@ public class ProtocolForOffererAsBuyer
|
||||
this.peersPayoutAddress = peersPayoutAddress;
|
||||
this.peersAccountId = peersAccountId;
|
||||
this.peersBankAccount = peersBankAccount;
|
||||
this.peersMessagePubKey = peersMessagePubKey;
|
||||
this.peersMessagePublicKey = peersMessagePublicKey;
|
||||
this.peersContractAsJson = peersContractAsJson;
|
||||
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
||||
this.txConnOutAsHex = txConnOutAsHex;
|
||||
@ -277,12 +278,12 @@ public class ProtocolForOffererAsBuyer
|
||||
accountId,
|
||||
tradeAmount,
|
||||
takeOfferFeeTxId,
|
||||
messagePubKey,
|
||||
messagePublicKey,
|
||||
offer,
|
||||
peersAccountId,
|
||||
bankAccount,
|
||||
peersBankAccount,
|
||||
peersMessagePubKey,
|
||||
peersMessagePublicKey,
|
||||
peersContractAsJson,
|
||||
accountKey);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.protocol.FaultHandler;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -21,17 +22,17 @@ public class VerifyAndSignContract
|
||||
String accountId,
|
||||
BigInteger tradeAmount,
|
||||
String takeOfferFeeTxId,
|
||||
String messagePubKeyAsHex,
|
||||
PublicKey messagePublicKey,
|
||||
Offer offer,
|
||||
String peersAccountId,
|
||||
BankAccount bankAccount,
|
||||
BankAccount peersBankAccount,
|
||||
String takerMessagePubKey,
|
||||
PublicKey takerMessagePublicKey,
|
||||
String peersContractAsJson,
|
||||
ECKey registrationKey)
|
||||
{
|
||||
log.trace("Run task");
|
||||
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, accountId, peersAccountId, bankAccount, peersBankAccount, messagePubKeyAsHex, takerMessagePubKey);
|
||||
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, accountId, peersAccountId, bankAccount, peersBankAccount, messagePublicKey, takerMessagePublicKey);
|
||||
|
||||
String contractAsJson = Utilities.objectToJson(contract);
|
||||
// log.trace("Offerer contract created: " + contract);
|
||||
|
@ -8,6 +8,7 @@ import io.bitsquare.trade.Offer;
|
||||
import io.bitsquare.trade.protocol.FaultHandler;
|
||||
import io.bitsquare.util.Utilities;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -23,8 +24,8 @@ public class CreateAndSignContract
|
||||
String takeOfferFeeTxId,
|
||||
String accountId,
|
||||
BankAccount bankAccount,
|
||||
String peersMessagePubKeyAsHex,
|
||||
String messagePubKeyAsHex,
|
||||
PublicKey peersMessagePublicKey,
|
||||
PublicKey messagePublicKey,
|
||||
String peersAccountId,
|
||||
BankAccount peersBankAccount,
|
||||
ECKey registrationKey)
|
||||
@ -32,7 +33,7 @@ public class CreateAndSignContract
|
||||
log.trace("Run task");
|
||||
try
|
||||
{
|
||||
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, peersAccountId, accountId, peersBankAccount, bankAccount, peersMessagePubKeyAsHex, messagePubKeyAsHex);
|
||||
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, peersAccountId, accountId, peersBankAccount, bankAccount, peersMessagePublicKey, messagePublicKey);
|
||||
|
||||
String contractAsJson = Utilities.objectToJson(contract);
|
||||
String signature = cryptoFacade.signContract(registrationKey, contractAsJson);
|
||||
|
@ -3,6 +3,7 @@ package io.bitsquare.trade.protocol.taker;
|
||||
import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.listeners.GetPeerAddressListener;
|
||||
import io.bitsquare.trade.protocol.FaultHandler;
|
||||
import java.security.PublicKey;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -11,10 +12,10 @@ public class GetPeerAddress
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
|
||||
|
||||
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, MessageFacade messageFacade, String messagePubKeyAsHex)
|
||||
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, MessageFacade messageFacade, PublicKey messagePublicKey)
|
||||
{
|
||||
log.trace("Run task");
|
||||
messageFacade.getPeerAddress(messagePubKeyAsHex, new GetPeerAddressListener()
|
||||
messageFacade.getPeerAddress(messagePublicKey, new GetPeerAddressListener()
|
||||
{
|
||||
@Override
|
||||
public void onResult(PeerAddress peerAddress)
|
||||
|
@ -16,6 +16,7 @@ import io.bitsquare.trade.protocol.offerer.RequestTakerDepositPaymentMessage;
|
||||
import io.bitsquare.trade.protocol.offerer.RespondToTakeOfferRequestMessage;
|
||||
import io.bitsquare.user.User;
|
||||
import java.math.BigInteger;
|
||||
import java.security.PublicKey;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -66,11 +67,11 @@ public class ProtocolForTakerAsSeller
|
||||
private final String tradeId;
|
||||
private final BankAccount bankAccount;
|
||||
private final String accountId;
|
||||
private final String messagePubKey;
|
||||
private final PublicKey messagePublicKey;
|
||||
private final BigInteger tradeAmount;
|
||||
private final String pubKeyForThatTrade;
|
||||
private final ECKey accountKey;
|
||||
private final String peersMessagePubKey;
|
||||
private final PublicKey peersMessagePublicKey;
|
||||
private final BigInteger collateral;
|
||||
private final String arbitratorPubKey;
|
||||
|
||||
@ -122,11 +123,11 @@ public class ProtocolForTakerAsSeller
|
||||
collateral = trade.getCollateralAmount();
|
||||
arbitratorPubKey = trade.getOffer().getArbitrator().getPubKeyAsHex();
|
||||
|
||||
peersMessagePubKey = offer.getMessagePubKeyAsHex();
|
||||
peersMessagePublicKey = offer.getMessagePublicKey();
|
||||
|
||||
bankAccount = user.getBankAccount(offer.getBankAccountId());
|
||||
accountId = user.getAccountId();
|
||||
messagePubKey = user.getMessagePubKeyAsHex();
|
||||
messagePublicKey = user.getMessagePublicKey();
|
||||
|
||||
pubKeyForThatTrade = walletFacade.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
|
||||
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
|
||||
@ -138,7 +139,7 @@ public class ProtocolForTakerAsSeller
|
||||
{
|
||||
log.debug("start called " + step++);
|
||||
state = State.GetPeerAddress;
|
||||
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, peersMessagePubKey);
|
||||
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, peersMessagePublicKey);
|
||||
}
|
||||
|
||||
public void onResultGetPeerAddress(PeerAddress peerAddress)
|
||||
@ -176,6 +177,7 @@ public class ProtocolForTakerAsSeller
|
||||
else
|
||||
{
|
||||
listener.onTakeOfferRequestRejected(trade);
|
||||
// exit case
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,8 +241,8 @@ public class ProtocolForTakerAsSeller
|
||||
takeOfferFeeTxId,
|
||||
accountId,
|
||||
bankAccount,
|
||||
peersMessagePubKey,
|
||||
messagePubKey,
|
||||
peersMessagePublicKey,
|
||||
messagePublicKey,
|
||||
peersAccountId,
|
||||
peersBankAccount,
|
||||
accountKey);
|
||||
@ -272,7 +274,7 @@ public class ProtocolForTakerAsSeller
|
||||
walletFacade,
|
||||
bankAccount,
|
||||
accountId,
|
||||
messagePubKey,
|
||||
messagePublicKey,
|
||||
tradeId,
|
||||
contractAsJson,
|
||||
takerSignature,
|
||||
|
@ -3,6 +3,7 @@ package io.bitsquare.trade.protocol.taker;
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.msg.TradeMessage;
|
||||
import java.io.Serializable;
|
||||
import java.security.PublicKey;
|
||||
|
||||
public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage
|
||||
{
|
||||
@ -10,7 +11,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||
private final String tradeId;
|
||||
private BankAccount bankAccount;
|
||||
private String accountID;
|
||||
private String takerMessagePubKey;
|
||||
private PublicKey takerMessagePublicKey;
|
||||
private String signedTakerDepositTxAsHex;
|
||||
private String txScriptSigAsHex;
|
||||
private String txConnOutAsHex;
|
||||
@ -25,7 +26,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||
public RequestOffererPublishDepositTxMessage(String tradeId,
|
||||
BankAccount bankAccount,
|
||||
String accountID,
|
||||
String takerMessagePubKey,
|
||||
PublicKey takerMessagePublicKey,
|
||||
String signedTakerDepositTxAsHex,
|
||||
String txScriptSigAsHex,
|
||||
String txConnOutAsHex,
|
||||
@ -39,7 +40,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||
this.tradeId = tradeId;
|
||||
this.bankAccount = bankAccount;
|
||||
this.accountID = accountID;
|
||||
this.takerMessagePubKey = takerMessagePubKey;
|
||||
this.takerMessagePublicKey = takerMessagePublicKey;
|
||||
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
||||
this.txScriptSigAsHex = txScriptSigAsHex;
|
||||
this.txConnOutAsHex = txConnOutAsHex;
|
||||
@ -72,9 +73,9 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||
return accountID;
|
||||
}
|
||||
|
||||
public String getTakerMessagePubKey()
|
||||
public PublicKey getTakerMessagePublicKey()
|
||||
{
|
||||
return takerMessagePubKey;
|
||||
return takerMessagePublicKey;
|
||||
}
|
||||
|
||||
public String getSignedTakerDepositTxAsHex()
|
||||
|
@ -8,6 +8,7 @@ import io.bitsquare.msg.MessageFacade;
|
||||
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
|
||||
import io.bitsquare.trade.protocol.FaultHandler;
|
||||
import io.bitsquare.trade.protocol.ResultHandler;
|
||||
import java.security.PublicKey;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -23,7 +24,7 @@ public class SendSignedTakerDepositTxAsHex
|
||||
WalletFacade walletFacade,
|
||||
BankAccount bankAccount,
|
||||
String accountId,
|
||||
String messagePubKeyAsHex,
|
||||
PublicKey messagePublicKey,
|
||||
String tradeId,
|
||||
String contractAsJson,
|
||||
String takerSignature,
|
||||
@ -36,7 +37,7 @@ public class SendSignedTakerDepositTxAsHex
|
||||
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(tradeId,
|
||||
bankAccount,
|
||||
accountId,
|
||||
messagePubKeyAsHex,
|
||||
messagePublicKey,
|
||||
Utils.bytesToHexString(signedTakerDepositTx.bitcoinSerialize()),
|
||||
Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes()),
|
||||
Utils.bytesToHexString(signedTakerDepositTx.getInput(1)
|
||||
|
@ -1,7 +1,10 @@
|
||||
package io.bitsquare.user;
|
||||
|
||||
import io.bitsquare.bank.BankAccount;
|
||||
import io.bitsquare.util.DSAKeyUtil;
|
||||
import java.io.Serializable;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
@ -11,19 +14,17 @@ public class User implements Serializable
|
||||
private static final long serialVersionUID = 7409078808248518638L;
|
||||
|
||||
transient private final SimpleBooleanProperty bankAccountChangedProperty = new SimpleBooleanProperty();
|
||||
transient private KeyPair messageKeyPair = DSAKeyUtil.getKeyPair();
|
||||
|
||||
|
||||
private PublicKey messagePublicKey;
|
||||
private String accountID;
|
||||
|
||||
private String messagePubKeyAsHex;
|
||||
private boolean isOnline;
|
||||
|
||||
private List<BankAccount> bankAccounts = new ArrayList<>();
|
||||
|
||||
private BankAccount currentBankAccount = null;
|
||||
|
||||
public User()
|
||||
{
|
||||
messagePublicKey = messageKeyPair.getPublic();
|
||||
}
|
||||
|
||||
|
||||
@ -36,11 +37,14 @@ public class User implements Serializable
|
||||
if (savedUser != null)
|
||||
{
|
||||
accountID = savedUser.getAccountId();
|
||||
messagePubKeyAsHex = savedUser.getMessagePubKeyAsHex();
|
||||
// TODO handled by DSAKeyUtil -> change that storage check is only done here
|
||||
// messagePublicKey = savedUser.getMessagePublicKey();
|
||||
isOnline = savedUser.getIsOnline();
|
||||
bankAccounts = savedUser.getBankAccounts();
|
||||
currentBankAccount = savedUser.getCurrentBankAccount();
|
||||
}
|
||||
|
||||
messagePublicKey = messageKeyPair.getPublic();
|
||||
}
|
||||
|
||||
public void addBankAccount(BankAccount bankAccount)
|
||||
@ -94,17 +98,6 @@ public class User implements Serializable
|
||||
}
|
||||
|
||||
|
||||
public String getMessagePubKeyAsHex()
|
||||
{
|
||||
return messagePubKeyAsHex;
|
||||
}
|
||||
|
||||
public void setMessagePubKeyAsHex(String messageID)
|
||||
{
|
||||
this.messagePubKeyAsHex = messageID;
|
||||
}
|
||||
|
||||
|
||||
public String getAccountId()
|
||||
{
|
||||
return accountID;
|
||||
@ -172,17 +165,27 @@ public class User implements Serializable
|
||||
return bankAccountChangedProperty;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "User{" +
|
||||
"bankAccountChangedProperty=" + bankAccountChangedProperty +
|
||||
", messageKeyPair=" + messageKeyPair +
|
||||
", messagePublicKey=" + messagePublicKey +
|
||||
", accountID='" + accountID + '\'' +
|
||||
", messagePubKeyAsHex='" + messagePubKeyAsHex + '\'' +
|
||||
", isOnline=" + isOnline +
|
||||
", bankAccounts=" + bankAccounts +
|
||||
", currentBankAccount=" + currentBankAccount +
|
||||
'}';
|
||||
}
|
||||
|
||||
public KeyPair getMessageKeyPair()
|
||||
{
|
||||
return messageKeyPair;
|
||||
}
|
||||
|
||||
public PublicKey getMessagePublicKey()
|
||||
{
|
||||
return messagePublicKey;
|
||||
}
|
||||
}
|
||||
|
139
src/main/java/lighthouse/protocol/LHUtils.java
Normal file
139
src/main/java/lighthouse/protocol/LHUtils.java
Normal file
@ -0,0 +1,139 @@
|
||||
package lighthouse.protocol;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.stream.Stream;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LHUtils
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(LHUtils.class);
|
||||
|
||||
public static List<Path> listDir(Path dir) throws IOException
|
||||
{
|
||||
List<Path> contents = new LinkedList<>();
|
||||
try (Stream<Path> list = Files.list(dir))
|
||||
{
|
||||
list.forEach(contents::add);
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
//region Generic Java 8 enhancements
|
||||
public interface UncheckedRun<T>
|
||||
{
|
||||
public T run() throws Throwable;
|
||||
}
|
||||
|
||||
public interface UncheckedRunnable
|
||||
{
|
||||
public void run() throws Throwable;
|
||||
}
|
||||
|
||||
public static <T> T unchecked(UncheckedRun<T> run)
|
||||
{
|
||||
try
|
||||
{
|
||||
return run.run();
|
||||
} catch (Throwable throwable)
|
||||
{
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void uncheck(UncheckedRunnable run)
|
||||
{
|
||||
try
|
||||
{
|
||||
run.run();
|
||||
} catch (Throwable throwable)
|
||||
{
|
||||
throw new RuntimeException(throwable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ignoreAndLog(UncheckedRunnable runnable)
|
||||
{
|
||||
try
|
||||
{
|
||||
runnable.run();
|
||||
} catch (Throwable t)
|
||||
{
|
||||
log.error("Ignoring error", t);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T ignoredAndLogged(UncheckedRun<T> runnable)
|
||||
{
|
||||
try
|
||||
{
|
||||
return runnable.run();
|
||||
} catch (Throwable t)
|
||||
{
|
||||
log.error("Ignoring error", t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, E extends Throwable> T checkedGet(Future<T> future) throws E
|
||||
{
|
||||
try
|
||||
{
|
||||
return future.get();
|
||||
} catch (InterruptedException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
} catch (ExecutionException e)
|
||||
{
|
||||
throw (E) e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean didThrow(UncheckedRun run)
|
||||
{
|
||||
try
|
||||
{
|
||||
run.run();
|
||||
return false;
|
||||
} catch (Throwable throwable)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean didThrow(UncheckedRunnable run)
|
||||
{
|
||||
try
|
||||
{
|
||||
run.run();
|
||||
return false;
|
||||
} catch (Throwable throwable)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T stopwatched(String description, UncheckedRun<T> run)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
T result = unchecked(run::run);
|
||||
log.info("{}: {}ms", description, System.currentTimeMillis() - now);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void stopwatch(String description, UncheckedRunnable run)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
uncheck(run::run);
|
||||
log.info("{}: {}ms", description, System.currentTimeMillis() - now);
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
171
src/main/java/lighthouse/threading/AffinityExecutor.java
Normal file
171
src/main/java/lighthouse/threading/AffinityExecutor.java
Normal file
@ -0,0 +1,171 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import com.google.common.util.concurrent.Uninterruptibles;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javafx.application.Platform;
|
||||
import lighthouse.protocol.LHUtils;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkState;
|
||||
|
||||
/**
|
||||
* An extended executor interface that supports thread affinity assertions and short circuiting.
|
||||
*/
|
||||
public interface AffinityExecutor extends Executor
|
||||
{
|
||||
/**
|
||||
* Returns true if the current thread is equal to the thread this executor is backed by.
|
||||
*/
|
||||
public boolean isOnThread();
|
||||
|
||||
/**
|
||||
* Throws an IllegalStateException if the current thread is equal to the thread this executor is backed by.
|
||||
*/
|
||||
public void checkOnThread();
|
||||
|
||||
/**
|
||||
* If isOnThread() then runnable is invoked immediately, otherwise the closure is queued onto the backing thread.
|
||||
*/
|
||||
public void executeASAP(LHUtils.UncheckedRunnable runnable);
|
||||
|
||||
public abstract static class BaseAffinityExecutor implements AffinityExecutor
|
||||
{
|
||||
protected final Thread.UncaughtExceptionHandler exceptionHandler;
|
||||
|
||||
protected BaseAffinityExecutor()
|
||||
{
|
||||
exceptionHandler = Thread.currentThread().getUncaughtExceptionHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean isOnThread();
|
||||
|
||||
@Override
|
||||
public void checkOnThread()
|
||||
{
|
||||
checkState(isOnThread(), "On wrong thread: %s", Thread.currentThread());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void executeASAP(LHUtils.UncheckedRunnable runnable)
|
||||
{
|
||||
final Runnable command = () -> {
|
||||
try
|
||||
{
|
||||
runnable.run();
|
||||
} catch (Throwable throwable)
|
||||
{
|
||||
exceptionHandler.uncaughtException(Thread.currentThread(), throwable);
|
||||
}
|
||||
};
|
||||
if (isOnThread())
|
||||
command.run();
|
||||
else
|
||||
{
|
||||
execute(command);
|
||||
}
|
||||
}
|
||||
|
||||
// Must comply with the Executor definition w.r.t. exceptions here.
|
||||
@Override
|
||||
public abstract void execute(Runnable command);
|
||||
}
|
||||
|
||||
public static AffinityExecutor UI_THREAD = new BaseAffinityExecutor()
|
||||
{
|
||||
@Override
|
||||
public boolean isOnThread()
|
||||
{
|
||||
return Platform.isFxApplicationThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
Platform.runLater(command);
|
||||
}
|
||||
};
|
||||
|
||||
public static AffinityExecutor SAME_THREAD = new BaseAffinityExecutor()
|
||||
{
|
||||
@Override
|
||||
public boolean isOnThread()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
command.run();
|
||||
}
|
||||
};
|
||||
|
||||
public static class ServiceAffinityExecutor extends BaseAffinityExecutor
|
||||
{
|
||||
protected AtomicReference<Thread> whichThread = new AtomicReference<>(null);
|
||||
private final Thread.UncaughtExceptionHandler handler = Thread.currentThread().getUncaughtExceptionHandler();
|
||||
public final ScheduledExecutorService service;
|
||||
|
||||
public ServiceAffinityExecutor(String threadName)
|
||||
{
|
||||
service = Executors.newSingleThreadScheduledExecutor(runnable -> {
|
||||
Thread thread = new Thread(runnable);
|
||||
thread.setDaemon(true);
|
||||
thread.setName(threadName);
|
||||
thread.setUncaughtExceptionHandler(handler);
|
||||
whichThread.set(thread);
|
||||
return thread;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnThread()
|
||||
{
|
||||
return Thread.currentThread() == whichThread.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
service.execute(command);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An executor useful for unit tests: allows the current thread to block until a command arrives from another
|
||||
* thread, which is then executed. Inbound closures/commands stack up until they are cleared by looping.
|
||||
*/
|
||||
public static class Gate extends BaseAffinityExecutor
|
||||
{
|
||||
private final Thread thisThread = Thread.currentThread();
|
||||
private final LinkedBlockingQueue<Runnable> commandQ = new LinkedBlockingQueue<>();
|
||||
|
||||
@Override
|
||||
public boolean isOnThread()
|
||||
{
|
||||
return Thread.currentThread() == thisThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Runnable command)
|
||||
{
|
||||
Uninterruptibles.putUninterruptibly(commandQ, command);
|
||||
}
|
||||
|
||||
public void waitAndRun()
|
||||
{
|
||||
final Runnable runnable = Uninterruptibles.takeUninterruptibly(commandQ);
|
||||
System.err.println("Gate running " + runnable);
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
public int getTaskQueueSize()
|
||||
{
|
||||
return commandQ.size();
|
||||
}
|
||||
}
|
||||
}
|
101
src/main/java/lighthouse/threading/ConcatenatingList.java
Normal file
101
src/main/java/lighthouse/threading/ConcatenatingList.java
Normal file
@ -0,0 +1,101 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.ObservableListBase;
|
||||
import javafx.collections.WeakListChangeListener;
|
||||
|
||||
/**
|
||||
* This list is created by dynamically concatenating all the source lists together.
|
||||
*/
|
||||
public class ConcatenatingList<T> extends ObservableListBase<T> implements ObservableList<T>
|
||||
{
|
||||
private List<ObservableList<T>> sources = new ArrayList<>();
|
||||
private ListChangeListener<T> listener = this::sourceChanged;
|
||||
|
||||
@SafeVarargs
|
||||
public ConcatenatingList(ObservableList<T>... source)
|
||||
{
|
||||
super();
|
||||
for (ObservableList<T> s : source)
|
||||
{
|
||||
sources.add(s);
|
||||
s.addListener(new WeakListChangeListener<T>(listener));
|
||||
}
|
||||
if (sources.isEmpty())
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
private int calculateOffset(ObservableList<? extends T> source)
|
||||
{
|
||||
int cursor = 0;
|
||||
for (ObservableList<T> ts : sources)
|
||||
{
|
||||
if (ts == source) return cursor;
|
||||
cursor += ts.size();
|
||||
}
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private void sourceChanged(ListChangeListener.Change<? extends T> c)
|
||||
{
|
||||
ObservableList<? extends T> source = c.getList();
|
||||
int offset = calculateOffset(source);
|
||||
beginChange();
|
||||
while (c.next())
|
||||
{
|
||||
if (c.wasPermutated())
|
||||
{
|
||||
int[] perm = new int[c.getTo() - c.getFrom()];
|
||||
for (int i = c.getFrom(); i < c.getTo(); i++)
|
||||
perm[i - c.getFrom()] = c.getPermutation(i) + offset;
|
||||
nextPermutation(c.getFrom() + offset, c.getTo() + offset, perm);
|
||||
}
|
||||
else if (c.wasUpdated())
|
||||
{
|
||||
for (int i = c.getFrom(); i < c.getTo(); i++)
|
||||
{
|
||||
nextUpdate(i + offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.wasRemoved())
|
||||
{
|
||||
// Removed should come first to properly handle replacements, then add.
|
||||
nextRemove(c.getFrom() + offset, c.getRemoved());
|
||||
}
|
||||
if (c.wasAdded())
|
||||
{
|
||||
nextAdd(c.getFrom() + offset, c.getTo() + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
endChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int index)
|
||||
{
|
||||
for (ObservableList<T> source : sources)
|
||||
{
|
||||
if (index < source.size())
|
||||
{
|
||||
return source.get(index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index -= source.size();
|
||||
}
|
||||
}
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return sources.stream().mapToInt(List::size).sum();
|
||||
}
|
||||
}
|
113
src/main/java/lighthouse/threading/MappedList.java
Normal file
113
src/main/java/lighthouse/threading/MappedList.java
Normal file
@ -0,0 +1,113 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.collections.transformation.TransformationList;
|
||||
|
||||
/**
|
||||
* Maps elements of type F to E with change listeners working as expected.
|
||||
*/
|
||||
public class MappedList<E, F> extends TransformationList<E, F>
|
||||
{
|
||||
private final Function<F, E> mapper;
|
||||
private final ArrayList<E> mapped;
|
||||
|
||||
/**
|
||||
* Creates a new MappedList list wrapped around the source list.
|
||||
* Each element will have the given function applied to it, such that the list is cast through the mapper.
|
||||
*/
|
||||
public MappedList(ObservableList<? extends F> source, Function<F, E> mapper)
|
||||
{
|
||||
super(source);
|
||||
this.mapper = mapper;
|
||||
this.mapped = new ArrayList<>(source.size());
|
||||
mapAll();
|
||||
}
|
||||
|
||||
private void mapAll()
|
||||
{
|
||||
mapped.clear();
|
||||
for (F val : getSource())
|
||||
mapped.add(mapper.apply(val));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sourceChanged(ListChangeListener.Change<? extends F> c)
|
||||
{
|
||||
// Is all this stuff right for every case? Probably it doesn't matter for this app.
|
||||
beginChange();
|
||||
while (c.next())
|
||||
{
|
||||
if (c.wasPermutated())
|
||||
{
|
||||
int[] perm = new int[c.getTo() - c.getFrom()];
|
||||
for (int i = c.getFrom(); i < c.getTo(); i++)
|
||||
perm[i - c.getFrom()] = c.getPermutation(i);
|
||||
nextPermutation(c.getFrom(), c.getTo(), perm);
|
||||
}
|
||||
else if (c.wasUpdated())
|
||||
{
|
||||
for (int i = c.getFrom(); i < c.getTo(); i++)
|
||||
{
|
||||
remapIndex(i);
|
||||
nextUpdate(i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (c.wasRemoved())
|
||||
{
|
||||
// Removed should come first to properly handle replacements, then add.
|
||||
List<E> removed = mapped.subList(c.getFrom(), c.getFrom() + c.getRemovedSize());
|
||||
ArrayList<E> duped = new ArrayList<>(removed);
|
||||
removed.clear();
|
||||
nextRemove(c.getFrom(), duped);
|
||||
}
|
||||
if (c.wasAdded())
|
||||
{
|
||||
for (int i = c.getFrom(); i < c.getTo(); i++)
|
||||
{
|
||||
mapped.addAll(c.getFrom(), c.getAddedSubList().stream().map(mapper).collect(Collectors.toList()));
|
||||
remapIndex(i);
|
||||
}
|
||||
nextAdd(c.getFrom(), c.getTo());
|
||||
}
|
||||
}
|
||||
}
|
||||
endChange();
|
||||
}
|
||||
|
||||
private void remapIndex(int i)
|
||||
{
|
||||
if (i >= mapped.size())
|
||||
{
|
||||
for (int j = mapped.size(); j <= i; j++)
|
||||
{
|
||||
mapped.add(mapper.apply(getSource().get(j)));
|
||||
}
|
||||
}
|
||||
mapped.set(i, mapper.apply(getSource().get(i)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceIndex(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E get(int index)
|
||||
{
|
||||
return mapped.get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return mapped.size();
|
||||
}
|
||||
}
|
43
src/main/java/lighthouse/threading/MarshallingObservers.java
Normal file
43
src/main/java/lighthouse/threading/MarshallingObservers.java
Normal file
@ -0,0 +1,43 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.*;
|
||||
|
||||
/**
|
||||
* An attempt to make multi-threading and observable/reactive UI programming work together inside JavaFX without too
|
||||
* many headaches. This class allows you to register change listeners on the target Observable which will be
|
||||
* run with the given {@link java.util.concurrent.Executor}. In this way an observable collection which is updated by
|
||||
* one thread can be observed from another thread without needing to use explicit locks or explicit marshalling.
|
||||
*/
|
||||
public class MarshallingObservers
|
||||
{
|
||||
public static InvalidationListener addListener(Observable observable, InvalidationListener listener, Executor executor)
|
||||
{
|
||||
InvalidationListener l = x -> executor.execute(() -> listener.invalidated(x));
|
||||
observable.addListener(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public static <T> ListChangeListener<T> addListener(ObservableList<T> observable, ListChangeListener<T> listener, Executor executor)
|
||||
{
|
||||
ListChangeListener<T> l = (ListChangeListener.Change<? extends T> c) -> executor.execute(() -> listener.onChanged(c));
|
||||
observable.addListener(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public static <T> SetChangeListener<T> addListener(ObservableSet<T> observable, SetChangeListener<T> listener, Executor executor)
|
||||
{
|
||||
SetChangeListener<T> l = (SetChangeListener.Change<? extends T> c) -> executor.execute(() -> listener.onChanged(c));
|
||||
observable.addListener(l);
|
||||
return l;
|
||||
}
|
||||
|
||||
public static <K, V> MapChangeListener<K, V> addListener(ObservableMap<K, V> observable, MapChangeListener<K, V> listener, Executor executor)
|
||||
{
|
||||
MapChangeListener<K, V> l = (MapChangeListener.Change<? extends K, ? extends V> c) -> executor.execute(() -> listener.onChanged(c));
|
||||
observable.addListener(l);
|
||||
return l;
|
||||
}
|
||||
}
|
195
src/main/java/lighthouse/threading/ObservableMirrors.java
Normal file
195
src/main/java/lighthouse/threading/ObservableMirrors.java
Normal file
@ -0,0 +1,195 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javafx.beans.WeakListener;
|
||||
import javafx.collections.*;
|
||||
|
||||
/**
|
||||
* Utility functions that mirror changes from one list into another list. JavaFX already provides this functionality
|
||||
* of course under the name "content binding"; a mirror is a content binding that relays changes into other threads
|
||||
* first. Thus you can have an ObservableList which is updated in one thread, but still bound to directly in the UI
|
||||
* thread, without needing to worry about cross-thread interference.
|
||||
*/
|
||||
public class ObservableMirrors
|
||||
{
|
||||
/**
|
||||
* Creates an unmodifiable list that asynchronously follows changes in mirrored, with changes applied using
|
||||
* the given executor. This should only be called on the thread that owns the list to be mirrored, as the contents
|
||||
* will be read.
|
||||
*/
|
||||
public static <T> ObservableList<T> mirrorList(ObservableList<T> mirrored, AffinityExecutor runChangesIn)
|
||||
{
|
||||
ObservableList<T> result = FXCollections.observableArrayList();
|
||||
result.setAll(mirrored);
|
||||
mirrored.addListener(new ListMirror<T>(result, runChangesIn));
|
||||
return FXCollections.unmodifiableObservableList(result);
|
||||
}
|
||||
|
||||
private static class ListMirror<E> implements ListChangeListener<E>, WeakListener
|
||||
{
|
||||
private final WeakReference<ObservableList<E>> targetList;
|
||||
private final AffinityExecutor runChangesIn;
|
||||
|
||||
public ListMirror(ObservableList<E> list, AffinityExecutor runChangesIn)
|
||||
{
|
||||
this.targetList = new WeakReference<>(list);
|
||||
this.runChangesIn = runChangesIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(Change<? extends E> change)
|
||||
{
|
||||
final List<E> list = targetList.get();
|
||||
if (list == null)
|
||||
{
|
||||
change.getList().removeListener(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're already in the right thread this will just run the change immediately, as per normal.
|
||||
runChangesIn.executeASAP(() -> {
|
||||
while (change.next())
|
||||
{
|
||||
if (change.wasPermutated())
|
||||
{
|
||||
list.subList(change.getFrom(), change.getTo()).clear();
|
||||
list.addAll(change.getFrom(), change.getList().subList(change.getFrom(), change.getTo()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (change.wasRemoved())
|
||||
{
|
||||
list.subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
|
||||
}
|
||||
if (change.wasAdded())
|
||||
{
|
||||
list.addAll(change.getFrom(), change.getAddedSubList());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasGarbageCollected()
|
||||
{
|
||||
return targetList.get() == null;
|
||||
}
|
||||
|
||||
// Do we really need these?
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final List<E> list = targetList.get();
|
||||
return (list == null) ? 0 : list.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
final List<E> list1 = targetList.get();
|
||||
if (list1 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj instanceof ListMirror)
|
||||
{
|
||||
final ListMirror<?> other = (ListMirror<?>) obj;
|
||||
final List<?> list2 = other.targetList.get();
|
||||
return list1 == list2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an unmodifiable list that asynchronously follows changes in mirrored, with changes applied using
|
||||
* the given executor. This should only be called on the thread that owns the list to be mirrored, as the contents
|
||||
* will be read.
|
||||
*/
|
||||
public static <T> ObservableSet<T> mirrorSet(ObservableSet<T> mirrored, AffinityExecutor runChangesIn)
|
||||
{
|
||||
@SuppressWarnings("unchecked") ObservableSet<T> result = FXCollections.observableSet();
|
||||
result.addAll(mirrored);
|
||||
mirrored.addListener(new SetMirror<T>(result, runChangesIn));
|
||||
return FXCollections.unmodifiableObservableSet(result);
|
||||
}
|
||||
|
||||
private static class SetMirror<E> implements SetChangeListener<E>, WeakListener
|
||||
{
|
||||
private final WeakReference<ObservableSet<E>> targetSet;
|
||||
private final AffinityExecutor runChangesIn;
|
||||
|
||||
public SetMirror(ObservableSet<E> set, AffinityExecutor runChangesIn)
|
||||
{
|
||||
this.targetSet = new WeakReference<>(set);
|
||||
this.runChangesIn = runChangesIn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChanged(final Change<? extends E> change)
|
||||
{
|
||||
final ObservableSet<E> set = targetSet.get();
|
||||
if (set == null)
|
||||
{
|
||||
change.getSet().removeListener(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we're already in the right thread this will just run the change immediately, as per normal.
|
||||
runChangesIn.executeASAP(() -> {
|
||||
if (change.wasAdded())
|
||||
set.add(change.getElementAdded());
|
||||
if (change.wasRemoved())
|
||||
set.remove(change.getElementRemoved());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wasGarbageCollected()
|
||||
{
|
||||
return targetSet.get() == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final ObservableSet<E> set = targetSet.get();
|
||||
return (set == null) ? 0 : set.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
final Set<E> set1 = targetSet.get();
|
||||
if (set1 == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj instanceof SetMirror)
|
||||
{
|
||||
final SetMirror<?> other = (SetMirror<?>) obj;
|
||||
final Set<?> list2 = other.targetSet.get();
|
||||
return set1 == list2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
@ -22,25 +22,30 @@
|
||||
<appender-ref ref="FILE"/>
|
||||
</root>
|
||||
|
||||
<logger name="io.bitsquare" level="TRACE"/>
|
||||
<logger name="io.bitsquare" level="DEBUG"/>
|
||||
<logger name="com.google.bitcoin" level="WARN"/>
|
||||
|
||||
|
||||
<logger name="net.tomp2p" level="WARN"/>
|
||||
|
||||
<logger name="io.netty.util" level="WARN"/>
|
||||
<logger name="io.netty.channel" level="WARN"/>
|
||||
<logger name="io.netty.buffer" level="WARN"/>
|
||||
|
||||
<logger name="com.google.bitcoin" level="INFO"/>
|
||||
<logger name="net.tomp2p" level="INFO"/>
|
||||
|
||||
<!--
|
||||
-->
|
||||
<logger name="com.google.bitcoin.core.Wallet" level="INFO"/>
|
||||
<logger name="com.google.bitcoin.core.MemoryPool" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.net.discovery.DnsDiscovery" level="WARN" additivity="false"/>
|
||||
|
||||
<logger name="com.google.bitcoin.core.DownloadListener" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.TransactionOutput" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.BitcoinSerializer" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.Peer" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.PeerGroup" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.PeerSocketHandler" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.net.NioClientManager" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.net.ConnectionHandler" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.Wallet" level="INFO"/>
|
||||
<logger name="com.google.bitcoin.core.MemoryPool" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.net.discovery.DnsDiscovery" level="WARN" additivity="false"/>
|
||||
|
||||
<logger name="com.google.bitcoin.core.DownloadListener" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.TransactionOutput" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.BitcoinSerializer" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.Peer" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.PeerGroup" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.core.PeerSocketHandler" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.net.NioClientManager" level="WARN" additivity="false"/>
|
||||
<logger name="com.google.bitcoin.net.ConnectionHandler" level="WARN" additivity="false"/>
|
||||
-->
|
||||
|
||||
</configuration>
|
||||
|
18
src/test/java/io/bitsquare/BitSquareTestSuite.java
Normal file
18
src/test/java/io/bitsquare/BitSquareTestSuite.java
Normal file
@ -0,0 +1,18 @@
|
||||
package io.bitsquare;
|
||||
|
||||
import io.bitsquare.btc.BtcValidatorTest;
|
||||
import io.bitsquare.gui.util.BitSquareConverterTest;
|
||||
import io.bitsquare.gui.util.BitSquareValidatorTest;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
||||
@RunWith(Suite.class)
|
||||
@Suite.SuiteClasses({
|
||||
BtcValidatorTest.class,
|
||||
BitSquareConverterTest.class,
|
||||
BitSquareValidatorTest.class,
|
||||
})
|
||||
|
||||
public class BitSquareTestSuite
|
||||
{
|
||||
}
|
34
src/test/java/io/bitsquare/btc/BtcValidatorTest.java
Normal file
34
src/test/java/io/bitsquare/btc/BtcValidatorTest.java
Normal file
@ -0,0 +1,34 @@
|
||||
package io.bitsquare.btc;
|
||||
|
||||
import com.google.bitcoin.core.Transaction;
|
||||
import java.math.BigInteger;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BtcValidatorTest
|
||||
{
|
||||
@Test
|
||||
public void testIsMinSpendableAmount()
|
||||
{
|
||||
BigInteger amount = null;
|
||||
//noinspection ConstantConditions
|
||||
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
|
||||
|
||||
amount = BigInteger.ZERO;
|
||||
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
|
||||
|
||||
amount = FeePolicy.TX_FEE;
|
||||
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
|
||||
|
||||
amount = Transaction.MIN_NONDUST_OUTPUT;
|
||||
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
|
||||
|
||||
amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT);
|
||||
assertFalse("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
|
||||
|
||||
amount = FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT).add(BigInteger.ONE);
|
||||
assertTrue("tx unfunded, pending", BtcValidator.isMinSpendableAmount(amount));
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package io.bitsquare.gui.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class BitSquareConverterTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void testStringToDouble()
|
||||
{
|
||||
|
||||
assertEquals(1, BitSquareConverter.stringToDouble("1"), 0);
|
||||
assertEquals(0.1, BitSquareConverter.stringToDouble("0.1"), 0);
|
||||
assertEquals(0.1, BitSquareConverter.stringToDouble("0,1"), 0);
|
||||
assertEquals(1, BitSquareConverter.stringToDouble("1.0"), 0);
|
||||
assertEquals(1, BitSquareConverter.stringToDouble("1,0"), 0);
|
||||
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble("1,000.2"), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble("1,000.2"), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble(null), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble(""), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble(""), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble("."), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble(","), 0);
|
||||
assertEquals(Double.NEGATIVE_INFINITY, BitSquareConverter.stringToDouble("a"), 0);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package io.bitsquare.gui.util;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BitSquareValidatorTest
|
||||
{
|
||||
@Test
|
||||
public void testValidateStringAsDouble()
|
||||
{
|
||||
assertTrue(BitSquareValidator.validateStringAsDouble("0"));
|
||||
assertTrue(BitSquareValidator.validateStringAsDouble("1"));
|
||||
assertTrue(BitSquareValidator.validateStringAsDouble("0,1"));
|
||||
assertTrue(BitSquareValidator.validateStringAsDouble("0.01"));
|
||||
|
||||
assertFalse(BitSquareValidator.validateStringAsDouble(""));
|
||||
assertFalse(BitSquareValidator.validateStringAsDouble("a"));
|
||||
assertFalse(BitSquareValidator.validateStringAsDouble("0.0.1"));
|
||||
assertFalse(BitSquareValidator.validateStringAsDouble("1,000.1"));
|
||||
assertFalse(BitSquareValidator.validateStringAsDouble("1.000,1"));
|
||||
assertFalse(BitSquareValidator.validateStringAsDouble(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateStringNotEmpty()
|
||||
{
|
||||
assertTrue(BitSquareValidator.validateStringNotEmpty("a"));
|
||||
assertTrue(BitSquareValidator.validateStringNotEmpty("123"));
|
||||
|
||||
assertFalse(BitSquareValidator.validateStringNotEmpty(""));
|
||||
assertFalse(BitSquareValidator.validateStringNotEmpty(" "));
|
||||
assertFalse(BitSquareValidator.validateStringNotEmpty(null));
|
||||
}
|
||||
|
||||
|
||||
}
|
375
src/test/java/io/bitsquare/msg/P2PNodeTest.java
Normal file
375
src/test/java/io/bitsquare/msg/P2PNodeTest.java
Normal file
@ -0,0 +1,375 @@
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.util.Random;
|
||||
import net.tomp2p.dht.FutureGet;
|
||||
import net.tomp2p.dht.FuturePut;
|
||||
import net.tomp2p.dht.FutureRemove;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.FutureDirect;
|
||||
import net.tomp2p.peers.Number160;
|
||||
import net.tomp2p.peers.PeerAddress;
|
||||
import net.tomp2p.rpc.ObjectDataReply;
|
||||
import net.tomp2p.storage.Data;
|
||||
import net.tomp2p.utils.Utils;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class P2PNodeTest
|
||||
{
|
||||
private static final Logger log = LoggerFactory.getLogger(P2PNodeTest.class);
|
||||
|
||||
final private static Random rnd = new Random(42L);
|
||||
|
||||
@Test
|
||||
public void testSendData() throws Exception
|
||||
{
|
||||
PeerDHT[] peers = UtilsDHT2.createNodes(3, rnd, 41001);
|
||||
PeerDHT master = peers[0];
|
||||
PeerDHT client = peers[1];
|
||||
PeerDHT otherPeer = peers[2];
|
||||
UtilsDHT2.perfectRouting(peers);
|
||||
|
||||
|
||||
for (final PeerDHT peer : peers)
|
||||
{
|
||||
peer.peer().objectDataReply(new ObjectDataReply()
|
||||
{
|
||||
@Override
|
||||
public Object reply(PeerAddress sender, Object request) throws Exception
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
|
||||
keyGen.initialize(1024);
|
||||
KeyPair keyPairClient = keyGen.genKeyPair();
|
||||
KeyPair keyPairOtherPeer = keyGen.genKeyPair();
|
||||
|
||||
P2PNode node;
|
||||
Number160 locationKey;
|
||||
Object object;
|
||||
FutureDirect futureDirect;
|
||||
|
||||
node = new P2PNode(keyPairClient, client);
|
||||
object = "clients data";
|
||||
futureDirect = node.sendData(otherPeer.peerAddress(), object);
|
||||
futureDirect.awaitUninterruptibly();
|
||||
|
||||
assertTrue(futureDirect.isSuccess());
|
||||
// we return true from objectDataReply
|
||||
assertTrue((Boolean) futureDirect.object());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtectedPutGet() throws Exception
|
||||
{
|
||||
PeerDHT[] peers = UtilsDHT2.createNodes(3, rnd, 41001);
|
||||
PeerDHT master = peers[0];
|
||||
PeerDHT client = peers[1];
|
||||
PeerDHT otherPeer = peers[2];
|
||||
UtilsDHT2.perfectRouting(peers);
|
||||
|
||||
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
|
||||
keyGen.initialize(1024);
|
||||
KeyPair keyPairClient = keyGen.genKeyPair();
|
||||
KeyPair keyPairOtherPeer = keyGen.genKeyPair();
|
||||
|
||||
P2PNode node;
|
||||
Number160 locationKey;
|
||||
Data data;
|
||||
FuturePut futurePut;
|
||||
FutureGet futureGet;
|
||||
|
||||
// otherPeer tries to squat clients location store
|
||||
// he can do it but as he has not the domain key of the client he cannot do any harm
|
||||
// he only can store und that path: locationKey.otherPeerDomainKey.data
|
||||
node = new P2PNode(keyPairOtherPeer, otherPeer);
|
||||
locationKey = Number160.createHash("clients location");
|
||||
data = new Data("otherPeer data");
|
||||
futurePut = node.putDomainProtectedData(locationKey, data);
|
||||
futurePut.awaitUninterruptibly();
|
||||
assertTrue(futurePut.isSuccess());
|
||||
|
||||
futureGet = node.getDomainProtectedData(locationKey, keyPairOtherPeer.getPublic());
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
assertEquals("otherPeer data", futureGet.data().object());
|
||||
|
||||
// client store his data und his domainkey, no problem with previous occupied
|
||||
// he only can store und that path: locationKey.clientDomainKey.data
|
||||
node = new P2PNode(keyPairClient, client);
|
||||
locationKey = Number160.createHash("clients location");
|
||||
data = new Data("client data");
|
||||
futurePut = node.putDomainProtectedData(locationKey, data);
|
||||
futurePut.awaitUninterruptibly();
|
||||
assertTrue(futurePut.isSuccess());
|
||||
|
||||
futureGet = node.getDomainProtectedData(locationKey, keyPairClient.getPublic());
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
assertEquals("client data", futureGet.data().object());
|
||||
|
||||
// also other peers can read that data if they know the public key of the client
|
||||
node = new P2PNode(keyPairOtherPeer, otherPeer);
|
||||
futureGet = node.getDomainProtectedData(locationKey, keyPairClient.getPublic());
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
assertEquals("client data", futureGet.data().object());
|
||||
|
||||
|
||||
// other peer try to use pub key of other peer as domain key hash.
|
||||
// must fail as he don't have the full key pair (private key of client missing)
|
||||
locationKey = Number160.createHash("clients location");
|
||||
data = new Data("otherPeer data hack");
|
||||
|
||||
data.protectEntry(keyPairOtherPeer);
|
||||
// he use the pub key from the client
|
||||
final Number160 keyHash = Utils.makeSHAHash(keyPairClient.getPublic().getEncoded());
|
||||
futurePut = otherPeer.put(locationKey).data(data).keyPair(keyPairOtherPeer).domainKey(keyHash).protectDomain().start();
|
||||
|
||||
futurePut.awaitUninterruptibly();
|
||||
assertFalse(futurePut.isSuccess());
|
||||
|
||||
// he can read his prev. stored data
|
||||
node = new P2PNode(keyPairOtherPeer, otherPeer);
|
||||
futureGet = node.getDomainProtectedData(locationKey, keyPairOtherPeer.getPublic());
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
assertEquals("otherPeer data", futureGet.data().object());
|
||||
|
||||
// he can read clients data
|
||||
futureGet = node.getDomainProtectedData(locationKey, keyPairClient.getPublic());
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
assertEquals("client data", futureGet.data().object());
|
||||
|
||||
master.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddToListGetList() throws Exception
|
||||
{
|
||||
PeerDHT[] peers = UtilsDHT2.createNodes(3, rnd, 41001);
|
||||
PeerDHT master = peers[0];
|
||||
PeerDHT client = peers[1];
|
||||
PeerDHT otherPeer = peers[2];
|
||||
UtilsDHT2.perfectRouting(peers);
|
||||
|
||||
P2PNode node;
|
||||
final KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
|
||||
keyGen.initialize(1024);
|
||||
KeyPair keyPairClient = keyGen.genKeyPair();
|
||||
KeyPair keyPairOtherPeer = keyGen.genKeyPair();
|
||||
|
||||
Number160 locationKey;
|
||||
Data data;
|
||||
FuturePut futurePut;
|
||||
FutureGet futureGet;
|
||||
|
||||
// client add a value
|
||||
node = new P2PNode(keyPairClient, client);
|
||||
locationKey = Number160.createHash("add to list clients location");
|
||||
data = new Data("add to list client data1");
|
||||
Data data_1 = data;
|
||||
futurePut = node.addProtectedData(locationKey, data);
|
||||
futurePut.awaitUninterruptibly();
|
||||
assertTrue(futurePut.isSuccess());
|
||||
|
||||
data = new Data("add to list client data2");
|
||||
Data data_2 = data;
|
||||
futurePut = node.addProtectedData(locationKey, data);
|
||||
futurePut.awaitUninterruptibly();
|
||||
assertTrue(futurePut.isSuccess());
|
||||
|
||||
futureGet = node.getDataMap(locationKey);
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
boolean foundData1 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data1");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
boolean foundData2 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data2");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
assertTrue(foundData1);
|
||||
assertTrue(foundData2);
|
||||
assertEquals(2, futureGet.dataMap().values().size());
|
||||
|
||||
|
||||
// other peer tried to overwrite that entry
|
||||
// but will not succeed, instead he will add a new entry.
|
||||
// TODO investigate why it is not possible to overwrite the entry with that method
|
||||
// The protection entry with the key does not make any difference as also the client himself cannot overwrite any entry
|
||||
// http://tomp2p.net/doc/P2P-with-TomP2P-1.pdf
|
||||
// "add(location_key, value) is translated to put(location_key, hash(value), value)"
|
||||
|
||||
// fake content key with content key from previous clients entry
|
||||
Number160 contentKey = Number160.createHash("add to list client data1");
|
||||
|
||||
data = new Data("add to list other peer data HACK!");
|
||||
data.protectEntry(keyPairOtherPeer); // also with client key it does not work...
|
||||
futurePut = otherPeer.put(locationKey).data(contentKey, data).keyPair(keyPairOtherPeer).start();
|
||||
futurePut.awaitUninterruptibly();
|
||||
assertTrue(futurePut.isSuccess());
|
||||
|
||||
node = new P2PNode(keyPairOtherPeer, otherPeer);
|
||||
futureGet = node.getDataMap(locationKey);
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
|
||||
foundData1 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data1");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
foundData2 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data2");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
boolean foundData3 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list other peer data HACK!");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
assertTrue(foundData1);
|
||||
assertTrue(foundData2);
|
||||
assertTrue(foundData3);
|
||||
assertEquals(3, futureGet.dataMap().values().size());
|
||||
|
||||
|
||||
// client removes his entry -> OK
|
||||
node = new P2PNode(keyPairClient, client);
|
||||
FutureRemove futureRemove = node.removeFromDataMap(locationKey, data_1);
|
||||
futureRemove.awaitUninterruptibly();
|
||||
assertTrue(futureRemove.isSuccess());
|
||||
|
||||
futureGet = node.getDataMap(locationKey);
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
|
||||
foundData1 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data1");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
foundData2 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data2");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
foundData3 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list other peer data HACK!");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
assertFalse(foundData1);
|
||||
assertTrue(foundData2);
|
||||
assertTrue(foundData3);
|
||||
assertEquals(2, futureGet.dataMap().values().size());
|
||||
|
||||
|
||||
// otherPeer tries to removes client entry -> FAIL
|
||||
node = new P2PNode(keyPairOtherPeer, otherPeer);
|
||||
futureRemove = node.removeFromDataMap(locationKey, data_2);
|
||||
futureRemove.awaitUninterruptibly();
|
||||
assertFalse(futureRemove.isSuccess());
|
||||
|
||||
futureGet = node.getDataMap(locationKey);
|
||||
futureGet.awaitUninterruptibly();
|
||||
assertTrue(futureGet.isSuccess());
|
||||
|
||||
foundData1 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data1");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
foundData2 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list client data2");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
foundData3 = futureGet.dataMap().values().stream().anyMatch(data1 -> {
|
||||
try
|
||||
{
|
||||
return data1.object().equals("add to list other peer data HACK!");
|
||||
} catch (ClassNotFoundException | IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
assertFalse(foundData1);
|
||||
assertTrue(foundData2);
|
||||
assertTrue(foundData3);
|
||||
assertEquals(2, futureGet.dataMap().values().size());
|
||||
|
||||
|
||||
master.shutdown();
|
||||
}
|
||||
|
||||
|
||||
}
|
412
src/test/java/io/bitsquare/msg/UtilsDHT2.java
Normal file
412
src/test/java/io/bitsquare/msg/UtilsDHT2.java
Normal file
@ -0,0 +1,412 @@
|
||||
/*
|
||||
* Copyright 2012 Thomas Bocek
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package io.bitsquare.msg;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import net.tomp2p.connection.Bindings;
|
||||
import net.tomp2p.dht.PeerBuilderDHT;
|
||||
import net.tomp2p.dht.PeerDHT;
|
||||
import net.tomp2p.futures.FutureBootstrap;
|
||||
import net.tomp2p.futures.FutureDiscover;
|
||||
import net.tomp2p.message.Message;
|
||||
import net.tomp2p.message.Message.Type;
|
||||
import net.tomp2p.p2p.AutomaticFuture;
|
||||
import net.tomp2p.p2p.Peer;
|
||||
import net.tomp2p.p2p.PeerBuilder;
|
||||
import net.tomp2p.peers.*;
|
||||
|
||||
public class UtilsDHT2
|
||||
{
|
||||
/**
|
||||
* Used to make the testcases predictable. Used as an input for {@link java.util.Random}.
|
||||
*/
|
||||
public static final long THE_ANSWER = 42L;
|
||||
|
||||
/**
|
||||
* Having two peers in a network, the seed needs to be different, otherwise we create a peer with the same id twice.
|
||||
*/
|
||||
public static final long THE_ANSWER2 = 43L;
|
||||
|
||||
public static Message createDummyMessage() throws UnknownHostException
|
||||
{
|
||||
return createDummyMessage(false, false);
|
||||
}
|
||||
|
||||
public static Message createDummyMessage(boolean firewallUDP, boolean firewallTCP)
|
||||
throws UnknownHostException
|
||||
{
|
||||
return createDummyMessage(new Number160("0x4321"), "127.0.0.1", 8001, 8002, new Number160("0x1234"),
|
||||
"127.0.0.1", 8003, 8004, (byte) 0, Type.REQUEST_1, firewallUDP, firewallTCP);
|
||||
}
|
||||
|
||||
public static PeerAddress createAddress(Number160 id) throws UnknownHostException
|
||||
{
|
||||
return createAddress(id, "127.0.0.1", 8005, 8006, false, false);
|
||||
}
|
||||
|
||||
public static PeerAddress createAddress() throws UnknownHostException
|
||||
{
|
||||
return createAddress(new Number160("0x5678"), "127.0.0.1", 8005, 8006, false, false);
|
||||
}
|
||||
|
||||
public static PeerAddress createAddress(int id) throws UnknownHostException
|
||||
{
|
||||
return createAddress(new Number160(id), "127.0.0.1", 8005, 8006, false, false);
|
||||
}
|
||||
|
||||
public static PeerAddress createAddress(String id) throws UnknownHostException
|
||||
{
|
||||
return createAddress(new Number160(id), "127.0.0.1", 8005, 8006, false, false);
|
||||
}
|
||||
|
||||
public static PeerAddress createAddress(Number160 idSender, String inetSender, int tcpPortSender,
|
||||
int udpPortSender, boolean firewallUDP, boolean firewallTCP) throws UnknownHostException
|
||||
{
|
||||
InetAddress inetSend = InetAddress.getByName(inetSender);
|
||||
PeerSocketAddress peerSocketAddress = new PeerSocketAddress(inetSend, tcpPortSender, udpPortSender);
|
||||
PeerAddress n1 = new PeerAddress(idSender, peerSocketAddress, firewallTCP, firewallUDP, false,
|
||||
PeerAddress.EMPTY_PEER_SOCKET_ADDRESSES);
|
||||
return n1;
|
||||
}
|
||||
|
||||
public static Message createDummyMessage(Number160 idSender, String inetSender, int tcpPortSendor,
|
||||
int udpPortSender, Number160 idRecipien, String inetRecipient, int tcpPortRecipient,
|
||||
int udpPortRecipient, byte command, Type type, boolean firewallUDP, boolean firewallTCP)
|
||||
throws UnknownHostException
|
||||
{
|
||||
Message message = new Message();
|
||||
PeerAddress n1 = createAddress(idSender, inetSender, tcpPortSendor, udpPortSender, firewallUDP,
|
||||
firewallTCP);
|
||||
message.sender(n1);
|
||||
//
|
||||
PeerAddress n2 = createAddress(idRecipien, inetRecipient, tcpPortRecipient, udpPortRecipient,
|
||||
firewallUDP, firewallTCP);
|
||||
message.recipient(n2);
|
||||
message.type(type);
|
||||
message.command(command);
|
||||
return message;
|
||||
}
|
||||
|
||||
public static PeerDHT[] createNodes(int nrOfPeers, Random rnd, int port) throws Exception
|
||||
{
|
||||
return createNodes(nrOfPeers, rnd, port, null);
|
||||
}
|
||||
|
||||
public static PeerDHT[] createNodes(int nrOfPeers, Random rnd, int port, AutomaticFuture automaticFuture)
|
||||
throws Exception
|
||||
{
|
||||
return createNodes(nrOfPeers, rnd, port, automaticFuture, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates peers for testing. The first peer (peer[0]) will be used as the master. This means that shutting down
|
||||
* peer[0] will shut down all other peers
|
||||
*
|
||||
* @param nrOfPeers The number of peers to create including the master
|
||||
* @param rnd The random object to create random peer IDs
|
||||
* @param port The port where the master peer will listen to
|
||||
* @return All the peers, with the master peer at position 0 -> peer[0]
|
||||
* @throws Exception If the creation of nodes fail.
|
||||
*/
|
||||
public static PeerDHT[] createNodes(int nrOfPeers, Random rnd, int port, AutomaticFuture automaticFuture,
|
||||
boolean maintenance) throws Exception
|
||||
{
|
||||
if (nrOfPeers < 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot create less than 1 peer");
|
||||
}
|
||||
Bindings bindings = new Bindings();
|
||||
PeerDHT[] peers = new PeerDHT[nrOfPeers];
|
||||
final Peer master;
|
||||
if (automaticFuture != null)
|
||||
{
|
||||
Number160 peerId = new Number160(rnd);
|
||||
PeerMap peerMap = new PeerMap(new PeerMapConfiguration(peerId));
|
||||
master = new PeerBuilder(peerId)
|
||||
.ports(port).enableMaintenance(maintenance)
|
||||
.externalBindings(bindings).peerMap(peerMap).start().addAutomaticFuture(automaticFuture);
|
||||
peers[0] = new PeerBuilderDHT(master).start();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Number160 peerId = new Number160(rnd);
|
||||
PeerMap peerMap = new PeerMap(new PeerMapConfiguration(peerId));
|
||||
master = new PeerBuilder(peerId).enableMaintenance(maintenance).externalBindings(bindings)
|
||||
.peerMap(peerMap).ports(port).start();
|
||||
peers[0] = new PeerBuilderDHT(master).start();
|
||||
}
|
||||
|
||||
for (int i = 1; i < nrOfPeers; i++)
|
||||
{
|
||||
if (automaticFuture != null)
|
||||
{
|
||||
Number160 peerId = new Number160(rnd);
|
||||
PeerMap peerMap = new PeerMap(new PeerMapConfiguration(peerId));
|
||||
Peer peer = new PeerBuilder(peerId)
|
||||
.masterPeer(master)
|
||||
.enableMaintenance(maintenance).enableMaintenance(maintenance).peerMap(peerMap).externalBindings(bindings).start().addAutomaticFuture(automaticFuture);
|
||||
peers[i] = new PeerBuilderDHT(peer).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Number160 peerId = new Number160(rnd);
|
||||
PeerMap peerMap = new PeerMap(new PeerMapConfiguration(peerId).peerNoVerification());
|
||||
Peer peer = new PeerBuilder(peerId).enableMaintenance(maintenance)
|
||||
.externalBindings(bindings).peerMap(peerMap).masterPeer(master)
|
||||
.start();
|
||||
peers[i] = new PeerBuilderDHT(peer).start();
|
||||
}
|
||||
}
|
||||
System.err.println("peers created.");
|
||||
return peers;
|
||||
}
|
||||
|
||||
public static Peer[] createRealNodes(int nrOfPeers, Random rnd, int startPort,
|
||||
AutomaticFuture automaticFuture) throws Exception
|
||||
{
|
||||
if (nrOfPeers < 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot create less than 1 peer");
|
||||
}
|
||||
Peer[] peers = new Peer[nrOfPeers];
|
||||
for (int i = 0; i < nrOfPeers; i++)
|
||||
{
|
||||
peers[i] = new PeerBuilder(new Number160(rnd))
|
||||
.ports(startPort + i).start().addAutomaticFuture(automaticFuture);
|
||||
}
|
||||
System.err.println("real peers created.");
|
||||
return peers;
|
||||
}
|
||||
|
||||
public static Peer[] createNonMaintenanceNodes(int nrOfPeers, Random rnd, int port) throws IOException
|
||||
{
|
||||
if (nrOfPeers < 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot create less than 1 peer");
|
||||
}
|
||||
Peer[] peers = new Peer[nrOfPeers];
|
||||
peers[0] = new PeerBuilder(new Number160(rnd)).enableMaintenance(false).ports(port).start();
|
||||
for (int i = 1; i < nrOfPeers; i++)
|
||||
{
|
||||
peers[i] = new PeerBuilder(new Number160(rnd)).enableMaintenance(false).masterPeer(peers[0])
|
||||
.start();
|
||||
}
|
||||
System.err.println("non-maintenance peers created.");
|
||||
return peers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perfect routing, where each neighbor has contacted each other. This means that for small number of peers, every
|
||||
* peer knows every other peer.
|
||||
*
|
||||
* @param peers The peers taking part in the p2p network.
|
||||
*/
|
||||
public static void perfectRouting(PeerDHT... peers)
|
||||
{
|
||||
for (int i = 0; i < peers.length; i++)
|
||||
{
|
||||
for (int j = 0; j < peers.length; j++)
|
||||
peers[i].peer().peerBean().peerMap().peerFound(peers[j].peer().peerAddress(), null);
|
||||
}
|
||||
System.err.println("perfect routing done.");
|
||||
}
|
||||
|
||||
public static void perfectRoutingIndirect(PeerDHT... peers)
|
||||
{
|
||||
for (int i = 0; i < peers.length; i++)
|
||||
{
|
||||
for (int j = 0; j < peers.length; j++)
|
||||
peers[i].peerBean().peerMap().peerFound(peers[j].peerAddress(), peers[j].peerAddress());
|
||||
}
|
||||
System.err.println("perfect routing done.");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException
|
||||
{
|
||||
createTempDirectory();
|
||||
}
|
||||
|
||||
private static final int TEMP_DIR_ATTEMPTS = 10000;
|
||||
|
||||
public static File createTempDirectory() throws IOException
|
||||
{
|
||||
File baseDir = new File(System.getProperty("java.io.tmpdir"));
|
||||
String baseName = System.currentTimeMillis() + "-";
|
||||
|
||||
for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++)
|
||||
{
|
||||
File tempDir = new File(baseDir, baseName + counter);
|
||||
if (tempDir.mkdir())
|
||||
{
|
||||
return tempDir;
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("Failed to create directory within " + TEMP_DIR_ATTEMPTS
|
||||
+ " attempts (tried " + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
|
||||
}
|
||||
|
||||
public static Peer[] createAndAttachNodes(int nr, int port, Random rnd) throws Exception
|
||||
{
|
||||
Peer[] peers = new Peer[nr];
|
||||
for (int i = 0; i < nr; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
peers[0] = new PeerBuilder(new Number160(rnd)).ports(port).start();
|
||||
}
|
||||
else
|
||||
{
|
||||
peers[i] = new PeerBuilder(new Number160(rnd)).masterPeer(peers[0]).start();
|
||||
}
|
||||
}
|
||||
return peers;
|
||||
}
|
||||
|
||||
public static void bootstrap(Peer[] peers)
|
||||
{
|
||||
List<FutureBootstrap> futures1 = new ArrayList<FutureBootstrap>();
|
||||
List<FutureDiscover> futures2 = new ArrayList<FutureDiscover>();
|
||||
for (int i = 1; i < peers.length; i++)
|
||||
{
|
||||
FutureDiscover tmp = peers[i].discover().peerAddress(peers[0].peerAddress()).start();
|
||||
futures2.add(tmp);
|
||||
}
|
||||
for (FutureDiscover future : futures2)
|
||||
{
|
||||
future.awaitUninterruptibly();
|
||||
}
|
||||
for (int i = 1; i < peers.length; i++)
|
||||
{
|
||||
FutureBootstrap tmp = peers[i].bootstrap().peerAddress(peers[0].peerAddress()).start();
|
||||
futures1.add(tmp);
|
||||
}
|
||||
for (int i = 1; i < peers.length; i++)
|
||||
{
|
||||
FutureBootstrap tmp = peers[0].bootstrap().peerAddress(peers[i].peerAddress()).start();
|
||||
futures1.add(tmp);
|
||||
}
|
||||
for (FutureBootstrap future : futures1)
|
||||
future.awaitUninterruptibly();
|
||||
}
|
||||
|
||||
public static void routing(Number160 key, Peer[] peers, int start)
|
||||
{
|
||||
System.out.println("routing: searching for key " + key);
|
||||
NavigableSet<PeerAddress> pa1 = new TreeSet<PeerAddress>(PeerMap.createComparator(key));
|
||||
NavigableSet<PeerAddress> queried = new TreeSet<PeerAddress>(PeerMap.createComparator(key));
|
||||
Number160 result = Number160.ZERO;
|
||||
Number160 resultPeer = new Number160("0xd75d1a3d57841fbc9e2a3d175d6a35dc2e15b9f");
|
||||
int round = 0;
|
||||
while (!resultPeer.equals(result))
|
||||
{
|
||||
System.out.println("round " + round);
|
||||
round++;
|
||||
pa1.addAll(peers[start].peerBean().peerMap().all());
|
||||
queried.add(peers[start].peerAddress());
|
||||
System.out.println("closest so far: " + queried.first());
|
||||
PeerAddress next = pa1.pollFirst();
|
||||
while (queried.contains(next))
|
||||
{
|
||||
next = pa1.pollFirst();
|
||||
}
|
||||
result = next.peerId();
|
||||
start = findNr(next.peerId().toString(), peers);
|
||||
}
|
||||
}
|
||||
|
||||
public static void findInMap(PeerAddress key, Peer[] peers)
|
||||
{
|
||||
for (int i = 0; i < peers.length; i++)
|
||||
{
|
||||
if (peers[i].peerBean().peerMap().contains(key))
|
||||
{
|
||||
System.out.println("Peer " + i + " with the id " + peers[i].peerID() + " knows the peer "
|
||||
+ key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int findNr(String string, Peer[] peers)
|
||||
{
|
||||
for (int i = 0; i < peers.length; i++)
|
||||
{
|
||||
if (peers[i].peerID().equals(new Number160(string)))
|
||||
{
|
||||
System.out.println("we found the number " + i + " for peer with id " + string);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static Peer find(String string, Peer[] peers)
|
||||
{
|
||||
for (int i = 0; i < peers.length; i++)
|
||||
{
|
||||
if (peers[i].peerID().equals(new Number160(string)))
|
||||
{
|
||||
System.out.println("!!we found the number " + i + " for peer with id " + string);
|
||||
return peers[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void exec(String cmd) throws Exception
|
||||
{
|
||||
Process p = Runtime.getRuntime().exec(cmd);
|
||||
p.waitFor();
|
||||
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
|
||||
String line = null;
|
||||
while ((line = br.readLine()) != null)
|
||||
{
|
||||
System.out.println(line);
|
||||
}
|
||||
br.close();
|
||||
}
|
||||
|
||||
public static PeerAddress createAddressIP(String inet) throws UnknownHostException
|
||||
{
|
||||
return createAddress(Number160.createHash(inet), inet, 8005, 8006, false, false);
|
||||
}
|
||||
|
||||
public static PeerAddress[] createDummyAddress(int size, int portTCP, int portUDP) throws UnknownHostException
|
||||
{
|
||||
PeerAddress[] pa = new PeerAddress[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
pa[i] = createAddress(i + 1, portTCP, portUDP);
|
||||
}
|
||||
return pa;
|
||||
}
|
||||
|
||||
public static PeerAddress createAddress(int iid, int portTCP, int portUDP) throws UnknownHostException
|
||||
{
|
||||
Number160 id = new Number160(iid);
|
||||
InetAddress address = InetAddress.getByName("127.0.0.1");
|
||||
return new PeerAddress(id, address, portTCP, portUDP);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
public class ConcatenatingListTest
|
||||
{
|
||||
@Test
|
||||
public void basic() throws Exception
|
||||
{
|
||||
ObservableList<String> a = FXCollections.observableArrayList();
|
||||
ObservableList<String> b = FXCollections.observableArrayList();
|
||||
ConcatenatingList<String> concat = new ConcatenatingList<>(a, b);
|
||||
assertEquals(0, concat.size());
|
||||
a.add("1");
|
||||
assertEquals(1, concat.size());
|
||||
assertEquals("1", concat.get(0));
|
||||
b.add("2");
|
||||
assertEquals(2, concat.size());
|
||||
assertEquals("2", concat.get(1));
|
||||
}
|
||||
}
|
91
src/test/java/lighthouse/threading/MappedListTest.java
Normal file
91
src/test/java/lighthouse/threading/MappedListTest.java
Normal file
@ -0,0 +1,91 @@
|
||||
package lighthouse.threading;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MappedListTest
|
||||
{
|
||||
private ObservableList<String> inputs;
|
||||
private ObservableList<String> outputs;
|
||||
private Queue<ListChangeListener.Change<? extends String>> changes;
|
||||
|
||||
@Before
|
||||
public void setup()
|
||||
{
|
||||
inputs = FXCollections.observableArrayList();
|
||||
outputs = new MappedList<>(inputs, str -> "Hello " + str);
|
||||
changes = new LinkedList<>();
|
||||
outputs.addListener(changes::add);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void add() throws Exception
|
||||
{
|
||||
assertEquals(0, outputs.size());
|
||||
inputs.add("Mike");
|
||||
ListChangeListener.Change<? extends String> change = getChange();
|
||||
assertTrue(change.wasAdded());
|
||||
assertEquals("Hello Mike", change.getAddedSubList().get(0));
|
||||
assertEquals(1, outputs.size());
|
||||
assertEquals("Hello Mike", outputs.get(0));
|
||||
inputs.remove(0);
|
||||
assertEquals(0, outputs.size());
|
||||
}
|
||||
|
||||
private ListChangeListener.Change<? extends String> getChange()
|
||||
{
|
||||
ListChangeListener.Change<? extends String> change = changes.poll();
|
||||
change.next();
|
||||
return change;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove()
|
||||
{
|
||||
inputs.add("Mike");
|
||||
inputs.add("Dave");
|
||||
inputs.add("Katniss");
|
||||
getChange();
|
||||
getChange();
|
||||
getChange();
|
||||
assertEquals("Hello Mike", outputs.get(0));
|
||||
assertEquals("Hello Dave", outputs.get(1));
|
||||
assertEquals("Hello Katniss", outputs.get(2));
|
||||
inputs.remove(0);
|
||||
ListChangeListener.Change<? extends String> change = getChange();
|
||||
assertTrue(change.wasRemoved());
|
||||
assertEquals(2, outputs.size());
|
||||
assertEquals(1, change.getRemovedSize());
|
||||
assertEquals("Hello Mike", change.getRemoved().get(0));
|
||||
assertEquals("Hello Dave", outputs.get(0));
|
||||
|
||||
inputs.remove(1);
|
||||
assertEquals(1, outputs.size());
|
||||
assertEquals("Hello Dave", outputs.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replace() throws Exception
|
||||
{
|
||||
inputs.add("Mike");
|
||||
inputs.add("Dave");
|
||||
getChange();
|
||||
getChange();
|
||||
inputs.set(0, "Bob");
|
||||
assertEquals("Hello Bob", outputs.get(0));
|
||||
ListChangeListener.Change<? extends String> change = getChange();
|
||||
assertTrue(change.wasReplaced());
|
||||
assertEquals("Hello Mike", change.getRemoved().get(0));
|
||||
assertEquals("Hello Bob", change.getAddedSubList().get(0));
|
||||
}
|
||||
|
||||
// Could also test permutation here if I could figure out how to actually apply one!
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user