updated to master branch of tomp2p. refactored messaging. bootstrapping to seed node (server supported). right management.

This commit is contained in:
Manfred Karrer 2014-07-31 18:45:58 +02:00
parent 222045ca5a
commit 57d5ddcc4f
54 changed files with 3284 additions and 717 deletions

View File

@ -10,6 +10,10 @@ Then you can generate coins on demand with the Bitcoin qt client with that comma
See: https://bitcoinj.github.io/testing
You can change the network mode in the guice module: BitSquareModule.java
We use a fork of the actual TomP2P master branch: https://github.com/ManfredKarrer/TomP2P
You need to check that out as well and deploy it to the local maven repository:
mvn clean install -DskipTests
### Resources:
* Web: http://bitsquare.io

View File

@ -124,11 +124,13 @@
<version>0.11.3</version>
</dependency>
<!--
<dependency>
<groupId>net.tomp2p</groupId>
<artifactId>TomP2P</artifactId>
<version>4.4</version>
<artifactId>tomp2p-all</artifactId>
<version>5.0-Alpha24-SNAPSHOT</version>
</dependency>
-->
<dependency>
<groupId>junit</groupId>

View File

@ -1,3 +1,3 @@
Manifest-Version: 1.0
Main-Class: io.bitsquare.Relay
Main-Class: io.bitsquare.BootstrapNode_

View File

@ -38,7 +38,7 @@ public class BitSquare extends Application
{
private static final Logger log = LoggerFactory.getLogger(BitSquare.class);
public static boolean fillFormsWithDummyData = false;
public static boolean fillFormsWithDummyData = true;
private static String APP_NAME = "bitsquare";
private static Stage primaryStage;

View File

@ -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);
}
});
}
}
}

View 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);
}
}
}

View File

@ -22,5 +22,4 @@ class BitSquareWallet extends Wallet implements Serializable
super(params, keyCrypter);
}
}

View File

@ -190,11 +190,9 @@ public class WalletFacade
}
}
public void shutDown()
{
wallet.removeEventListener(walletEventListener);
walletAppKit.stopAsync();
}
@ -596,6 +594,7 @@ public class WalletFacade
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
// we don't allow spending of unconfirmed tx as with fake registrations we would open up doors for spam and market manipulation with fake offers
// so set includePending to false
sendRequest.coinSelector = new AddressBasedCoinSelector(params, getRegistrationAddressInfo(), false);
sendRequest.changeAddress = getRegistrationAddressInfo().getAddress();
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
@ -677,6 +676,8 @@ public class WalletFacade
}
// TODO: Trade process - use P2SH instead and optimize data exchange
///////////////////////////////////////////////////////////////////////////////////////////
// Trade process
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -15,6 +15,7 @@ import io.bitsquare.btc.FeePolicy;
import io.bitsquare.btc.WalletFacade;
import io.bitsquare.crypto.CryptoFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.SeedNodeAddress;
import io.bitsquare.settings.Settings;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Trading;
@ -52,6 +53,11 @@ public class BitSquareModule extends AbstractModule
//bind(String.class).annotatedWith(Names.named("networkType")).toInstance(WalletFacade.TEST_NET);
bind(NetworkParameters.class).toProvider(NetworkParametersProvider.class).asEagerSingleton();
bind(BitSquareWalletAppKit.class).toProvider(BitSquareWalletAppKitProvider.class).asEagerSingleton();
// bind(Boolean.class).annotatedWith(Names.named("useDiskStorage")).toInstance(new Boolean(true));
bind(Boolean.class).annotatedWith(Names.named("useDiskStorage")).toInstance(new Boolean(false));
bind(SeedNodeAddress.StaticSeedNodeAddresses.class).annotatedWith(Names.named("defaultSeedNode")).toInstance(SeedNodeAddress.StaticSeedNodeAddresses.LOCALHOST);
// bind(SeedNodeAddress.StaticSeedNodeAddresses.class).annotatedWith(Names.named("defaultSeedNode")).toInstance(SeedNodeAddress.StaticSeedNodeAddresses.DIGITAL_OCEAN);
}
}

View File

@ -11,6 +11,7 @@ import io.bitsquare.gui.orders.OrdersController;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.gui.util.Transitions;
import io.bitsquare.locale.Localisation;
import io.bitsquare.msg.BootstrapListener;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.storage.Storage;
import io.bitsquare.trade.Direction;
@ -176,8 +177,24 @@ public class MainController implements Initializable, NavigationController
private void init()
{
messageFacade.init(new BootstrapListener()
{
@Override
public void onCompleted()
{
messageFacadeInited();
}
messageFacade.init();
@Override
public void onFailed(Throwable throwable)
{
}
});
}
private void messageFacadeInited()
{
trading.addTakeOfferRequestListener(this::onTakeOfferRequested);

View File

@ -26,9 +26,14 @@ import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javax.inject.Inject;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;
/**
* TODO remove tomp2p dependencies
* import net.tomp2p.peers.Number160;
* import net.tomp2p.storage.Data;
*/
@SuppressWarnings({"ALL", "UnusedParameters"})
public class ArbitratorOverviewController implements Initializable, ChildController, NavigationController, ArbitratorListener
{
@ -135,7 +140,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
}
@Override
public void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success)
public void onArbitratorsReceived(Map<Number640, Data> dataMap, boolean success)
{
if (success && dataMap != null)
{
@ -145,7 +150,7 @@ public class ArbitratorOverviewController implements Initializable, ChildControl
{
try
{
Object arbitratorDataObject = arbitratorData.getObject();
Object arbitratorDataObject = arbitratorData.object();
if (arbitratorDataObject instanceof Arbitrator)
{
Arbitrator arbitrator = (Arbitrator) arbitratorDataObject;

View File

@ -22,8 +22,8 @@ import io.bitsquare.msg.MessageFacade;
import io.bitsquare.storage.Storage;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.user.Reputation;
import io.bitsquare.user.User;
import io.bitsquare.util.DSAKeyUtil;
import java.io.IOException;
import java.math.BigInteger;
import java.net.URL;
import java.util.*;
@ -48,6 +48,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
private final Storage storage;
private final WalletFacade walletFacade;
private final MessageFacade messageFacade;
private User user;
private Arbitrator arbitrator = new Arbitrator();
private ArbitratorProfileController arbitratorProfileController;
private boolean isEditMode;
@ -92,11 +93,12 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
private ArbitratorRegistrationController(Storage storage, WalletFacade walletFacade, MessageFacade messageFacade)
private ArbitratorRegistrationController(Storage storage, WalletFacade walletFacade, MessageFacade messageFacade, User user)
{
this.storage = storage;
this.walletFacade = walletFacade;
this.messageFacade = messageFacade;
this.user = user;
}
@ -338,13 +340,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
}
}
try
{
messageFacade.addArbitrator(arbitrator);
} catch (IOException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
messageFacade.addArbitrator(arbitrator);
}
@FXML
@ -474,7 +470,7 @@ public class ArbitratorRegistrationController implements Initializable, ChildCon
minArbitrationFeeTextField);
String pubKeyAsHex = walletFacade.getArbitratorDepositAddressInfo().getPubKeyAsHexString();
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey());
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey());
String name = nameTextField.getText();
double maxTradeVolume = BitSquareConverter.stringToDouble(maxTradeVolumeTextField.getText());

View File

