diff --git a/.gitignore b/.gitignore index 98aa482d23d..4c1c83ee6f9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ bitsquare.iml *.wallet *.ser *.wallet + +*.log diff --git a/README.md b/README.md index f996bfdea03..e0286edf296 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,26 @@ # bitsquare.io -Bitsquare is a P2P Fiat-BTC Exchange, extensible to a generic P2P trading platform (include commodities and cryptocurrencies) +Bitsquare is a P2P Fiat-BTC Exchange. The project use Java 8 and Maven. -We use the bitcoinj library and TomP2P for DHT and messaging. +We use the bitcoinj library and TomP2P for DHT and direct messaging. +For local testing it is best to use the regtest mode from Bitcoin qt clients. If you want to use the RegTest mode you need to set regtest=1 in the bitcoin.config file inside the bitcoin data directory (https://en.bitcoin.it/wiki/Running_Bitcoin). Then you can generate coins on demand with the Bitcoin qt client with that command in the console: setgenerate true 101 (101 only for the first start because the coin maturity of 100 blocks). -See: https://bitcoinj.github.io/testing +More information about how to use regtest mode can be found here: https://bitcoinj.github.io/testing +Take care if you have real bitcoin in your Bitcoin qt wallet (backup and copy first your data directory). You can change the network mode in the guice module: BitSquareModule.java +Testnet should also work, but was not tested a while now as for developing regtest is much more convenient. +Please don't use main net with real money, as the software is under heavy development and you can easily lose your funds. + +We use a fork of the actual TomP2P master branch: https://github.com/bitsquare/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 -* Whitepaper: https://docs.google.com/document/d/1d3EiWZdaM89-P6MVhS53unXv2-pDpSFsN3W4kCGXKgY/edit?pli=1 -* Overview: http://bitsquare.io/images/overview.png -* Discussion: https://bitcointalk.org/index.php?topic=647457 -* Video of POC prototype: https://www.youtube.com/watch?v=ByfnzJzi0bo ### Screenshots of basic the use cases: @@ -33,9 +37,9 @@ You can change the network mode in the guice module: BitSquareModule.java ### Transactions of a test trade on main net: -Offerer registration tx: https://blockchain.info/de/tx/06ea3c2a5fb79f622d3e3def7c6a20274274fcbf9ec69b95bdfe9b347bbbdf76 -Taker registration tx: https://blockchain.info/tx/8352ab9fe78593f48ef70d414d494ebd614d99fab147d0342910525e9284ba8f -Create offer fee tx: https://blockchain.info/tx/24f4d229edace44d9123628363a16cd7041f5d34ba6bef812807b9be03a64692 -Take offer fee tx: https://blockchain.info/tx/06ea3c2a5fb79f622d3e3def7c6a20274274fcbf9ec69b95bdfe9b347bbbdf76 -Deposit tx: https://blockchain.info/de/tx/98c6ae55963022871216a6a124c1e1ed7f6308560e76b72617b6b54cf50ef412 -Payout tx: https://blockchain.info/tx/498e2c299ca991b27f61b63fb6ee457819ee9e33ee5a1d250fde47eb15199adc +* [Offerer registration tx](https://blockchain.info/de/tx/06ea3c2a5fb79f622d3e3def7c6a20274274fcbf9ec69b95bdfe9b347bbbdf76) +* [Taker registration tx](https://blockchain.info/tx/8352ab9fe78593f48ef70d414d494ebd614d99fab147d0342910525e9284ba8f) +* [Create offer fee tx](https://blockchain.info/tx/24f4d229edace44d9123628363a16cd7041f5d34ba6bef812807b9be03a64692) +* [Take offer fee tx](https://blockchain.info/tx/06ea3c2a5fb79f622d3e3def7c6a20274274fcbf9ec69b95bdfe9b347bbbdf76) +* [Deposit tx](https://blockchain.info/de/tx/98c6ae55963022871216a6a124c1e1ed7f6308560e76b72617b6b54cf50ef412) +* [Payout tx](https://blockchain.info/tx/498e2c299ca991b27f61b63fb6ee457819ee9e33ee5a1d250fde47eb15199adc) diff --git a/bitsquare.ipr b/bitsquare.ipr new file mode 100644 index 00000000000..8888f5b32d4 --- /dev/null +++ b/bitsquare.ipr @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bitsquare.iws b/bitsquare.iws new file mode 100644 index 00000000000..03c854e9833 --- /dev/null +++ b/bitsquare.iws @@ -0,0 +1,418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml index 34e2fb7294b..6a6d28025b5 100644 --- a/pom.xml +++ b/pom.xml @@ -6,10 +6,10 @@ io.bitsquare bitsquare - 0.01-SNAPSHOT + 0.1-SNAPSHOT BitSquare - A P2P Fiat-Bitcoin Exchange - https://www.bitsquare.io + The P2P Fiat-Bitcoin Exchange + http://www.bitsquare.io bitsquare.io @@ -53,13 +53,11 @@ https://oss.sonatype.org/content/groups/public - bitcoinj-release http://distribution.bitcoinj.googlecode.com/git/releases - mvn-adamgent http://mvn-adamgent.googlecode.com/svn/maven/release @@ -73,7 +71,6 @@ - bitsquare @@ -109,10 +106,15 @@ io.bitsquare.BitSquare + + + org.twdata.maven + mojo-executor + 2.1.0 + + - - @@ -126,8 +128,8 @@ net.tomp2p - TomP2P - 4.4 + tomp2p-all + 5.0-Alpha24-SNAPSHOT @@ -142,13 +144,7 @@ slf4j-api 1.7.7 - + ch.qos.logback logback-core @@ -161,7 +157,6 @@ compile - com.google.inject guice @@ -210,32 +205,19 @@ jcip-annotations 1.0 - org.jetbrains annotations 13.0 - - - - - - - + + com.typesafe.akka + akka-actor_2.10 + 2.3.4 + + UTF-8 diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF index de29527d5cd..4a577d5aa64 100644 --- a/src/main/java/META-INF/MANIFEST.MF +++ b/src/main/java/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ Manifest-Version: 1.0 -Main-Class: io.bitsquare.Relay +Main-Class: io.bitsquare.BootstrapNode_ diff --git a/src/main/java/io/bitsquare/BitSquare.java b/src/main/java/io/bitsquare/BitSquare.java index 559a855de84..b2bc1b4cb1b 100644 --- a/src/main/java/io/bitsquare/BitSquare.java +++ b/src/main/java/io/bitsquare/BitSquare.java @@ -1,5 +1,6 @@ package io.bitsquare; +import akka.actor.ActorSystem; import com.google.common.base.Throwables; import com.google.inject.Guice; import com.google.inject.Injector; @@ -11,7 +12,7 @@ import io.bitsquare.locale.Localisation; import io.bitsquare.msg.MessageFacade; import io.bitsquare.settings.Settings; -import io.bitsquare.storage.Storage; +import io.bitsquare.storage.Persistence; import io.bitsquare.user.User; import io.bitsquare.util.AWTSystemTray; import io.bitsquare.util.FileUtil; @@ -33,17 +34,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; - 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; private WalletFacade walletFacade; private MessageFacade messageFacade; + private final ActorSystem system = ActorSystem.create(APP_NAME); public static void main(String[] args) { @@ -74,6 +75,9 @@ public void start(Stage primaryStage) throws IOException log.trace("Startup: start"); BitSquare.primaryStage = primaryStage; + //log.trace("Startup: setupAkka"); + //setupAkka(); + Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> Popups.handleUncaughtExceptions(Throwables.getRootCause(throwable))); StorageDirectory.setStorageDirectory(new File(StorageDirectory.getApplicationDirectory().getCanonicalPath() + "/data")); @@ -90,10 +94,14 @@ public void start(Stage primaryStage) throws IOException // apply stored data final User user = injector.getInstance(User.class); final Settings settings = injector.getInstance(Settings.class); - final Storage storage = injector.getInstance(Storage.class); - storage.init(); - user.updateFromStorage((User) storage.read(user.getClass().getName())); - settings.updateFromStorage((Settings) storage.read(settings.getClass().getName())); + final Persistence persistence = injector.getInstance(Persistence.class); + persistence.init(); + + User persistedUser = (User) persistence.read(user); + user.applyPersistedUser(persistedUser); + persistence.write(user); + + settings.applyPersistedSettings((Settings) persistence.read(settings.getClass().getName())); primaryStage.setTitle("BitSquare (" + getUID() + ")"); @@ -111,10 +119,10 @@ public void start(Stage primaryStage) throws IOException setupCloseHandlers(primaryStage, scene); primaryStage.setScene(scene); - primaryStage.setMinWidth(800); - primaryStage.setMinHeight(400); - primaryStage.setWidth(800); - primaryStage.setHeight(600); + primaryStage.setMinWidth(750); + primaryStage.setMinHeight(500); + primaryStage.setWidth(1000); + primaryStage.setHeight(750); primaryStage.show(); @@ -131,6 +139,12 @@ private void setupCloseHandlers(Stage primaryStage, Scene scene) }); } + private void setupAkka() + { + log.trace("SetupAkka: create actors"); + + } + private MenuBar getMenuBar() { MenuBar menuBar = new MenuBar(); @@ -156,7 +170,6 @@ private MenuBar getMenuBar() return menuBar; } - @Override public void stop() throws Exception { diff --git a/src/main/java/io/bitsquare/Relay.java b/src/main/java/io/bitsquare/Relay.java deleted file mode 100644 index 5e0e0521dfe..00000000000 --- a/src/main/java/io/bitsquare/Relay.java +++ /dev/null @@ -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); - } - }); - } - } - -} diff --git a/src/main/java/io/bitsquare/SeedNode.java b/src/main/java/io/bitsquare/SeedNode.java new file mode 100644 index 00000000000..eb035c5001c --- /dev/null +++ b/src/main/java/io/bitsquare/SeedNode.java @@ -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. + *

