mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-05-14 12:32:15 -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
54 changed files with 3284 additions and 717 deletions
|
@ -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
|
See: https://bitcoinj.github.io/testing
|
||||||
You can change the network mode in the guice module: BitSquareModule.java
|
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:
|
### Resources:
|
||||||
* Web: http://bitsquare.io
|
* Web: http://bitsquare.io
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -124,11 +124,13 @@
|
||||||
<version>0.11.3</version>
|
<version>0.11.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.tomp2p</groupId>
|
<groupId>net.tomp2p</groupId>
|
||||||
<artifactId>TomP2P</artifactId>
|
<artifactId>tomp2p-all</artifactId>
|
||||||
<version>4.4</version>
|
<version>5.0-Alpha24-SNAPSHOT</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
Manifest-Version: 1.0
|
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);
|
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 String APP_NAME = "bitsquare";
|
||||||
private static Stage primaryStage;
|
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);
|
super(params, keyCrypter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,11 +190,9 @@ public class WalletFacade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void shutDown()
|
public void shutDown()
|
||||||
{
|
{
|
||||||
wallet.removeEventListener(walletEventListener);
|
wallet.removeEventListener(walletEventListener);
|
||||||
|
|
||||||
walletAppKit.stopAsync();
|
walletAppKit.stopAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -596,6 +594,7 @@ public class WalletFacade
|
||||||
|
|
||||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
|
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
|
// 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.coinSelector = new AddressBasedCoinSelector(params, getRegistrationAddressInfo(), false);
|
||||||
sendRequest.changeAddress = getRegistrationAddressInfo().getAddress();
|
sendRequest.changeAddress = getRegistrationAddressInfo().getAddress();
|
||||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
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
|
// Trade process
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -15,6 +15,7 @@ import io.bitsquare.btc.FeePolicy;
|
||||||
import io.bitsquare.btc.WalletFacade;
|
import io.bitsquare.btc.WalletFacade;
|
||||||
import io.bitsquare.crypto.CryptoFacade;
|
import io.bitsquare.crypto.CryptoFacade;
|
||||||
import io.bitsquare.msg.MessageFacade;
|
import io.bitsquare.msg.MessageFacade;
|
||||||
|
import io.bitsquare.msg.SeedNodeAddress;
|
||||||
import io.bitsquare.settings.Settings;
|
import io.bitsquare.settings.Settings;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.Trading;
|
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(String.class).annotatedWith(Names.named("networkType")).toInstance(WalletFacade.TEST_NET);
|
||||||
bind(NetworkParameters.class).toProvider(NetworkParametersProvider.class).asEagerSingleton();
|
bind(NetworkParameters.class).toProvider(NetworkParametersProvider.class).asEagerSingleton();
|
||||||
bind(BitSquareWalletAppKit.class).toProvider(BitSquareWalletAppKitProvider.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.Icons;
|
||||||
import io.bitsquare.gui.util.Transitions;
|
import io.bitsquare.gui.util.Transitions;
|
||||||
import io.bitsquare.locale.Localisation;
|
import io.bitsquare.locale.Localisation;
|
||||||
|
import io.bitsquare.msg.BootstrapListener;
|
||||||
import io.bitsquare.msg.MessageFacade;
|
import io.bitsquare.msg.MessageFacade;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.Direction;
|
import io.bitsquare.trade.Direction;
|
||||||
|
@ -176,8 +177,24 @@ public class MainController implements Initializable, NavigationController
|
||||||
|
|
||||||
private void init()
|
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);
|
trading.addTakeOfferRequestListener(this::onTakeOfferRequested);
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,14 @@ import javafx.scene.layout.AnchorPane;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import net.tomp2p.peers.Number160;
|
import net.tomp2p.peers.Number640;
|
||||||
import net.tomp2p.storage.Data;
|
import net.tomp2p.storage.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO remove tomp2p dependencies
|
||||||
|
* import net.tomp2p.peers.Number160;
|
||||||
|
* import net.tomp2p.storage.Data;
|
||||||
|
*/
|
||||||
@SuppressWarnings({"ALL", "UnusedParameters"})
|
@SuppressWarnings({"ALL", "UnusedParameters"})
|
||||||
public class ArbitratorOverviewController implements Initializable, ChildController, NavigationController, ArbitratorListener
|
public class ArbitratorOverviewController implements Initializable, ChildController, NavigationController, ArbitratorListener
|
||||||
{
|
{
|
||||||
|
@ -135,7 +140,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success)
|
public void onArbitratorsReceived(Map<Number640, Data> dataMap, boolean success)
|
||||||
{
|
{
|
||||||
if (success && dataMap != null)
|
if (success && dataMap != null)
|
||||||
{
|
{
|
||||||
|
@ -145,7 +150,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object arbitratorDataObject = arbitratorData.getObject();
|
Object arbitratorDataObject = arbitratorData.object();
|
||||||
if (arbitratorDataObject instanceof Arbitrator)
|
if (arbitratorDataObject instanceof Arbitrator)
|
||||||
{
|
{
|
||||||
Arbitrator arbitrator = (Arbitrator) arbitratorDataObject;
|
Arbitrator arbitrator = (Arbitrator) arbitratorDataObject;
|
||||||
|
|
|
@ -22,8 +22,8 @@ import io.bitsquare.msg.MessageFacade;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.user.Arbitrator;
|
import io.bitsquare.user.Arbitrator;
|
||||||
import io.bitsquare.user.Reputation;
|
import io.bitsquare.user.Reputation;
|
||||||
|
import io.bitsquare.user.User;
|
||||||
import io.bitsquare.util.DSAKeyUtil;
|
import io.bitsquare.util.DSAKeyUtil;
|
||||||
import java.io.IOException;
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -48,6 +48,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||||
private final Storage storage;
|
private final Storage storage;
|
||||||
private final WalletFacade walletFacade;
|
private final WalletFacade walletFacade;
|
||||||
private final MessageFacade messageFacade;
|
private final MessageFacade messageFacade;
|
||||||
|
private User user;
|
||||||
private Arbitrator arbitrator = new Arbitrator();
|
private Arbitrator arbitrator = new Arbitrator();
|
||||||
private ArbitratorProfileController arbitratorProfileController;
|
private ArbitratorProfileController arbitratorProfileController;
|
||||||
private boolean isEditMode;
|
private boolean isEditMode;
|
||||||
|
@ -92,11 +93,12 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private ArbitratorRegistrationController(Storage storage, WalletFacade walletFacade, MessageFacade messageFacade)
|
private ArbitratorRegistrationController(Storage storage, WalletFacade walletFacade, MessageFacade messageFacade, User user)
|
||||||
{
|
{
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.walletFacade = walletFacade;
|
this.walletFacade = walletFacade;
|
||||||
this.messageFacade = messageFacade;
|
this.messageFacade = messageFacade;
|
||||||
|
this.user = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -338,13 +340,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
messageFacade.addArbitrator(arbitrator);
|
messageFacade.addArbitrator(arbitrator);
|
||||||
} catch (IOException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
@ -474,7 +470,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
|
||||||
minArbitrationFeeTextField);
|
minArbitrationFeeTextField);
|
||||||
|
|
||||||
String pubKeyAsHex = walletFacade.getArbitratorDepositAddressInfo().getPubKeyAsHexString();
|
String pubKeyAsHex = walletFacade.getArbitratorDepositAddressInfo().getPubKeyAsHexString();
|
||||||
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey());
|
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey());
|
||||||
String name = nameTextField.getText();
|
String name = nameTextField.getText();
|
||||||
|
|
||||||
double maxTradeVolume = BitSquareConverter.stringToDouble(maxTradeVolumeTextField.getText());
|
double maxTradeVolume = BitSquareConverter.stringToDouble(maxTradeVolumeTextField.getText());
|
||||||
|
|
|
@ -205,11 +205,9 @@ public class CreateOfferController implements Initializable, ChildController, Hi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("create offer pubkey " + user.getMessagePubKeyAsHex());
|
|
||||||
|
|
||||||
if (user.getCurrentBankAccount() != null)
|
if (user.getCurrentBankAccount() != null)
|
||||||
{
|
{
|
||||||
offer = new Offer(user.getMessagePubKeyAsHex(),
|
offer = new Offer(user.getMessagePublicKey(),
|
||||||
direction,
|
direction,
|
||||||
BitSquareConverter.stringToDouble(priceTextField.getText()),
|
BitSquareConverter.stringToDouble(priceTextField.getText()),
|
||||||
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
|
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),
|
||||||
|
|
|
@ -42,12 +42,13 @@
|
||||||
<Label text="Balance:" GridPane.rowIndex="7"/>
|
<Label text="Balance:" GridPane.rowIndex="7"/>
|
||||||
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="8">
|
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="8">
|
||||||
<GridPane.margin>
|
<GridPane.margin>
|
||||||
<Insets top="10.0"/>
|
<Insets top="10.0"/>
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
</Label>
|
</Label>
|
||||||
|
-->
|
||||||
|
|
||||||
<Label text="Bank account type:" GridPane.rowIndex="9"/>
|
<Label text="Bank account type:" GridPane.rowIndex="9"/>
|
||||||
<TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.columnIndex="1" 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"/>
|
<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"/>
|
<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="15"/>
|
<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"
|
<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>
|
<GridPane.margin>
|
||||||
<Insets top="2.0"/>
|
<Insets top="2.0"/>
|
||||||
</GridPane.margin>
|
</GridPane.margin>
|
||||||
</ConfidenceProgressIndicator>
|
</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>
|
||||||
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>
|
<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.OrderBook;
|
||||||
import io.bitsquare.trade.orderbook.OrderBookFilter;
|
import io.bitsquare.trade.orderbook.OrderBookFilter;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import io.bitsquare.util.DSAKeyUtil;
|
|
||||||
import io.bitsquare.util.Utilities;
|
import io.bitsquare.util.Utilities;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -304,10 +303,6 @@ public class OrderBookController implements Initializable, ChildController
|
||||||
{
|
{
|
||||||
user.setAccountID(walletFacade.getRegistrationAddressInfo().toString());
|
user.setAccountID(walletFacade.getRegistrationAddressInfo().toString());
|
||||||
}
|
}
|
||||||
if (messageFacade != null && messageFacade.getPubKey() != null)
|
|
||||||
{
|
|
||||||
user.setMessagePubKeyAsHex(DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey()));
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.write(user.getClass().getName(), user);
|
storage.write(user.getClass().getName(), user);
|
||||||
} catch (InsufficientMoneyException e1)
|
} catch (InsufficientMoneyException e1)
|
||||||
|
@ -437,7 +432,7 @@ public class OrderBookController implements Initializable, ChildController
|
||||||
Image icon;
|
Image icon;
|
||||||
Offer offer = orderBookListItem.getOffer();
|
Offer offer = orderBookListItem.getOffer();
|
||||||
|
|
||||||
if (offer.getMessagePubKeyAsHex().equals(user.getMessagePubKeyAsHex()))
|
if (offer.getMessagePublicKey().equals(user.getMessagePublicKey()))
|
||||||
{
|
{
|
||||||
icon = Icons.getIconImage(Icons.REMOVE);
|
icon = Icons.getIconImage(Icons.REMOVE);
|
||||||
title = "Remove";
|
title = "Remove";
|
||||||
|
|
|
@ -191,7 +191,7 @@ public class TakerOfferController implements Initializable, ChildController
|
||||||
public void onBankTransferInited(String tradeId)
|
public void onBankTransferInited(String tradeId)
|
||||||
{
|
{
|
||||||
setTradeId(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.");
|
infoLabel.setText("Check your bank account and continue \n" + "when you have received the money.");
|
||||||
receivedFiatButton.setDisable(false);
|
receivedFiatButton.setDisable(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
</Label>
|
</Label>
|
||||||
|
|
||||||
<!-- row 7 -->
|
<!-- 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.columnIndex="1"
|
||||||
GridPane.rowIndex="7"/>
|
GridPane.rowIndex="7"/>
|
||||||
|
|
||||||
|
|
|
@ -139,7 +139,7 @@ public class SettingsController implements Initializable, ChildController, Navig
|
||||||
if (settings.getAcceptedArbitrators().isEmpty())
|
if (settings.getAcceptedArbitrators().isEmpty())
|
||||||
{
|
{
|
||||||
String pubKeyAsHex = Utils.bytesToHexString(new ECKey().getPubKey());
|
String pubKeyAsHex = Utils.bytesToHexString(new ECKey().getPubKey());
|
||||||
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey());
|
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey());
|
||||||
List<Locale> languages = new ArrayList<>();
|
List<Locale> languages = new ArrayList<>();
|
||||||
languages.add(LanguageUtil.getDefaultLanguageLocale());
|
languages.add(LanguageUtil.getDefaultLanguageLocale());
|
||||||
List<Arbitrator.METHOD> arbitrationMethods = new ArrayList<>();
|
List<Arbitrator.METHOD> arbitrationMethods = new ArrayList<>();
|
||||||
|
@ -168,13 +168,7 @@ public class SettingsController implements Initializable, ChildController, Navig
|
||||||
settings.addAcceptedArbitrator(arbitrator);
|
settings.addAcceptedArbitrator(arbitrator);
|
||||||
storage.write(settings.getClass().getName(), settings);
|
storage.write(settings.getClass().getName(), settings);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
messageFacade.addArbitrator(arbitrator);
|
messageFacade.addArbitrator(arbitrator);
|
||||||
} catch (IOException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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;
|
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.msg.listeners.*;
|
||||||
import io.bitsquare.trade.Offer;
|
import io.bitsquare.trade.Offer;
|
||||||
import io.bitsquare.user.Arbitrator;
|
import io.bitsquare.user.Arbitrator;
|
||||||
import io.bitsquare.util.DSAKeyUtil;
|
import io.bitsquare.user.User;
|
||||||
import io.bitsquare.util.StorageDirectory;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.PublicKey;
|
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.application.Platform;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import net.tomp2p.connection.Bindings;
|
import net.tomp2p.dht.FutureGet;
|
||||||
import net.tomp2p.connection.PeerConnection;
|
import net.tomp2p.dht.FuturePut;
|
||||||
import net.tomp2p.futures.*;
|
import net.tomp2p.dht.FutureRemove;
|
||||||
import net.tomp2p.p2p.Peer;
|
import net.tomp2p.dht.PeerDHT;
|
||||||
import net.tomp2p.p2p.PeerMaker;
|
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.Number160;
|
||||||
import net.tomp2p.peers.PeerAddress;
|
import net.tomp2p.peers.PeerAddress;
|
||||||
import net.tomp2p.storage.Data;
|
import net.tomp2p.storage.Data;
|
||||||
import net.tomp2p.storage.StorageDisk;
|
|
||||||
import net.tomp2p.utils.Utils;
|
import net.tomp2p.utils.Utils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
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.
|
* 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 implements MessageBroker
|
||||||
public class MessageFacade
|
|
||||||
{
|
{
|
||||||
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
|
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
|
||||||
// private static final String PING = "ping";
|
private static final String ARBITRATORS_ROOT = "ArbitratorsRoot";
|
||||||
// private static final String PONG = "pong";
|
|
||||||
private static final int MASTER_PEER_PORT = 5001;
|
private final P2PNode p2pNode;
|
||||||
|
|
||||||
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
|
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
|
||||||
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
|
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
|
||||||
|
|
||||||
private final List<IncomingTradeMessageListener> incomingTradeMessageListeners = 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
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@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,57 +67,53 @@ public class MessageFacade
|
||||||
// Public Methods
|
// 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);
|
p2pNode.start(new FutureCallback<PeerDHT>()
|
||||||
if (BitSquare.getAppName().contains("taker"))
|
|
||||||
{
|
{
|
||||||
port = 4501;
|
@Override
|
||||||
}
|
public void onSuccess(@Nullable PeerDHT result)
|
||||||
else if (BitSquare.getAppName().contains("offerer"))
|
|
||||||
{
|
{
|
||||||
port = 4500;
|
log.debug("p2pNode.start success result = " + result);
|
||||||
|
Platform.runLater(() -> bootstrapListener.onCompleted());
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
@Override
|
||||||
|
public void onFailure(Throwable t)
|
||||||
{
|
{
|
||||||
createMyPeerInstance(port);
|
log.error(t.toString());
|
||||||
// setupStorage();
|
Platform.runLater(() -> bootstrapListener.onFailed(t));
|
||||||
//TODO save periodically or get informed if network address changes
|
|
||||||
saveMyAddressToDHT();
|
|
||||||
setupReplyHandler();
|
|
||||||
} catch (IOException e)
|
|
||||||
{
|
|
||||||
shutDown();
|
|
||||||
log.error("Error at init myPeerInstance" + e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutDown()
|
public void shutDown()
|
||||||
{
|
{
|
||||||
if (myPeer != null)
|
if (p2pNode != null)
|
||||||
{
|
p2pNode.shutDown();
|
||||||
myPeer.shutdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Find peer address
|
// Find peer address by publicKey
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
|
|
||||||
|
public void getPeerAddress(PublicKey publicKey, GetPeerAddressListener listener)
|
||||||
{
|
{
|
||||||
final Number160 location = Number160.createHash(pubKeyAsHex);
|
final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded());
|
||||||
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
|
try
|
||||||
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
{
|
||||||
|
FutureGet futureGet = p2pNode.getDomainProtectedData(locationKey, publicKey);
|
||||||
|
|
||||||
|
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||||
{
|
{
|
||||||
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
|
if (baseFuture.isSuccess() && futureGet.data() != null)
|
||||||
{
|
{
|
||||||
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
|
final PeerAddress peerAddress = (PeerAddress) futureGet.data().object();
|
||||||
Platform.runLater(() -> listener.onResult(peerAddress));
|
Platform.runLater(() -> listener.onResult(peerAddress));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -127,92 +122,119 @@ public class MessageFacade
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} catch (IOException | ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error(e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Publish offer
|
// Offer
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void addOffer(Offer offer) throws IOException
|
public void addOffer(Offer offer)
|
||||||
{
|
{
|
||||||
log.trace("addOffer");
|
|
||||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||||
final Number160 contentKey = Number160.createHash(offer.getId());
|
try
|
||||||
final Data offerData = new Data(offer);
|
{
|
||||||
//offerData.setTTLSeconds(5);
|
final Data data = new Data(offer);
|
||||||
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, offerData).start();
|
// the offer is default 30 days valid
|
||||||
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
|
int defaultOfferTTL = 30 * 24 * 60 * 60 * 1000;
|
||||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
data.ttlSeconds(defaultOfferTTL);
|
||||||
|
|
||||||
|
FuturePut futurePut = p2pNode.addProtectedData(locationKey, data);
|
||||||
|
futurePut.addListener(new BaseFutureListener<BaseFuture>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(BaseFuture future) throws Exception
|
public void operationComplete(BaseFuture future) throws Exception
|
||||||
{
|
{
|
||||||
Platform.runLater(() -> onOfferAdded(offerData, future.isSuccess(), locationKey));
|
Platform.runLater(() -> {
|
||||||
}
|
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(data, future.isSuccess()));
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onOfferAdded(Data offerData, boolean success, Number160 locationKey)
|
|
||||||
{
|
|
||||||
log.trace("onOfferAdded");
|
|
||||||
setDirty(locationKey);
|
setDirty(locationKey);
|
||||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(offerData, success));
|
});
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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
|
@Override
|
||||||
public void operationComplete(BaseFuture future) throws Exception
|
public void exceptionCaught(Throwable t) throws Exception
|
||||||
{
|
{
|
||||||
final Map<Number160, Data> dataMap = getOffersFuture.getDataMap();
|
log.error(t.toString());
|
||||||
Platform.runLater(() -> onOffersReceived(dataMap, future.isSuccess()));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
} catch (IOException | ClassNotFoundException e)
|
||||||
|
|
||||||
private void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
|
|
||||||
{
|
{
|
||||||
log.trace("onOffersReceived");
|
e.printStackTrace();
|
||||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(dataMap, success));
|
log.error(e.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Remove offer
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void removeOffer(Offer offer)
|
public void removeOffer(Offer offer)
|
||||||
{
|
{
|
||||||
log.trace("removeOffer");
|
|
||||||
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
|
||||||
Number160 contentKey = Number160.createHash(offer.getId());
|
try
|
||||||
log.debug("removeOffer");
|
{
|
||||||
FutureDHT removeFuture = myPeer.remove(locationKey).setReturnResults().setContentKey(contentKey).start();
|
final Data data = new Data(offer);
|
||||||
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
FutureRemove futureRemove = p2pNode.removeFromDataMap(locationKey, data);
|
||||||
|
futureRemove.addListener(new BaseFutureListener<BaseFuture>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(BaseFuture future) throws Exception
|
public void operationComplete(BaseFuture future) throws Exception
|
||||||
{
|
{
|
||||||
Platform.runLater(() -> onOfferRemoved(removeFuture.getData(), future.isSuccess(), locationKey));
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onOfferRemoved(Data data, boolean success, Number160 locationKey)
|
@Override
|
||||||
|
public void exceptionCaught(Throwable t) throws Exception
|
||||||
{
|
{
|
||||||
log.trace("onOfferRemoved");
|
log.error(t.toString());
|
||||||
setDirty(locationKey);
|
}
|
||||||
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferRemoved(data, success));
|
});
|
||||||
|
} 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
|
||||||
|
{
|
||||||
|
Platform.runLater(() -> orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(futureGet.dataMap(), baseFuture.isSuccess())));
|
||||||
|
if (baseFuture.isSuccess())
|
||||||
|
{
|
||||||
|
log.trace("Get offers from DHT was successful. Stored data: [key: " + locationKey + ", values: " + futureGet.dataMap() + "]");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
log.error("Get offers from DHT failed with reason:" + baseFuture.failedReason());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,14 +244,13 @@ public class MessageFacade
|
||||||
|
|
||||||
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, OutgoingTradeMessageListener listener)
|
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, OutgoingTradeMessageListener listener)
|
||||||
{
|
{
|
||||||
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
|
FutureDirect futureDirect = p2pNode.sendData(peerAddress, tradeMessage);
|
||||||
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
|
futureDirect.addListener(new BaseFutureListener<BaseFuture>()
|
||||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
|
||||||
{
|
{
|
||||||
@Override
|
@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());
|
Platform.runLater(() -> listener.onResult());
|
||||||
}
|
}
|
||||||
|
@ -238,316 +259,109 @@ public class MessageFacade
|
||||||
Platform.runLater(() -> listener.onFailed());
|
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
|
// Arbitrators
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void addArbitrator(Arbitrator arbitrator) throws IOException
|
public void addArbitrator(Arbitrator arbitrator)
|
||||||
|
{
|
||||||
|
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
|
||||||
|
try
|
||||||
{
|
{
|
||||||
Number160 locationKey = Number160.createHash("Arbitrators");
|
|
||||||
final Number160 contentKey = Number160.createHash(arbitrator.getId());
|
|
||||||
final Data arbitratorData = new Data(arbitrator);
|
final Data arbitratorData = new Data(arbitrator);
|
||||||
//offerData.setTTLSeconds(5);
|
|
||||||
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, arbitratorData).start();
|
FuturePut addFuture = p2pNode.addProtectedData(locationKey, arbitratorData);
|
||||||
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
|
|
||||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(BaseFuture future) throws Exception
|
public void operationComplete(BaseFuture future) throws Exception
|
||||||
{
|
{
|
||||||
Platform.runLater(() -> onArbitratorAdded(arbitratorData, future.isSuccess(), locationKey));
|
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorAdded(arbitratorData, addFuture.isSuccess())));
|
||||||
}
|
if (addFuture.isSuccess())
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedParameters")
|
|
||||||
private void onArbitratorAdded(Data arbitratorData, boolean success, Number160 locationKey)
|
|
||||||
{
|
{
|
||||||
}
|
log.trace("Add arbitrator to DHT was successful. Stored data: [key: " + locationKey + ", values: " + arbitratorData + "]");
|
||||||
|
|
||||||
|
|
||||||
@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
|
else
|
||||||
{
|
{
|
||||||
lastTimeStamp++;
|
log.error("Add arbitrator to DHT failed with reason:" + addFuture.failedReason());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
FutureDHT putFuture = myPeer.put(getDirtyLocationKey(locationKey)).setData(new Data(lastTimeStamp)).start();
|
|
||||||
putFuture.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");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} catch (IOException e)
|
} catch (IOException e)
|
||||||
{
|
{
|
||||||
log.warn("Error at writing dirty flag (timeStamp) " + e.getMessage());
|
e.printStackTrace();
|
||||||
|
} catch (ClassNotFoundException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void removeArbitrator(Arbitrator arbitrator) throws IOException, ClassNotFoundException
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Send message
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/* public boolean sendMessage(Object message)
|
|
||||||
{
|
{
|
||||||
boolean result = false;
|
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
|
||||||
if (otherPeerAddress != null)
|
final Data arbitratorData = new Data(arbitrator);
|
||||||
{
|
FutureRemove removeFuture = p2pNode.removeFromDataMap(locationKey, arbitratorData);
|
||||||
if (peerConnection != null)
|
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||||
peerConnection.close();
|
|
||||||
|
|
||||||
peerConnection = myPeer.createPeerConnection(otherPeerAddress, 20);
|
|
||||||
if (!peerConnection.isClosed())
|
|
||||||
{
|
|
||||||
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(message).start();
|
|
||||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
public void operationComplete(BaseFuture future) throws Exception
|
||||||
{
|
{
|
||||||
if (sendFuture.isSuccess())
|
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorRemoved(arbitratorData, removeFuture.isSuccess())));
|
||||||
|
if (removeFuture.isSuccess())
|
||||||
{
|
{
|
||||||
final Object object = sendFuture.getObject();
|
log.trace("Remove arbitrator from DHT was successful. Stored data: [key: " + locationKey + ", values: " + arbitratorData + "]");
|
||||||
Platform.runLater(() -> onResponseFromSend(object));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Platform.runLater(() -> onSendFailed());
|
log.error("Remove arbitrators from DHT failed with reason:" + removeFuture.failedReason());
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} */
|
|
||||||
/*
|
|
||||||
private void onResponseFromSend(Object response)
|
|
||||||
{
|
|
||||||
for (MessageListener messageListener : messageListeners)
|
|
||||||
messageListener.onResponseFromSend(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSendFailed()
|
|
||||||
{
|
|
||||||
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>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
|
||||||
{
|
|
||||||
final Data data = getPeerAddressFuture.getData();
|
|
||||||
if (data != null && data.getObject() instanceof PeerAddress)
|
|
||||||
{
|
|
||||||
final PeerAddress peerAddress = (PeerAddress) data.getObject();
|
|
||||||
Platform.runLater(() -> onAddressFoundPingPeer(peerAddress));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onAddressFoundPingPeer(PeerAddress peerAddress)
|
public void getArbitrators(Locale languageLocale)
|
||||||
{
|
{
|
||||||
try
|
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
|
||||||
{
|
FutureGet futureGet = p2pNode.getDataMap(locationKey);
|
||||||
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
|
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
|
||||||
if (!peerConnection.isClosed())
|
|
||||||
{
|
|
||||||
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(PING).start();
|
|
||||||
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
|
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(BaseFuture baseFuture) throws Exception
|
public void operationComplete(BaseFuture baseFuture) throws Exception
|
||||||
{
|
{
|
||||||
if (sendFuture.isSuccess())
|
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorsReceived(futureGet.dataMap(), baseFuture.isSuccess())));
|
||||||
|
if (baseFuture.isSuccess())
|
||||||
{
|
{
|
||||||
final String pong = (String) sendFuture.getObject();
|
log.trace("Get arbitrators from DHT was successful. Stored data: [key: " + locationKey + ", values: " + futureGet.dataMap() + "]");
|
||||||
Platform.runLater(() -> onResponseFromPing(PONG.equals(pong)));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
peerConnection.close();
|
log.error("Get arbitrators from DHT failed with reason:" + baseFuture.failedReason());
|
||||||
Platform.runLater(() -> onResponseFromPing(false));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
} 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
|
// Event Listeners
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void addMessageListener(OrderBookListener listener)
|
public void addOrderBookListener(OrderBookListener listener)
|
||||||
{
|
{
|
||||||
orderBookListeners.add(listener);
|
orderBookListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeMessageListener(OrderBookListener listener)
|
public void removeOrderBookListener(OrderBookListener listener)
|
||||||
{
|
{
|
||||||
orderBookListeners.remove(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
|
// Incoming message handler
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void onMessage(Object request, PeerAddress sender)
|
|
||||||
{
|
|
||||||
if (request 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
|
@Override
|
||||||
public void operationComplete(BaseFuture future) throws Exception
|
public void handleMessage(Object message, PeerAddress peerAddress)
|
||||||
{
|
{
|
||||||
if (futureBootstrap.getBootstrapTo() != null)
|
if (message instanceof TradeMessage)
|
||||||
{
|
{
|
||||||
PeerAddress masterPeerAddress = futureBootstrap.getBootstrapTo().iterator().next();
|
log.error("####################");
|
||||||
final FutureDiscover futureDiscover = myPeer.discover().setPeerAddress(masterPeerAddress).start();
|
Platform.runLater(() -> incomingTradeMessageListeners.stream().forEach(e -> e.onMessage((TradeMessage) message, peerAddress)));
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
package io.bitsquare.msg.listeners;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.tomp2p.peers.Number160;
|
import net.tomp2p.peers.Number640;
|
||||||
import net.tomp2p.storage.Data;
|
import net.tomp2p.storage.Data;
|
||||||
|
|
||||||
@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
|
@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
|
||||||
|
@ -9,7 +9,7 @@ public interface ArbitratorListener
|
||||||
{
|
{
|
||||||
void onArbitratorAdded(Data offerData, boolean success);
|
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);
|
void onArbitratorRemoved(Data data, boolean success);
|
||||||
}
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package io.bitsquare.msg.listeners;
|
package io.bitsquare.msg.listeners;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.tomp2p.peers.Number160;
|
import net.tomp2p.peers.Number640;
|
||||||
import net.tomp2p.storage.Data;
|
import net.tomp2p.storage.Data;
|
||||||
|
|
||||||
public interface OrderBookListener
|
public interface OrderBookListener
|
||||||
|
@ -9,7 +9,7 @@ public interface OrderBookListener
|
||||||
@SuppressWarnings("UnusedParameters")
|
@SuppressWarnings("UnusedParameters")
|
||||||
void onOfferAdded(Data offerData, boolean success);
|
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);
|
void onOfferRemoved(Data data, boolean success);
|
||||||
}
|
}
|
|
@ -1,8 +1,10 @@
|
||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.bank.BankAccount;
|
import io.bitsquare.bank.BankAccount;
|
||||||
|
import io.bitsquare.util.DSAKeyUtil;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
public class Contract implements Serializable
|
public class Contract implements Serializable
|
||||||
{
|
{
|
||||||
|
@ -15,8 +17,8 @@ public class Contract implements Serializable
|
||||||
private final String takerAccountID;
|
private final String takerAccountID;
|
||||||
private final BankAccount offererBankAccount;
|
private final BankAccount offererBankAccount;
|
||||||
private final BankAccount takerBankAccount;
|
private final BankAccount takerBankAccount;
|
||||||
private final String offererMessagePubKeyAsHex;
|
private final String offererMessagePublicKeyAsString;
|
||||||
private final String takerMessagePubKeyAsHex;
|
private final String takerMessagePublicKeyAsString;
|
||||||
|
|
||||||
public Contract(Offer offer,
|
public Contract(Offer offer,
|
||||||
BigInteger tradeAmount,
|
BigInteger tradeAmount,
|
||||||
|
@ -25,8 +27,8 @@ public class Contract implements Serializable
|
||||||
String takerAccountID,
|
String takerAccountID,
|
||||||
BankAccount offererBankAccount,
|
BankAccount offererBankAccount,
|
||||||
BankAccount takerBankAccount,
|
BankAccount takerBankAccount,
|
||||||
String offererMessagePubKeyAsHex,
|
PublicKey offererMessagePublicKey,
|
||||||
String takerMessagePubKeyAsHex)
|
PublicKey takerMessagePublicKey)
|
||||||
{
|
{
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.tradeAmount = tradeAmount;
|
this.tradeAmount = tradeAmount;
|
||||||
|
@ -35,8 +37,8 @@ public class Contract implements Serializable
|
||||||
this.takerAccountID = takerAccountID;
|
this.takerAccountID = takerAccountID;
|
||||||
this.offererBankAccount = offererBankAccount;
|
this.offererBankAccount = offererBankAccount;
|
||||||
this.takerBankAccount = takerBankAccount;
|
this.takerBankAccount = takerBankAccount;
|
||||||
this.offererMessagePubKeyAsHex = offererMessagePubKeyAsHex;
|
this.offererMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(offererMessagePublicKey);
|
||||||
this.takerMessagePubKeyAsHex = takerMessagePubKeyAsHex;
|
this.takerMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(takerMessagePublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -79,17 +81,16 @@ public class Contract implements Serializable
|
||||||
return takerBankAccount;
|
return takerBankAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTakerMessagePubKeyAsHex()
|
public String getTakerMessagePublicKey()
|
||||||
{
|
{
|
||||||
return takerMessagePubKeyAsHex;
|
return takerMessagePublicKeyAsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getOffererMessagePubKeyAsHex()
|
public String getOffererMessagePublicKey()
|
||||||
{
|
{
|
||||||
return offererMessagePubKeyAsHex;
|
return offererMessagePublicKeyAsString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
@ -101,10 +102,8 @@ public class Contract implements Serializable
|
||||||
", takerAccountID='" + takerAccountID + '\'' +
|
", takerAccountID='" + takerAccountID + '\'' +
|
||||||
", offererBankAccount=" + offererBankAccount +
|
", offererBankAccount=" + offererBankAccount +
|
||||||
", takerBankAccount=" + takerBankAccount +
|
", takerBankAccount=" + takerBankAccount +
|
||||||
", offererMessagePubKeyAsHex='" + offererMessagePubKeyAsHex + '\'' +
|
", takerMessagePublicKeyAsString=" + takerMessagePublicKeyAsString +
|
||||||
", takerMessagePubKeyAsHex='" + takerMessagePubKeyAsHex + '\'' +
|
", offererMessagePublicKeyAsString=" + offererMessagePublicKeyAsString +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.bitsquare.locale.Country;
|
||||||
import io.bitsquare.user.Arbitrator;
|
import io.bitsquare.user.Arbitrator;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Offer implements Serializable
|
public class Offer implements Serializable
|
||||||
|
@ -23,7 +24,7 @@ public class Offer implements Serializable
|
||||||
private final double price;
|
private final double price;
|
||||||
private final BigInteger amount;
|
private final BigInteger amount;
|
||||||
private final BigInteger minAmount;
|
private final BigInteger minAmount;
|
||||||
private final String messagePubKeyAsHex;
|
private final PublicKey messagePublicKey;
|
||||||
private final BankAccountType bankAccountType;
|
private final BankAccountType bankAccountType;
|
||||||
private final Country bankAccountCountry;
|
private final Country bankAccountCountry;
|
||||||
|
|
||||||
|
@ -39,7 +40,7 @@ public class Offer implements Serializable
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public Offer(String messagePubKeyAsHex,
|
public Offer(PublicKey messagePublicKey,
|
||||||
Direction direction,
|
Direction direction,
|
||||||
double price,
|
double price,
|
||||||
BigInteger amount,
|
BigInteger amount,
|
||||||
|
@ -53,7 +54,7 @@ public class Offer implements Serializable
|
||||||
List<Country> acceptedCountries,
|
List<Country> acceptedCountries,
|
||||||
List<Locale> acceptedLanguageLocales)
|
List<Locale> acceptedLanguageLocales)
|
||||||
{
|
{
|
||||||
this.messagePubKeyAsHex = messagePubKeyAsHex;
|
this.messagePublicKey = messagePublicKey;
|
||||||
this.direction = direction;
|
this.direction = direction;
|
||||||
this.price = price;
|
this.price = price;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
|
@ -77,9 +78,9 @@ public class Offer implements Serializable
|
||||||
// Setters
|
// Setters
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public String getMessagePubKeyAsHex()
|
public PublicKey getMessagePublicKey()
|
||||||
{
|
{
|
||||||
return messagePubKeyAsHex;
|
return messagePublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -183,7 +184,7 @@ public class Offer implements Serializable
|
||||||
", price=" + price +
|
", price=" + price +
|
||||||
", amount=" + amount +
|
", amount=" + amount +
|
||||||
", minAmount=" + minAmount +
|
", minAmount=" + minAmount +
|
||||||
", messagePubKey=" + messagePubKeyAsHex.hashCode() +
|
", messagePubKey=" + messagePublicKey.hashCode() +
|
||||||
", bankAccountTypeEnum=" + bankAccountType +
|
", bankAccountTypeEnum=" + bankAccountType +
|
||||||
", bankAccountCountryLocale=" + bankAccountCountry +
|
", bankAccountCountryLocale=" + bankAccountCountry +
|
||||||
", collateral=" + collateral +
|
", collateral=" + collateral +
|
||||||
|
|
|
@ -136,11 +136,11 @@ public class Trading
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeOffer(Offer offer)
|
public void removeOffer(Offer offer)
|
||||||
{
|
{ //TODO
|
||||||
if (!offers.containsKey(offer.getId()))
|
/* if (!offers.containsKey(offer.getId()))
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("offers does not contain the offer with the ID " + offer.getId());
|
throw new IllegalStateException("offers does not contain the offer with the ID " + offer.getId());
|
||||||
}
|
}*/
|
||||||
|
|
||||||
offers.remove(offer.getId());
|
offers.remove(offer.getId());
|
||||||
saveOffers();
|
saveOffers();
|
||||||
|
|
|
@ -21,11 +21,17 @@ import javafx.collections.ObservableList;
|
||||||
import javafx.collections.transformation.FilteredList;
|
import javafx.collections.transformation.FilteredList;
|
||||||
import javafx.collections.transformation.SortedList;
|
import javafx.collections.transformation.SortedList;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import net.tomp2p.peers.Number160;
|
import net.tomp2p.peers.Number640;
|
||||||
import net.tomp2p.storage.Data;
|
import net.tomp2p.storage.Data;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO
|
||||||
|
remove dependencies to tomp2p
|
||||||
|
import net.tomp2p.peers.Number160;
|
||||||
|
import net.tomp2p.storage.Data;
|
||||||
|
*/
|
||||||
public class OrderBook implements OrderBookListener
|
public class OrderBook implements OrderBookListener
|
||||||
{
|
{
|
||||||
private static final Logger log = LoggerFactory.getLogger(OrderBook.class);
|
private static final Logger log = LoggerFactory.getLogger(OrderBook.class);
|
||||||
|
@ -59,12 +65,12 @@ public class OrderBook implements OrderBookListener
|
||||||
|
|
||||||
public void init()
|
public void init()
|
||||||
{
|
{
|
||||||
messageFacade.addMessageListener(this);
|
messageFacade.addOrderBookListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cleanup()
|
public void cleanup()
|
||||||
{
|
{
|
||||||
messageFacade.removeMessageListener(this);
|
messageFacade.removeOrderBookListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadOffers()
|
public void loadOffers()
|
||||||
|
@ -177,7 +183,7 @@ public class OrderBook implements OrderBookListener
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object offerDataObject = offerData.getObject();
|
Object offerDataObject = offerData.object();
|
||||||
if (offerDataObject instanceof Offer)
|
if (offerDataObject instanceof Offer)
|
||||||
{
|
{
|
||||||
Offer offer = (Offer) offerDataObject;
|
Offer offer = (Offer) offerDataObject;
|
||||||
|
@ -190,7 +196,7 @@ public class OrderBook implements OrderBookListener
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
|
public void onOffersReceived(Map<Number640, Data> dataMap, boolean success)
|
||||||
{
|
{
|
||||||
if (success && dataMap != null)
|
if (success && dataMap != null)
|
||||||
{
|
{
|
||||||
|
@ -200,7 +206,7 @@ public class OrderBook implements OrderBookListener
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object offerDataObject = offerData.getObject();
|
Object offerDataObject = offerData.object();
|
||||||
if (offerDataObject instanceof Offer)
|
if (offerDataObject instanceof Offer)
|
||||||
{
|
{
|
||||||
Offer offer = (Offer) offerDataObject;
|
Offer offer = (Offer) offerDataObject;
|
||||||
|
@ -226,7 +232,7 @@ public class OrderBook implements OrderBookListener
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Object offerDataObject = offerData.getObject();
|
Object offerDataObject = offerData.object();
|
||||||
if (offerDataObject instanceof Offer)
|
if (offerDataObject instanceof Offer)
|
||||||
{
|
{
|
||||||
Offer offer = (Offer) offerDataObject;
|
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.trade.protocol.taker.TakeOfferFeePayedMessage;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
import net.tomp2p.peers.PeerAddress;
|
import net.tomp2p.peers.PeerAddress;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -76,7 +77,7 @@ public class ProtocolForOffererAsBuyer
|
||||||
private final String arbitratorPubKey;
|
private final String arbitratorPubKey;
|
||||||
private final BankAccount bankAccount;
|
private final BankAccount bankAccount;
|
||||||
private final String accountId;
|
private final String accountId;
|
||||||
private final String messagePubKey;
|
private final PublicKey messagePublicKey;
|
||||||
private final ECKey accountKey;
|
private final ECKey accountKey;
|
||||||
private final String payoutAddress;
|
private final String payoutAddress;
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ public class ProtocolForOffererAsBuyer
|
||||||
private String peersPayoutAddress;
|
private String peersPayoutAddress;
|
||||||
private String peersAccountId;
|
private String peersAccountId;
|
||||||
private BankAccount peersBankAccount;
|
private BankAccount peersBankAccount;
|
||||||
private String peersMessagePubKey;
|
private PublicKey peersMessagePublicKey;
|
||||||
private String peersContractAsJson;
|
private String peersContractAsJson;
|
||||||
private String signedTakerDepositTxAsHex;
|
private String signedTakerDepositTxAsHex;
|
||||||
private String txConnOutAsHex;
|
private String txConnOutAsHex;
|
||||||
|
@ -129,7 +130,7 @@ public class ProtocolForOffererAsBuyer
|
||||||
|
|
||||||
bankAccount = user.getBankAccount(trade.getOffer().getBankAccountId());
|
bankAccount = user.getBankAccount(trade.getOffer().getBankAccountId());
|
||||||
accountId = user.getAccountId();
|
accountId = user.getAccountId();
|
||||||
messagePubKey = user.getMessagePubKeyAsHex();
|
messagePublicKey = user.getMessagePublicKey();
|
||||||
|
|
||||||
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
|
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
|
||||||
payoutAddress = walletFacade.getAddressInfoByTradeID(tradeId).getAddressString();
|
payoutAddress = walletFacade.getAddressInfoByTradeID(tradeId).getAddressString();
|
||||||
|
@ -241,7 +242,7 @@ public class ProtocolForOffererAsBuyer
|
||||||
String peersPayoutAddress = nonEmptyStringOf(message.getTakerPayoutAddress());
|
String peersPayoutAddress = nonEmptyStringOf(message.getTakerPayoutAddress());
|
||||||
String peersAccountId = nonEmptyStringOf(message.getTakerAccountId());
|
String peersAccountId = nonEmptyStringOf(message.getTakerAccountId());
|
||||||
BankAccount peersBankAccount = checkNotNull(message.getTakerBankAccount());
|
BankAccount peersBankAccount = checkNotNull(message.getTakerBankAccount());
|
||||||
String peersMessagePubKey = nonEmptyStringOf(message.getTakerMessagePubKey());
|
PublicKey peersMessagePublicKey = checkNotNull(message.getTakerMessagePublicKey());
|
||||||
String peersContractAsJson = nonEmptyStringOf(message.getTakerContractAsJson());
|
String peersContractAsJson = nonEmptyStringOf(message.getTakerContractAsJson());
|
||||||
String signedTakerDepositTxAsHex = nonEmptyStringOf(message.getSignedTakerDepositTxAsHex());
|
String signedTakerDepositTxAsHex = nonEmptyStringOf(message.getSignedTakerDepositTxAsHex());
|
||||||
String txConnOutAsHex = nonEmptyStringOf(message.getTxConnOutAsHex());
|
String txConnOutAsHex = nonEmptyStringOf(message.getTxConnOutAsHex());
|
||||||
|
@ -253,7 +254,7 @@ public class ProtocolForOffererAsBuyer
|
||||||
this.peersPayoutAddress = peersPayoutAddress;
|
this.peersPayoutAddress = peersPayoutAddress;
|
||||||
this.peersAccountId = peersAccountId;
|
this.peersAccountId = peersAccountId;
|
||||||
this.peersBankAccount = peersBankAccount;
|
this.peersBankAccount = peersBankAccount;
|
||||||
this.peersMessagePubKey = peersMessagePubKey;
|
this.peersMessagePublicKey = peersMessagePublicKey;
|
||||||
this.peersContractAsJson = peersContractAsJson;
|
this.peersContractAsJson = peersContractAsJson;
|
||||||
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
||||||
this.txConnOutAsHex = txConnOutAsHex;
|
this.txConnOutAsHex = txConnOutAsHex;
|
||||||
|
@ -277,12 +278,12 @@ public class ProtocolForOffererAsBuyer
|
||||||
accountId,
|
accountId,
|
||||||
tradeAmount,
|
tradeAmount,
|
||||||
takeOfferFeeTxId,
|
takeOfferFeeTxId,
|
||||||
messagePubKey,
|
messagePublicKey,
|
||||||
offer,
|
offer,
|
||||||
peersAccountId,
|
peersAccountId,
|
||||||
bankAccount,
|
bankAccount,
|
||||||
peersBankAccount,
|
peersBankAccount,
|
||||||
peersMessagePubKey,
|
peersMessagePublicKey,
|
||||||
peersContractAsJson,
|
peersContractAsJson,
|
||||||
accountKey);
|
accountKey);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import io.bitsquare.trade.Offer;
|
||||||
import io.bitsquare.trade.protocol.FaultHandler;
|
import io.bitsquare.trade.protocol.FaultHandler;
|
||||||
import io.bitsquare.util.Utilities;
|
import io.bitsquare.util.Utilities;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -21,17 +22,17 @@ public class VerifyAndSignContract
|
||||||
String accountId,
|
String accountId,
|
||||||
BigInteger tradeAmount,
|
BigInteger tradeAmount,
|
||||||
String takeOfferFeeTxId,
|
String takeOfferFeeTxId,
|
||||||
String messagePubKeyAsHex,
|
PublicKey messagePublicKey,
|
||||||
Offer offer,
|
Offer offer,
|
||||||
String peersAccountId,
|
String peersAccountId,
|
||||||
BankAccount bankAccount,
|
BankAccount bankAccount,
|
||||||
BankAccount peersBankAccount,
|
BankAccount peersBankAccount,
|
||||||
String takerMessagePubKey,
|
PublicKey takerMessagePublicKey,
|
||||||
String peersContractAsJson,
|
String peersContractAsJson,
|
||||||
ECKey registrationKey)
|
ECKey registrationKey)
|
||||||
{
|
{
|
||||||
log.trace("Run task");
|
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);
|
String contractAsJson = Utilities.objectToJson(contract);
|
||||||
// log.trace("Offerer contract created: " + 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.trade.protocol.FaultHandler;
|
||||||
import io.bitsquare.util.Utilities;
|
import io.bitsquare.util.Utilities;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
@ -23,8 +24,8 @@ public class CreateAndSignContract
|
||||||
String takeOfferFeeTxId,
|
String takeOfferFeeTxId,
|
||||||
String accountId,
|
String accountId,
|
||||||
BankAccount bankAccount,
|
BankAccount bankAccount,
|
||||||
String peersMessagePubKeyAsHex,
|
PublicKey peersMessagePublicKey,
|
||||||
String messagePubKeyAsHex,
|
PublicKey messagePublicKey,
|
||||||
String peersAccountId,
|
String peersAccountId,
|
||||||
BankAccount peersBankAccount,
|
BankAccount peersBankAccount,
|
||||||
ECKey registrationKey)
|
ECKey registrationKey)
|
||||||
|
@ -32,7 +33,7 @@ public class CreateAndSignContract
|
||||||
log.trace("Run task");
|
log.trace("Run task");
|
||||||
try
|
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 contractAsJson = Utilities.objectToJson(contract);
|
||||||
String signature = cryptoFacade.signContract(registrationKey, contractAsJson);
|
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.MessageFacade;
|
||||||
import io.bitsquare.msg.listeners.GetPeerAddressListener;
|
import io.bitsquare.msg.listeners.GetPeerAddressListener;
|
||||||
import io.bitsquare.trade.protocol.FaultHandler;
|
import io.bitsquare.trade.protocol.FaultHandler;
|
||||||
|
import java.security.PublicKey;
|
||||||
import net.tomp2p.peers.PeerAddress;
|
import net.tomp2p.peers.PeerAddress;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -11,10 +12,10 @@ public class GetPeerAddress
|
||||||
{
|
{
|
||||||
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
|
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");
|
log.trace("Run task");
|
||||||
messageFacade.getPeerAddress(messagePubKeyAsHex, new GetPeerAddressListener()
|
messageFacade.getPeerAddress(messagePublicKey, new GetPeerAddressListener()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void onResult(PeerAddress peerAddress)
|
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.trade.protocol.offerer.RespondToTakeOfferRequestMessage;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
import java.security.PublicKey;
|
||||||
import net.tomp2p.peers.PeerAddress;
|
import net.tomp2p.peers.PeerAddress;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -66,11 +67,11 @@ public class ProtocolForTakerAsSeller
|
||||||
private final String tradeId;
|
private final String tradeId;
|
||||||
private final BankAccount bankAccount;
|
private final BankAccount bankAccount;
|
||||||
private final String accountId;
|
private final String accountId;
|
||||||
private final String messagePubKey;
|
private final PublicKey messagePublicKey;
|
||||||
private final BigInteger tradeAmount;
|
private final BigInteger tradeAmount;
|
||||||
private final String pubKeyForThatTrade;
|
private final String pubKeyForThatTrade;
|
||||||
private final ECKey accountKey;
|
private final ECKey accountKey;
|
||||||
private final String peersMessagePubKey;
|
private final PublicKey peersMessagePublicKey;
|
||||||
private final BigInteger collateral;
|
private final BigInteger collateral;
|
||||||
private final String arbitratorPubKey;
|
private final String arbitratorPubKey;
|
||||||
|
|
||||||
|
@ -122,11 +123,11 @@ public class ProtocolForTakerAsSeller
|
||||||
collateral = trade.getCollateralAmount();
|
collateral = trade.getCollateralAmount();
|
||||||
arbitratorPubKey = trade.getOffer().getArbitrator().getPubKeyAsHex();
|
arbitratorPubKey = trade.getOffer().getArbitrator().getPubKeyAsHex();
|
||||||
|
|
||||||
peersMessagePubKey = offer.getMessagePubKeyAsHex();
|
peersMessagePublicKey = offer.getMessagePublicKey();
|
||||||
|
|
||||||
bankAccount = user.getBankAccount(offer.getBankAccountId());
|
bankAccount = user.getBankAccount(offer.getBankAccountId());
|
||||||
accountId = user.getAccountId();
|
accountId = user.getAccountId();
|
||||||
messagePubKey = user.getMessagePubKeyAsHex();
|
messagePublicKey = user.getMessagePublicKey();
|
||||||
|
|
||||||
pubKeyForThatTrade = walletFacade.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
|
pubKeyForThatTrade = walletFacade.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
|
||||||
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
|
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
|
||||||
|
@ -138,7 +139,7 @@ public class ProtocolForTakerAsSeller
|
||||||
{
|
{
|
||||||
log.debug("start called " + step++);
|
log.debug("start called " + step++);
|
||||||
state = State.GetPeerAddress;
|
state = State.GetPeerAddress;
|
||||||
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, peersMessagePubKey);
|
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, peersMessagePublicKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResultGetPeerAddress(PeerAddress peerAddress)
|
public void onResultGetPeerAddress(PeerAddress peerAddress)
|
||||||
|
@ -176,6 +177,7 @@ public class ProtocolForTakerAsSeller
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listener.onTakeOfferRequestRejected(trade);
|
listener.onTakeOfferRequestRejected(trade);
|
||||||
|
// exit case
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,8 +241,8 @@ public class ProtocolForTakerAsSeller
|
||||||
takeOfferFeeTxId,
|
takeOfferFeeTxId,
|
||||||
accountId,
|
accountId,
|
||||||
bankAccount,
|
bankAccount,
|
||||||
peersMessagePubKey,
|
peersMessagePublicKey,
|
||||||
messagePubKey,
|
messagePublicKey,
|
||||||
peersAccountId,
|
peersAccountId,
|
||||||
peersBankAccount,
|
peersBankAccount,
|
||||||
accountKey);
|
accountKey);
|
||||||
|
@ -272,7 +274,7 @@ public class ProtocolForTakerAsSeller
|
||||||
walletFacade,
|
walletFacade,
|
||||||
bankAccount,
|
bankAccount,
|
||||||
accountId,
|
accountId,
|
||||||
messagePubKey,
|
messagePublicKey,
|
||||||
tradeId,
|
tradeId,
|
||||||
contractAsJson,
|
contractAsJson,
|
||||||
takerSignature,
|
takerSignature,
|
||||||
|
|
|
@ -3,6 +3,7 @@ package io.bitsquare.trade.protocol.taker;
|
||||||
import io.bitsquare.bank.BankAccount;
|
import io.bitsquare.bank.BankAccount;
|
||||||
import io.bitsquare.msg.TradeMessage;
|
import io.bitsquare.msg.TradeMessage;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.security.PublicKey;
|
||||||
|
|
||||||
public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage
|
public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage
|
||||||
{
|
{
|
||||||
|
@ -10,7 +11,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||||
private final String tradeId;
|
private final String tradeId;
|
||||||
private BankAccount bankAccount;
|
private BankAccount bankAccount;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
private String takerMessagePubKey;
|
private PublicKey takerMessagePublicKey;
|
||||||
private String signedTakerDepositTxAsHex;
|
private String signedTakerDepositTxAsHex;
|
||||||
private String txScriptSigAsHex;
|
private String txScriptSigAsHex;
|
||||||
private String txConnOutAsHex;
|
private String txConnOutAsHex;
|
||||||
|
@ -25,7 +26,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||||
public RequestOffererPublishDepositTxMessage(String tradeId,
|
public RequestOffererPublishDepositTxMessage(String tradeId,
|
||||||
BankAccount bankAccount,
|
BankAccount bankAccount,
|
||||||
String accountID,
|
String accountID,
|
||||||
String takerMessagePubKey,
|
PublicKey takerMessagePublicKey,
|
||||||
String signedTakerDepositTxAsHex,
|
String signedTakerDepositTxAsHex,
|
||||||
String txScriptSigAsHex,
|
String txScriptSigAsHex,
|
||||||
String txConnOutAsHex,
|
String txConnOutAsHex,
|
||||||
|
@ -39,7 +40,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||||
this.tradeId = tradeId;
|
this.tradeId = tradeId;
|
||||||
this.bankAccount = bankAccount;
|
this.bankAccount = bankAccount;
|
||||||
this.accountID = accountID;
|
this.accountID = accountID;
|
||||||
this.takerMessagePubKey = takerMessagePubKey;
|
this.takerMessagePublicKey = takerMessagePublicKey;
|
||||||
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
|
||||||
this.txScriptSigAsHex = txScriptSigAsHex;
|
this.txScriptSigAsHex = txScriptSigAsHex;
|
||||||
this.txConnOutAsHex = txConnOutAsHex;
|
this.txConnOutAsHex = txConnOutAsHex;
|
||||||
|
@ -72,9 +73,9 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
|
||||||
return accountID;
|
return accountID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTakerMessagePubKey()
|
public PublicKey getTakerMessagePublicKey()
|
||||||
{
|
{
|
||||||
return takerMessagePubKey;
|
return takerMessagePublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSignedTakerDepositTxAsHex()
|
public String getSignedTakerDepositTxAsHex()
|
||||||
|
|
|
@ -8,6 +8,7 @@ import io.bitsquare.msg.MessageFacade;
|
||||||
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
|
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
|
||||||
import io.bitsquare.trade.protocol.FaultHandler;
|
import io.bitsquare.trade.protocol.FaultHandler;
|
||||||
import io.bitsquare.trade.protocol.ResultHandler;
|
import io.bitsquare.trade.protocol.ResultHandler;
|
||||||
|
import java.security.PublicKey;
|
||||||
import net.tomp2p.peers.PeerAddress;
|
import net.tomp2p.peers.PeerAddress;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -23,7 +24,7 @@ public class SendSignedTakerDepositTxAsHex
|
||||||
WalletFacade walletFacade,
|
WalletFacade walletFacade,
|
||||||
BankAccount bankAccount,
|
BankAccount bankAccount,
|
||||||
String accountId,
|
String accountId,
|
||||||
String messagePubKeyAsHex,
|
PublicKey messagePublicKey,
|
||||||
String tradeId,
|
String tradeId,
|
||||||
String contractAsJson,
|
String contractAsJson,
|
||||||
String takerSignature,
|
String takerSignature,
|
||||||
|
@ -36,7 +37,7 @@ public class SendSignedTakerDepositTxAsHex
|
||||||
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(tradeId,
|
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(tradeId,
|
||||||
bankAccount,
|
bankAccount,
|
||||||
accountId,
|
accountId,
|
||||||
messagePubKeyAsHex,
|
messagePublicKey,
|
||||||
Utils.bytesToHexString(signedTakerDepositTx.bitcoinSerialize()),
|
Utils.bytesToHexString(signedTakerDepositTx.bitcoinSerialize()),
|
||||||
Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes()),
|
Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes()),
|
||||||
Utils.bytesToHexString(signedTakerDepositTx.getInput(1)
|
Utils.bytesToHexString(signedTakerDepositTx.getInput(1)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package io.bitsquare.user;
|
package io.bitsquare.user;
|
||||||
|
|
||||||
import io.bitsquare.bank.BankAccount;
|
import io.bitsquare.bank.BankAccount;
|
||||||
|
import io.bitsquare.util.DSAKeyUtil;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
@ -11,19 +14,17 @@ public class User implements Serializable
|
||||||
private static final long serialVersionUID = 7409078808248518638L;
|
private static final long serialVersionUID = 7409078808248518638L;
|
||||||
|
|
||||||
transient private final SimpleBooleanProperty bankAccountChangedProperty = new SimpleBooleanProperty();
|
transient private final SimpleBooleanProperty bankAccountChangedProperty = new SimpleBooleanProperty();
|
||||||
|
transient private KeyPair messageKeyPair = DSAKeyUtil.getKeyPair();
|
||||||
|
|
||||||
|
private PublicKey messagePublicKey;
|
||||||
private String accountID;
|
private String accountID;
|
||||||
|
|
||||||
private String messagePubKeyAsHex;
|
|
||||||
private boolean isOnline;
|
private boolean isOnline;
|
||||||
|
|
||||||
private List<BankAccount> bankAccounts = new ArrayList<>();
|
private List<BankAccount> bankAccounts = new ArrayList<>();
|
||||||
|
|
||||||
private BankAccount currentBankAccount = null;
|
private BankAccount currentBankAccount = null;
|
||||||
|
|
||||||
public User()
|
public User()
|
||||||
{
|
{
|
||||||
|
messagePublicKey = messageKeyPair.getPublic();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,11 +37,14 @@ public class User implements Serializable
|
||||||
if (savedUser != null)
|
if (savedUser != null)
|
||||||
{
|
{
|
||||||
accountID = savedUser.getAccountId();
|
accountID = savedUser.getAccountId();
|
||||||
messagePubKeyAsHex = savedUser.getMessagePubKeyAsHex();
|
// TODO handled by DSAKeyUtil -> change that storage check is only done here
|
||||||
|
// messagePublicKey = savedUser.getMessagePublicKey();
|
||||||
isOnline = savedUser.getIsOnline();
|
isOnline = savedUser.getIsOnline();
|
||||||
bankAccounts = savedUser.getBankAccounts();
|
bankAccounts = savedUser.getBankAccounts();
|
||||||
currentBankAccount = savedUser.getCurrentBankAccount();
|
currentBankAccount = savedUser.getCurrentBankAccount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
messagePublicKey = messageKeyPair.getPublic();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addBankAccount(BankAccount bankAccount)
|
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()
|
public String getAccountId()
|
||||||
{
|
{
|
||||||
return accountID;
|
return accountID;
|
||||||
|
@ -172,17 +165,27 @@ public class User implements Serializable
|
||||||
return bankAccountChangedProperty;
|
return bankAccountChangedProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return "User{" +
|
return "User{" +
|
||||||
"bankAccountChangedProperty=" + bankAccountChangedProperty +
|
"bankAccountChangedProperty=" + bankAccountChangedProperty +
|
||||||
|
", messageKeyPair=" + messageKeyPair +
|
||||||
|
", messagePublicKey=" + messagePublicKey +
|
||||||
", accountID='" + accountID + '\'' +
|
", accountID='" + accountID + '\'' +
|
||||||
", messagePubKeyAsHex='" + messagePubKeyAsHex + '\'' +
|
|
||||||
", isOnline=" + isOnline +
|
", isOnline=" + isOnline +
|
||||||
", bankAccounts=" + bankAccounts +
|
", bankAccounts=" + bankAccounts +
|
||||||
", currentBankAccount=" + currentBankAccount +
|
", 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,13 +22,18 @@
|
||||||
<appender-ref ref="FILE"/>
|
<appender-ref ref="FILE"/>
|
||||||
</root>
|
</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.Wallet" level="INFO"/>
|
||||||
<logger name="com.google.bitcoin.core.MemoryPool" level="WARN" additivity="false"/>
|
<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.net.discovery.DnsDiscovery" level="WARN" additivity="false"/>
|
||||||
|
@ -41,6 +46,6 @@
|
||||||
<logger name="com.google.bitcoin.core.PeerSocketHandler" 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.NioClientManager" level="WARN" additivity="false"/>
|
||||||
<logger name="com.google.bitcoin.net.ConnectionHandler" level="WARN" additivity="false"/>
|
<logger name="com.google.bitcoin.net.ConnectionHandler" level="WARN" additivity="false"/>
|
||||||
|
-->
|
||||||
|
|
||||||
</configuration>
|
</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…
Add table
Add a link
Reference in a new issue