@ -205,11 +205,9 @@ public class CreateOfferController implements Initializable, ChildController, Hi
return;
}
log.debug("create offer pubkey " + user.getMessagePubKeyAsHex());
if (user.getCurrentBankAccount() != null)
{
offer = new Offer(user.getMessagePubKeyAsHex(),
offer = new Offer(user.getMessagePublicKey(),
direction,
BitSquareConverter.stringToDouble(priceTextField.getText()),
BtcFormatter.stringValueToSatoshis(amountTextField.getText()),

View File

@ -42,12 +42,13 @@
<Label text="Balance:" GridPane.rowIndex="7"/>
<BalanceTextField fx:id="balanceTextField" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="8">
<GridPane.margin>
<Insets top="10.0"/>
</GridPane.margin>
</Label>
<!--
<Label id="form-header-text" text="Offer details" GridPane.rowIndex="8">
<GridPane.margin>
<Insets top="10.0"/>
</GridPane.margin>
</Label>
-->
<Label text="Bank account type:" GridPane.rowIndex="9"/>
<TextField fx:id="bankAccountTypeTextField" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="9"/>
@ -67,17 +68,17 @@
<Button fx:id="placeOfferButton" defaultButton="true" onAction="#onPlaceOffer" text="Place offer" GridPane.columnIndex="1" GridPane.rowIndex="14"/>
<Label fx:id="txTitleLabel" text="Transaction ID:" visible="false" GridPane.rowIndex="15"/>
<TextField fx:id="txTextField" visible="false" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="15"/>
<Label fx:id="txTitleLabel" text="Transaction ID:" visible="false" GridPane.rowIndex="14"/>
<TextField fx:id="txTextField" visible="false" editable="false" GridPane.columnIndex="1" GridPane.rowIndex="14"/>
<ConfidenceProgressIndicator fx:id="progressIndicator" visible="false" progress="0" GridPane.columnIndex="2" GridPane.halignment="LEFT"
GridPane.rowIndex="15" GridPane.rowSpan="2" GridPane.valignment="TOP">
GridPane.rowIndex="14" GridPane.rowSpan="2" GridPane.valignment="TOP">
<GridPane.margin>
<Insets top="2.0"/>
</GridPane.margin>
</ConfidenceProgressIndicator>
<Label fx:id="confirmationLabel" text="Checking confirmations..." visible="false" GridPane.columnIndex="3" GridPane.rowIndex="15"/>
<Label fx:id="confirmationLabel" text="Checking confirmations..." visible="false" GridPane.columnIndex="3" GridPane.rowIndex="14"/>
<Button fx:id="closeButton" visible="false" defaultButton="true" onAction="#onClose" text="Close" GridPane.columnIndex="1" GridPane.rowIndex="16"/>
<Button fx:id="closeButton" visible="false" defaultButton="true" onAction="#onClose" text="Close" GridPane.columnIndex="1" GridPane.rowIndex="15"/>
<columnConstraints>
<ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES"/>

View File

@ -28,7 +28,6 @@ import io.bitsquare.trade.Offer;
import io.bitsquare.trade.orderbook.OrderBook;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import io.bitsquare.user.User;
import io.bitsquare.util.DSAKeyUtil;
import io.bitsquare.util.Utilities;
import java.math.BigInteger;
import java.net.URL;
@ -304,10 +303,6 @@ public class OrderBookController implements Initializable, ChildController
{
user.setAccountID(walletFacade.getRegistrationAddressInfo().toString());
}
if (messageFacade != null && messageFacade.getPubKey() != null)
{
user.setMessagePubKeyAsHex(DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey()));
}
storage.write(user.getClass().getName(), user);
} catch (InsufficientMoneyException e1)
@ -437,7 +432,7 @@ public class OrderBookController implements Initializable, ChildController
Image icon;
Offer offer = orderBookListItem.getOffer();
if (offer.getMessagePubKeyAsHex().equals(user.getMessagePubKeyAsHex()))
if (offer.getMessagePublicKey().equals(user.getMessagePublicKey()))
{
icon = Icons.getIconImage(Icons.REMOVE);
title = "Remove";

View File

@ -191,7 +191,7 @@ public class TakerOfferController implements Initializable, ChildController
public void onBankTransferInited(String tradeId)
{
setTradeId(tradeId);
headLineLabel.setText("Bank transfer inited");
headLineLabel.setText("Bank transfer initialised");
infoLabel.setText("Check your bank account and continue \n" + "when you have received the money.");
receivedFiatButton.setDisable(false);
}

View File

@ -118,7 +118,7 @@
</Label>
<!-- row 7 -->
<Button fx:id="bankTransferInitedButton" defaultButton="true" onAction="#bankTransferInited" disable="true" text="Bank transfer inited"
<Button fx:id="bankTransferInitedButton" defaultButton="true" onAction="#bankTransferInited" disable="true" text="Bank transfer initialised "
GridPane.columnIndex="1"
GridPane.rowIndex="7"/>

View File

@ -139,7 +139,7 @@ public class SettingsController implements Initializable, ChildController, Navig
if (settings.getAcceptedArbitrators().isEmpty())
{
String pubKeyAsHex = Utils.bytesToHexString(new ECKey().getPubKey());
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(messageFacade.getPubKey());
String messagePubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(user.getMessagePublicKey());
List<Locale> languages = new ArrayList<>();
languages.add(LanguageUtil.getDefaultLanguageLocale());
List<Arbitrator.METHOD> arbitrationMethods = new ArrayList<>();
@ -168,13 +168,7 @@ public class SettingsController implements Initializable, ChildController, Navig
settings.addAcceptedArbitrator(arbitrator);
storage.write(settings.getClass().getName(), settings);
try
{
messageFacade.addArbitrator(arbitrator);
} catch (IOException e)
{
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
messageFacade.addArbitrator(arbitrator);
}
}

View File

@ -0,0 +1,9 @@
package io.bitsquare.msg;
public interface BootstrapListener
{
public void onCompleted();
public void onFailed(Throwable throwable);
}

View 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 dont 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())); */
}
}

View File

@ -0,0 +1,8 @@
package io.bitsquare.msg;
import net.tomp2p.peers.PeerAddress;
public interface MessageBroker
{
void handleMessage(Object message, PeerAddress peerAddress);
}

View File