+ * 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 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() + { + @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() + { + @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); + } + } + +} diff --git a/src/main/java/io/bitsquare/bank/BankAccount.java b/src/main/java/io/bitsquare/bank/BankAccount.java index 428ee3184e7..1eb9a929a6a 100644 --- a/src/main/java/io/bitsquare/bank/BankAccount.java +++ b/src/main/java/io/bitsquare/bank/BankAccount.java @@ -4,26 +4,21 @@ import java.io.Serializable; import java.util.Currency; import java.util.Objects; +import javax.annotation.concurrent.Immutable; +@Immutable public class BankAccount implements Serializable { private static final long serialVersionUID = 1792577576443221268L; - private final BankAccountType bankAccountType; - - private final String accountPrimaryID; - - private final String accountSecondaryID; - + private final String accountPrimaryID; // like IBAN + private final String accountSecondaryID; // like BIC private final String accountHolderName; - - private final Country country; - + private final Country country; // where bank is registered + // The main currency if account support multiple currencies. + // The user can create multiple bank accounts with same bank account but other currency if his bank account support that. private final Currency currency; - - private final String uid; - private final String accountTitle; public BankAccount(BankAccountType bankAccountType, Currency currency, Country country, String accountTitle, String accountHolderName, String accountPrimaryID, String accountSecondaryID) @@ -35,28 +30,20 @@ public BankAccount(BankAccountType bankAccountType, Currency currency, Country c this.accountHolderName = accountHolderName; this.accountPrimaryID = accountPrimaryID; this.accountSecondaryID = accountSecondaryID; - - uid = accountTitle; } public int hashCode() { - return Objects.hashCode(uid); + return Objects.hashCode(accountTitle); } public boolean equals(Object obj) { - if (!(obj instanceof BankAccount)) - { - return false; - } - if (obj == this) - { - return true; - } + if (!(obj instanceof BankAccount)) return false; + if (obj == this) return true; final BankAccount other = (BankAccount) obj; - return uid.equals(other.getUid()); + return accountTitle.equals(other.getUid()); } @@ -65,49 +52,42 @@ public String getAccountPrimaryID() return accountPrimaryID; } - public String getAccountSecondaryID() { return accountSecondaryID; } - public String getAccountHolderName() { return accountHolderName; } - public BankAccountType getBankAccountType() { return bankAccountType; } - public Currency getCurrency() { return currency; } - public Country getCountry() { return country; } - + // we use the accountTitle as unique id public String getUid() { - return uid; + return accountTitle; } - public String getAccountTitle() { return accountTitle; } - @Override public String toString() { @@ -116,11 +96,9 @@ public String toString() ", accountPrimaryID='" + accountPrimaryID + '\'' + ", accountSecondaryID='" + accountSecondaryID + '\'' + ", accountHolderName='" + accountHolderName + '\'' + - ", countryLocale=" + country + + ", country=" + country + ", currency=" + currency + - ", uid='" + uid + '\'' + ", accountTitle='" + accountTitle + '\'' + '}'; } - } diff --git a/src/main/java/io/bitsquare/bank/BankAccountType.java b/src/main/java/io/bitsquare/bank/BankAccountType.java index 4df67a36a21..59b59275297 100644 --- a/src/main/java/io/bitsquare/bank/BankAccountType.java +++ b/src/main/java/io/bitsquare/bank/BankAccountType.java @@ -13,9 +13,7 @@ public enum BankAccountType PERFECT_MONEY("primary ID", "secondary ID"), OTHER("primary ID", "secondary ID"); - private final String primaryId; - private final String secondaryId; BankAccountType(String primaryId, String secondaryId) @@ -24,19 +22,16 @@ public enum BankAccountType this.secondaryId = secondaryId; } - public static ArrayList getAllBankAccountTypes() { return new ArrayList<>(Arrays.asList(BankAccountType.values())); } - public String getPrimaryId() { return primaryId; } - public String getSecondaryId() { return secondaryId; diff --git a/src/main/java/io/bitsquare/btc/BitSquareWallet.java b/src/main/java/io/bitsquare/btc/BitSquareWallet.java index 9f35712e2e2..cbcc1d4621d 100644 --- a/src/main/java/io/bitsquare/btc/BitSquareWallet.java +++ b/src/main/java/io/bitsquare/btc/BitSquareWallet.java @@ -22,5 +22,4 @@ private BitSquareWallet(NetworkParameters params, KeyCrypter keyCrypter) super(params, keyCrypter); } - } diff --git a/src/main/java/io/bitsquare/btc/WalletFacade.java b/src/main/java/io/bitsquare/btc/WalletFacade.java index 31312cbb3ca..aa7a4e22ae9 100644 --- a/src/main/java/io/bitsquare/btc/WalletFacade.java +++ b/src/main/java/io/bitsquare/btc/WalletFacade.java @@ -17,7 +17,7 @@ import io.bitsquare.btc.listeners.BalanceListener; import io.bitsquare.btc.listeners.ConfidenceListener; import io.bitsquare.crypto.CryptoFacade; -import io.bitsquare.storage.Storage; +import io.bitsquare.storage.Persistence; import java.io.Serializable; import java.math.BigInteger; import java.util.*; @@ -48,12 +48,11 @@ public class WalletFacade private final ReentrantLock lock = Threading.lock("lock"); - private final String saveAddressEntryListId; private final NetworkParameters params; private final BitSquareWalletAppKit walletAppKit; private final FeePolicy feePolicy; private final CryptoFacade cryptoFacade; - private final Storage storage; + private final Persistence persistence; private final List downloadListeners = new ArrayList<>(); private final List confidenceListeners = new ArrayList<>(); private final List balanceListeners = new ArrayList<>(); @@ -68,15 +67,13 @@ public class WalletFacade /////////////////////////////////////////////////////////////////////////////////////////// @Inject - public WalletFacade(NetworkParameters params, BitSquareWalletAppKit walletAppKit, FeePolicy feePolicy, CryptoFacade cryptoFacade, Storage storage) + public WalletFacade(NetworkParameters params, BitSquareWalletAppKit walletAppKit, FeePolicy feePolicy, CryptoFacade cryptoFacade, Persistence persistence) { this.params = params; this.walletAppKit = walletAppKit; this.feePolicy = feePolicy; this.cryptoFacade = cryptoFacade; - this.storage = storage; - - saveAddressEntryListId = this.getClass().getName() + ".addressEntryList"; + this.persistence = persistence; } @@ -173,11 +170,11 @@ public void onScriptsAdded(Wallet wallet, List