@ -1,66 +1,65 @@
package io.bitsquare.msg;
import io.bitsquare.BitSquare;
import com.google.common.util.concurrent.FutureCallback;
import com.google.inject.name.Named;
import io.bitsquare.msg.listeners.*;
import io.bitsquare.trade.Offer;
import io.bitsquare.user.Arbitrator;
import io.bitsquare.util.DSAKeyUtil;
import io.bitsquare.util.StorageDirectory;
import io.bitsquare.user.User;
import java.io.IOException;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.*;
import java.util.ArrayList;
import java.util.Currency;
import java.util.List;
import java.util.Locale;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javax.annotation.Nullable;
import javax.inject.Inject;
import net.tomp2p.connection.Bindings;
import net.tomp2p.connection.PeerConnection;
import net.tomp2p.futures.*;
import net.tomp2p.p2p.Peer;
import net.tomp2p.p2p.PeerMaker;
import net.tomp2p.dht.FutureGet;
import net.tomp2p.dht.FuturePut;
import net.tomp2p.dht.FutureRemove;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.futures.BaseFuture;
import net.tomp2p.futures.BaseFutureAdapter;
import net.tomp2p.futures.BaseFutureListener;
import net.tomp2p.futures.FutureDirect;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.PeerAddress;
import net.tomp2p.storage.Data;
import net.tomp2p.storage.StorageDisk;
import net.tomp2p.utils.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* That facade delivers messaging functionality from the TomP2P library
* That facade delivers direct messaging and DHT functionality from the TomP2P library
* It is the translating domain specific functionality to the messaging layer.
* The TomP2P library codebase shall not be used outside that facade.
* That way a change of the library will only affect that class.
* That way we limit the dependency of the TomP2P library only to that class (and it's sub components).
* <p>
* TODO: improve callbacks that Platform.runLater is not necessary. We call usually that methods form teh UI thread.
*/
@SuppressWarnings({"EmptyMethod", "ConstantConditions"})
public class MessageFacade
public class MessageFacade implements MessageBroker
{
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
// private static final String PING = "ping";
// private static final String PONG = "pong";
private static final int MASTER_PEER_PORT = 5001;
private static final String ARBITRATORS_ROOT = "ArbitratorsRoot";
private final P2PNode p2pNode;
private final List<OrderBookListener> orderBookListeners = new ArrayList<>();
private final List<ArbitratorListener> arbitratorListeners = new ArrayList<>();
private final List<IncomingTradeMessageListener> incomingTradeMessageListeners = new ArrayList<>();
// private final List<PingPeerListener> pingPeerListeners = new ArrayList<>();
private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
private Peer myPeer;
private KeyPair keyPair;
private Long lastTimeStamp = -3L;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
public MessageFacade()
public MessageFacade(User user, @Named("useDiskStorage") Boolean useDiskStorage, @Named("defaultSeedNode") SeedNodeAddress.StaticSeedNodeAddresses defaultStaticSeedNodeAddresses)
{
this.p2pNode = new P2PNode(user.getMessageKeyPair(), useDiskStorage, defaultStaticSeedNodeAddresses, this);
}
@ -68,168 +67,190 @@ public class MessageFacade
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
public void init()
public void init(BootstrapListener bootstrapListener)
{
int port = Bindings.MAX_PORT - Math.abs(new Random().nextInt()) % (Bindings.MAX_PORT - Bindings.MIN_DYN_PORT);
if (BitSquare.getAppName().contains("taker"))
p2pNode.start(new FutureCallback<PeerDHT>()
{
port = 4501;
}
else if (BitSquare.getAppName().contains("offerer"))
{
port = 4500;
}
@Override
public void onSuccess(@Nullable PeerDHT result)
{
log.debug("p2pNode.start success result = " + result);
Platform.runLater(() -> bootstrapListener.onCompleted());
}
try
{
createMyPeerInstance(port);
// setupStorage();
//TODO save periodically or get informed if network address changes
saveMyAddressToDHT();
setupReplyHandler();
} catch (IOException e)
{
shutDown();
log.error("Error at init myPeerInstance" + e.getMessage());
}
@Override
public void onFailure(Throwable t)
{
log.error(t.toString());
Platform.runLater(() -> bootstrapListener.onFailed(t));
}
});
}
public void shutDown()
{
if (myPeer != null)
if (p2pNode != null)
p2pNode.shutDown();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Find peer address by publicKey
///////////////////////////////////////////////////////////////////////////////////////////
public void getPeerAddress(PublicKey publicKey, GetPeerAddressListener listener)
{
final Number160 locationKey = Utils.makeSHAHash(publicKey.getEncoded());
try
{
myPeer.shutdown();
FutureGet futureGet = p2pNode.getDomainProtectedData(locationKey, publicKey);
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (baseFuture.isSuccess() && futureGet.data() != null)
{
final PeerAddress peerAddress = (PeerAddress) futureGet.data().object();
Platform.runLater(() -> listener.onResult(peerAddress));
}
else
{
Platform.runLater(() -> listener.onFailed());
}
}
});
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
log.error(e.toString());
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Find peer address
// Offer
///////////////////////////////////////////////////////////////////////////////////////////
public void getPeerAddress(String pubKeyAsHex, GetPeerAddressListener listener)
public void addOffer(Offer offer)
{
final Number160 location = Number160.createHash(pubKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
try
{
final Data data = new Data(offer);
// the offer is default 30 days valid
int defaultOfferTTL = 30 * 24 * 60 * 60 * 1000;
data.ttlSeconds(defaultOfferTTL);
FuturePut futurePut = p2pNode.addProtectedData(locationKey, data);
futurePut.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> {
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(data, future.isSuccess()));
setDirty(locationKey);
});
if (future.isSuccess())
{
log.trace("Add offer to DHT was successful. Stored data: [key: " + locationKey + ", value: " + data + "]");
}
else
{
log.error("Add offer to DHT failed. Reason: " + future.failedReason());
}
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
log.error(t.toString());
}
});
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
log.error(e.toString());
}
}
public void removeOffer(Offer offer)
{
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
try
{
final Data data = new Data(offer);
FutureRemove futureRemove = p2pNode.removeFromDataMap(locationKey, data);
futureRemove.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> {
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferRemoved(data, future.isSuccess()));
setDirty(locationKey);
});
if (future.isSuccess())
{
log.trace("Remove offer from DHT was successful. Stored data: [key: " + locationKey + ", value: " + data + "]");
}
else
{
log.error("Remove offer from DHT failed. Reason: " + future.failedReason());
}
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
log.error(t.toString());
}
});
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
public void getOffers(String currencyCode)
{
Number160 locationKey = Number160.createHash(currencyCode);
FutureGet futureGet = p2pNode.getDataMap(locationKey);
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (baseFuture.isSuccess() && getPeerAddressFuture.getData() != null)
Platform.runLater(() -> orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(futureGet.dataMap(), baseFuture.isSuccess())));
if (baseFuture.isSuccess())
{
final PeerAddress peerAddress = (PeerAddress) getPeerAddressFuture.getData().getObject();
Platform.runLater(() -> listener.onResult(peerAddress));
log.trace("Get offers from DHT was successful. Stored data: [key: " + locationKey + ", values: " + futureGet.dataMap() + "]");
}
else
{
Platform.runLater(() -> listener.onFailed());
log.error("Get offers from DHT failed with reason:" + baseFuture.failedReason());
}
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Publish offer
///////////////////////////////////////////////////////////////////////////////////////////
public void addOffer(Offer offer) throws IOException
{
log.trace("addOffer");
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
final Number160 contentKey = Number160.createHash(offer.getId());
final Data offerData = new Data(offer);
//offerData.setTTLSeconds(5);
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, offerData).start();
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> onOfferAdded(offerData, future.isSuccess(), locationKey));
}
});
}
private void onOfferAdded(Data offerData, boolean success, Number160 locationKey)
{
log.trace("onOfferAdded");
setDirty(locationKey);
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferAdded(offerData, success));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Get offers
///////////////////////////////////////////////////////////////////////////////////////////
public void getOffers(String currency)
{
log.trace("getOffers");
final Number160 locationKey = Number160.createHash(currency);
final FutureDHT getOffersFuture = myPeer.get(locationKey).setAll().start();
getOffersFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
final Map<Number160, Data> dataMap = getOffersFuture.getDataMap();
Platform.runLater(() -> onOffersReceived(dataMap, future.isSuccess()));
}
});
}
private void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
{
log.trace("onOffersReceived");
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOffersReceived(dataMap, success));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Remove offer
///////////////////////////////////////////////////////////////////////////////////////////
public void removeOffer(Offer offer)
{
log.trace("removeOffer");
Number160 locationKey = Number160.createHash(offer.getCurrency().getCurrencyCode());
Number160 contentKey = Number160.createHash(offer.getId());
log.debug("removeOffer");
FutureDHT removeFuture = myPeer.remove(locationKey).setReturnResults().setContentKey(contentKey).start();
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> onOfferRemoved(removeFuture.getData(), future.isSuccess(), locationKey));
}
});
}
private void onOfferRemoved(Data data, boolean success, Number160 locationKey)
{
log.trace("onOfferRemoved");
setDirty(locationKey);
orderBookListeners.stream().forEach(orderBookListener -> orderBookListener.onOfferRemoved(data, success));
}
///////////////////////////////////////////////////////////////////////////////////////////
// Trade process
///////////////////////////////////////////////////////////////////////////////////////////
public void sendTradeMessage(PeerAddress peerAddress, TradeMessage tradeMessage, OutgoingTradeMessageListener listener)
{
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
final FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(tradeMessage).start();
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
FutureDirect futureDirect = p2pNode.sendData(peerAddress, tradeMessage);
futureDirect.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
public void operationComplete(BaseFuture future) throws Exception
{
if (sendFuture.isSuccess())
if (futureDirect.isSuccess())
{
Platform.runLater(() -> listener.onResult());
}
@ -238,316 +259,109 @@ public class MessageFacade
Platform.runLater(() -> listener.onFailed());
}
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
Platform.runLater(() -> listener.onFailed());
}
});
}
///////////////////////////////////////////////////////////////////////////////////////////
// Reputation
///////////////////////////////////////////////////////////////////////////////////////////
public void setupReputationRoot() throws IOException
{
String pubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(getPubKey()); // out message ID
final Number160 locationKey = Number160.createHash("REPUTATION_" + pubKeyAsHex); // out reputation root storage location
final Number160 contentKey = Utils.makeSHAHash(getPubKey().getEncoded()); // my pubKey -> i may only put in 1 reputation
final Data reputationData = new Data(Number160.ZERO).setProtectedEntry().setPublicKey(getPubKey()); // at registration time we add a null value as data
// we use a pubkey where we provable cannot own the private key.
// the domain key must be verifiable by peers to be sure the reputation root was net deleted by the owner.
// so we use the locationKey as it already meets our requirements (verifiable and impossible to create a private key out of it)
myPeer.put(locationKey).setData(contentKey, reputationData).setDomainKey(locationKey).setProtectDomain().start();
}
public void addReputation(String pubKeyAsHex) throws IOException
{
final Number160 locationKey = Number160.createHash("REPUTATION_" + pubKeyAsHex); // reputation root storage location ot the peer
final Number160 contentKey = Utils.makeSHAHash(getPubKey().getEncoded()); // my pubKey -> i may only put in 1 reputation, I may update it later. eg. counter for 5 trades...
final Data reputationData = new Data("TODO: some reputation data..., content signed and sig attached").setProtectedEntry().setPublicKey(getPubKey());
myPeer.put(locationKey).setData(contentKey, reputationData).start();
}
// At any offer or take offer fee payment the trader add the tx id and the pubKey and the signature of that tx to that entry.
// That way he can prove with the signature that he is the payer of the offer fee.
// It does not assure that the trade was really executed, but we can protect the traders privacy that way.
// If we use the trade, we would link all trades together and would reveal the whole trading history.
@SuppressWarnings("UnusedParameters")
public void addOfferFeePaymentToReputation(String txId, String pubKeyOfFeePayment) throws IOException
{
String pubKeyAsHex = DSAKeyUtil.getHexStringFromPublicKey(getPubKey()); // out message ID
final Number160 locationKey = Number160.createHash("REPUTATION_" + pubKeyAsHex); // reputation root storage location ot the peer
final Number160 contentKey = Utils.makeSHAHash(getPubKey().getEncoded()); // my pubKey -> i may only put in 1 reputation, I may update it later. eg. counter for 5 trades...
final Data reputationData = new Data("TODO: tx, btc_pubKey, sig(tx), content signed and sig attached").setProtectedEntry().setPublicKey(getPubKey());
myPeer.put(locationKey).setData(contentKey, reputationData).start();
}
///////////////////////////////////////////////////////////////////////////////////////////
// Arbitrators
///////////////////////////////////////////////////////////////////////////////////////////
public void addArbitrator(Arbitrator arbitrator) throws IOException
public void addArbitrator(Arbitrator arbitrator)
{
Number160 locationKey = Number160.createHash("Arbitrators");
final Number160 contentKey = Number160.createHash(arbitrator.getId());
final Data arbitratorData = new Data(arbitrator);
//offerData.setTTLSeconds(5);
final FutureDHT addFuture = myPeer.put(locationKey).setData(contentKey, arbitratorData).start();
//final FutureDHT addFuture = myPeer.add(locationKey).setData(offerData).start();
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Platform.runLater(() -> onArbitratorAdded(arbitratorData, future.isSuccess(), locationKey));
}
});
}
@SuppressWarnings("UnusedParameters")
private void onArbitratorAdded(Data arbitratorData, boolean success, Number160 locationKey)
{
}
@SuppressWarnings("UnusedParameters")
public void getArbitrators(Locale languageLocale)
{
final Number160 locationKey = Number160.createHash("Arbitrators");
final FutureDHT getArbitratorsFuture = myPeer.get(locationKey).setAll().start();
getArbitratorsFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
final Map<Number160, Data> dataMap = getArbitratorsFuture.getDataMap();
Platform.runLater(() -> onArbitratorsReceived(dataMap, future.isSuccess()));
}
});
}
private void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success)
{
for (ArbitratorListener arbitratorListener : arbitratorListeners)
{
arbitratorListener.onArbitratorsReceived(dataMap, success);
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Check dirty flag for a location key
///////////////////////////////////////////////////////////////////////////////////////////
// TODO just temp...
public BooleanProperty getIsDirtyProperty()
{
return isDirty;
}
public void getDirtyFlag(Currency currency)
{
Number160 locationKey = Number160.createHash(currency.getCurrencyCode());
FutureDHT getFuture = myPeer.get(getDirtyLocationKey(locationKey)).start();
getFuture.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Data data = getFuture.getData();
if (data != null)
{
Object object = data.getObject();
if (object instanceof Long)
{
Platform.runLater(() -> onGetDirtyFlag((Long) object));
}
}
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
System.out.println("getFuture exceptionCaught " + System.currentTimeMillis());
}
});
}
private void onGetDirtyFlag(long timeStamp)
{
// TODO don't get updates at first execute....
if (lastTimeStamp != timeStamp)
{
isDirty.setValue(!isDirty.get());
}
if (lastTimeStamp > 0)
{
lastTimeStamp = timeStamp;
}
else
{
lastTimeStamp++;
}
}
private Number160 getDirtyLocationKey(Number160 locationKey)
{
return Number160.createHash(locationKey + "Dirty");
}
private void setDirty(Number160 locationKey)
{
// we don't want to get an update from dirty for own changes, so update the lastTimeStamp to omit a change trigger
lastTimeStamp = System.currentTimeMillis();
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
try
{
FutureDHT putFuture = myPeer.put(getDirtyLocationKey(locationKey)).setData(new Data(lastTimeStamp)).start();
putFuture.addListener(new BaseFutureListener<BaseFuture>()
final Data arbitratorData = new Data(arbitrator);
FuturePut addFuture = p2pNode.addProtectedData(locationKey, arbitratorData);
addFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
//System.out.println("operationComplete");
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
System.err.println("exceptionCaught");
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorAdded(arbitratorData, addFuture.isSuccess())));
if (addFuture.isSuccess())
{
log.trace("Add arbitrator to DHT was successful. Stored data: [key: " + locationKey + ", values: " + arbitratorData + "]");
}
else
{
log.error("Add arbitrator to DHT failed with reason:" + addFuture.failedReason());
}
}
});
} catch (IOException e)
{
log.warn("Error at writing dirty flag (timeStamp) " + e.getMessage());
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Send message
///////////////////////////////////////////////////////////////////////////////////////////
/* public boolean sendMessage(Object message)
{
boolean result = false;
if (otherPeerAddress != null)
e.printStackTrace();
} catch (ClassNotFoundException e)
{
if (peerConnection != null)
peerConnection.close();
peerConnection = myPeer.createPeerConnection(otherPeerAddress, 20);
if (!peerConnection.isClosed())
{
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(message).start();
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (sendFuture.isSuccess())
{
final Object object = sendFuture.getObject();
Platform.runLater(() -> onResponseFromSend(object));
}
else
{
Platform.runLater(() -> onSendFailed());
}
}
}
);
result = true;
}
e.printStackTrace();
}
return result;
} */
/*
private void onResponseFromSend(Object response)
{
for (MessageListener messageListener : messageListeners)
messageListener.onResponseFromSend(response);
}
private void onSendFailed()
public void removeArbitrator(Arbitrator arbitrator) throws IOException, ClassNotFoundException
{
for (MessageListener messageListener : messageListeners)
messageListener.onSendFailed();
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Ping peer
///////////////////////////////////////////////////////////////////////////////////////////
//TODO not working anymore...
/* public void pingPeer(String publicKeyAsHex)
{
Number160 location = Number160.createHash(publicKeyAsHex);
final FutureDHT getPeerAddressFuture = myPeer.get(location).start();
getPeerAddressFuture.addListener(new BaseFutureAdapter<BaseFuture>()
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
final Data arbitratorData = new Data(arbitrator);
FutureRemove removeFuture = p2pNode.removeFromDataMap(locationKey, arbitratorData);
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
public void operationComplete(BaseFuture future) throws Exception
{
final Data data = getPeerAddressFuture.getData();
if (data != null && data.getObject() instanceof PeerAddress)
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorRemoved(arbitratorData, removeFuture.isSuccess())));
if (removeFuture.isSuccess())
{
final PeerAddress peerAddress = (PeerAddress) data.getObject();
Platform.runLater(() -> onAddressFoundPingPeer(peerAddress));
log.trace("Remove arbitrator from DHT was successful. Stored data: [key: " + locationKey + ", values: " + arbitratorData + "]");
}
else
{
log.error("Remove arbitrators from DHT failed with reason:" + removeFuture.failedReason());
}
}
});
}
private void onAddressFoundPingPeer(PeerAddress peerAddress)
public void getArbitrators(Locale languageLocale)
{
try
Number160 locationKey = Number160.createHash(ARBITRATORS_ROOT);
FutureGet futureGet = p2pNode.getDataMap(locationKey);
futureGet.addListener(new BaseFutureAdapter<BaseFuture>()
{
final PeerConnection peerConnection = myPeer.createPeerConnection(peerAddress, 10);
if (!peerConnection.isClosed())
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
FutureResponse sendFuture = myPeer.sendDirect(peerConnection).setObject(PING).start();
sendFuture.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture baseFuture) throws Exception
{
if (sendFuture.isSuccess())
{
final String pong = (String) sendFuture.getObject();
Platform.runLater(() -> onResponseFromPing(PONG.equals(pong)));
}
else
{
peerConnection.close();
Platform.runLater(() -> onResponseFromPing(false));
}
}
}
);
Platform.runLater(() -> arbitratorListeners.stream().forEach(listener -> listener.onArbitratorsReceived(futureGet.dataMap(), baseFuture.isSuccess())));
if (baseFuture.isSuccess())
{
log.trace("Get arbitrators from DHT was successful. Stored data: [key: " + locationKey + ", values: " + futureGet.dataMap() + "]");
}
else
{
log.error("Get arbitrators from DHT failed with reason:" + baseFuture.failedReason());
}
}
} catch (Exception e)
{
// ClosedChannelException can happen, check out if there is a better way to ping a myPeerInstance for online status
}
});
}
private void onResponseFromPing(boolean success)
{
for (PingPeerListener pingPeerListener : pingPeerListeners)
pingPeerListener.onPingPeerResult(success);
}
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Event Listeners
///////////////////////////////////////////////////////////////////////////////////////////
public void addMessageListener(OrderBookListener listener)
public void addOrderBookListener(OrderBookListener listener)
{
orderBookListeners.add(listener);
}
public void removeMessageListener(OrderBookListener listener)
public void removeOrderBookListener(OrderBookListener listener)
{
orderBookListeners.remove(listener);
}
@ -584,12 +398,99 @@ public class MessageFacade
///////////////////////////////////////////////////////////////////////////////////////////
// Getters
// Check dirty flag for a location key
///////////////////////////////////////////////////////////////////////////////////////////
public PublicKey getPubKey()
// TODO just temp...
public BooleanProperty getIsDirtyProperty()
{
return keyPair.getPublic();
return isDirty;
}
public void getDirtyFlag(Currency currency)
{
Number160 locationKey = Number160.createHash(currency.getCurrencyCode());
try
{
FutureGet getFuture = p2pNode.getData(getDirtyLocationKey(locationKey));
getFuture.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
Data data = getFuture.data();
if (data != null)
{
Object object = data.object();
if (object instanceof Long)
{
Platform.runLater(() -> onGetDirtyFlag((Long) object));
}
}
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
log.error("getFuture exceptionCaught " + t.toString());
}
});
} catch (IOException | ClassNotFoundException e)
{
e.printStackTrace();
}
}
private Long lastTimeStamp = -3L;
private final BooleanProperty isDirty = new SimpleBooleanProperty(false);
private void onGetDirtyFlag(long timeStamp)
{
// TODO don't get updates at first execute....
if (lastTimeStamp != timeStamp)
{
isDirty.setValue(!isDirty.get());
}
if (lastTimeStamp > 0)
{
lastTimeStamp = timeStamp;
}
else
{
lastTimeStamp++;
}
}
private void setDirty(Number160 locationKey)
{
// we don't want to get an update from dirty for own changes, so update the lastTimeStamp to omit a change trigger
lastTimeStamp = System.currentTimeMillis();
try
{
FuturePut putFuture = p2pNode.putData(getDirtyLocationKey(locationKey), new Data(lastTimeStamp));
putFuture.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
// log.trace("operationComplete");
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
log.warn("Error at writing dirty flag (timeStamp) " + t.toString());
}
});
} catch (IOException | ClassNotFoundException e)
{
log.warn("Error at writing dirty flag (timeStamp) " + e.getMessage());
}
}
private Number160 getDirtyLocationKey(Number160 locationKey)
{
return Number160.createHash(locationKey + "Dirty");
}
@ -597,89 +498,13 @@ public class MessageFacade
// Incoming message handler
///////////////////////////////////////////////////////////////////////////////////////////
private void onMessage(Object request, PeerAddress sender)
@Override
public void handleMessage(Object message, PeerAddress peerAddress)
{
if (request instanceof TradeMessage)
if (message instanceof TradeMessage)
{
incomingTradeMessageListeners.stream().forEach(e -> e.onMessage((TradeMessage) request, sender));
}
/* else
{
for (OrderBookListener orderBookListener : orderBookListeners)
orderBookListener.onMessage(request);
} */
}
///////////////////////////////////////////////////////////////////////////////////////////
// Private Methods
///////////////////////////////////////////////////////////////////////////////////////////
private void createMyPeerInstance(int port) throws IOException
{
keyPair = DSAKeyUtil.getKeyPair();
myPeer = new PeerMaker(keyPair).setPorts(port).makeAndListen();
final FutureBootstrap futureBootstrap = myPeer.bootstrap().setBroadcast().setPorts(MASTER_PEER_PORT).start();
futureBootstrap.addListener(new BaseFutureAdapter<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
if (futureBootstrap.getBootstrapTo() != null)
{
PeerAddress masterPeerAddress = futureBootstrap.getBootstrapTo().iterator().next();
final FutureDiscover futureDiscover = myPeer.discover().setPeerAddress(masterPeerAddress).start();
futureDiscover.addListener(new BaseFutureListener<BaseFuture>()
{
@Override
public void operationComplete(BaseFuture future) throws Exception
{
//System.out.println("operationComplete");
}
@Override
public void exceptionCaught(Throwable t) throws Exception
{
System.err.println("exceptionCaught");
}
});
}
}
});
}
private void setupReplyHandler()
{
myPeer.setObjectDataReply((sender, request) -> {
if (!sender.equals(myPeer.getPeerAddress()))
{
Platform.runLater(() -> onMessage(request, sender));
}
else
{
log.error("Received msg from myself. That should never happen.");
}
//noinspection ReturnOfNull
return null;
});
}
private void setupStorage()
{
try
{
myPeer.getPeerBean().setStorage(new StorageDisk(StorageDirectory.getStorageDirectory().getCanonicalPath() + "/" + BitSquare.getAppName() + "_tomP2P"));
} catch (IOException e)
{
e.printStackTrace();
log.error("####################");
Platform.runLater(() -> incomingTradeMessageListeners.stream().forEach(e -> e.onMessage((TradeMessage) message, peerAddress)));
}
}
private void saveMyAddressToDHT() throws IOException
{
Number160 location = Number160.createHash(DSAKeyUtil.getHexStringFromPublicKey(getPubKey()));
//log.debug("saveMyAddressToDHT location "+location.toString());
myPeer.put(location).setData(new Data(myPeer.getPeerAddress())).start();
}
}

View 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();
}
}
}

View 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;
}
}
}

View File

@ -1,7 +1,7 @@
package io.bitsquare.msg.listeners;
import java.util.Map;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;
@SuppressWarnings({"EmptyMethod", "UnusedParameters"})
@ -9,7 +9,7 @@ public interface ArbitratorListener
{
void onArbitratorAdded(Data offerData, boolean success);
void onArbitratorsReceived(Map<Number160, Data> dataMap, boolean success);
void onArbitratorsReceived(Map<Number640, Data> dataMap, boolean success);
void onArbitratorRemoved(Data data, boolean success);
}

View File

@ -1,7 +1,7 @@
package io.bitsquare.msg.listeners;
import java.util.Map;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;
public interface OrderBookListener
@ -9,7 +9,7 @@ public interface OrderBookListener
@SuppressWarnings("UnusedParameters")
void onOfferAdded(Data offerData, boolean success);
void onOffersReceived(Map<Number160, Data> dataMap, boolean success);
void onOffersReceived(Map<Number640, Data> dataMap, boolean success);
void onOfferRemoved(Data data, boolean success);
}

View File

@ -1,8 +1,10 @@
package io.bitsquare.trade;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.util.DSAKeyUtil;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.PublicKey;
public class Contract implements Serializable
{
@ -15,8 +17,8 @@ public class Contract implements Serializable
private final String takerAccountID;
private final BankAccount offererBankAccount;
private final BankAccount takerBankAccount;
private final String offererMessagePubKeyAsHex;
private final String takerMessagePubKeyAsHex;
private final String offererMessagePublicKeyAsString;
private final String takerMessagePublicKeyAsString;
public Contract(Offer offer,
BigInteger tradeAmount,
@ -25,8 +27,8 @@ public class Contract implements Serializable
String takerAccountID,
BankAccount offererBankAccount,
BankAccount takerBankAccount,
String offererMessagePubKeyAsHex,
String takerMessagePubKeyAsHex)
PublicKey offererMessagePublicKey,
PublicKey takerMessagePublicKey)
{
this.offer = offer;
this.tradeAmount = tradeAmount;
@ -35,8 +37,8 @@ public class Contract implements Serializable
this.takerAccountID = takerAccountID;
this.offererBankAccount = offererBankAccount;
this.takerBankAccount = takerBankAccount;
this.offererMessagePubKeyAsHex = offererMessagePubKeyAsHex;
this.takerMessagePubKeyAsHex = takerMessagePubKeyAsHex;
this.offererMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(offererMessagePublicKey);
this.takerMessagePublicKeyAsString = DSAKeyUtil.getHexStringFromPublicKey(takerMessagePublicKey);
}
@ -79,17 +81,16 @@ public class Contract implements Serializable
return takerBankAccount;
}
public String getTakerMessagePubKeyAsHex()
public String getTakerMessagePublicKey()
{
return takerMessagePubKeyAsHex;
return takerMessagePublicKeyAsString;
}
public String getOffererMessagePubKeyAsHex()
public String getOffererMessagePublicKey()
{
return offererMessagePubKeyAsHex;
return offererMessagePublicKeyAsString;
}
@Override
public String toString()
{
@ -101,10 +102,8 @@ public class Contract implements Serializable
", takerAccountID='" + takerAccountID + '\'' +
", offererBankAccount=" + offererBankAccount +
", takerBankAccount=" + takerBankAccount +
", offererMessagePubKeyAsHex='" + offererMessagePubKeyAsHex + '\'' +
", takerMessagePubKeyAsHex='" + takerMessagePubKeyAsHex + '\'' +
", takerMessagePublicKeyAsString=" + takerMessagePublicKeyAsString +
", offererMessagePublicKeyAsString=" + offererMessagePublicKeyAsString +
'}';
}
}

View File

@ -6,6 +6,7 @@ import io.bitsquare.locale.Country;
import io.bitsquare.user.Arbitrator;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.PublicKey;
import java.util.*;
public class Offer implements Serializable
@ -23,7 +24,7 @@ public class Offer implements Serializable
private final double price;
private final BigInteger amount;
private final BigInteger minAmount;
private final String messagePubKeyAsHex;
private final PublicKey messagePublicKey;
private final BankAccountType bankAccountType;
private final Country bankAccountCountry;
@ -39,7 +40,7 @@ public class Offer implements Serializable
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public Offer(String messagePubKeyAsHex,
public Offer(PublicKey messagePublicKey,
Direction direction,
double price,
BigInteger amount,
@ -53,7 +54,7 @@ public class Offer implements Serializable
List<Country> acceptedCountries,
List<Locale> acceptedLanguageLocales)
{
this.messagePubKeyAsHex = messagePubKeyAsHex;
this.messagePublicKey = messagePublicKey;
this.direction = direction;
this.price = price;
this.amount = amount;
@ -77,9 +78,9 @@ public class Offer implements Serializable
// Setters
///////////////////////////////////////////////////////////////////////////////////////////
public String getMessagePubKeyAsHex()
public PublicKey getMessagePublicKey()
{
return messagePubKeyAsHex;
return messagePublicKey;
}
@ -183,7 +184,7 @@ public class Offer implements Serializable
", price=" + price +
", amount=" + amount +
", minAmount=" + minAmount +
", messagePubKey=" + messagePubKeyAsHex.hashCode() +
", messagePubKey=" + messagePublicKey.hashCode() +
", bankAccountTypeEnum=" + bankAccountType +
", bankAccountCountryLocale=" + bankAccountCountry +
", collateral=" + collateral +

View File

@ -136,11 +136,11 @@ public class Trading
}
public void removeOffer(Offer offer)
{
if (!offers.containsKey(offer.getId()))
{ //TODO
/* if (!offers.containsKey(offer.getId()))
{
throw new IllegalStateException("offers does not contain the offer with the ID " + offer.getId());
}
}*/
offers.remove(offer.getId());
saveOffers();

View File

@ -21,11 +21,17 @@ import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javax.inject.Inject;
import net.tomp2p.peers.Number160;
import net.tomp2p.peers.Number640;
import net.tomp2p.storage.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/*
TODO
remove dependencies to tomp2p
import net.tomp2p.peers.Number160;
import net.tomp2p.storage.Data;
*/
public class OrderBook implements OrderBookListener
{
private static final Logger log = LoggerFactory.getLogger(OrderBook.class);
@ -59,12 +65,12 @@ public class OrderBook implements OrderBookListener
public void init()
{
messageFacade.addMessageListener(this);
messageFacade.addOrderBookListener(this);
}
public void cleanup()
{
messageFacade.removeMessageListener(this);
messageFacade.removeOrderBookListener(this);
}
public void loadOffers()
@ -177,7 +183,7 @@ public class OrderBook implements OrderBookListener
{
try
{
Object offerDataObject = offerData.getObject();
Object offerDataObject = offerData.object();
if (offerDataObject instanceof Offer)
{
Offer offer = (Offer) offerDataObject;
@ -190,7 +196,7 @@ public class OrderBook implements OrderBookListener
}
@Override
public void onOffersReceived(Map<Number160, Data> dataMap, boolean success)
public void onOffersReceived(Map<Number640, Data> dataMap, boolean success)
{
if (success && dataMap != null)
{
@ -200,7 +206,7 @@ public class OrderBook implements OrderBookListener
{
try
{
Object offerDataObject = offerData.getObject();
Object offerDataObject = offerData.object();
if (offerDataObject instanceof Offer)
{
Offer offer = (Offer) offerDataObject;
@ -226,7 +232,7 @@ public class OrderBook implements OrderBookListener
{
try
{
Object offerDataObject = offerData.getObject();
Object offerDataObject = offerData.object();
if (offerDataObject instanceof Offer)
{
Offer offer = (Offer) offerDataObject;

View File

@ -16,6 +16,7 @@ import io.bitsquare.trade.protocol.taker.RequestOffererPublishDepositTxMessage;
import io.bitsquare.trade.protocol.taker.TakeOfferFeePayedMessage;
import io.bitsquare.user.User;
import java.math.BigInteger;
import java.security.PublicKey;
import net.tomp2p.peers.PeerAddress;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
@ -76,7 +77,7 @@ public class ProtocolForOffererAsBuyer
private final String arbitratorPubKey;
private final BankAccount bankAccount;
private final String accountId;
private final String messagePubKey;
private final PublicKey messagePublicKey;
private final ECKey accountKey;
private final String payoutAddress;
@ -90,7 +91,7 @@ public class ProtocolForOffererAsBuyer
private String peersPayoutAddress;
private String peersAccountId;
private BankAccount peersBankAccount;
private String peersMessagePubKey;
private PublicKey peersMessagePublicKey;
private String peersContractAsJson;
private String signedTakerDepositTxAsHex;
private String txConnOutAsHex;
@ -129,7 +130,7 @@ public class ProtocolForOffererAsBuyer
bankAccount = user.getBankAccount(trade.getOffer().getBankAccountId());
accountId = user.getAccountId();
messagePubKey = user.getMessagePubKeyAsHex();
messagePublicKey = user.getMessagePublicKey();
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
payoutAddress = walletFacade.getAddressInfoByTradeID(tradeId).getAddressString();
@ -241,7 +242,7 @@ public class ProtocolForOffererAsBuyer
String peersPayoutAddress = nonEmptyStringOf(message.getTakerPayoutAddress());
String peersAccountId = nonEmptyStringOf(message.getTakerAccountId());
BankAccount peersBankAccount = checkNotNull(message.getTakerBankAccount());
String peersMessagePubKey = nonEmptyStringOf(message.getTakerMessagePubKey());
PublicKey peersMessagePublicKey = checkNotNull(message.getTakerMessagePublicKey());
String peersContractAsJson = nonEmptyStringOf(message.getTakerContractAsJson());
String signedTakerDepositTxAsHex = nonEmptyStringOf(message.getSignedTakerDepositTxAsHex());
String txConnOutAsHex = nonEmptyStringOf(message.getTxConnOutAsHex());
@ -253,7 +254,7 @@ public class ProtocolForOffererAsBuyer
this.peersPayoutAddress = peersPayoutAddress;
this.peersAccountId = peersAccountId;
this.peersBankAccount = peersBankAccount;
this.peersMessagePubKey = peersMessagePubKey;
this.peersMessagePublicKey = peersMessagePublicKey;
this.peersContractAsJson = peersContractAsJson;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txConnOutAsHex = txConnOutAsHex;
@ -277,12 +278,12 @@ public class ProtocolForOffererAsBuyer
accountId,
tradeAmount,
takeOfferFeeTxId,
messagePubKey,
messagePublicKey,
offer,
peersAccountId,
bankAccount,
peersBankAccount,
peersMessagePubKey,
peersMessagePublicKey,
peersContractAsJson,
accountKey);
}

View File

@ -8,6 +8,7 @@ import io.bitsquare.trade.Offer;
import io.bitsquare.trade.protocol.FaultHandler;
import io.bitsquare.util.Utilities;
import java.math.BigInteger;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -21,17 +22,17 @@ public class VerifyAndSignContract
String accountId,
BigInteger tradeAmount,
String takeOfferFeeTxId,
String messagePubKeyAsHex,
PublicKey messagePublicKey,
Offer offer,
String peersAccountId,
BankAccount bankAccount,
BankAccount peersBankAccount,
String takerMessagePubKey,
PublicKey takerMessagePublicKey,
String peersContractAsJson,
ECKey registrationKey)
{
log.trace("Run task");
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, accountId, peersAccountId, bankAccount, peersBankAccount, messagePubKeyAsHex, takerMessagePubKey);
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, accountId, peersAccountId, bankAccount, peersBankAccount, messagePublicKey, takerMessagePublicKey);
String contractAsJson = Utilities.objectToJson(contract);
// log.trace("Offerer contract created: " + contract);

View File

@ -8,6 +8,7 @@ import io.bitsquare.trade.Offer;
import io.bitsquare.trade.protocol.FaultHandler;
import io.bitsquare.util.Utilities;
import java.math.BigInteger;
import java.security.PublicKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -23,8 +24,8 @@ public class CreateAndSignContract
String takeOfferFeeTxId,
String accountId,
BankAccount bankAccount,
String peersMessagePubKeyAsHex,
String messagePubKeyAsHex,
PublicKey peersMessagePublicKey,
PublicKey messagePublicKey,
String peersAccountId,
BankAccount peersBankAccount,
ECKey registrationKey)
@ -32,7 +33,7 @@ public class CreateAndSignContract
log.trace("Run task");
try
{
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, peersAccountId, accountId, peersBankAccount, bankAccount, peersMessagePubKeyAsHex, messagePubKeyAsHex);
Contract contract = new Contract(offer, tradeAmount, takeOfferFeeTxId, peersAccountId, accountId, peersBankAccount, bankAccount, peersMessagePublicKey, messagePublicKey);
String contractAsJson = Utilities.objectToJson(contract);
String signature = cryptoFacade.signContract(registrationKey, contractAsJson);

View File

@ -3,6 +3,7 @@ package io.bitsquare.trade.protocol.taker;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.GetPeerAddressListener;
import io.bitsquare.trade.protocol.FaultHandler;
import java.security.PublicKey;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -11,10 +12,10 @@ public class GetPeerAddress
{
private static final Logger log = LoggerFactory.getLogger(GetPeerAddress.class);
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, MessageFacade messageFacade, String messagePubKeyAsHex)
public static void run(ResultHandler resultHandler, FaultHandler faultHandler, MessageFacade messageFacade, PublicKey messagePublicKey)
{
log.trace("Run task");
messageFacade.getPeerAddress(messagePubKeyAsHex, new GetPeerAddressListener()
messageFacade.getPeerAddress(messagePublicKey, new GetPeerAddressListener()
{
@Override
public void onResult(PeerAddress peerAddress)

View File

@ -16,6 +16,7 @@ import io.bitsquare.trade.protocol.offerer.RequestTakerDepositPaymentMessage;
import io.bitsquare.trade.protocol.offerer.RespondToTakeOfferRequestMessage;
import io.bitsquare.user.User;
import java.math.BigInteger;
import java.security.PublicKey;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -66,11 +67,11 @@ public class ProtocolForTakerAsSeller
private final String tradeId;
private final BankAccount bankAccount;
private final String accountId;
private final String messagePubKey;
private final PublicKey messagePublicKey;
private final BigInteger tradeAmount;
private final String pubKeyForThatTrade;
private final ECKey accountKey;
private final String peersMessagePubKey;
private final PublicKey peersMessagePublicKey;
private final BigInteger collateral;
private final String arbitratorPubKey;
@ -122,11 +123,11 @@ public class ProtocolForTakerAsSeller
collateral = trade.getCollateralAmount();
arbitratorPubKey = trade.getOffer().getArbitrator().getPubKeyAsHex();
peersMessagePubKey = offer.getMessagePubKeyAsHex();
peersMessagePublicKey = offer.getMessagePublicKey();
bankAccount = user.getBankAccount(offer.getBankAccountId());
accountId = user.getAccountId();
messagePubKey = user.getMessagePubKeyAsHex();
messagePublicKey = user.getMessagePublicKey();
pubKeyForThatTrade = walletFacade.getAddressInfoByTradeID(tradeId).getPubKeyAsHexString();
accountKey = walletFacade.getRegistrationAddressInfo().getKey();
@ -138,7 +139,7 @@ public class ProtocolForTakerAsSeller
{
log.debug("start called " + step++);
state = State.GetPeerAddress;
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, peersMessagePubKey);
GetPeerAddress.run(this::onResultGetPeerAddress, this::onFault, messageFacade, peersMessagePublicKey);
}
public void onResultGetPeerAddress(PeerAddress peerAddress)
@ -176,6 +177,7 @@ public class ProtocolForTakerAsSeller
else
{
listener.onTakeOfferRequestRejected(trade);
// exit case
}
}
@ -239,8 +241,8 @@ public class ProtocolForTakerAsSeller
takeOfferFeeTxId,
accountId,
bankAccount,
peersMessagePubKey,
messagePubKey,
peersMessagePublicKey,
messagePublicKey,
peersAccountId,
peersBankAccount,
accountKey);
@ -272,7 +274,7 @@ public class ProtocolForTakerAsSeller
walletFacade,
bankAccount,
accountId,
messagePubKey,
messagePublicKey,
tradeId,
contractAsJson,
takerSignature,

View File

@ -3,6 +3,7 @@ package io.bitsquare.trade.protocol.taker;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.msg.TradeMessage;
import java.io.Serializable;
import java.security.PublicKey;
public class RequestOffererPublishDepositTxMessage implements Serializable, TradeMessage
{
@ -10,7 +11,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
private final String tradeId;
private BankAccount bankAccount;
private String accountID;
private String takerMessagePubKey;
private PublicKey takerMessagePublicKey;
private String signedTakerDepositTxAsHex;
private String txScriptSigAsHex;
private String txConnOutAsHex;
@ -25,7 +26,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
public RequestOffererPublishDepositTxMessage(String tradeId,
BankAccount bankAccount,
String accountID,
String takerMessagePubKey,
PublicKey takerMessagePublicKey,
String signedTakerDepositTxAsHex,
String txScriptSigAsHex,
String txConnOutAsHex,
@ -39,7 +40,7 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
this.tradeId = tradeId;
this.bankAccount = bankAccount;
this.accountID = accountID;
this.takerMessagePubKey = takerMessagePubKey;
this.takerMessagePublicKey = takerMessagePublicKey;
this.signedTakerDepositTxAsHex = signedTakerDepositTxAsHex;
this.txScriptSigAsHex = txScriptSigAsHex;
this.txConnOutAsHex = txConnOutAsHex;
@ -72,9 +73,9 @@ public class RequestOffererPublishDepositTxMessage implements Serializable, Trad
return accountID;
}
public String getTakerMessagePubKey()
public PublicKey getTakerMessagePublicKey()
{
return takerMessagePubKey;
return takerMessagePublicKey;
}
public String getSignedTakerDepositTxAsHex()

View File

@ -8,6 +8,7 @@ import io.bitsquare.msg.MessageFacade;
import io.bitsquare.msg.listeners.OutgoingTradeMessageListener;
import io.bitsquare.trade.protocol.FaultHandler;
import io.bitsquare.trade.protocol.ResultHandler;
import java.security.PublicKey;
import net.tomp2p.peers.PeerAddress;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -23,7 +24,7 @@ public class SendSignedTakerDepositTxAsHex
WalletFacade walletFacade,
BankAccount bankAccount,
String accountId,
String messagePubKeyAsHex,
PublicKey messagePublicKey,
String tradeId,
String contractAsJson,
String takerSignature,
@ -36,7 +37,7 @@ public class SendSignedTakerDepositTxAsHex
RequestOffererPublishDepositTxMessage tradeMessage = new RequestOffererPublishDepositTxMessage(tradeId,
bankAccount,
accountId,
messagePubKeyAsHex,
messagePublicKey,
Utils.bytesToHexString(signedTakerDepositTx.bitcoinSerialize()),
Utils.bytesToHexString(signedTakerDepositTx.getInput(1).getScriptBytes()),
Utils.bytesToHexString(signedTakerDepositTx.getInput(1)

View File

@ -1,7 +1,10 @@
package io.bitsquare.user;
import io.bitsquare.bank.BankAccount;
import io.bitsquare.util.DSAKeyUtil;
import java.io.Serializable;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.SimpleBooleanProperty;
@ -11,19 +14,17 @@ public class User implements Serializable
private static final long serialVersionUID = 7409078808248518638L;
transient private final SimpleBooleanProperty bankAccountChangedProperty = new SimpleBooleanProperty();
transient private KeyPair messageKeyPair = DSAKeyUtil.getKeyPair();
private PublicKey messagePublicKey;
private String accountID;
private String messagePubKeyAsHex;
private boolean isOnline;
private List<BankAccount> bankAccounts = new ArrayList<>();
private BankAccount currentBankAccount = null;
public User()
{
messagePublicKey = messageKeyPair.getPublic();
}
@ -36,11 +37,14 @@ public class User implements Serializable
if (savedUser != null)
{
accountID = savedUser.getAccountId();
messagePubKeyAsHex = savedUser.getMessagePubKeyAsHex();
// TODO handled by DSAKeyUtil -> change that storage check is only done here
// messagePublicKey = savedUser.getMessagePublicKey();
isOnline = savedUser.getIsOnline();
bankAccounts = savedUser.getBankAccounts();
currentBankAccount = savedUser.getCurrentBankAccount();
}
messagePublicKey = messageKeyPair.getPublic();
}
public void addBankAccount(BankAccount bankAccount)
@ -94,17 +98,6 @@ public class User implements Serializable
}
public String getMessagePubKeyAsHex()
{
return messagePubKeyAsHex;
}
public void setMessagePubKeyAsHex(String messageID)
{
this.messagePubKeyAsHex = messageID;
}
public String getAccountId()
{
return accountID;
@ -172,17 +165,27 @@ public class User implements Serializable
return bankAccountChangedProperty;
}
@Override
public String toString()
{
return "User{" +
"bankAccountChangedProperty=" + bankAccountChangedProperty +
", messageKeyPair=" + messageKeyPair +
", messagePublicKey=" + messagePublicKey +
", accountID='" + accountID + '\'' +
", messagePubKeyAsHex='" + messagePubKeyAsHex + '\'' +
", isOnline=" + isOnline +
", bankAccounts=" + bankAccounts +
", currentBankAccount=" + currentBankAccount +
'}';
}
public KeyPair getMessageKeyPair()
{
return messageKeyPair;
}
public PublicKey getMessagePublicKey()
{
return messagePublicKey;
}
}

View 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
}

View 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();
}
}
}

View 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();
}
}

View 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();
}
}

View 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;
}
}

View 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

View File

@ -22,25 +22,30 @@
<appender-ref ref="FILE"/>
</root>
<logger name="io.bitsquare" level="TRACE"/>
<logger name="io.bitsquare" level="DEBUG"/>
<logger name="com.google.bitcoin" level="WARN"/>
<logger name="net.tomp2p" level="WARN"/>
<logger name="io.netty.util" level="WARN"/>
<logger name="io.netty.channel" level="WARN"/>
<logger name="io.netty.buffer" level="WARN"/>
<logger name="com.google.bitcoin" level="INFO"/>
<logger name="net.tomp2p" level="INFO"/>
<!--
-->
<logger name="com.google.bitcoin.core.Wallet" level="INFO"/>
<logger name="com.google.bitcoin.core.MemoryPool" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.net.discovery.DnsDiscovery" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.DownloadListener" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.TransactionOutput" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.BitcoinSerializer" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.Peer" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerGroup" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerSocketHandler" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.net.NioClientManager" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.net.ConnectionHandler" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.Wallet" level="INFO"/>
<logger name="com.google.bitcoin.core.MemoryPool" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.net.discovery.DnsDiscovery" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.DownloadListener" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.TransactionOutput" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.BitcoinSerializer" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.Peer" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerGroup" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.core.PeerSocketHandler" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.net.NioClientManager" level="WARN" additivity="false"/>
<logger name="com.google.bitcoin.net.ConnectionHandler" level="WARN" additivity="false"/>
-->
</configuration>

View 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
{
}

View 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));
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}

View 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();
}
}

View 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);
}
}

View File

@ -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));
}
}

View 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!
}